<commit_before>/*
Copyright 2019 New Vector Ltd
Copyright 2019 The Matrix.org Foundation C.I.C.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import AutocompleteWrapperModel from "./autocomplete";
import Avatar from "../Avatar";
import MatrixClientPeg from "../MatrixClientPeg";

class BasePart {
    constructor(text = "") {
        this._text = text;
    }

    acceptsInsertion(chr) {
        return true;
    }

    acceptsRemoval(position, chr) {
        return true;
    }

    merge(part) {
        return false;
    }

    split(offset) {
        const splitText = this.text.substr(offset);
        this._text = this.text.substr(0, offset);
        return new PlainPart(splitText);
    }

    // removes len chars, or returns the plain text this part should be replaced with
    // if the part would become invalid if it removed everything.
    remove(offset, len) {
        // validate
        const strWithRemoval = this.text.substr(0, offset) + this.text.substr(offset + len);
        for (let i = offset; i < (len + offset); ++i) {
            const chr = this.text.charAt(i);
            if (!this.acceptsRemoval(i, chr)) {
                return strWithRemoval;
            }
        }
        this._text = strWithRemoval;
    }

    // append str, returns the remaining string if a character was rejected.
    appendUntilRejected(str) {
        for (let i = 0; i < str.length; ++i) {
            const chr = str.charAt(i);
            if (!this.acceptsInsertion(chr, i)) {
                this._text = this._text + str.substr(0, i);
                return str.substr(i);
            }
        }
        this._text = this._text + str;
    }

    // inserts str at offset if all the characters in str were accepted, otherwise don't do anything
    // return whether the str was accepted or not.
    insertAll(offset, str) {
        for (let i = 0; i < str.length; ++i) {
            const chr = str.charAt(i);
            if (!this.acceptsInsertion(chr)) {
                return false;
            }
        }
        const beforeInsert = this._text.substr(0, offset);
        const afterInsert = this._text.substr(offset);
        this._text = beforeInsert + str + afterInsert;
        return true;
    }

    createAutoComplete() {}

    trim(len) {
        const remaining = this._text.substr(len);
        this._text = this._text.substr(0, len);
        return remaining;
    }

    get text() {
        return this._text;
    }

    get canEdit() {
        return true;
    }

    toString() {
        return `${this.type}(${this.text})`;
    }

    serialize() {
        return {type: this.type, text: this.text};
    }
}

export class PlainPart extends BasePart {
    acceptsInsertion(chr) {
        return chr !== "@" && chr !== "#" && chr !== ":" && chr !== "\n";
    }

    toDOMNode() {
        return document.createTextNode(this.text);
    }

    merge(part) {
        if (part.type === this.type) {
            this._text = this.text + part.text;
            return true;
        }
        return false;
    }

    get type() {
        return "plain";
    }

    updateDOMNode(node) {
        if (node.textContent !== this.text) {
            // console.log("changing plain text from", node.textContent, "to", this.text);
            node.textContent = this.text;
        }
    }

    canUpdateDOMNode(node) {
        return node.nodeType === Node.TEXT_NODE;
    }
}

class PillPart extends BasePart {
    constructor(resourceId, label) {
        super(label);
        this.resourceId = resourceId;
    }

    acceptsInsertion(chr) {
        return chr !== " ";
    }

    acceptsRemoval(position, chr) {
        return position !== 0;  //if you remove initial # or @, pill should become plain
    }

    toDOMNode() {
        const container = document.createElement("span");
        container.className = this.className;
        container.appendChild(document.createTextNode(this.text));
        this.setAvatar(container);
        return container;
    }

    updateDOMNode(node) {
        const textNode = node.childNodes[0];
        if (textNode.textContent !== this.text) {
            textNode.textContent = this.text;
        }
        if (node.className !== this.className) {
            node.className = this.className;
        }
        this.setAvatar(node);
    }

    canUpdateDOMNode(node) {
        return node.nodeType === Node.ELEMENT_NODE &&
               node.nodeName === "SPAN" &&
               node.childNodes.length === 1 &&
               node.childNodes[0].nodeType === Node.TEXT_NODE;
    }

    // helper method for subclasses
    _setAvatarVars(node, avatarUrl, initialLetter) {
        const avatarBackground = `url('${avatarUrl}')`;
        const avatarLetter = `'${initialLetter}'`;
        // check if the value is changing,
        // otherwise the avatars flicker on every keystroke while updating.
        if (node.style.getPropertyValue("--avatar-background") !== avatarBackground) {
            node.style.setProperty("--avatar-background", avatarBackground);
        }
        if (node.style.getPropertyValue("--avatar-letter") !== avatarLetter) {
            node.style.setProperty("--avatar-letter", avatarLetter);
        }
    }

    get canEdit() {
        return false;
    }
}

export class NewlinePart extends BasePart {
    acceptsInsertion(chr, i) {
        return (this.text.length + i) === 0 && chr === "\n";
    }

    acceptsRemoval(position, chr) {
        return true;
    }

    toDOMNode() {
        return document.createElement("br");
    }

    merge() {
        return false;
    }

    updateDOMNode() {}

    canUpdateDOMNode(node) {
        return node.tagName === "BR";
    }

    get type() {
        return "newline";
    }

    // this makes the cursor skip this part when it is inserted
    // rather than trying to append to it, which is what we want.
    // As a newline can also be only one character, it makes sense
    // as it can only be one character long. This caused #9741.
    get canEdit() {
        return false;
    }
}

export class RoomPillPart extends PillPart {
    constructor(displayAlias, client) {
        super(displayAlias, displayAlias);
        this._room = this._findRoomByAlias(displayAlias, client);
    }

    _findRoomByAlias(alias, client) {
        if (alias[0] === '#') {
            return client.getRooms().find((r) => {
                return r.getAliases().includes(alias);
            });
        } else {
            return client.getRoom(alias);
        }
    }

    setAvatar(node) {
        let initialLetter = "";
        let avatarUrl = Avatar.avatarUrlForRoom(this._room, 16 * window.devicePixelRatio, 16 * window.devicePixelRatio);
        if (!avatarUrl) {
            initialLetter = Avatar.getInitialLetter(this._room.name);
            avatarUrl = `../../${Avatar.defaultAvatarUrlForString(this._room.roomId)}`;
        }
        this._setAvatarVars(node, avatarUrl, initialLetter);
    }

    get type() {
        return "room-pill";
    }

    get className() {
        return "mx_RoomPill mx_Pill";
    }
}

export class UserPillPart extends PillPart {
    constructor(userId, displayName, member) {
        super(userId, displayName);
        this._member = member;
    }

    setAvatar(node) {
        const name = this._member.name || this._member.userId;
        const defaultAvatarUrl = Avatar.defaultAvatarUrlForString(this._member.userId);
        let avatarUrl = Avatar.avatarUrlForMember(
            this._member,
            16 * window.devicePixelRatio,
            16 * window.devicePixelRatio);
        let initialLetter = "";
        if (avatarUrl === defaultAvatarUrl) {
            // the url from defaultAvatarUrlForString is meant to go in an img element,
            // which has the base of the document. we're using it in css,
            // which has the base of the theme css file, two levels deeper than the document,
            // so go up to the level of the document.
            avatarUrl = `../../${avatarUrl}`;
            initialLetter = Avatar.getInitialLetter(name);
        }
        this._setAvatarVars(node, avatarUrl, initialLetter);
    }

    get type() {
        return "user-pill";
    }

    get className() {
        return "mx_UserPill mx_Pill";
    }

    serialize() {
        const obj = super.serialize();
        obj.userId = this.resourceId;
        return obj;
    }
}

export class PillCandidatePart extends PlainPart {
    constructor(text, autoCompleteCreator) {
        super(text);
        this._autoCompleteCreator = autoCompleteCreator;
    }

    createAutoComplete(updateCallback) {
        return this._autoCompleteCreator(updateCallback);
    }

    acceptsInsertion(chr, i) {
        if ((this.text.length + i) === 0) {
            return true;
        } else {
            return super.acceptsInsertion(chr, i);
        }
    }

    merge() {
        return false;
    }

    acceptsRemoval(position, chr) {
        return true;
    }

    get type() {
        return "pill-candidate";
    }
}

export class PartCreator {
    constructor(getAutocompleterComponent, updateQuery, room, client) {
        this._room = room;
        this._client = client;
        this._autoCompleteCreator = (updateCallback) => {
            return new AutocompleteWrapperModel(
                updateCallback,
                getAutocompleterComponent,
                updateQuery,
                room,
                client,
            );
        };
    }

    createPartForInput(input) {
        switch (input[0]) {
            case "#":
            case "@":
            case ":":
                return new PillCandidatePart("", this._autoCompleteCreator);
            case "\n":
                return new NewlinePart();
            default:
                return new PlainPart();
        }
    }

    createDefaultPart(text) {
        return new PlainPart(text);
    }

    deserializePart(part) {
        switch (part.type) {
            case "plain":
                return new PlainPart(part.text);
            case "newline":
                return new NewlinePart(part.text);
            case "pill-candidate":
                return new PillCandidatePart(part.text, this._autoCompleteCreator);
            case "room-pill":
                return new RoomPillPart(part.text, this._client);
            case "user-pill": {
                const member = this._room.getMember(part.userId);
                return new UserPillPart(part.userId, part.text, member);
            }
        }
    }
}

<commit_msg>fix lint<commit_after>/*
Copyright 2019 New Vector Ltd
Copyright 2019 The Matrix.org Foundation C.I.C.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import AutocompleteWrapperModel from "./autocomplete";
import Avatar from "../Avatar";

class BasePart {
    constructor(text = "") {
        this._text = text;
    }

    acceptsInsertion(chr) {
        return true;
    }

    acceptsRemoval(position, chr) {
        return true;
    }

    merge(part) {
        return false;
    }

    split(offset) {
        const splitText = this.text.substr(offset);
        this._text = this.text.substr(0, offset);
        return new PlainPart(splitText);
    }

    // removes len chars, or returns the plain text this part should be replaced with
    // if the part would become invalid if it removed everything.
    remove(offset, len) {
        // validate
        const strWithRemoval = this.text.substr(0, offset) + this.text.substr(offset + len);
        for (let i = offset; i < (len + offset); ++i) {
            const chr = this.text.charAt(i);
            if (!this.acceptsRemoval(i, chr)) {
                return strWithRemoval;
            }
        }
        this._text = strWithRemoval;
    }

    // append str, returns the remaining string if a character was rejected.
    appendUntilRejected(str) {
        for (let i = 0; i < str.length; ++i) {
            const chr = str.charAt(i);
            if (!this.acceptsInsertion(chr, i)) {
                this._text = this._text + str.substr(0, i);
                return str.substr(i);
            }
        }
        this._text = this._text + str;
    }

    // inserts str at offset if all the characters in str were accepted, otherwise don't do anything
    // return whether the str was accepted or not.
    insertAll(offset, str) {
        for (let i = 0; i < str.length; ++i) {
            const chr = str.charAt(i);
            if (!this.acceptsInsertion(chr)) {
                return false;
            }
        }
        const beforeInsert = this._text.substr(0, offset);
        const afterInsert = this._text.substr(offset);
        this._text = beforeInsert + str + afterInsert;
        return true;
    }

    createAutoComplete() {}

    trim(len) {
        const remaining = this._text.substr(len);
        this._text = this._text.substr(0, len);
        return remaining;
    }

    get text() {
        return this._text;
    }

    get canEdit() {
        return true;
    }

    toString() {
        return `${this.type}(${this.text})`;
    }

    serialize() {
        return {type: this.type, text: this.text};
    }
}

export class PlainPart extends BasePart {
    acceptsInsertion(chr) {
        return chr !== "@" && chr !== "#" && chr !== ":" && chr !== "\n";
    }

    toDOMNode() {
        return document.createTextNode(this.text);
    }

    merge(part) {
        if (part.type === this.type) {
            this._text = this.text + part.text;
            return true;
        }
        return false;
    }

    get type() {
        return "plain";
    }

    updateDOMNode(node) {
        if (node.textContent !== this.text) {
            // console.log("changing plain text from", node.textContent, "to", this.text);
            node.textContent = this.text;
        }
    }

    canUpdateDOMNode(node) {
        return node.nodeType === Node.TEXT_NODE;
    }
}

class PillPart extends BasePart {
    constructor(resourceId, label) {
        super(label);
        this.resourceId = resourceId;
    }

    acceptsInsertion(chr) {
        return chr !== " ";
    }

    acceptsRemoval(position, chr) {
        return position !== 0;  //if you remove initial # or @, pill should become plain
    }

    toDOMNode() {
        const container = document.createElement("span");
        container.className = this.className;
        container.appendChild(document.createTextNode(this.text));
        this.setAvatar(container);
        return container;
    }

    updateDOMNode(node) {
        const textNode = node.childNodes[0];
        if (textNode.textContent !== this.text) {
            textNode.textContent = this.text;
        }
        if (node.className !== this.className) {
            node.className = this.className;
        }
        this.setAvatar(node);
    }

    canUpdateDOMNode(node) {
        return node.nodeType === Node.ELEMENT_NODE &&
               node.nodeName === "SPAN" &&
               node.childNodes.length === 1 &&
               node.childNodes[0].nodeType === Node.TEXT_NODE;
    }

    // helper method for subclasses
    _setAvatarVars(node, avatarUrl, initialLetter) {
        const avatarBackground = `url('${avatarUrl}')`;
        const avatarLetter = `'${initialLetter}'`;
        // check if the value is changing,
        // otherwise the avatars flicker on every keystroke while updating.
        if (node.style.getPropertyValue("--avatar-background") !== avatarBackground) {
            node.style.setProperty("--avatar-background", avatarBackground);
        }
        if (node.style.getPropertyValue("--avatar-letter") !== avatarLetter) {
            node.style.setProperty("--avatar-letter", avatarLetter);
        }
    }

    get canEdit() {
        return false;
    }
}

export class NewlinePart extends BasePart {
    acceptsInsertion(chr, i) {
        return (this.text.length + i) === 0 && chr === "\n";
    }

    acceptsRemoval(position, chr) {
        return true;
    }

    toDOMNode() {
        return document.createElement("br");
    }

    merge() {
        return false;
    }

    updateDOMNode() {}

    canUpdateDOMNode(node) {
        return node.tagName === "BR";
    }

    get type() {
        return "newline";
    }

    // this makes the cursor skip this part when it is inserted
    // rather than trying to append to it, which is what we want.
    // As a newline can also be only one character, it makes sense
    // as it can only be one character long. This caused #9741.
    get canEdit() {
        return false;
    }
}

export class RoomPillPart extends PillPart {
    constructor(displayAlias, client) {
        super(displayAlias, displayAlias);
        this._room = this._findRoomByAlias(displayAlias, client);
    }

    _findRoomByAlias(alias, client) {
        if (alias[0] === '#') {
            return client.getRooms().find((r) => {
                return r.getAliases().includes(alias);
            });
        } else {
            return client.getRoom(alias);
        }
    }

    setAvatar(node) {
        let initialLetter = "";
        let avatarUrl = Avatar.avatarUrlForRoom(this._room, 16 * window.devicePixelRatio, 16 * window.devicePixelRatio);
        if (!avatarUrl) {
            initialLetter = Avatar.getInitialLetter(this._room.name);
            avatarUrl = `../../${Avatar.defaultAvatarUrlForString(this._room.roomId)}`;
        }
        this._setAvatarVars(node, avatarUrl, initialLetter);
    }

    get type() {
        return "room-pill";
    }

    get className() {
        return "mx_RoomPill mx_Pill";
    }
}

export class UserPillPart extends PillPart {
    constructor(userId, displayName, member) {
        super(userId, displayName);
        this._member = member;
    }

    setAvatar(node) {
        const name = this._member.name || this._member.userId;
        const defaultAvatarUrl = Avatar.defaultAvatarUrlForString(this._member.userId);
        let avatarUrl = Avatar.avatarUrlForMember(
            this._member,
            16 * window.devicePixelRatio,
            16 * window.devicePixelRatio);
        let initialLetter = "";
        if (avatarUrl === defaultAvatarUrl) {
            // the url from defaultAvatarUrlForString is meant to go in an img element,
            // which has the base of the document. we're using it in css,
            // which has the base of the theme css file, two levels deeper than the document,
            // so go up to the level of the document.
            avatarUrl = `../../${avatarUrl}`;
            initialLetter = Avatar.getInitialLetter(name);
        }
        this._setAvatarVars(node, avatarUrl, initialLetter);
    }

    get type() {
        return "user-pill";
    }

    get className() {
        return "mx_UserPill mx_Pill";
    }

    serialize() {
        const obj = super.serialize();
        obj.userId = this.resourceId;
        return obj;
    }
}

export class PillCandidatePart extends PlainPart {
    constructor(text, autoCompleteCreator) {
        super(text);
        this._autoCompleteCreator = autoCompleteCreator;
    }

    createAutoComplete(updateCallback) {
        return this._autoCompleteCreator(updateCallback);
    }

    acceptsInsertion(chr, i) {
        if ((this.text.length + i) === 0) {
            return true;
        } else {
            return super.acceptsInsertion(chr, i);
        }
    }

    merge() {
        return false;
    }

    acceptsRemoval(position, chr) {
        return true;
    }

    get type() {
        return "pill-candidate";
    }
}

export class PartCreator {
    constructor(getAutocompleterComponent, updateQuery, room, client) {
        this._room = room;
        this._client = client;
        this._autoCompleteCreator = (updateCallback) => {
            return new AutocompleteWrapperModel(
                updateCallback,
                getAutocompleterComponent,
                updateQuery,
                room,
                client,
            );
        };
    }

    createPartForInput(input) {
        switch (input[0]) {
            case "#":
            case "@":
            case ":":
                return new PillCandidatePart("", this._autoCompleteCreator);
            case "\n":
                return new NewlinePart();
            default:
                return new PlainPart();
        }
    }

    createDefaultPart(text) {
        return new PlainPart(text);
    }

    deserializePart(part) {
        switch (part.type) {
            case "plain":
                return new PlainPart(part.text);
            case "newline":
                return new NewlinePart(part.text);
            case "pill-candidate":
                return new PillCandidatePart(part.text, this._autoCompleteCreator);
            case "room-pill":
                return new RoomPillPart(part.text, this._client);
            case "user-pill": {
                const member = this._room.getMember(part.userId);
                return new UserPillPart(part.userId, part.text, member);
            }
        }
    }
}

<|endoftext|>


<commit_before>/*
 * Copyright (c) 2014, Wanadev <http://www.wanadev.fr/>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *   * Redistributions of source code must retain the above copyright notice, this
 *     list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above copyright notice,
 *     this list of conditions and the following disclaimer in the documentation
 *     and/or other materials provided with the distribution.
 *   * Neither the name of Wanadev nor the names of its contributors may be used
 *     to endorse or promote products derived from this software without specific
 *     prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authored by: Fabien LOISON <http://flozz.fr/>
 */

/**
 * PhotonUI - Javascript Web User Interface.
 *
 * @module PhotonUI
 * @submodule Input
 * @namespace photonui
 */

var photonui = photonui || {};

/**
 * Numeric field.
 *
 * @class NumericField
 * @constructor
 * @extends photonui.Field
 */
photonui.NumericField = photonui.Field.$extend({

    // Constructor
    __init__: function(params) {
        this.$super(params);
        this._updateProperties(["value"]);
        this._bindFieldEvents();
        this._bindEvent("keypress", this.__html.field, "keypress", this.__onKeypress.bind(this));
        this._bindEvent("keyup", this.__html.field, "keyup", this.__onKeyup.bind(this));
        this._bindEvent("keydown", this.__html.field, "keydown", this.__onKeydown.bind(this));
        this._bindEvent("change", this.__html.field, "change", this.__onChange.bind(this));
        this._bindEvent("mousewheel", this.__html.field, "mousewheel", this.__onMouseWheel.bind(this));
        this._bindEvent("mousewheel-firefox", this.__html.field, "DOMMouseScroll", this.__onMouseWheel.bind(this));
    },

    //////////////////////////////////////////
    // Properties and Accessors             //
    //////////////////////////////////////////

    // ====== Public properties ======

    /**
     * The minimum value of the field.
     *
     * @property min
     * @type Number
     * default null (no minimum);
     */
    _min: null,

    getMin: function() {
        return this._min;
    },

    setMin: function(min) {
        this._min = min;
    },

    /**
     * The maximum value of the field.
     *
     * @property max
     * @type Number
     * default null (no maximum);
     */
    _max: null,

    getMax: function() {
        return this._max;
    },

    setMax: function(max) {
        this._max = max;
    },

    /**
     * The incrementation step of the field.
     *
     * @property step
     * @type Number
     * default 1
     */
    _step: 1,

    getStep: function() {
        return this._step;
    },

    setStep: function(step) {
        this._step = Math.abs(step);
    },

    /**
     * The number of digit after the decimal dot.
     *
     * @property decimalDigits
     * @type Number
     * @default null (no limite)
     */
    _decimalDigits: null,

    getDecimalDigits: function() {
        return this._decimalDigits;
    },

    setDecimalDigits: function(decimalDigits) {
        this._decimalDigits = decimalDigits;
    },

    /**
     * The decimal symbol ("." or ",").
     *
     * @property decimalSymbol
     * @type String
     * @default: "."
     */
    _decimalSymbol: ".",

    getDecimalSymbol: function() {
        return this._decimalSymbol;
    },

    setDecimalSymbol: function(decimalSymbol) {
        this._decimalSymbol = decimalSymbol;
    },

    /**
     * The field value.
     *
     * @property value
     * @type Number
     * @default 0
     */
    _value: 0,

    getValue: function() {
        return parseFloat(this._value);
    },

    setValue: function(value) {
        this._updateValue(value);
        this._updateFieldValue();
    },

    //////////////////////////////////////////
    // Methods                              //
    //////////////////////////////////////////

    // ====== Private methods ======

    /**
     * Update the value (in the widget).
     *
     * @method _updateValue
     * @private
     * @param {String|Number} value The raw value.
     */
    _updateValue: function(value) {
        value = ("" + value).replace(",", "."); // ","
        value = value.replace(/ /g, "");  // remove spaces
        value = parseFloat(value);
        if (isNaN(value)) {
            value = 0;
        }

        if (this.min != null) {
            value = Math.max(this.min, value);
        }

        if (this.max != null) {
            value = Math.min(this.max, value);
        }

        if (this.decimalDigits != null) {
            value = value.toFixed(this.decimalDigits);
        }

        this._value = value;
    },

    /**
     * Update the value in the html field.
     *
     * @method _updateFieldValue
     * @private
     */
    _updateFieldValue: function() {
        this.__html.field.value = ("" + this._value).replace(".", this.decimalSymbol);
    },

    /**
     * Validate the user inputs.
     *
     * @method _validateInput
     * @private
     * @param {String} value
     * @return {Boolean}
     */
    _validateInput: function(value) {
        var value = "" + value;
        value = value.replace(/ /g, "");  // remove spaces
        if (/^-?[0-9]*(\.|,)?[0-9]*$/.test(value)) {
            if (this.decimalDigits == 0 && !/^-?[0-9]*$/.test(value)) {
                return false;
            }
            if (this.min !== null && this.min >= 0 && value[0] == "-") {
                return false;
            }
            return true;
        }
        return false;
    },

    /**
     * Build the widget HTML.
     *
     * @method _buildHtml
     * @private
     */
    _buildHtml: function() {
        this.__html.field = document.createElement("input");
        this.__html.field.className = "photonui-widget photonui-field photonui-field-numeric";
        this.__html.field.type = "text";
    },

    //////////////////////////////////////////
    // Internal Events Callbacks            //
    //////////////////////////////////////////

    /**
     * @method __onKeypress
     * @private
     * @param event
     */
    __onKeypress: function(event) {
        if (event.ctrlKey) {
            return;
        }
        else if (event.keyCode == 13) {  // Enter
            this._updateFieldValue();
            this._callCallbacks("value-changed", [this.value]);
        }
        else {
            var field = this.__html.field;
            var value = field.value.slice(0, field.selectionStart)
                        + String.fromCharCode(event.charCode)
                        + field.value.slice(field.selectionEnd);
            if (!this._validateInput(value)) {
                event.preventDefault();
            }
        }
    },

    /**
     * @method __onKeyup
     * @private
     * @param event
     */
    __onKeyup: function(event) {
        var value = this.__html.field.value.replace(/[^0-9.,-]*/g, "");
        if (value != this.__html.field.value) {
            this.__html.field.value = value;
        }
        this._updateValue(this.__html.field.value);
    },

    /**
     * @method __onChange
     * @private
     * @param event
     */
    __onChange: function(event) {
        this._updateFieldValue();
        this._callCallbacks("value-changed", [this.value]);
    },

    /**
     * @method __onMouseWheel
     * @private
     * @param event
     */
    __onMouseWheel: function(event) {
        if (document.activeElement != this.__html.field) {
            return;
        }

        var wheelDelta = null;

        // Webkit
        if (event.wheelDeltaY != undefined) {
            wheelDelta = event.wheelDeltaY;
        }
        // MSIE
        if (event.wheelDelta != undefined) {
            wheelDelta = event.wheelDelta;
        }
        // Firefox
        if (event.axis != undefined && event.detail != undefined) {
            if (event.axis == 2) { // Y
                wheelDelta = - event.detail;
            }
        }

        if (wheelDelta != null) {
           if (wheelDelta >= 0) {
                this.value += this.step;
            }
            else {
                this.value -= this.step;
            }
            event.preventDefault();
        }
        this._callCallbacks("value-changed", [this.value]);
    },

    /**
     * @method __onKeydown
     * @private
     * @param event
     */
    __onKeydown: function(event) {
        if (event.keyCode == 38) {
            this.setValue(this.getValue() + this.step);
            event.preventDefault();
            this._callCallbacks("value-changed", [this.value]);
        }
        else if (event.keyCode == 40) {
            this.setValue(this.getValue() - this.step);
            event.preventDefault();
            this._callCallbacks("value-changed", [this.value]);
        }
    }
});
<commit_msg>Fix double call of the 'value-changed' callbacks on NumericField widgets<commit_after>/*
 * Copyright (c) 2014, Wanadev <http://www.wanadev.fr/>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *   * Redistributions of source code must retain the above copyright notice, this
 *     list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above copyright notice,
 *     this list of conditions and the following disclaimer in the documentation
 *     and/or other materials provided with the distribution.
 *   * Neither the name of Wanadev nor the names of its contributors may be used
 *     to endorse or promote products derived from this software without specific
 *     prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authored by: Fabien LOISON <http://flozz.fr/>
 */

/**
 * PhotonUI - Javascript Web User Interface.
 *
 * @module PhotonUI
 * @submodule Input
 * @namespace photonui
 */

var photonui = photonui || {};

/**
 * Numeric field.
 *
 * @class NumericField
 * @constructor
 * @extends photonui.Field
 */
photonui.NumericField = photonui.Field.$extend({

    // Constructor
    __init__: function(params) {
        this.$super(params);
        this._updateProperties(["value"]);
        this._bindFieldEvents();
        this._unbindEvent("value-changed");
        this._bindEvent("keypress", this.__html.field, "keypress", this.__onKeypress.bind(this));
        this._bindEvent("keyup", this.__html.field, "keyup", this.__onKeyup.bind(this));
        this._bindEvent("keydown", this.__html.field, "keydown", this.__onKeydown.bind(this));
        this._bindEvent("change", this.__html.field, "change", this.__onChange.bind(this));
        this._bindEvent("mousewheel", this.__html.field, "mousewheel", this.__onMouseWheel.bind(this));
        this._bindEvent("mousewheel-firefox", this.__html.field, "DOMMouseScroll", this.__onMouseWheel.bind(this));
    },

    //////////////////////////////////////////
    // Properties and Accessors             //
    //////////////////////////////////////////

    // ====== Public properties ======

    /**
     * The minimum value of the field.
     *
     * @property min
     * @type Number
     * default null (no minimum);
     */
    _min: null,

    getMin: function() {
        return this._min;
    },

    setMin: function(min) {
        this._min = min;
    },

    /**
     * The maximum value of the field.
     *
     * @property max
     * @type Number
     * default null (no maximum);
     */
    _max: null,

    getMax: function() {
        return this._max;
    },

    setMax: function(max) {
        this._max = max;
    },

    /**
     * The incrementation step of the field.
     *
     * @property step
     * @type Number
     * default 1
     */
    _step: 1,

    getStep: function() {
        return this._step;
    },

    setStep: function(step) {
        this._step = Math.abs(step);
    },

    /**
     * The number of digit after the decimal dot.
     *
     * @property decimalDigits
     * @type Number
     * @default null (no limite)
     */
    _decimalDigits: null,

    getDecimalDigits: function() {
        return this._decimalDigits;
    },

    setDecimalDigits: function(decimalDigits) {
        this._decimalDigits = decimalDigits;
    },

    /**
     * The decimal symbol ("." or ",").
     *
     * @property decimalSymbol
     * @type String
     * @default: "."
     */
    _decimalSymbol: ".",

    getDecimalSymbol: function() {
        return this._decimalSymbol;
    },

    setDecimalSymbol: function(decimalSymbol) {
        this._decimalSymbol = decimalSymbol;
    },

    /**
     * The field value.
     *
     * @property value
     * @type Number
     * @default 0
     */
    _value: 0,

    getValue: function() {
        return parseFloat(this._value);
    },

    setValue: function(value) {
        this._updateValue(value);
        this._updateFieldValue();
    },

    //////////////////////////////////////////
    // Methods                              //
    //////////////////////////////////////////

    // ====== Private methods ======

    /**
     * Update the value (in the widget).
     *
     * @method _updateValue
     * @private
     * @param {String|Number} value The raw value.
     */
    _updateValue: function(value) {
        value = ("" + value).replace(",", "."); // ","
        value = value.replace(/ /g, "");  // remove spaces
        value = parseFloat(value);
        if (isNaN(value)) {
            value = 0;
        }

        if (this.min != null) {
            value = Math.max(this.min, value);
        }

        if (this.max != null) {
            value = Math.min(this.max, value);
        }

        if (this.decimalDigits != null) {
            value = value.toFixed(this.decimalDigits);
        }

        this._value = value;
    },

    /**
     * Update the value in the html field.
     *
     * @method _updateFieldValue
     * @private
     */
    _updateFieldValue: function() {
        this.__html.field.value = ("" + this._value).replace(".", this.decimalSymbol);
    },

    /**
     * Validate the user inputs.
     *
     * @method _validateInput
     * @private
     * @param {String} value
     * @return {Boolean}
     */
    _validateInput: function(value) {
        var value = "" + value;
        value = value.replace(/ /g, "");  // remove spaces
        if (/^-?[0-9]*(\.|,)?[0-9]*$/.test(value)) {
            if (this.decimalDigits == 0 && !/^-?[0-9]*$/.test(value)) {
                return false;
            }
            if (this.min !== null && this.min >= 0 && value[0] == "-") {
                return false;
            }
            return true;
        }
        return false;
    },

    /**
     * Build the widget HTML.
     *
     * @method _buildHtml
     * @private
     */
    _buildHtml: function() {
        this.__html.field = document.createElement("input");
        this.__html.field.className = "photonui-widget photonui-field photonui-field-numeric";
        this.__html.field.type = "text";
    },

    //////////////////////////////////////////
    // Internal Events Callbacks            //
    //////////////////////////////////////////

    /**
     * @method __onKeypress
     * @private
     * @param event
     */
    __onKeypress: function(event) {
        if (event.ctrlKey) {
            return;
        }
        else if (event.keyCode == 13) {  // Enter
            this._updateFieldValue();
            this._callCallbacks("value-changed", [this.value]);
        }
        else {
            var field = this.__html.field;
            var value = field.value.slice(0, field.selectionStart)
                        + String.fromCharCode(event.charCode)
                        + field.value.slice(field.selectionEnd);
            if (!this._validateInput(value)) {
                event.preventDefault();
            }
        }
    },

    /**
     * @method __onKeyup
     * @private
     * @param event
     */
    __onKeyup: function(event) {
        var value = this.__html.field.value.replace(/[^0-9.,-]*/g, "");
        if (value != this.__html.field.value) {
            this.__html.field.value = value;
        }
        this._updateValue(this.__html.field.value);
    },

    /**
     * @method __onChange
     * @private
     * @param event
     */
    __onChange: function(event) {
        this._updateFieldValue();
        this._callCallbacks("value-changed", [this.value]);
    },

    /**
     * @method __onMouseWheel
     * @private
     * @param event
     */
    __onMouseWheel: function(event) {
        if (document.activeElement != this.__html.field) {
            return;
        }

        var wheelDelta = null;

        // Webkit
        if (event.wheelDeltaY != undefined) {
            wheelDelta = event.wheelDeltaY;
        }
        // MSIE
        if (event.wheelDelta != undefined) {
            wheelDelta = event.wheelDelta;
        }
        // Firefox
        if (event.axis != undefined && event.detail != undefined) {
            if (event.axis == 2) { // Y
                wheelDelta = - event.detail;
            }
        }

        if (wheelDelta != null) {
           if (wheelDelta >= 0) {
                this.value += this.step;
            }
            else {
                this.value -= this.step;
            }
            event.preventDefault();
        }
        this._callCallbacks("value-changed", [this.value]);
    },

    /**
     * @method __onKeydown
     * @private
     * @param event
     */
    __onKeydown: function(event) {
        if (event.keyCode == 38) {
            this.setValue(this.getValue() + this.step);
            event.preventDefault();
            this._callCallbacks("value-changed", [this.value]);
        }
        else if (event.keyCode == 40) {
            this.setValue(this.getValue() - this.step);
            event.preventDefault();
            this._callCallbacks("value-changed", [this.value]);
        }
    }
});
<|endoftext|>


<commit_before>var fs = require("file-system");
var types = require("utils/types");

var Sound = (function () {
    function Sound() {
        if (arguments.length === 1) {
            var path = arguments[0];
            var fileName = types.isString(path) ? path.trim() : "";

            if (fileName.indexOf("~/") === 0) {
                console.log(fileName);
                fileName = fs.path.join(fs.knownFolders.currentApp().path, fileName.replace("~/", ""));
            }

            if (!fs.File.exists(fileName)) {
                console.error("Sound not initialized; file not found.");
                return;
            }

            this._path = fileName;
        }
    }
    Sound.prototype.stop = function () {
        this._player.stop();
    };
    return Sound;
})();
exports.Sound = Sound;<commit_msg>Removed log statement<commit_after>var fs = require("file-system");
var types = require("utils/types");

var Sound = (function () {
    function Sound() {
        if (arguments.length === 1) {
            var path = arguments[0];
            var fileName = types.isString(path) ? path.trim() : "";

            if (fileName.indexOf("~/") === 0) {
                fileName = fs.path.join(fs.knownFolders.currentApp().path, fileName.replace("~/", ""));
            }

            if (!fs.File.exists(fileName)) {
                console.error("Sound not initialized; file not found.");
                return;
            }

            this._path = fileName;
        }
    }
    Sound.prototype.stop = function () {
        this._player.stop();
    };
    return Sound;
})();
exports.Sound = Sound;<|endoftext|>


<commit_before>const moment = require('moment');
require('moment/locale/en-gb');
moment.locale('en-gb');

let autoSaveFrequency = 30;

let autoSave = false;
let draftErroring = false;

let currentContent = {
    title: false,
    html: false
};

let lastSave = 0;

function mounted() {
    let elem = this.$el;
    this.draftsEnabled = elem.getAttribute('drafts-enabled') === 'true';
    this.editorType = elem.getAttribute('editor-type');
    this.pageId= Number(elem.getAttribute('page-id'));
    this.isNewDraft = Number(elem.getAttribute('page-new-draft')) === 1;
    this.isUpdateDraft = Number(elem.getAttribute('page-update-draft')) === 1;

    if (this.pageId !== 0 && this.draftsEnabled) {
        window.setTimeout(() => {
            this.startAutoSave();
        }, 1000);
    }

    if (this.isUpdateDraft || this.isNewDraft) {
        this.draftText = trans('entities.pages_editing_draft');
    } else {
        this.draftText = trans('entities.pages_editing_page');
    }

    // Listen to save draft events from editor
    window.$events.listen('editor-save-draft', this.saveDraft);

    // Listen to content changes from the editor
    window.$events.listen('editor-html-change', html => {
        this.editorHTML = html;
    });
    window.$events.listen('editor-markdown-change', markdown => {
        this.editorMarkdown = markdown;
    });
}

let data = {
    draftsEnabled: false,
    editorType: 'wysiwyg',
    pagedId: 0,
    isNewDraft: false,
    isUpdateDraft: false,

    draftText: '',
    draftUpdated : false,
    changeSummary: '',

    editorHTML: '',
    editorMarkdown: '',
};

let methods = {

    startAutoSave() {
        currentContent.title = document.getElementById('name').value.trim();
        currentContent.html = this.editorHTML;

        autoSave = window.setInterval(() => {
            // Return if manually saved recently to prevent bombarding the server
            if (Date.now() - lastSave < (1000 * autoSaveFrequency)/2) return;
            let newTitle = document.getElementById('name').value.trim();
            let newHtml = this.editorHTML;

            if (newTitle !== currentContent.title || newHtml !== currentContent.html) {
                currentContent.html = newHtml;
                currentContent.title = newTitle;
                this.saveDraft();
            }

        }, 1000 * autoSaveFrequency);
    },

    saveDraft() {
        if (!this.draftsEnabled) return;

        let data = {
            name: document.getElementById('name').value.trim(),
            html: this.editorHTML
        };

        if (this.editorType === 'markdown') data.markdown = this.editorMarkdown;

        let url = window.baseUrl(`/ajax/page/${this.pageId}/save-draft`);
        window.$http.put(url, data).then(response => {
            draftErroring = false;
            let updateTime = moment.utc(moment.unix(response.data.timestamp)).toDate();
            if (!this.isNewPageDraft) this.isUpdateDraft = true;
            this.draftNotifyChange(response.data.message + moment(updateTime).format('HH:mm'));
            lastSave = Date.now();
        }, errorRes => {
            if (draftErroring) return;
            window.$events('error', trans('errors.page_draft_autosave_fail'));
            draftErroring = true;
        });
    },

    draftNotifyChange(text) {
        this.draftText = text;
        this.draftUpdated = true;
        window.setTimeout(() => {
            this.draftUpdated = false;
        }, 2000);
    },

    discardDraft() {
        let url = window.baseUrl(`/ajax/page/${this.pageId}`);
        window.$http.get(url).then(response => {
            if (autoSave) window.clearInterval(autoSave);

            this.draftText = trans('entities.pages_editing_page');
            this.isUpdateDraft = false;
            window.$events.emit('editor-html-update', response.data.html);
            window.$events.emit('editor-markdown-update', response.data.markdown || response.data.html);

            document.getElementById('name').value = response.data.name;
            window.setTimeout(() => {
                this.startAutoSave();
            }, 1000);
            window.$events.emit('success', trans('entities.pages_draft_discarded'));
        });
    },

};

let computed = {
    changeSummaryShort() {
        let len = this.changeSummary.length;
        if (len === 0) return trans('entities.pages_edit_set_changelog');
        if (len <= 16) return this.changeSummary;
        return this.changeSummary.slice(0, 16) + '...';
    }
};

module.exports = {
    mounted, data, methods, computed,
};<commit_msg>Prevented 'Discard draft' option showing after saving a draft page<commit_after>const moment = require('moment');
require('moment/locale/en-gb');
moment.locale('en-gb');

let autoSaveFrequency = 30;

let autoSave = false;
let draftErroring = false;

let currentContent = {
    title: false,
    html: false
};

let lastSave = 0;

function mounted() {
    let elem = this.$el;
    this.draftsEnabled = elem.getAttribute('drafts-enabled') === 'true';
    this.editorType = elem.getAttribute('editor-type');
    this.pageId= Number(elem.getAttribute('page-id'));
    this.isNewDraft = Number(elem.getAttribute('page-new-draft')) === 1;
    this.isUpdateDraft = Number(elem.getAttribute('page-update-draft')) === 1;

    if (this.pageId !== 0 && this.draftsEnabled) {
        window.setTimeout(() => {
            this.startAutoSave();
        }, 1000);
    }

    if (this.isUpdateDraft || this.isNewDraft) {
        this.draftText = trans('entities.pages_editing_draft');
    } else {
        this.draftText = trans('entities.pages_editing_page');
    }

    // Listen to save draft events from editor
    window.$events.listen('editor-save-draft', this.saveDraft);

    // Listen to content changes from the editor
    window.$events.listen('editor-html-change', html => {
        this.editorHTML = html;
    });
    window.$events.listen('editor-markdown-change', markdown => {
        this.editorMarkdown = markdown;
    });
}

let data = {
    draftsEnabled: false,
    editorType: 'wysiwyg',
    pagedId: 0,
    isNewDraft: false,
    isUpdateDraft: false,

    draftText: '',
    draftUpdated : false,
    changeSummary: '',

    editorHTML: '',
    editorMarkdown: '',
};

let methods = {

    startAutoSave() {
        currentContent.title = document.getElementById('name').value.trim();
        currentContent.html = this.editorHTML;

        autoSave = window.setInterval(() => {
            // Return if manually saved recently to prevent bombarding the server
            if (Date.now() - lastSave < (1000 * autoSaveFrequency)/2) return;
            let newTitle = document.getElementById('name').value.trim();
            let newHtml = this.editorHTML;

            if (newTitle !== currentContent.title || newHtml !== currentContent.html) {
                currentContent.html = newHtml;
                currentContent.title = newTitle;
                this.saveDraft();
            }

        }, 1000 * autoSaveFrequency);
    },

    saveDraft() {
        if (!this.draftsEnabled) return;

        let data = {
            name: document.getElementById('name').value.trim(),
            html: this.editorHTML
        };

        if (this.editorType === 'markdown') data.markdown = this.editorMarkdown;

        let url = window.baseUrl(`/ajax/page/${this.pageId}/save-draft`);
        window.$http.put(url, data).then(response => {
            draftErroring = false;
            let updateTime = moment.utc(moment.unix(response.data.timestamp)).toDate();
            if (!this.isNewDraft) this.isUpdateDraft = true;
            this.draftNotifyChange(response.data.message + moment(updateTime).format('HH:mm'));
            lastSave = Date.now();
        }, errorRes => {
            if (draftErroring) return;
            window.$events('error', trans('errors.page_draft_autosave_fail'));
            draftErroring = true;
        });
    },

    draftNotifyChange(text) {
        this.draftText = text;
        this.draftUpdated = true;
        window.setTimeout(() => {
            this.draftUpdated = false;
        }, 2000);
    },

    discardDraft() {
        let url = window.baseUrl(`/ajax/page/${this.pageId}`);
        window.$http.get(url).then(response => {
            if (autoSave) window.clearInterval(autoSave);

            this.draftText = trans('entities.pages_editing_page');
            this.isUpdateDraft = false;
            window.$events.emit('editor-html-update', response.data.html);
            window.$events.emit('editor-markdown-update', response.data.markdown || response.data.html);

            document.getElementById('name').value = response.data.name;
            window.setTimeout(() => {
                this.startAutoSave();
            }, 1000);
            window.$events.emit('success', trans('entities.pages_draft_discarded'));
        });
    },

};

let computed = {
    changeSummaryShort() {
        let len = this.changeSummary.length;
        if (len === 0) return trans('entities.pages_edit_set_changelog');
        if (len <= 16) return this.changeSummary;
        return this.changeSummary.slice(0, 16) + '...';
    }
};

module.exports = {
    mounted, data, methods, computed,
};<|endoftext|>


<commit_before>const path = Plugin.path;
const less = Npm.require('less');
const Future = Npm.require('fibers/future');

Plugin.registerCompiler({
  // *.lessimport has been deprecated since 0.7.1, but it still works. We
  // *recommend *.import.less or the imports subdirectory instead.
  extensions: ['less', 'lessimport'],
  archMatching: 'web'
}, () => new LessCompiler());

// CompileResult is {css, sourceMap}.
class LessCompiler extends MultiFileCachingCompiler {
  constructor() {
    super({
      compilerName: 'less',
      defaultCacheSize: 1024*1024*10,
    });
  }

  getCacheKey(inputFile) {
    return inputFile.getSourceHash();
  }

  compileResultSize(compileResult) {
    return compileResult.css.length +
      this.sourceMapSize(compileResult.sourceMap);
  }

  // The heuristic is that a file is an import (ie, is not itself processed as a
  // root) if it is in a subdirectory named 'imports' or if it matches
  // *.import.less. This can be overridden in either direction via an explicit
  // `isImport` file option in api.addFiles.
  isRoot(inputFile) {
    const fileOptions = inputFile.getFileOptions();
    if (fileOptions.hasOwnProperty('isImport')) {
      return !fileOptions.isImport;
    }

    const pathInPackage = inputFile.getPathInPackage();
    return !(/\.import\.less$/.test(pathInPackage) ||
             /\.lessimport$/.test(pathInPackage) ||
             /(?:^|\/)imports\//.test(pathInPackage));
  }

  compileOneFile(inputFile, allFiles) {
    const importPlugin = new MeteorImportLessPlugin(allFiles);

    const f = new Future;
    let output;
    try {
      less.render(inputFile.getContentsAsBuffer().toString('utf8'), {
        filename: this.getAbsoluteImportPath(inputFile),
        plugins: [importPlugin],
        // Generate a source map, and include the source files in the
        // sourcesContent field.  (Note that source files which don't themselves
        // produce text (eg, are entirely variable definitions) won't end up in
        // the source map!)
        sourceMap: { outputSourceFiles: true }
      }, f.resolver());
      output = f.wait();
    } catch (e) {
      inputFile.error({
        message: e.message,
        sourcePath: decodeFilePath(e.filename),
        line: e.line,
        column: e.column
      });
      return null;
    }

    if (output.map) {
      const map = JSON.parse(output.map);
      map.sources = map.sources.map(decodeFilePath);
      output.map = map;
    }

    const compileResult = {css: output.css, sourceMap: output.map};
    const referencedImportPaths = [];
    output.imports.forEach((path) => {
      // Some files that show up in output.imports are not actually files; for
      // example @import url("...");
      if (allFiles.has(path)) {
        referencedImportPaths.push(path);
      }
    });

    return {compileResult, referencedImportPaths};
  }

  addCompileResult(inputFile, compileResult) {
    inputFile.addStylesheet({
      data: compileResult.css,
      path: inputFile.getPathInPackage() + '.css',
      sourceMap: compileResult.sourceMap
    });
  }
}

class MeteorImportLessPlugin {
  constructor(allFiles) {
    this.allFiles = allFiles;
    this.minVersion = [2, 5, 0];
  }

  install(less, pluginManager) {
    pluginManager.addFileManager(
      new MeteorImportLessFileManager(this.allFiles));
  }
}

class MeteorImportLessFileManager extends less.AbstractFileManager {
  constructor(allFiles) {
    super();
    this.allFiles = allFiles;
  }

  // We want to be the only active FileManager, so claim to support everything.
  supports(filename) {
    // We shouldn't process files that start with `//` or a protocol because
    // those are not relative to the app at all; they are probably native
    // CSS imports
    if (! filename.match(/^(https?:)?\/\//)) {
      return true;
    }

    return false;
  }

  loadFile(filename, currentDirectory, options, environment, cb) {
    const packageMatch = currentDirectory.match(/^(\{[^}]*\})/);
    if (! packageMatch) {
      // shouldn't happen.  all filenames less ever sees should involve this {}
      // thing!
      cb(new Error('file without Meteor context? ' + currentDirectory));
      return;
    }
    const currentPackagePrefix = packageMatch[1];

    let resolvedFilename;
    if (filename[0] === '/') {
      // Map `/foo/bar.less` onto `{thispackage}/foo/bar.less`
      resolvedFilename = currentPackagePrefix + filename;
    } else if (filename[0] === '{') {
      resolvedFilename = filename;
    } else {
      resolvedFilename = path.join(currentDirectory, filename);
    }

    if (!this.allFiles.has(resolvedFilename)) {
      cb({type: 'File', message: 'Unknown import: ' + filename});
      return;
    }
    cb(null, {
      contents: this.allFiles.get(resolvedFilename)
        .getContentsAsBuffer().toString('utf8'),
      filename: resolvedFilename
    });
  }
}

function decodeFilePath (filePath) {
  const match = filePath.match(/^{(.*)}\/(.*)$/);
  if (! match)
    throw new Error('Failed to decode Less path: ' + filePath);

  if (match[1] === '') {
    // app
    return match[2];
  }

  return 'packages/' + match[1] + '/' + match[2];
}
<commit_msg>add autoprefixer<commit_after>const path = Plugin.path;
const less = Npm.require('less');
const Future = Npm.require('fibers/future');
const LessPluginAutoprefixer = Npm.require('less-plugin-autoprefix');

Plugin.registerCompiler({
  // *.lessimport has been deprecated since 0.7.1, but it still works. We
  // *recommend *.import.less or the imports subdirectory instead.
  extensions: ['less', 'lessimport'],
  archMatching: 'web'
}, () => new LessCompiler());

// CompileResult is {css, sourceMap}.
class LessCompiler extends MultiFileCachingCompiler {
  constructor() {
    super({
      compilerName: 'less',
      defaultCacheSize: 1024*1024*10,
    });
  }

  getCacheKey(inputFile) {
    return inputFile.getSourceHash();
  }

  compileResultSize(compileResult) {
    return compileResult.css.length +
      this.sourceMapSize(compileResult.sourceMap);
  }

  // The heuristic is that a file is an import (ie, is not itself processed as a
  // root) if it is in a subdirectory named 'imports' or if it matches
  // *.import.less. This can be overridden in either direction via an explicit
  // `isImport` file option in api.addFiles.
  isRoot(inputFile) {
    const fileOptions = inputFile.getFileOptions();
    if (fileOptions.hasOwnProperty('isImport')) {
      return !fileOptions.isImport;
    }

    const pathInPackage = inputFile.getPathInPackage();
    return !(/\.import\.less$/.test(pathInPackage) ||
             /\.lessimport$/.test(pathInPackage) ||
             /(?:^|\/)imports\//.test(pathInPackage));
  }

  compileOneFile(inputFile, allFiles) {
    const importPlugin = new MeteorImportLessPlugin(allFiles);
    const autoprefixerPlugin = new LessPluginAutoprefixer(parseAutoprefixerOptions(process.env.AUTOPREFIXER_OPTIONS || 'null'));

    const f = new Future;
    let output;
    try {
      less.render(inputFile.getContentsAsBuffer().toString('utf8'), {
        filename: this.getAbsoluteImportPath(inputFile),
        plugins: [importPlugin, autoprefixerPlugin],
        // Generate a source map, and include the source files in the
        // sourcesContent field.  (Note that source files which don't themselves
        // produce text (eg, are entirely variable definitions) won't end up in
        // the source map!)
        sourceMap: { outputSourceFiles: true }
      }, f.resolver());
      output = f.wait();
    } catch (e) {
      inputFile.error({
        message: e.message,
        sourcePath: decodeFilePath(e.filename),
        line: e.line,
        column: e.column
      });
      return null;
    }

    if (output.map) {
      const map = JSON.parse(output.map);
      // Removed non file reference generated by autoprefixerPlugin in map.sources array, e.g. '<input css 33>'
      const index = map.sources.findIndex(x => x.match(/<input/) )
      map.sources.splice(index, 1)
      map.sources = map.sources.map(decodeFilePath);
      output.map = map;
    }

    const compileResult = {css: output.css, sourceMap: output.map};
    const referencedImportPaths = [];
    output.imports.forEach((path) => {
      // Some files that show up in output.imports are not actually files; for
      // example @import url("...");
      if (allFiles.has(path)) {
        referencedImportPaths.push(path);
      }
    });

    return {compileResult, referencedImportPaths};
  }

  addCompileResult(inputFile, compileResult) {
    inputFile.addStylesheet({
      data: compileResult.css,
      path: inputFile.getPathInPackage() + '.css',
      sourceMap: compileResult.sourceMap
    });
  }
}

class MeteorImportLessPlugin {
  constructor(allFiles) {
    this.allFiles = allFiles;
    this.minVersion = [2, 5, 0];
  }

  install(less, pluginManager) {
    pluginManager.addFileManager(
      new MeteorImportLessFileManager(this.allFiles));
  }
}

class MeteorImportLessFileManager extends less.AbstractFileManager {
  constructor(allFiles) {
    super();
    this.allFiles = allFiles;
  }

  // We want to be the only active FileManager, so claim to support everything.
  supports(filename) {
    // We shouldn't process files that start with `//` or a protocol because
    // those are not relative to the app at all; they are probably native
    // CSS imports
    if (! filename.match(/^(https?:)?\/\//)) {
      return true;
    }

    return false;
  }

  loadFile(filename, currentDirectory, options, environment, cb) {
    const packageMatch = currentDirectory.match(/^(\{[^}]*\})/);
    if (! packageMatch) {
      // shouldn't happen.  all filenames less ever sees should involve this {}
      // thing!
      cb(new Error('file without Meteor context? ' + currentDirectory));
      return;
    }
    const currentPackagePrefix = packageMatch[1];

    let resolvedFilename;
    if (filename[0] === '/') {
      // Map `/foo/bar.less` onto `{thispackage}/foo/bar.less`
      resolvedFilename = currentPackagePrefix + filename;
    } else if (filename[0] === '{') {
      resolvedFilename = filename;
    } else {
      resolvedFilename = path.join(currentDirectory, filename);
    }

    if (!this.allFiles.has(resolvedFilename)) {
      cb({type: 'File', message: 'Unknown import: ' + filename});
      return;
    }
    cb(null, {
      contents: this.allFiles.get(resolvedFilename)
        .getContentsAsBuffer().toString('utf8'),
      filename: resolvedFilename
    });
  }
}

function decodeFilePath (filePath) {
  const match = filePath.match(/^{(.*)}\/(.*)$/);
  if (! match)
    throw new Error('Failed to decode Less path: ' + filePath);

  if (match[1] === '') {
    // app
    return match[2];
  }

  return 'packages/' + match[1] + '/' + match[2];
}

function parseAutoprefixerOptions(options) {
  try {
    const o = JSON.parse(options);
    if (o && typeof o === "object" && o !== null) {
      if (Object.keys(o)[0] !== "browsers"){
        console.log('\n less-autoprefixer: invalid AUTOPREFIXER_OPTIONS - "browsers" key not found, falling back to default options - { browsers: "> 1%, last 2 versions, Firefox ESR, Opera 12.1"}, more info - https://github.com/postcss/autoprefixer#options');
      } else {
        return o;
      }
    }
  }
  catch (e) {
    console.log("\n less-autoprefixer: invalid JSON format in AUTOPREFIXER_OPTIONS -", e);
    console.log(' less-autoprefixer: falling back to default options - { browsers: "> 1%, last 2 versions, Firefox ESR, Opera 12.1"}, more info - https://github.com/postcss/autoprefixer#options');
  }
  return {};
}
<|endoftext|>


<commit_before>/**
 * TSlider client class.
 * This clas is mainly based on Scriptaculous Slider control (http://script.aculo.us)
 */

Prado.WebUI.TSlider = Class.extend(Prado.WebUI.PostBackControl,
{
	onInit : function (options)
	{
		var slider = this;
		this.options=options || {};
		this.track = $(options.ID+'_track');
		this.handle =$(options.ID+'_handle');
		this.progress = $(options.ID+'_progress');
		this.axis  = this.options.axis || 'horizontal';
		this.range = this.options.range || $R(0,1);
		this.value = 0;
		this.maximum   = this.options.maximum || this.range.end;
		this.minimum   = this.options.minimum || this.range.start;
		this.hiddenField=$(this.options.ID+'_1');

		// Issue 181
		this.element.stopObserving();
		this.track.stopObserving();
		this.handle.stopObserving();
		this.progress.stopObserving();
		this.hiddenField.stopObserving();

		// Will be used to align the handle onto the track, if necessary
		this.alignX = parseInt(this.options.alignX || - this.track.offsetLeft);
		this.alignY = parseInt(this.options.alignY || - this.track.offsetTop);

		this.trackLength = this.maximumOffset() - this.minimumOffset();
		this.handleLength = this.isVertical() ?
			(this.handle.offsetHeight != 0 ?
				this.handle.offsetHeight : this.handles.style.height.replace(/px$/,"")) :
				(this.handle.offsetWidth != 0 ? this.handle.offsetWidth :
					this.handle.style.width.replace(/px$/,""));

		this.active   = false;
		this.dragging = false;
		this.disabled = false;

		if(this.options.disabled) this.setDisabled();

		// Allowed values array
		this.allowedValues = this.options.values ? this.options.values.sortBy(Prototype.K) : false;
		if(this.allowedValues) {
			this.minimum = this.allowedValues.min();
			this.maximum = this.allowedValues.max();
		}

		this.eventMouseDown = this.startDrag.bindAsEventListener(this);
		this.eventMouseUp   = this.endDrag.bindAsEventListener(this);
		this.eventMouseMove = this.update.bindAsEventListener(this);

		// Initialize handle
		this.setValue(parseFloat(slider.options.sliderValue));
		Element.makePositioned(this.handle); // fix IE
		Event.observe (this.handle, "mousedown", this.eventMouseDown);

		Event.observe (this.track, "mousedown", this.eventMouseDown);
		if (this.progress) Event.observe (this.progress, "mousedown", this.eventMouseDown);

		// Issue 181
		document.stopObserving("mouseup", this.eventMouseUp);
		document.stopObserving("mousemove", this.eventMouseMove);
		Event.observe (document, "mouseup", this.eventMouseUp);
		Event.observe (document, "mousemove", this.eventMouseMove);

		this.initialized=true;

		if(this.options['AutoPostBack']==true)
			Event.observe(this.hiddenField, "change", Prado.PostBack.bindEvent(this,options));

	},

	dispose: function() {
		var slider = this;
		Event.stopObserving(this.track, "mousedown", this.eventMouseDown);
		Event.stopObserving(document, "mouseup", this.eventMouseUp);
		Event.stopObserving(document, "mousemove", this.eventMouseMove);

		Event.stopObserving(this.handle, "mousedown", slider.eventMouseDown);
 	},

	setDisabled: function(){
		this.disabled = true;
	},
	setEnabled: function(){
		this.disabled = false;
	},
	getNearestValue: function(value){
		if(this.allowedValues){
			if(value >= this.allowedValues.max()) return(this.allowedValues.max());
			if(value <= this.allowedValues.min()) return(this.allowedValues.min());

			var offset = Math.abs(this.allowedValues[0] - value);
			var newValue = this.allowedValues[0];
			this.allowedValues.each( function(v) {
				var currentOffset = Math.abs(v - value);
				if(currentOffset <= offset){
					newValue = v;
					offset = currentOffset;
				}
			});
			return newValue;
		}
		if(value > this.range.end) return this.range.end;
		if(value < this.range.start) return this.range.start;
		return value;
	},

	setValue: function(sliderValue){
		if(!this.active) {
			this.updateStyles();
		}
		this.value = this.getNearestValue(sliderValue);
		var pixelValue= this.translateToPx(this.value);
		this.handle.style[this.isVertical() ? 'top' : 'left'] =	pixelValue;
		if (this.progress)
			this.progress.style[this.isVertical() ? 'height' : 'width'] = pixelValue;

		//this.drawSpans();
		if(!this.dragging || !this.event) this.updateFinished();
	},

	setValueBy: function(delta) {
    	this.setValue(this.value + delta);
	},

	translateToPx: function(value) {
		return Math.round(
      		((this.trackLength-this.handleLength)/(this.range.end-this.range.start)) * (value - this.range.start)) + "px";
	},

	translateToValue: function(offset) {
		return ((offset/(this.trackLength-this.handleLength) * (this.range.end-this.range.start)) + this.range.start);
	},

	getRange: function(range) {
		var v = this.values.sortBy(Prototype.K);
		range = range || 0;
		return $R(v[range],v[range+1]);
	},

	minimumOffset: function(){
		return(this.isVertical() ? this.alignY : this.alignX);
  	},

	maximumOffset: function(){
		return(this.isVertical() ?
			(this.track.offsetHeight != 0 ? this.track.offsetHeight :
				this.track.style.height.replace(/px$/,"")) - this.alignY :
				(this.track.offsetWidth != 0 ? this.track.offsetWidth :
				this.track.style.width.replace(/px$/,"")) - this.alignX);
	},

	isVertical:  function(){
		return (this.axis == 'vertical');
	},

	updateStyles: function() {
		if (this.active)
			Element.addClassName(this.handle, 'selected');
		else
			Element.removeClassName(this.handle, 'selected');
	},

	startDrag: function(event) {
		if(Event.isLeftClick(event)) {
			if(!this.disabled){
				this.active = true;
				var handle = Event.element(event);
				var pointer  = [Event.pointerX(event), Event.pointerY(event)];
				var track = handle;
				if(track==this.track) {
					var offsets  = this.track.cumulativeOffset();
					this.event = event;
					this.setValue(this.translateToValue(
						(this.isVertical() ? pointer[1]-offsets[1] : pointer[0]-offsets[0])-(this.handleLength/2)
					));
					var offsets  = this.handle.cumulativeOffset();
					this.offsetX = (pointer[0] - offsets[0]);
					this.offsetY = (pointer[1] - offsets[1]);
				} else {
					this.updateStyles();
					var offsets  = this.handle.cumulativeOffset();
					this.offsetX = (pointer[0] - offsets[0]);
					this.offsetY = (pointer[1] - offsets[1]);
				}
			}
			Event.stop(event);
		}
	},

	update: function(event) {
		if(this.active) {
			if(!this.dragging) this.dragging = true;
			this.draw(event);
			if(Prototype.Browser.WebKit) window.scrollBy(0,0);
			Event.stop(event);
		}
	},

	draw: function(event) {
		var pointer = [Event.pointerX(event), Event.pointerY(event)];
		var offsets = this.track.cumulativeOffset();
		pointer[0] -= this.offsetX + offsets[0];
		pointer[1] -= this.offsetY + offsets[1];
		this.event = event;
		this.setValue(this.translateToValue( this.isVertical() ? pointer[1] : pointer[0] ));
		if(this.initialized && this.options.onSlide)
			this.options.onSlide(this.value, this);
	},

	endDrag: function(event) {
		if(this.active && this.dragging) {
			this.finishDrag(event, true);
			Event.stop(event);
		}
		this.active = false;
		this.dragging = false;
	},

	finishDrag: function(event, success) {
		this.active = false;
		this.dragging = false;
		this.updateFinished();
	},

	updateFinished: function() {
		this.hiddenField.value=this.value;
		this.updateStyles();
		if(this.initialized && this.options.onChange)
			this.options.onChange(this.value, this);
		this.event = null;
		if (this.options['AutoPostBack']==true)
		{
			Event.fireEvent(this.hiddenField,"change");
		}
	}

});<commit_msg>Forgot optional ProgressIndicator property in previous fix. <commit_after>/**
 * TSlider client class.
 * This clas is mainly based on Scriptaculous Slider control (http://script.aculo.us)
 */

Prado.WebUI.TSlider = Class.extend(Prado.WebUI.PostBackControl,
{
	onInit : function (options)
	{
		var slider = this;
		this.options=options || {};
		this.track = $(options.ID+'_track');
		this.handle =$(options.ID+'_handle');
		this.progress = $(options.ID+'_progress');
		this.axis  = this.options.axis || 'horizontal';
		this.range = this.options.range || $R(0,1);
		this.value = 0;
		this.maximum   = this.options.maximum || this.range.end;
		this.minimum   = this.options.minimum || this.range.start;
		this.hiddenField=$(this.options.ID+'_1');

		// Issue 181
		this.element.stopObserving();
		this.track.stopObserving();
		this.handle.stopObserving();
		if (this.progress) this.progress.stopObserving();
		this.hiddenField.stopObserving();

		// Will be used to align the handle onto the track, if necessary
		this.alignX = parseInt(this.options.alignX || - this.track.offsetLeft);
		this.alignY = parseInt(this.options.alignY || - this.track.offsetTop);

		this.trackLength = this.maximumOffset() - this.minimumOffset();
		this.handleLength = this.isVertical() ?
			(this.handle.offsetHeight != 0 ?
				this.handle.offsetHeight : this.handles.style.height.replace(/px$/,"")) :
				(this.handle.offsetWidth != 0 ? this.handle.offsetWidth :
					this.handle.style.width.replace(/px$/,""));

		this.active   = false;
		this.dragging = false;
		this.disabled = false;

		if(this.options.disabled) this.setDisabled();

		// Allowed values array
		this.allowedValues = this.options.values ? this.options.values.sortBy(Prototype.K) : false;
		if(this.allowedValues) {
			this.minimum = this.allowedValues.min();
			this.maximum = this.allowedValues.max();
		}

		this.eventMouseDown = this.startDrag.bindAsEventListener(this);
		this.eventMouseUp   = this.endDrag.bindAsEventListener(this);
		this.eventMouseMove = this.update.bindAsEventListener(this);

		// Initialize handle
		this.setValue(parseFloat(slider.options.sliderValue));
		Element.makePositioned(this.handle); // fix IE
		Event.observe (this.handle, "mousedown", this.eventMouseDown);

		Event.observe (this.track, "mousedown", this.eventMouseDown);
		if (this.progress) Event.observe (this.progress, "mousedown", this.eventMouseDown);

		// Issue 181
		document.stopObserving("mouseup", this.eventMouseUp);
		document.stopObserving("mousemove", this.eventMouseMove);
		Event.observe (document, "mouseup", this.eventMouseUp);
		Event.observe (document, "mousemove", this.eventMouseMove);

		this.initialized=true;

		if(this.options['AutoPostBack']==true)
			Event.observe(this.hiddenField, "change", Prado.PostBack.bindEvent(this,options));

	},

	dispose: function() {
		var slider = this;
		Event.stopObserving(this.track, "mousedown", this.eventMouseDown);
		Event.stopObserving(document, "mouseup", this.eventMouseUp);
		Event.stopObserving(document, "mousemove", this.eventMouseMove);

		Event.stopObserving(this.handle, "mousedown", slider.eventMouseDown);
 	},

	setDisabled: function(){
		this.disabled = true;
	},
	setEnabled: function(){
		this.disabled = false;
	},
	getNearestValue: function(value){
		if(this.allowedValues){
			if(value >= this.allowedValues.max()) return(this.allowedValues.max());
			if(value <= this.allowedValues.min()) return(this.allowedValues.min());

			var offset = Math.abs(this.allowedValues[0] - value);
			var newValue = this.allowedValues[0];
			this.allowedValues.each( function(v) {
				var currentOffset = Math.abs(v - value);
				if(currentOffset <= offset){
					newValue = v;
					offset = currentOffset;
				}
			});
			return newValue;
		}
		if(value > this.range.end) return this.range.end;
		if(value < this.range.start) return this.range.start;
		return value;
	},

	setValue: function(sliderValue){
		if(!this.active) {
			this.updateStyles();
		}
		this.value = this.getNearestValue(sliderValue);
		var pixelValue= this.translateToPx(this.value);
		this.handle.style[this.isVertical() ? 'top' : 'left'] =	pixelValue;
		if (this.progress)
			this.progress.style[this.isVertical() ? 'height' : 'width'] = pixelValue;

		//this.drawSpans();
		if(!this.dragging || !this.event) this.updateFinished();
	},

	setValueBy: function(delta) {
    	this.setValue(this.value + delta);
	},

	translateToPx: function(value) {
		return Math.round(
      		((this.trackLength-this.handleLength)/(this.range.end-this.range.start)) * (value - this.range.start)) + "px";
	},

	translateToValue: function(offset) {
		return ((offset/(this.trackLength-this.handleLength) * (this.range.end-this.range.start)) + this.range.start);
	},

	getRange: function(range) {
		var v = this.values.sortBy(Prototype.K);
		range = range || 0;
		return $R(v[range],v[range+1]);
	},

	minimumOffset: function(){
		return(this.isVertical() ? this.alignY : this.alignX);
  	},

	maximumOffset: function(){
		return(this.isVertical() ?
			(this.track.offsetHeight != 0 ? this.track.offsetHeight :
				this.track.style.height.replace(/px$/,"")) - this.alignY :
				(this.track.offsetWidth != 0 ? this.track.offsetWidth :
				this.track.style.width.replace(/px$/,"")) - this.alignX);
	},

	isVertical:  function(){
		return (this.axis == 'vertical');
	},

	updateStyles: function() {
		if (this.active)
			Element.addClassName(this.handle, 'selected');
		else
			Element.removeClassName(this.handle, 'selected');
	},

	startDrag: function(event) {
		if(Event.isLeftClick(event)) {
			if(!this.disabled){
				this.active = true;
				var handle = Event.element(event);
				var pointer  = [Event.pointerX(event), Event.pointerY(event)];
				var track = handle;
				if(track==this.track) {
					var offsets  = this.track.cumulativeOffset();
					this.event = event;
					this.setValue(this.translateToValue(
						(this.isVertical() ? pointer[1]-offsets[1] : pointer[0]-offsets[0])-(this.handleLength/2)
					));
					var offsets  = this.handle.cumulativeOffset();
					this.offsetX = (pointer[0] - offsets[0]);
					this.offsetY = (pointer[1] - offsets[1]);
				} else {
					this.updateStyles();
					var offsets  = this.handle.cumulativeOffset();
					this.offsetX = (pointer[0] - offsets[0]);
					this.offsetY = (pointer[1] - offsets[1]);
				}
			}
			Event.stop(event);
		}
	},

	update: function(event) {
		if(this.active) {
			if(!this.dragging) this.dragging = true;
			this.draw(event);
			if(Prototype.Browser.WebKit) window.scrollBy(0,0);
			Event.stop(event);
		}
	},

	draw: function(event) {
		var pointer = [Event.pointerX(event), Event.pointerY(event)];
		var offsets = this.track.cumulativeOffset();
		pointer[0] -= this.offsetX + offsets[0];
		pointer[1] -= this.offsetY + offsets[1];
		this.event = event;
		this.setValue(this.translateToValue( this.isVertical() ? pointer[1] : pointer[0] ));
		if(this.initialized && this.options.onSlide)
			this.options.onSlide(this.value, this);
	},

	endDrag: function(event) {
		if(this.active && this.dragging) {
			this.finishDrag(event, true);
			Event.stop(event);
		}
		this.active = false;
		this.dragging = false;
	},

	finishDrag: function(event, success) {
		this.active = false;
		this.dragging = false;
		this.updateFinished();
	},

	updateFinished: function() {
		this.hiddenField.value=this.value;
		this.updateStyles();
		if(this.initialized && this.options.onChange)
			this.options.onChange(this.value, this);
		this.event = null;
		if (this.options['AutoPostBack']==true)
		{
			Event.fireEvent(this.hiddenField,"change");
		}
	}

});<|endoftext|>


<commit_before>'use strict'

const request = require('request'),
    Q = require('q'),
    companySearch = require('./api/company.js'),
    postSearch = require('./api/post.js'),
    guideSearch = require('./api/guide.js'),
    preparePayload = require('./api/payloads-slack.js');

module.exports = {
  isHandlerForRequest: isHandlerForRequest,
  getResponsePayload: getResponsePayload,
  sendResponseToPlatform: sendResponseToPlatform,
  getErrorPayload: getErrorPayload,
  sendErrorResponse: sendErrorResponse
}

function isHandlerForRequest(platformRequestContext) {
  var responseUrl = platformRequestContext.userRequest.response_url || '';
  return (responseUrl && responseUrl.includes('hooks.slack.com')) ? true : false;
}

// returns result of payload objects, ready to send on
function getResponsePayload(platformRequestContext) {
  // var channelId = platformRequestContext.userRequest.body.channel_id;
  var textInput = platformRequestContext.userRequest.text;
  var result = {
    raw: {},
    data: {},
    context: platformRequestContext
  }
  // will this export properly?
  if (textInput) {
    result.data =
      Q.all([
          postSearch.findByText(textInput),
          companySearch.findByText(textInput)
      ])
      .then(function (postAndCompanySearchResults) {
          var posts = postAndCompanySearchResults[0];
          console.log("Posts returned by first query: " + JSON.stringify(posts).substring(0,200));
          var companies = postAndCompanySearchResults[1];
          console.log("Companies returned by first query:: " + JSON.stringify(companies).substring(0,200));
          if (posts && posts.length) {
              return attachCompaniesAndGuides(posts)
                .then(function (posts){
                    return preparePayload.posts(posts);
                });
          }
          else if (companies && companies.length) {
              return preparePayload.companies(companies);
          }
          else {
              return preparePayload.nothingFound();
          }
      })
    } else {
      result.data = preparePayload.inputPrompt();
    };
  result.raw = result.data;
  console.log("Payload prepared by slack handler: " + JSON.stringify(result));
  return Q.when(result);
}

function sendResponseToPlatform(payload) {
  var deferred = Q.defer();
  var path = process.env.INCOMING_WEBHOOK_PATH;
  var uri = 'https://hooks.slack.com/services/' + path;

  payload.data.channel = payload.context.userRequest.channel_id;

  console.log("Payload about to be sent back to Slack: " + JSON.stringify(payload.data));

  request({
    uri: uri,
    method: 'POST',
    body: JSON.stringify(payload.data)
  }, function (error, response, body) {
    if (error) {
      deferred.reject(error);
    }
    else {
        deferred.resolve();
    }
  });
  return deferred.promise;
}

function getErrorPayload(err, platformRequestContext) {
  var result = {
    raw: {},
    data: {},
    context: platformRequestContext
  };
  result.data = preparePayload.error(err);
  result.raw = result.data;
  return Q.when(result);
}

function sendErrorResponse(errorPayload) {
  console.log("Ran into an error: " + error);
  sendResponseToPlatform(errorPayload);
}

//  ---------- Helper Methods ----------------

// access Company and Guide objects matching each Question, return one mega-Posts object
//  !!!! WE DONT EVEN NEED THE GUIDES - ON THE CHOPPING BLOCK !!!!
// method could also be refactored
function attachCompaniesAndGuides(posts) {
    var companyIDs = [];
    var guideIDs = [];
    for (let i = 0; i < posts.length; i++) {
        companyIDs.push(posts[i].companyId);
        guideIDs.push(posts[i].guideId);
    };

    return Q.all([
            companySearch.findByIds(companyIDs),
            guideSearch.findByIds(guideIDs)
        ])
    .then(function (companiesAndGuides) {
        var companies = companiesAndGuides[0];
        var guides = companiesAndGuides[1];
        // refactor this
        var companyTable = {};
        var guideTable = {};
        for (let i = 0; i < companies.length; i++) {
            companyTable[companies[i]._id] = companies[i];
            guideTable[guides[i]._id] = guides[i];
        };
        for (let i = 0; i < posts.length; i++) {
            let cID = posts[i].companyId;
            posts[i].company = companyTable[cID];
            let gID = posts[i].guideId;
            posts[i].guide = guideTable[gID];
        };
        return posts;
    })
}<commit_msg>Debug<commit_after>'use strict'

const request = require('request'),
    Q = require('q'),
    companySearch = require('./api/company.js'),
    postSearch = require('./api/post.js'),
    guideSearch = require('./api/guide.js'),
    preparePayload = require('./api/payloads-slack.js');

module.exports = {
  isHandlerForRequest: isHandlerForRequest,
  getResponsePayload: getResponsePayload,
  sendResponseToPlatform: sendResponseToPlatform,
  getErrorPayload: getErrorPayload,
  sendErrorResponse: sendErrorResponse
}

function isHandlerForRequest(platformRequestContext) {
  var responseUrl = platformRequestContext.userRequest.response_url || '';
  return (responseUrl && responseUrl.includes('hooks.slack.com')) ? true : false;
}

// returns result of payload objects, ready to send on
function getResponsePayload(platformRequestContext) {
  // var channelId = platformRequestContext.userRequest.body.channel_id;
  var textInput = platformRequestContext.userRequest.text;
  var result = {
    raw: {},
    data: {},
    context: platformRequestContext
  }
  if (textInput) {
    result.data =
      Q.all([
          postSearch.findByText(textInput),
          companySearch.findByText(textInput)
      ])
      .then(function (postAndCompanySearchResults) {
          var posts = postAndCompanySearchResults[0];
          console.log("Posts returned by first query: " + JSON.stringify(posts).substring(0,200));
          var companies = postAndCompanySearchResults[1];
          console.log("Companies returned by first query:: " + JSON.stringify(companies).substring(0,200));
          if (posts && posts.length) {
              return attachCompaniesAndGuides(posts)
                .then(function (posts){
                    return preparePayload.posts(posts);
                });
          }
          else if (companies && companies.length) {
              return preparePayload.companies(companies);
          }
          else {
              return preparePayload.nothingFound();
          }
      }).then(function() {
          result.raw = result.data;
          console.log("Payload prepared by slack handler for input " + textInput + ": " + JSON.stringify(result));
          return result;
      })
    } else {
      result.data = preparePayload.inputPrompt();
      result.raw = result.data;
      console.log("Payload prepared by slack handler for NO TEXT INPUT: " + JSON.stringify(result));
      return Q.when(result);
    };
}

function sendResponseToPlatform(payload) {
  var deferred = Q.defer();
  var path = process.env.INCOMING_WEBHOOK_PATH;
  var uri = 'https://hooks.slack.com/services/' + path;

  payload.data.channel = payload.context.userRequest.channel_id;

  console.log("Payload about to be sent back to Slack: " + JSON.stringify(payload.data));

  request({
    uri: uri,
    method: 'POST',
    body: JSON.stringify(payload.data)
  }, function (error, response, body) {
    if (error) {
      deferred.reject(error);
    }
    else {
        deferred.resolve();
    }
  });
  return deferred.promise;
}

function getErrorPayload(err, platformRequestContext) {
  var result = {
    raw: {},
    data: {},
    context: platformRequestContext
  };
  result.data = preparePayload.error(err);
  result.raw = result.data;
  return Q.when(result);
}

function sendErrorResponse(errorPayload) {
  console.log("Ran into an error: " + error);
  sendResponseToPlatform(errorPayload);
}

//  ---------- Helper Methods ----------------

// access Company and Guide objects matching each Question, return one mega-Posts object
//  !!!! WE DONT EVEN NEED THE GUIDES - ON THE CHOPPING BLOCK !!!!
// method could also be refactored
function attachCompaniesAndGuides(posts) {
    var companyIDs = [];
    var guideIDs = [];
    for (let i = 0; i < posts.length; i++) {
        companyIDs.push(posts[i].companyId);
        guideIDs.push(posts[i].guideId);
    };

    return Q.all([
            companySearch.findByIds(companyIDs),
            guideSearch.findByIds(guideIDs)
        ])
    .then(function (companiesAndGuides) {
        var companies = companiesAndGuides[0];
        var guides = companiesAndGuides[1];
        // refactor this
        var companyTable = {};
        var guideTable = {};
        for (let i = 0; i < companies.length; i++) {
            companyTable[companies[i]._id] = companies[i];
            guideTable[guides[i]._id] = guides[i];
        };
        for (let i = 0; i < posts.length; i++) {
            let cID = posts[i].companyId;
            posts[i].company = companyTable[cID];
            let gID = posts[i].guideId;
            posts[i].guide = guideTable[gID];
        };
        return posts;
    })
}<|endoftext|>


<commit_before>goog.provide('ol.test.geom.flat.center');

describe('ol.geom.flat.center', function() {

  describe('ol.geom.flat.center.linearRingss', function() {

    it('calculates the center of a square', function() {
      var squareMultiPoly = new ol.geom.MultiPolygon([[
        [[0, 0], [0, 1], [1, 1], [1, 0], [0, 0]]
      ]]);
      var got = ol.geom.flat.center.linearRingss(
        squareMultiPoly.flatCoordinates,
        0,
        squareMultiPoly.endss_,
        2
      );
      expect(got).to.eql([0.5, 0.5]);
    });

    it('calculates the centers of two squares', function() {
      var squareMultiPoly = new ol.geom.MultiPolygon([
          [
            [[0, 0], [0, 1], [1, 1], [1, 0], [0, 0]]
          ],
          [
            [[3, 0], [3, 1], [4, 1], [4, 0], [3, 0]]
          ]
      ]);
      var got = ol.geom.flat.center.linearRingss(
        squareMultiPoly.flatCoordinates,
        0,
        squareMultiPoly.endss_,
        2
      );
      expect(got).to.eql([0.5, 0.5, 3.5, 0.5]);
    });

    it('does not care about holes', function() {
      var polywithHole = new ol.geom.MultiPolygon([[
          [[0, 0], [0, 5], [5, 5], [5, 0], [0, 0]],
          [[1, 1], [1, 4], [4, 4], [4, 1], [1, 1]]
      ]]);
      var got = ol.geom.flat.center.linearRingss(
        polywithHole.flatCoordinates,
        0,
        polywithHole.endss_,
        2
      );
      expect(got).to.eql([2.5, 2.5]);
    });

  });

});

goog.require('ol.geom.flat.center');
goog.require('ol.geom.MultiPolygon');
<commit_msg>Fix indentation<commit_after>goog.provide('ol.test.geom.flat.center');

describe('ol.geom.flat.center', function() {

  describe('ol.geom.flat.center.linearRingss', function() {

    it('calculates the center of a square', function() {
      var squareMultiPoly = new ol.geom.MultiPolygon([[
        [[0, 0], [0, 1], [1, 1], [1, 0], [0, 0]]
      ]]);
      var got = ol.geom.flat.center.linearRingss(
        squareMultiPoly.flatCoordinates,
        0,
        squareMultiPoly.endss_,
        2
      );
      expect(got).to.eql([0.5, 0.5]);
    });

    it('calculates the centers of two squares', function() {
      var squareMultiPoly = new ol.geom.MultiPolygon([
        [
          [[0, 0], [0, 1], [1, 1], [1, 0], [0, 0]]
        ],
        [
          [[3, 0], [3, 1], [4, 1], [4, 0], [3, 0]]
        ]
      ]);
      var got = ol.geom.flat.center.linearRingss(
        squareMultiPoly.flatCoordinates,
        0,
        squareMultiPoly.endss_,
        2
      );
      expect(got).to.eql([0.5, 0.5, 3.5, 0.5]);
    });

    it('does not care about holes', function() {
      var polywithHole = new ol.geom.MultiPolygon([[
          [[0, 0], [0, 5], [5, 5], [5, 0], [0, 0]],
          [[1, 1], [1, 4], [4, 4], [4, 1], [1, 1]]
      ]]);
      var got = ol.geom.flat.center.linearRingss(
        polywithHole.flatCoordinates,
        0,
        polywithHole.endss_,
        2
      );
      expect(got).to.eql([2.5, 2.5]);
    });

  });

});

goog.require('ol.geom.flat.center');
goog.require('ol.geom.MultiPolygon');
<|endoftext|>


<commit_before>(function() {
	var impl = document.implementation;
	var mother;
	if (impl && impl.createHTMLDocument) {
		mother = impl.createHTMLDocument("");
	} else {
		mother = document.createElement('iframe').contentWindow.document;
	}
	document.implementation.createHTMLDocument = function(str) {
		var copy = mother.cloneNode();
		if (str != null) copy.title = str;
		return copy;
	};
})();
<commit_msg>Do not overwrite twice<commit_after>(function() {
	var impl = document.implementation;
	var mother;
	if (impl && impl.createHTMLDocument) {
		if (impl.custom) return;
		mother = impl.createHTMLDocument("");
	} else {
		mother = document.createElement('iframe').contentWindow.document;
	}
	document.implementation.createHTMLDocument = function(str) {
		var copy = mother.cloneNode();
		if (str != null) copy.title = str;
		return copy;
	};
	document.implementation.createHTMLDocument.custom = true;
})();
<|endoftext|>