<commit_before>import HttpModule from './HttpModule';
import Timing from '../Timing';
import {defer} from '../../utils/helpers';
import {enumerable} from '../../utils/decorators';

/**
 * @public
 * @typedef {Object} BandwidthModule~settingsObject
 * @extends HttpModule~settingsObject
 * @property {Object} data
 * @property {number} data.size The amount of data to initially use.
 * @property {number} [data.multiplier=2] If the measure period can't reach the delay defined in the settings, the data amount is multiplied by the following value.
 */

/**
 * Apply a new set of custom settings.
 * @public
 * @method BandwidthModule#settings
 * @param {BandwidthModule~settingsObject} settings A set of custom settings.
 * @returns {BandwidthModule}
 */
/**
 * Return the current set of settings.
 * @public
 * @method BandwidthModule#settings^2
 * @returns {BandwidthModule~settingsObject}
 */

/**
 * @class BandwidthModule
 * @extends HttpModule
 * @param {string} loadingType The loading type, `upload` or `download`.
 * @param {BandwidthModule~settingsObject} [settings={}] A set of custom settings.
 */
export default class BandwidthModule extends HttpModule {

    /**
     *
     * @private
     * @member {string} BandwidthModule#_loadingType
     */
    @enumerable(false)
    _loadingType = undefined;

    /**
     *
     * @private
     * @member {boolean} BandwidthModule#_intendedEnd
     */
    @enumerable(false)
    _intendedEnd = false;

    /**
     *
     * @private
     * @member {boolean} BandwidthModule#_isRestarting
     */
    @enumerable(false)
    _isRestarting = false;

    /**
     * Tracks the value of the `loaded` property for each progress event.
     * @private
     * @member {?number} BandwidthModule#_lastLoadedValue
     */
    @enumerable(false)
    _lastLoadedValue = null;

    /**
     * The recorded measures of speed.
     * @private
     * @member {number[]} BandwidthModule#_speedRecords
     */
    @enumerable(false)
    _speedRecords = [];

    /**
     * The average speed.
     * @private
     * @member {number} BandwidthModule#_avgSpeed
     */
    @enumerable(false)
    _avgSpeed = undefined;

    /**
     * The ID of the current request.
     * @private
     * @member {number} BandwidthModule#_requestID
     */
    @enumerable(false)
    _requestID = 0;

    /**
     * The ID of the current progress event.
     * @private
     * @member {number} BandwidthModule#_progressID
     */
    @enumerable(false)
    _progressID = 0;

    /**
     * Defines if measures have started.
     * @private
     * @member {boolean} BandwidthModule#_started
     */
    @enumerable(false)
    _started = false;

    /**
     * Defines if the current progress event is the first one triggered for the current request.
     * @private
     * @member {boolean} BandwidthModule#_firstProgress
     */
    @enumerable(false)
    _firstProgress = true;

    /**
     * @private
     * @member {Defer} BandwidthModule#_deferredProgress
     */
    @enumerable(false)
    _deferredProgress = undefined;

    /**
     * Unique labels for each request, exclusively used to make measures.
     * @private
     * @member {Object} BandwidthModule#_timingLabels
     * @property {?string} start
     * @property {?string} progress
     * @property {?string} end
     * @property {?string} measure
     */
    @enumerable(false)
    _timingLabels = {
        start: null,
        progress: null,
        end: null,
        measure: null
    };

    _downloadSizes = [
        '350x350', '500x500', '750x750', '1000x1000', '1500x1500', '2000x2000',
        '2500x2500', '3000x3000', '3500x3500', '4000x4000'
    ]

    constructor(loadingType, settings = {})
    {
        loadingType = (~['upload', 'download'].indexOf(loadingType)) ? loadingType : 'download';

        super(loadingType);

        this._extendDefaultSettings({
            data: {
                // 20 MB for upload, 4000x4000 image for download
                size: loadingType === 'upload' ? (20 * 1024 * 1024) : this._downloadSizes[9],
                multiplier: 2
            }
        }).settings(settings);

        // override endpoint url to include image for download
        if (loadingType === 'download') {
            var baseUrl = this._settings.endpoint.split('/').slice(0, -1).join('/');
            this.settings({
                endpoint: baseUrl + '/random' + this._settings.data.size + '.jpg'
            });
        }

        this._loadingType = loadingType;

        // Bind to XHR events
        this.on('xhr-upload-loadstart', () => Timing.mark(this._timingLabels.start));
        this.on('xhr-readystatechange', xhr => {
            if (!this._started && xhr.readyState === XMLHttpRequest.LOADING) {
                Timing.mark(this._timingLabels.start);
                this._started = true;
            }
        });

        var eventsPrefix = (loadingType === 'upload') ? 'xhr-upload' : 'xhr';

        this.on(`${eventsPrefix}-progress`, (xhr, event) => this._progress(event));
        this.on(`${eventsPrefix}-timeout`, () => this._timeout());
        this.on(`${eventsPrefix}-loadend`, () => this._end());
    }

    /**
     * Start requesting the server to make measures.
     * @public
     * @method BandwidthModule#start
     * @returns {BandwidthModule}
     */
    start()
    {
        var loadingType = this._loadingType,
            dataSettings = this.settings().data,
            reqID = this._requestID++;

        this._intendedEnd = false;
        this._lastLoadedValue = null;
        this._speedRecords = [];
        this._started = false;
        this._firstProgress = true;
        this._deferredProgress = defer();

        // Trigger the start event
        if (!this._isRestarting) {
            this.trigger('start', dataSettings.size);
        }

        // Create unique timing labels for the new request
        var labels = this._timingLabels;
        labels.start = `${loadingType}-${reqID}-start`;
        labels.progress = `${loadingType}-${reqID}-progress`;
        labels.end = `${loadingType}-${reqID}-end`;
        labels.measure = `${loadingType}-${reqID}-measure`;

        // Generate some random data to upload to the server. Here we're using a Blob instead of an ArrayBuffer because
        // of a bug in Chrome (tested in v33.0.1750.146), causing a freeze of the page while trying to directly upload
        // an ArrayBuffer (through an ArrayBufferView). The freeze lasts nearly 4.5s for 10MB of data. Using a Blob
        // seems to solve the problem.
        var blob = (loadingType === 'upload') ? new Blob([new ArrayBuffer(dataSettings.size)]) : null;

        var type = (loadingType === 'download') ? 'GET' : 'POST';

        // Initiate and send a new request
        this._newRequest(type)._sendRequest(blob);

        return this;
    }

    /**
     * Abort the measures.
     * @public
     * @method BandwidthModule#abort
     * @returns {BandwidthModule}
     */
    abort()
    {
        this._intendedEnd = true;
        return this._abort();
    }

    /**
     * Make bandwidth measures for the current request.
     * @private
     * @method BandwidthModule#_progress
     * @param {ProgressEvent} event The event associated with the progress event of the current request.
     * @returns {BandwidthModule}
     */
    _progress(event)
    {
        // Ignore the first progress event, it generally contributes to get incoherent values.
        if (this._firstProgress) {
            this._firstProgress = false;
            return this;
        }

        // Execute the previous progress trigger
        this._deferredProgress.run();

        var labels = this._timingLabels,
            progressID = this._progressID++,
            markLabel = `${labels.progress}-${progressID}`,
            loaded = event.loaded;

        Timing.mark(markLabel);

        // Measure the average speed (B/s) since the request started
        var avgMeasure = Timing.measure(
                `${labels.measure}-avg-${progressID}`,
                labels.start,
                markLabel
            ),
            avgSpeed = loaded / avgMeasure * 1000;

        var instantSpeed;

        if (this._lastLoadedValue === null) { // We are executing the first progress event of the current request
            instantSpeed = avgSpeed; // The instant speed of the first progress event is equal to the average one
        } else {
            // Measure the instant speed (B/s), which defines the speed between two progress events.
            var instantMeasure = Timing.measure(
                `${labels.measure}-instant-${progressID}`,
                // Set the mark of the previous progress event as the starting point
                `${labels.progress}-${progressID - 1}`,
                markLabel
            );
            instantSpeed = (loaded - this._lastLoadedValue) / instantMeasure * 1000;
        }

        // Save the `loaded` property of the event for the next progress event
        this._lastLoadedValue = loaded;

        // Defer measures saving and event triggering, this allows to cancel the last progress event, which can generate
        // incoherent values.
        this._deferredProgress = defer(() => {
            this._avgSpeed = avgSpeed;
            this._speedRecords.push(instantSpeed);

            this.trigger('progress', avgSpeed, instantSpeed);
        });

        return this;
    }

    /**
     * Mark the current request as entirely finished (this means it ended after a time out).
     * @private
     * @method BandwidthModule#_timeout
     * @returns {BandwidthModule}
     */
    _timeout()
    {
        this._intendedEnd = true;
        return this;
    }

    /**
     * End the measures.
     * @private
     * @method BandwidthModule#_end
     * @returns {BandwidthModule}
     */
    _end()
    {
        // A timeout or an abort occured, bypass the further requests and trigger the "end" event.
        if (this._intendedEnd) {
            this._isRestarting = false;
            this.trigger('end', this._avgSpeed, this._speedRecords);
        }

        // The request ended to early, restart it with an increased data size.
        else {
            var dataSettings = this.settings().data;
            var size;
            if ((this._loadingType) === 'download') {
                var thisIndex = this._downloadSizes.indexOf(dataSettings.size);
                var nextIndex = thisIndex < this._downloadSizes.length ? thisIndex + (dataSettings.multiplier - 1) : thisIndex;
                size = this._downloadSizes[nextIndex];
            } else {
                size = dataSettings.size * dataSettings.multiplier;
            }

            this.settings({data: {size}});
            this.trigger('restart', size);

            this._isRestarting = true;
            this.start();
        }

        return this;
    }

}
<commit_msg>fix infinite download test for high speed connections<commit_after>import HttpModule from './HttpModule';
import Timing from '../Timing';
import {defer} from '../../utils/helpers';
import {enumerable} from '../../utils/decorators';

/**
 * @public
 * @typedef {Object} BandwidthModule~settingsObject
 * @extends HttpModule~settingsObject
 * @property {Object} data
 * @property {number} data.size The amount of data to initially use.
 * @property {number} [data.multiplier=2] If the measure period can't reach the delay defined in the settings, the data amount is multiplied by the following value.
 */

/**
 * Apply a new set of custom settings.
 * @public
 * @method BandwidthModule#settings
 * @param {BandwidthModule~settingsObject} settings A set of custom settings.
 * @returns {BandwidthModule}
 */
/**
 * Return the current set of settings.
 * @public
 * @method BandwidthModule#settings^2
 * @returns {BandwidthModule~settingsObject}
 */

/**
 * @class BandwidthModule
 * @extends HttpModule
 * @param {string} loadingType The loading type, `upload` or `download`.
 * @param {BandwidthModule~settingsObject} [settings={}] A set of custom settings.
 */
export default class BandwidthModule extends HttpModule {

    /**
     *
     * @private
     * @member {string} BandwidthModule#_loadingType
     */
    @enumerable(false)
    _loadingType = undefined;

    /**
     *
     * @private
     * @member {boolean} BandwidthModule#_intendedEnd
     */
    @enumerable(false)
    _intendedEnd = false;

    /**
     *
     * @private
     * @member {boolean} BandwidthModule#_isRestarting
     */
    @enumerable(false)
    _isRestarting = false;

    /**
     * Tracks the value of the `loaded` property for each progress event.
     * @private
     * @member {?number} BandwidthModule#_lastLoadedValue
     */
    @enumerable(false)
    _lastLoadedValue = null;

    /**
     * The recorded measures of speed.
     * @private
     * @member {number[]} BandwidthModule#_speedRecords
     */
    @enumerable(false)
    _speedRecords = [];

    /**
     * The average speed.
     * @private
     * @member {number} BandwidthModule#_avgSpeed
     */
    @enumerable(false)
    _avgSpeed = undefined;

    /**
     * The ID of the current request.
     * @private
     * @member {number} BandwidthModule#_requestID
     */
    @enumerable(false)
    _requestID = 0;

    /**
     * The ID of the current progress event.
     * @private
     * @member {number} BandwidthModule#_progressID
     */
    @enumerable(false)
    _progressID = 0;

    /**
     * Defines if measures have started.
     * @private
     * @member {boolean} BandwidthModule#_started
     */
    @enumerable(false)
    _started = false;

    /**
     * Defines if the current progress event is the first one triggered for the current request.
     * @private
     * @member {boolean} BandwidthModule#_firstProgress
     */
    @enumerable(false)
    _firstProgress = true;

    /**
     * @private
     * @member {Defer} BandwidthModule#_deferredProgress
     */
    @enumerable(false)
    _deferredProgress = undefined;

    /**
     * Unique labels for each request, exclusively used to make measures.
     * @private
     * @member {Object} BandwidthModule#_timingLabels
     * @property {?string} start
     * @property {?string} progress
     * @property {?string} end
     * @property {?string} measure
     */
    @enumerable(false)
    _timingLabels = {
        start: null,
        progress: null,
        end: null,
        measure: null
    };

    _downloadSizes = [
        '350x350', '500x500', '750x750', '1000x1000', '1500x1500', '2000x2000',
        '2500x2500', '3000x3000', '3500x3500', '4000x4000'
    ]

    constructor(loadingType, settings = {})
    {
        loadingType = (~['upload', 'download'].indexOf(loadingType)) ? loadingType : 'download';

        super(loadingType);

        this._extendDefaultSettings({
            data: {
                // 20 MB for upload, 4000x4000 image for download
                size: loadingType === 'upload' ? (20 * 1024 * 1024) : this._downloadSizes[9],
                multiplier: 2
            }
        }).settings(settings);

        // override endpoint url to include image for download
        if (loadingType === 'download') {
            var baseUrl = this._settings.endpoint.split('/').slice(0, -1).join('/');
            this.settings({
                endpoint: baseUrl + '/random' + this._settings.data.size + '.jpg'
            });
        }

        this._loadingType = loadingType;

        // Bind to XHR events
        this.on('xhr-upload-loadstart', () => Timing.mark(this._timingLabels.start));
        this.on('xhr-readystatechange', xhr => {
            if (!this._started && xhr.readyState === XMLHttpRequest.LOADING) {
                Timing.mark(this._timingLabels.start);
                this._started = true;
            }
        });

        var eventsPrefix = (loadingType === 'upload') ? 'xhr-upload' : 'xhr';

        this.on(`${eventsPrefix}-progress`, (xhr, event) => this._progress(event));
        this.on(`${eventsPrefix}-timeout`, () => this._timeout());
        this.on(`${eventsPrefix}-loadend`, () => this._end());
    }

    /**
     * Start requesting the server to make measures.
     * @public
     * @method BandwidthModule#start
     * @returns {BandwidthModule}
     */
    start()
    {
        var loadingType = this._loadingType,
            dataSettings = this.settings().data,
            reqID = this._requestID++;

        this._intendedEnd = false;
        this._lastLoadedValue = null;
        this._speedRecords = [];
        this._started = false;
        this._firstProgress = true;
        this._deferredProgress = defer();

        // Trigger the start event
        if (!this._isRestarting) {
            this.trigger('start', dataSettings.size);
        }

        // Create unique timing labels for the new request
        var labels = this._timingLabels;
        labels.start = `${loadingType}-${reqID}-start`;
        labels.progress = `${loadingType}-${reqID}-progress`;
        labels.end = `${loadingType}-${reqID}-end`;
        labels.measure = `${loadingType}-${reqID}-measure`;

        // Generate some random data to upload to the server. Here we're using a Blob instead of an ArrayBuffer because
        // of a bug in Chrome (tested in v33.0.1750.146), causing a freeze of the page while trying to directly upload
        // an ArrayBuffer (through an ArrayBufferView). The freeze lasts nearly 4.5s for 10MB of data. Using a Blob
        // seems to solve the problem.
        var blob = (loadingType === 'upload') ? new Blob([new ArrayBuffer(dataSettings.size)]) : null;

        var type = (loadingType === 'download') ? 'GET' : 'POST';

        // Initiate and send a new request
        this._newRequest(type)._sendRequest(blob);

        return this;
    }

    /**
     * Abort the measures.
     * @public
     * @method BandwidthModule#abort
     * @returns {BandwidthModule}
     */
    abort()
    {
        this._intendedEnd = true;
        return this._abort();
    }

    /**
     * Make bandwidth measures for the current request.
     * @private
     * @method BandwidthModule#_progress
     * @param {ProgressEvent} event The event associated with the progress event of the current request.
     * @returns {BandwidthModule}
     */
    _progress(event)
    {
        // Ignore the first progress event, it generally contributes to get incoherent values.
        if (this._firstProgress) {
            this._firstProgress = false;
            return this;
        }

        // Execute the previous progress trigger
        this._deferredProgress.run();

        var labels = this._timingLabels,
            progressID = this._progressID++,
            markLabel = `${labels.progress}-${progressID}`,
            loaded = event.loaded;

        Timing.mark(markLabel);

        // Measure the average speed (B/s) since the request started
        var avgMeasure = Timing.measure(
                `${labels.measure}-avg-${progressID}`,
                labels.start,
                markLabel
            ),
            avgSpeed = loaded / avgMeasure * 1000;

        var instantSpeed;

        if (this._lastLoadedValue === null) { // We are executing the first progress event of the current request
            instantSpeed = avgSpeed; // The instant speed of the first progress event is equal to the average one
        } else {
            // Measure the instant speed (B/s), which defines the speed between two progress events.
            var instantMeasure = Timing.measure(
                `${labels.measure}-instant-${progressID}`,
                // Set the mark of the previous progress event as the starting point
                `${labels.progress}-${progressID - 1}`,
                markLabel
            );
            instantSpeed = (loaded - this._lastLoadedValue) / instantMeasure * 1000;
        }

        // Save the `loaded` property of the event for the next progress event
        this._lastLoadedValue = loaded;

        // Defer measures saving and event triggering, this allows to cancel the last progress event, which can generate
        // incoherent values.
        this._deferredProgress = defer(() => {
            this._avgSpeed = avgSpeed;
            this._speedRecords.push(instantSpeed);

            this.trigger('progress', avgSpeed, instantSpeed);
        });

        return this;
    }

    /**
     * Mark the current request as entirely finished (this means it ended after a time out).
     * @private
     * @method BandwidthModule#_timeout
     * @returns {BandwidthModule}
     */
    _timeout()
    {
        this._intendedEnd = true;
        return this;
    }

    /**
     * End the measures.
     * @private
     * @method BandwidthModule#_end
     * @returns {BandwidthModule}
     */
    _end()
    {
        // A timeout or an abort occured, bypass the further requests and trigger the "end" event.
        if (this._intendedEnd) {
            this._isRestarting = false;
            this.trigger('end', this._avgSpeed, this._speedRecords);
        }

        // The request ended to early, restart it with an increased data size.
        else {
            var settings = this.settings();
            var dataSettings = settings.data;
            var size;
            var delay = settings.delay;
            if ((this._loadingType) === 'download') {
                var thisIndex = this._downloadSizes.indexOf(dataSettings.size);
                var nextIndex;
                if (thisIndex < this._downloadSizes.length) {
                    nextIndex = thisIndex + (dataSettings.multiplier - 1);
                } else {
                    // decrease delay because we have a finite image size
                    delay = delay / dataSettings.multiplier;
                    nextIndex = thisIndex;
                }
                size = this._downloadSizes[nextIndex];
            } else {
                size = dataSettings.size * dataSettings.multiplier;
            }

            this.settings({delay: delay, data: {size}});
            this.trigger('restart', size);

            this._isRestarting = true;
            this.start();
        }

        return this;
    }

}
<|endoftext|>


<commit_before>function afterLoadGlobalSettingJSP() {
    var $detailsTab = $("#right_panel_content #tab_content_details");

    //edit button ***
    var $readonlyFields  = $detailsTab.find("#value");
    var $editFields = $detailsTab.find("#value_edit");
    initializeEditFunction($readonlyFields, $editFields, doUpdateGlobalSetting);

    //initialize dialogs
    initDialogWithOK("dialog_alert_restart_management_server");
}

function doUpdateGlobalSetting() {
    // validate values
    var $detailsTab = $("#right_panel_content #tab_content_details");

    var isValid = true;
    isValid &= validateString("Value", $detailsTab.find("#value_edit"), $detailsTab.find("#value_edit_errormsg"), true);
    if (!isValid)
        return;

	var jsonObj = $detailsTab.data("jsonObj");
	var name = jsonObj.name;
    var value = trim($detailsTab.find("#value_edit").val());

    $.ajax({
      data: createURL("command=updateConfiguration&name="+todb(name)+"&value="+todb(value)+"&response=json"),
	    dataType: "json",
	    success: function(json) {
		    $detailsTab.find("#value").text(value);

		    //no embedded object returned, so....
		    jsonObj.value = value;

		    globalSettingToMidmenu(jsonObj, $("#"+globalSettingGetMidmenuId(jsonObj)));
		    $("#dialog_alert_restart_management_server").dialog("open");
	    }
    });
}

function globalSettingGetMidmenuId(jsonObj) {
    return "midmenuItem_" + fromdb(jsonObj.name).replace(/\./g, "_").replace(/\s/g, ""); //remove all spaces in jsonObj.name
}

function globalSettingToMidmenu(jsonObj, $midmenuItem1) {
    var id = globalSettingGetMidmenuId(jsonObj);
    $midmenuItem1.attr("id", id);

    $midmenuItem1.data("jsonObj", jsonObj);

    //var $iconContainer = $midmenuItem1.find("#icon_container").show();
    //$iconContainer.find("#icon").attr("src", "images/midmenuicon_storage_volume.png");

    $midmenuItem1.find("#first_row").text(fromdb(jsonObj.name).substring(0,25));
    $midmenuItem1.find("#second_row").text(fromdb(jsonObj.value).substring(0,25));
}

function globalSettingToRigntPanel($midmenuItem) {
    var jsonObj = $midmenuItem.data("jsonObj");
    globalSettingJsonToDetailsTab(jsonObj);
}

function globalSettingJsonToDetailsTab(jsonObj) {
    var $detailsTab = $("#right_panel_content #tab_content_details");
    $detailsTab.data("jsonObj", jsonObj);
    $detailsTab.find("#name").text(fromdb(jsonObj.name));
    $detailsTab.find("#value").text(fromdb(jsonObj.value));
    $detailsTab.find("#value_edit").val(fromdb(jsonObj.value));
    $detailsTab.find("#description").text(fromdb(jsonObj.description));
    $detailsTab.find("#category").text(fromdb(jsonObj.category));
}<commit_msg>new UI - Global Settings page - update middle menu and right panel after global setting is changed.<commit_after>function afterLoadGlobalSettingJSP() {
    var $detailsTab = $("#right_panel_content #tab_content_details");

    //edit button ***
    var $readonlyFields  = $detailsTab.find("#value");
    var $editFields = $detailsTab.find("#value_edit");
    initializeEditFunction($readonlyFields, $editFields, doUpdateGlobalSetting);

    //initialize dialogs
    initDialogWithOK("dialog_alert_restart_management_server");
}

function doUpdateGlobalSetting() {
    // validate values
    var $detailsTab = $("#right_panel_content #tab_content_details");

    var isValid = true;
    isValid &= validateString("Value", $detailsTab.find("#value_edit"), $detailsTab.find("#value_edit_errormsg"), true);
    if (!isValid)
        return;

	var jsonObj = $detailsTab.data("jsonObj");
	var name = jsonObj.name;
    var value = trim($detailsTab.find("#value_edit").val());

    $.ajax({
      data: createURL("command=updateConfiguration&name="+todb(name)+"&value="+todb(value)+"&response=json"),
	    dataType: "json",
	    success: function(json) {
	        //call listConfigurations before bug 6506("What updateConfiguration API returns should include an embedded object") is fixed.
		    var jsonObj;
		    $.ajax({
		        data: createURL("command=listConfigurations&name="+name),
		        dataType: "json",
		        async: false,
		        success: function(json) {
		            jsonObj = json.listconfigurationsresponse.configuration[0];
		        }
		    });
		    var $midmenuItem1 = $("#"+globalSettingGetMidmenuId(jsonObj));
		    globalSettingToMidmenu(jsonObj, $midmenuItem1);
		    globalSettingToRigntPanel($midmenuItem1);

		    $("#dialog_alert_restart_management_server").dialog("open");
	    }
    });
}

function globalSettingGetMidmenuId(jsonObj) {
    return "midmenuItem_" + fromdb(jsonObj.name).replace(/\./g, "_").replace(/\s/g, ""); //remove all spaces in jsonObj.name
}

function globalSettingToMidmenu(jsonObj, $midmenuItem1) {
    var id = globalSettingGetMidmenuId(jsonObj);
    $midmenuItem1.attr("id", id);

    $midmenuItem1.data("jsonObj", jsonObj);

    //var $iconContainer = $midmenuItem1.find("#icon_container").show();
    //$iconContainer.find("#icon").attr("src", "images/midmenuicon_storage_volume.png");

    $midmenuItem1.find("#first_row").text(fromdb(jsonObj.name).substring(0,25));
    $midmenuItem1.find("#second_row").text(fromdb(jsonObj.value).substring(0,25));
}

function globalSettingToRigntPanel($midmenuItem) {
    var jsonObj = $midmenuItem.data("jsonObj");
    globalSettingJsonToDetailsTab(jsonObj);
}

function globalSettingJsonToDetailsTab(jsonObj) {
    var $detailsTab = $("#right_panel_content #tab_content_details");
    $detailsTab.data("jsonObj", jsonObj);
    $detailsTab.find("#name").text(fromdb(jsonObj.name));
    $detailsTab.find("#value").text(fromdb(jsonObj.value));
    $detailsTab.find("#value_edit").val(fromdb(jsonObj.value));
    $detailsTab.find("#description").text(fromdb(jsonObj.description));
    $detailsTab.find("#category").text(fromdb(jsonObj.category));
}<|endoftext|>


<commit_before>(function() {
'use strict';

angular.module('ionic.ui.content', ['ionic.ui.service'])

/**
 * Panel is a simple 100% width and height, fixed panel. It's meant for content to be
 * added to it, or animated around.
 */
.directive('pane', function() {
  return {
    restrict: 'E',
    link: function(scope, element, attr) {
      element.addClass('pane');
    }
  };
})

// The content directive is a core scrollable content area
// that is part of many View hierarchies
.directive('content', ['$parse', '$timeout', 'ScrollDelegate', function($parse, $timeout, ScrollDelegate) {
  return {
    restrict: 'E',
    replace: true,
    template: '

',
    transclude: true,
    scope: {
      onRefresh: '&',
      onRefreshOpening: '&',
      onScroll: '&',
      onScrollComplete: '&',
      refreshComplete: '=',
      scroll: '@',
      hasScrollX: '@',
      hasScrollY: '@',
      scrollbarX: '@',
      scrollbarY: '@',
      scrollEventInterval: '@'
    },
    compile: function(element, attr, transclude) {
      return function($scope, $element, $attr) {
        var clone, sc, sv,
          addedPadding = false,
          c = $element.eq(0);

        if(attr.hasHeader == "true") { c.addClass('has-header'); }
        if(attr.hasSubheader == "true") { c.addClass('has-subheader'); }
        if(attr.hasFooter == "true") { c.addClass('has-footer'); }
        if(attr.hasTabs == "true") { c.addClass('has-tabs'); }

        // If they want plain overflow scrolling, add that as a class
        if($scope.scroll === "false") {
          clone = transclude($scope.$parent);
          $element.append(clone);
        } else if(attr.overflowScroll === "true") {
          c.addClass('overflow-scroll');
          clone = transclude($scope.$parent);
          $element.append(clone);
        } else {
          sc = document.createElement('div');
          sc.className = 'scroll';
          if(attr.padding == "true") {
            sc.className += ' padding';
            addedPadding = true;
          }
          $element.append(sc);

          // Pass the parent scope down to the child
          clone = transclude($scope.$parent);
          angular.element($element[0].firstElementChild).append(clone);

          var refresher = $element[0].querySelector('.scroll-refresher');
          var refresherHeight = refresher && refresher.clientHeight || 0;

          if(attr.refreshComplete) {
            $scope.refreshComplete = function() {
              if($scope.scrollView) {
                refresher && refresher.classList.remove('active');
                $scope.scrollView.finishPullToRefresh();
                $scope.$parent.$broadcast('scroll.onRefreshComplete');
              }
            };
          }

          // Otherwise, supercharge this baby!
          $timeout(function() {
            sv = new ionic.views.Scroll({
              el: $element[0],
              scrollbarX: $scope.$eval($scope.scrollbarX) !== false,
              scrollbarY: $scope.$eval($scope.scrollbarY) !== false,
              scrollingX: $scope.$eval($scope.hasScrollX) === true,
              scrollingY: $scope.$eval($scope.hasScrollY) !== false,
              scrollEventInterval: parseInt($scope.scrollEventInterval, 10) || 20,
              scrollingComplete: function() {
                $scope.onScrollComplete({
                  scrollTop: this.__scrollTop,
                  scrollLeft: this.__scrollLeft
                });
              }
            });

            // Activate pull-to-refresh
            if(refresher) {
              sv.activatePullToRefresh(50, function() {
                refresher.classList.add('active');
              }, function() {
                refresher.classList.remove('refreshing');
                refresher.classList.remove('active');
              }, function() {
                refresher.classList.add('refreshing');
                $scope.onRefresh();
                $scope.$parent.$broadcast('scroll.onRefresh');
              });
            }

            // Register for scroll delegate event handling
            ScrollDelegate.register($scope, $element);

            // Let child scopes access this
            $scope.$parent.scrollView = sv;
          });

        }

        // if padding attribute is true, then add padding if it wasn't added to the .scroll
        if(attr.padding == "true" && !addedPadding) {
          c.addClass('padding');
        }

      };
    }
  };
}])

.directive('refresher', function() {
  return {
    restrict: 'E',
    replace: true,
    require: ['^?content', '^?list'],
    template: '

',
    scope: true
  };
})

.directive('scrollRefresher', function() {
  return {
    restrict: 'E',
    replace: true,
    transclude: true,
    template: '

'
  };
});

})();
<commit_msg>Add infinite scroll directive and handle infinite scroll in content directive<commit_after>(function() {
'use strict';

angular.module('ionic.ui.content', ['ionic.ui.service'])

/**
 * Panel is a simple 100% width and height, fixed panel. It's meant for content to be
 * added to it, or animated around.
 */
.directive('pane', function() {
  return {
    restrict: 'E',
    link: function(scope, element, attr) {
      element.addClass('pane');
    }
  };
})

// The content directive is a core scrollable content area
// that is part of many View hierarchies
.directive('content', ['$parse', '$timeout', 'ScrollDelegate', function($parse, $timeout, ScrollDelegate) {
  return {
    restrict: 'E',
    replace: true,
    template: '

',
    transclude: true,
    scope: {
      onRefresh: '&',
      onRefreshOpening: '&',
      onScroll: '&',
      onScrollComplete: '&',
      refreshComplete: '=',
      scroll: '@',
      hasScrollX: '@',
      hasScrollY: '@',
      scrollbarX: '@',
      scrollbarY: '@',
      scrollEventInterval: '@'
    },
    compile: function(element, attr, transclude) {
      return function($scope, $element, $attr) {
        var clone, sc, sv,
          addedPadding = false,
          c = $element.eq(0);

        if(attr.hasHeader == "true") { c.addClass('has-header'); }
        if(attr.hasSubheader == "true") { c.addClass('has-subheader'); }
        if(attr.hasFooter == "true") { c.addClass('has-footer'); }
        if(attr.hasTabs == "true") { c.addClass('has-tabs'); }

        // If they want plain overflow scrolling, add that as a class
        if($scope.scroll === "false") {
          clone = transclude($scope.$parent);
          $element.append(clone);
        } else if(attr.overflowScroll === "true") {
          c.addClass('overflow-scroll');
          clone = transclude($scope.$parent);
          $element.append(clone);
        } else {
          sc = document.createElement('div');
          sc.className = 'scroll';
          if(attr.padding == "true") {
            sc.className += ' padding';
            addedPadding = true;
          }
          $element.append(sc);

          // Pass the parent scope down to the child
          clone = transclude($scope.$parent);
          angular.element($element[0].firstElementChild).append(clone);

          var refresher = $element[0].querySelector('.scroll-refresher');
          var refresherHeight = refresher && refresher.clientHeight || 0;

          if(attr.refreshComplete) {
            $scope.refreshComplete = function() {
              if($scope.scrollView) {
                refresher && refresher.classList.remove('active');
                $scope.scrollView.finishPullToRefresh();
                $scope.$parent.$broadcast('scroll.onRefreshComplete');
              }
            };
          }

          // Otherwise, supercharge this baby!
          $timeout(function() {
            sv = new ionic.views.Scroll({
              el: $element[0],
              scrollbarX: $scope.$eval($scope.scrollbarX) !== false,
              scrollbarY: $scope.$eval($scope.scrollbarY) !== false,
              scrollingX: $scope.$eval($scope.hasScrollX) === true,
              scrollingY: $scope.$eval($scope.hasScrollY) !== false,
              scrollEventInterval: parseInt($scope.scrollEventInterval, 10) || 20,
              scrollingComplete: function() {
                $scope.onScrollComplete({
                  scrollTop: this.__scrollTop,
                  scrollLeft: this.__scrollLeft
                });
              }
            });

            // Activate pull-to-refresh
            if(refresher) {
              sv.activatePullToRefresh(50, function() {
                refresher.classList.add('active');
              }, function() {
                refresher.classList.remove('refreshing');
                refresher.classList.remove('active');
              }, function() {
                refresher.classList.add('refreshing');
                $scope.onRefresh();
                $scope.$parent.$broadcast('scroll.onRefresh');
              });
            }

            // Register for scroll delegate event handling
            ScrollDelegate.register($scope, $element);

            // Let child scopes access this
            $scope.$parent.scrollView = sv;
          });

          // Infinite scroll
          var infiniteScroll = $element.find('infinite-scroll');
          var infiniteStarted = false;
          if(infiniteScroll) {
            $element.bind('scroll', function(e) {
              if( sv && !infiniteStarted && (sv.getValues().top > (sv.getScrollMax().top * 0.99) ) ) {
                infiniteStarted = true;
                infiniteScroll.addClass('active');
                var cb = function() {
                  infiniteStarted = false;
                  infiniteScroll.removeClass('active');
                }
                $scope.$apply(angular.bind($scope, $scope.onInfiniteScroll, cb));
              }
            });
          }
        }

        // if padding attribute is true, then add padding if it wasn't added to the .scroll
        if(attr.padding == "true" && !addedPadding) {
          c.addClass('padding');
        }

      };
    }
  };
}])

.directive('refresher', function() {
  return {
    restrict: 'E',
    replace: true,
    require: ['^?content', '^?list'],
    template: '

',
    scope: true
  };
})

.directive('scrollRefresher', function() {
  return {
    restrict: 'E',
    replace: true,
    transclude: true,
    template: '

'
  };
}))

.directive('infiniteScroll', function() {
  return {
    restrict: 'E',
    replace: false,
    template: '

'
  };
});

})();
<|endoftext|>


<commit_before>"use strict";

class gsuiKeys {
	constructor() {
		const root = document.createElement( "div" );

		this.rootElement = root;
		this._nlKeys = root.childNodes;
		this._nbOct = 0;
		root.className = "gsuiKeys";
		root.onmousedown = this._evmdRoot.bind( this );
	}

	remove() {
		this.empty();
		this.rootElement.remove();
	}
	empty() {
		Array.from( this._nlKeys ).forEach( el => {
			el.remove();
			el._rowElement.remove();
		} );
	}
	octaves( start, nbOct ) {
		const root = this.rootElement,
			maxOct = start + nbOct;

		this.empty();
		this._nbOct = nbOct;
		this._octStart = start;
		root.style.counterReset = `octave ${ maxOct }`;
		for ( let i = 0; i < nbOct; ++i ) {
			root.append.apply( root, gsuiKeys.template.cloneNode( true ).children );
		}
		Array.from( root.children ).reduce( ( midi, elKey ) => {
			const elRow = elKey.firstElementChild;

			elKey._rowElement = elRow;
			elRow._keyElement = elKey;
			elKey.dataset.midi =
			elRow.dataset.midi = midi - 1;
			return midi - 1;
		}, maxOct * 12 );
	}
	getKeyElementFromMidi( midi ) {
		return this._nlKeys[ this._nlKeys.length - 1 - ( midi - this._octStart * 12 ) ];
	}
	getMidiKeyFromKeyboard( e ) {
		const k = gsuiKeys.keyboardToKey[ e.code ];

		return k
			? ( 4 + k[ 0 ] ) * 12 + k[ 1 ]
			: false;
	}
	midiKeyDown( midi ) {
		const el = this.getKeyElementFromMidi( midi );

		el && el.classList.add( "gsui-active" );
	}
	midiKeyUp( midi ) {
		const el = this.getKeyElementFromMidi( midi );

		el && el.classList.remove( "gsui-active" );
	}

	// private:
	_isBlack( keyInd ) {
		return keyInd === 1 || keyInd === 3 || keyInd === 5 || keyInd === 8 || keyInd === 10;
	}
	_releaseKeyMouse() {
		if ( this._elKeyMouse ) {
			this._elKeyMouse.classList.remove( "gsui-active" );
			this.onkeyup && this.onkeyup( this._midiKeyMouse, this._gain );
		}
	}

	// events:
	_evmdRoot( e ) {
		if ( this._nbOct ) {
			const blackKeyBCR = this.rootElement.childNodes[ 1 ].getBoundingClientRect();

			this._rootTop = this.rootElement.getBoundingClientRect().top;
			this._blackKeyR = blackKeyBCR.right;
			this._blackKeyH = blackKeyBCR.height;
			this._gain = Math.min( e.layerX / ( e.target.clientWidth - 1 ), 1 );
			this._evMouseup = this._evmuRoot.bind( this );
			this._evMousemove = this._evmmRoot.bind( this );
			document.addEventListener( "mouseup", this._evMouseup );
			document.addEventListener( "mousemove", this._evMousemove );
			this._evmmRoot( e );
		}
	}
	_evmuRoot() {
		this._releaseKeyMouse();
		document.removeEventListener( "mouseup", this._evMouseup );
		document.removeEventListener( "mousemove", this._evMousemove );
		delete this._elKeyMouse;
		delete this._keyIndMouse;
	}
	_evmmRoot( e ) {
		const fKeyInd = ( e.clientY - this._rootTop ) / this._blackKeyH;
		let iKeyInd = ~~fKeyInd;

		if ( e.clientX > this._blackKeyR && this._isBlack( ~~( iKeyInd % 12 ) ) ) {
			iKeyInd += fKeyInd - iKeyInd < .5 ? -1 : 1;
		}
		if ( this._keyIndMouse !== iKeyInd ) {
			const elKey = this._nlKeys[ iKeyInd ];

			if ( elKey ) {
				this._releaseKeyMouse();
				this._keyIndMouse = iKeyInd;
				this._elKeyMouse = elKey;
				this._midiKeyMouse = +elKey.dataset.midi;
				elKey.classList.add( "gsui-active" );
				this.onkeydown && this.onkeydown( this._midiKeyMouse, this._gain );
			}
		}
	}
}

gsuiKeys.template = document.querySelector( "#gsuiKeys-octave-template" );
gsuiKeys.template.remove();
gsuiKeys.template.removeAttribute( "id" );

gsuiKeys.keyIds = "c c# d d# e f f# g g# a a# b".split( " " );
gsuiKeys.keyIds.forEach( ( k, i, arr ) => arr[ k ] = i );
gsuiKeys.midiToKeyStr = m => gsuiKeys.keyIds[ m % 12 ] + ~~( m / 12 );
gsuiKeys.keyStrToMidi = k => {
	const key = k.substr( 0, k[ 1 ] !== "#" ? 1 : 2 );

	return k.substr( key.length ) * 12 + gsuiKeys.keyIds[ key ];
};
<commit_msg>gsuiKeys: Object.seal( this )<commit_after>"use strict";

class gsuiKeys {
	constructor() {
		const root = document.createElement( "div" );

		this.rootElement = root;
		this._nlKeys = root.childNodes;
		this._gain =
		this._nbOct =
		this._rootTop =
		this._octStart =
		this._blackKeyR =
		this._blackKeyH =
		this._keyIndMouse =
		this._midiKeyMouse = 0;
		this.onkeyup =
		this.onkeydown =
		this._evMouseup =
		this._elKeyMouse =
		this._evMousemove = null;
		Object.seal( this );

		root.className = "gsuiKeys";
		root.onmousedown = this._evmdRoot.bind( this );
	}

	remove() {
		this.empty();
		this.rootElement.remove();
	}
	empty() {
		Array.from( this._nlKeys ).forEach( el => {
			el.remove();
			el._rowElement.remove();
		} );
	}
	octaves( start, nbOct ) {
		const root = this.rootElement,
			maxOct = start + nbOct;

		this.empty();
		this._nbOct = nbOct;
		this._octStart = start;
		root.style.counterReset = `octave ${ maxOct }`;
		for ( let i = 0; i < nbOct; ++i ) {
			root.append.apply( root, gsuiKeys.template.cloneNode( true ).children );
		}
		Array.from( root.children ).reduce( ( midi, elKey ) => {
			const elRow = elKey.firstElementChild;

			elKey._rowElement = elRow;
			elRow._keyElement = elKey;
			elKey.dataset.midi =
			elRow.dataset.midi = midi - 1;
			return midi - 1;
		}, maxOct * 12 );
	}
	getKeyElementFromMidi( midi ) {
		return this._nlKeys[ this._nlKeys.length - 1 - ( midi - this._octStart * 12 ) ];
	}
	getMidiKeyFromKeyboard( e ) {
		const k = gsuiKeys.keyboardToKey[ e.code ];

		return k
			? ( 4 + k[ 0 ] ) * 12 + k[ 1 ]
			: false;
	}
	midiKeyDown( midi ) {
		const el = this.getKeyElementFromMidi( midi );

		el && el.classList.add( "gsui-active" );
	}
	midiKeyUp( midi ) {
		const el = this.getKeyElementFromMidi( midi );

		el && el.classList.remove( "gsui-active" );
	}

	// private:
	_isBlack( keyInd ) {
		return keyInd === 1 || keyInd === 3 || keyInd === 5 || keyInd === 8 || keyInd === 10;
	}
	_releaseKeyMouse() {
		if ( this._elKeyMouse ) {
			this._elKeyMouse.classList.remove( "gsui-active" );
			this.onkeyup && this.onkeyup( this._midiKeyMouse, this._gain );
		}
	}

	// events:
	_evmdRoot( e ) {
		if ( this._nbOct ) {
			const blackKeyBCR = this.rootElement.childNodes[ 1 ].getBoundingClientRect();

			this._rootTop = this.rootElement.getBoundingClientRect().top;
			this._blackKeyR = blackKeyBCR.right;
			this._blackKeyH = blackKeyBCR.height;
			this._gain = Math.min( e.layerX / ( e.target.clientWidth - 1 ), 1 );
			this._evMouseup = this._evmuRoot.bind( this );
			this._evMousemove = this._evmmRoot.bind( this );
			document.addEventListener( "mouseup", this._evMouseup );
			document.addEventListener( "mousemove", this._evMousemove );
			this._evmmRoot( e );
		}
	}
	_evmuRoot() {
		this._releaseKeyMouse();
		document.removeEventListener( "mouseup", this._evMouseup );
		document.removeEventListener( "mousemove", this._evMousemove );
		this._elKeyMouse =
		this._keyIndMouse = null;
	}
	_evmmRoot( e ) {
		const fKeyInd = ( e.clientY - this._rootTop ) / this._blackKeyH;
		let iKeyInd = ~~fKeyInd;

		if ( e.clientX > this._blackKeyR && this._isBlack( ~~( iKeyInd % 12 ) ) ) {
			iKeyInd += fKeyInd - iKeyInd < .5 ? -1 : 1;
		}
		if ( this._keyIndMouse !== iKeyInd ) {
			const elKey = this._nlKeys[ iKeyInd ];

			if ( elKey ) {
				this._releaseKeyMouse();
				this._keyIndMouse = iKeyInd;
				this._elKeyMouse = elKey;
				this._midiKeyMouse = +elKey.dataset.midi;
				elKey.classList.add( "gsui-active" );
				this.onkeydown && this.onkeydown( this._midiKeyMouse, this._gain );
			}
		}
	}
}

gsuiKeys.template = document.querySelector( "#gsuiKeys-octave-template" );
gsuiKeys.template.remove();
gsuiKeys.template.removeAttribute( "id" );

gsuiKeys.keyIds = "c c# d d# e f f# g g# a a# b".split( " " );
gsuiKeys.keyIds.forEach( ( k, i, arr ) => arr[ k ] = i );
gsuiKeys.midiToKeyStr = m => gsuiKeys.keyIds[ m % 12 ] + ~~( m / 12 );
gsuiKeys.keyStrToMidi = k => {
	const key = k.substr( 0, k[ 1 ] !== "#" ? 1 : 2 );

	return k.substr( key.length ) * 12 + gsuiKeys.keyIds[ key ];
};
<|endoftext|>


<commit_before>export default {
  fields: [
    {
      "type": "radio",
      "label": "Type",
      "name": "user.type",
      "optionsPerRow": 4,
      "options": (context) => {
        const currentUser = context.formData.currentUser;
        if (!currentUser.admin && !currentUser.instituteAdmin) {
          return [];
        }

        const options = [
          { "caption": "Super Administrator", "value": "SUPER" },
          { "caption": "Institute Administrator", "value": "INSTITUTE" },
          { "caption": "Contact", "value": "CONTACT" },
          { "caption": "Regular", "value": "NONE" }
        ];

        if (currentUser.instituteAdmin) {
          options.splice(0, 1);
        }

        return options;
      },
      "showWhen": "currentUser.admin || (user.type != 'SUPER' && currentUser.instituteAdmin)"
    },

    {
      "type": "text",
      "label": "First Name",
      "name": "user.firstName",
      "validations": {
        "required": {
          "message": "First name is mandatory"
        }
      }
    },

    {
      "type": "text",
      "label": "Last Name",
      "name": "user.lastName",
      "validations": {
        "required": {
          "message": "Last name is mandatory"
        }
      }
    },

    {
      "type": "text",
      "label": "Email Address",
      "name": "user.emailAddress",
      "validations": {
        "required": {
          "message": "Email address is mandatory"
        },
        "email": {
          "message": "Email address is invalid"
        }
      }
    },

    {
      "type": "text",
      "label": "Phone Number",
      "name": "user.phoneNumber"
    },

    {
      "type": "dropdown",
      "label": "Domain Name",
      "name": "user.domainName",
      "listSource": {
        "apiUrl": "auth-domains",
        "selectProp": "name",
        "displayProp": "name"
      },
      "showWhen": "type != 'CONTACT'",
      "validations": {
        "required": {
          "message": "Domain name is mandatory"
        }
      }
    },

    {
      "type": "text",
      "label": "Login Name",
      "name": "user.loginName",
      "showWhen": "type != 'CONTACT'",
      "validations": {
        "required": {
          "message": "Login name is mandatory"
        }
      }
    },

    {
      "type": "dropdown",
      "label": "Institute",
      "name": "user.instituteName",
      "listSource": {
        "apiUrl": "institutes",
        "selectProp": "name",
        "displayProp": "name",
        "searchProp": "name"
      },
      "validations": {
        "required": {
          "message": "Institute is mandatory"
        }
      }
    },

    {
      "type": "dropdown",
      "label": "Primary Site",
      "name": "user.primarySite",
      "showWhen": "!!instituteName",
      "listSource": {
        "apiUrl": "sites",
        "selectProp": "name",
        "displayProp": "name",
        "searchProp": "name",
        "queryParams": {
          "dynamic": {
            "institute": "instituteName"
          }
        }
      }
    },

    {
      "type": "dropdown",
      "label": "Time Zone",
      "name": "user.timeZone",
      "listSource": {
        "apiUrl": "time-zones",
        "displayProp": "name",
        "selectProp": "id"
      }
    },

    {
      "type": "radio",
      "label": "Manage Forms?",
      "name": "user.manageForms",
      "optionsPerRow": 2,
      "options": [
        { "caption": "Yes", "value": true },
        { "caption": "No", "value": false }
      ],
      "showWhen": "!type || type == 'NONE'"
    },

    {
      "type": "radio",
      "label": "Disable Notifications?",
      "name": "user.dnd",
      "optionsPerRow": 2,
      "options": [
        { "caption": "Yes", "value": true },
        { "caption": "No", "value": false }
      ]
    },

    {
      "type": "radio",
      "label": "API User?",
      "name": "user.apiUser",
      "showWhen": "type != 'CONTACT'",
      "optionsPerRow": 2,
      "options": [
        { "caption": "Yes", "value": true },
        { "caption": "No", "value": false }
      ]
    },

    {
      "type": "text",
      "label": "IP Address",
      "name": "user.ipRange",
      "showWhen": "type != 'CONTACT' && apiUser == true",
      "validations": {
        "required": {
          "message": "IP address is mandatory"
        }
      }
    },

    {
      "type": "textarea",
      "label": "Address",
      "name": "user.address",
      "rows": 2
    }
  ]
}
<commit_msg>OPSMN-5784: Fixes to display primary site dropdown on the user add/edit page.<commit_after>export default {
  fields: [
    {
      "type": "radio",
      "label": "Type",
      "name": "user.type",
      "optionsPerRow": 4,
      "options": (context) => {
        const currentUser = context.formData.currentUser;
        if (!currentUser.admin && !currentUser.instituteAdmin) {
          return [];
        }

        const options = [
          { "caption": "Super Administrator", "value": "SUPER" },
          { "caption": "Institute Administrator", "value": "INSTITUTE" },
          { "caption": "Contact", "value": "CONTACT" },
          { "caption": "Regular", "value": "NONE" }
        ];

        if (currentUser.instituteAdmin) {
          options.splice(0, 1);
        }

        return options;
      },
      "showWhen": "currentUser.admin || (user.type != 'SUPER' && currentUser.instituteAdmin)"
    },

    {
      "type": "text",
      "label": "First Name",
      "name": "user.firstName",
      "validations": {
        "required": {
          "message": "First name is mandatory"
        }
      }
    },

    {
      "type": "text",
      "label": "Last Name",
      "name": "user.lastName",
      "validations": {
        "required": {
          "message": "Last name is mandatory"
        }
      }
    },

    {
      "type": "text",
      "label": "Email Address",
      "name": "user.emailAddress",
      "validations": {
        "required": {
          "message": "Email address is mandatory"
        },
        "email": {
          "message": "Email address is invalid"
        }
      }
    },

    {
      "type": "text",
      "label": "Phone Number",
      "name": "user.phoneNumber"
    },

    {
      "type": "dropdown",
      "label": "Domain Name",
      "name": "user.domainName",
      "listSource": {
        "apiUrl": "auth-domains",
        "selectProp": "name",
        "displayProp": "name"
      },
      "showWhen": "type != 'CONTACT'",
      "validations": {
        "required": {
          "message": "Domain name is mandatory"
        }
      }
    },

    {
      "type": "text",
      "label": "Login Name",
      "name": "user.loginName",
      "showWhen": "type != 'CONTACT'",
      "validations": {
        "required": {
          "message": "Login name is mandatory"
        }
      }
    },

    {
      "type": "dropdown",
      "label": "Institute",
      "name": "user.instituteName",
      "listSource": {
        "apiUrl": "institutes",
        "selectProp": "name",
        "displayProp": "name",
        "searchProp": "name"
      },
      "validations": {
        "required": {
          "message": "Institute is mandatory"
        }
      }
    },

    {
      "type": "dropdown",
      "label": "Primary Site",
      "name": "user.primarySite",
      "showWhen": "!!user.instituteName",
      "listSource": {
        "apiUrl": "sites",
        "selectProp": "name",
        "displayProp": "name",
        "searchProp": "name",
        "queryParams": {
          "dynamic": {
            "institute": "user.instituteName"
          }
        }
      }
    },

    {
      "type": "dropdown",
      "label": "Time Zone",
      "name": "user.timeZone",
      "listSource": {
        "apiUrl": "time-zones",
        "displayProp": "name",
        "selectProp": "id"
      }
    },

    {
      "type": "radio",
      "label": "Manage Forms?",
      "name": "user.manageForms",
      "optionsPerRow": 2,
      "options": [
        { "caption": "Yes", "value": true },
        { "caption": "No", "value": false }
      ],
      "showWhen": "!type || type == 'NONE'"
    },

    {
      "type": "radio",
      "label": "Disable Notifications?",
      "name": "user.dnd",
      "optionsPerRow": 2,
      "options": [
        { "caption": "Yes", "value": true },
        { "caption": "No", "value": false }
      ]
    },

    {
      "type": "radio",
      "label": "API User?",
      "name": "user.apiUser",
      "showWhen": "type != 'CONTACT'",
      "optionsPerRow": 2,
      "options": [
        { "caption": "Yes", "value": true },
        { "caption": "No", "value": false }
      ]
    },

    {
      "type": "text",
      "label": "IP Address",
      "name": "user.ipRange",
      "showWhen": "type != 'CONTACT' && apiUser == true",
      "validations": {
        "required": {
          "message": "IP address is mandatory"
        }
      }
    },

    {
      "type": "textarea",
      "label": "Address",
      "name": "user.address",
      "rows": 2
    }
  ]
}
<|endoftext|>


<commit_before>/*global OOGL: false */
/*global context: false */

/**
 * @module context
 */

/**
 * Manages asynchronous asset loading with progress feedback.
 *
 * @class context.Loader
 * @extends OOGL.TaskQueue
 * @constructor
 * @param tasks* {Function} Zero or more asynchronous tasks to queue. See the
 * {{#crossLink "OOGL.TaskQueue"}}TaskQueue{{/crossLink}} description for more
 * information.
 * @example
 *	TODO
 */
context.Loader = function () {
	OOGL.TaskQueue.apply(this, arguments);
	var thisObject = this;

	var textures = {};
	var programs = {};
	var data = {};

	/**
	 * Queues an asynchronous task that loads and creates a texture given its
	 * URL.
	 *
	 * Internally the `queueTexture` method uses the
	 * {{#crossLink "context.AsyncTexture"}}AsyncTexture{{/crossLink}} class.
	 *
	 * After being loaded, the texture can be retrieved via the
	 * {{#crossLink "OOGL.Loader/getTexture"}}getTexture{{/crossLink}} method as
	 * a {{#crossLink "context.Texture2D"}}Texture2D{{/crossLink}} object.
	 *
	 * @method queueTexture
	 * @chainable
	 * @param id {String} The URL of the texture image to load.
	 * @param [magFilter=gl.LINEAR] {Number} An optional value for the
	 * magnifying filter. See
	 * {{#crossLink "context.AsyncTexture"}}AsyncTexture{{/crossLink}} for more
	 * information.
	 * @param [minFilter=gl.LINEAR] {Number} An optional value for the minifying
	 * filter. See
	 * {{#crossLink "context.AsyncTexture"}}AsyncTexture{{/crossLink}} for more
	 * information.
	 * @example
	 *	TODO
	 */
	this.queueTexture = function (id, minFilter, magFilter) {
		if (arguments.length < 2) {
			magFilter = context.LINEAR;
			minFilter = context.LINEAR;
		} else if (arguments.length < 3) {
			minFilter = context.LINEAR;
		}
		return thisObject.queue(function (next) {
			textures[id] = new context.AsyncTexture(id, next, minFilter, magFilter);
		});
	};

	/**
	 * Queues asynchronous tasks that load and create zero or more textures
	 * given their URLs.
	 *
	 * Internally the `queueTextures` method uses the
	 * {{#crossLink "context.AsyncTexture"}}AsyncTexture{{/crossLink}} class.
	 *
	 * After being loaded, the textures can be retrieved via the
	 * {{#crossLink "OOGL.Loader/getTexture"}}getTexture{{/crossLink}} method as
	 * {{#crossLink "context.Texture2D"}}Texture2D{{/crossLink}} objects.
	 *
	 * @method queueTextures
	 * @chainable
	 * @param ids {String[]} An array of image URLs.
	 * @example
	 *	TODO
	 */
	this.queueTextures = function (ids) {
		return thisObject.queue.apply(thisObject, ids.map(function (id) {
			return function (next) {
				textures[id] = new context.AsyncTexture(id, next);
			};
		}));
	};

	/**
	 * Retrieves a {{#crossLink "context.Texture2D"}}Texture2D{{/crossLink}}
	 * object loaded from the image identified by the specified URL.
	 *
	 * @method getTexture
	 * @param id {String} The URL of a previously loaded texture image.
	 * @return {context.Texture2D} A `Texture2D` object representing the GL
	 * texture.
	 * @example
	 *	TODO
	 */
	this.getTexture = function (id) {
		if (textures.hasOwnProperty(id)) {
			return textures[id];
		}
	};

	/**
	 * Queues an asynchronous task that loads, compiles and links the specified
	 * shader pair, and returns it as a
	 * {{#crossLink "context.Program"}}Program{{/crossLink}} object.
	 *
	 * Internally the `queueProgram` method uses the
	 * {{#crossLink "context.AjaxProgram"}}AjaxProgram{{/crossLink}} class.
	 *
	 * The loaded program can then be retrieved using the
	 * {{#crossLink "context.Loader/getProgram"}}getProgram{{/crossLink}}
	 * method.
	 *
	 * @method queueProgram
	 * @chainable
	 * @param id {String} The URL of the GLSL shaders to load, excluding the
	 * filename extension which is automatically appended (`.frag` for fragment
	 * shaders and `.vert` for vertex shaders).
	 * @param attributes {String[]} An array of attribute array variable names
	 * to associate to array indices. See the
	 * {{#crossLink "context.AjaxProgram"}}AjaxProgam{{/crossLink}} class for
	 * more information.
	 * @example
	 *	TODO
	 */
	this.queueProgram = function (id, attributes) {
		return thisObject.queue(function (next) {
			programs[id] = new context.AjaxProgram(id, attributes, next);
		});
	};

	/**
	 * Queues asynchronous tasks that load, compile and link zero or more shader
	 * pairs, and return them as
	 * {{#crossLink "context.Program"}}Program{{/crossLink}} objects.
	 *
	 * Internally the `queuePrograms` method uses the
	 * {{#crossLink "context.AjaxProgram"}}AjaxProgram{{/crossLink}} class to
	 * load each shader pair.
	 *
	 * The loaded programs can then be retrieved using the
	 * {{#crossLink "context.Loader/getProgram"}}getProgram{{/crossLink}}
	 * method.
	 *
	 * @method queuePrograms
	 * @chainable
	 * @param map {Object} A dictionary object that associates shader pair URLs
	 * to attribute arrays. For example:
	 *
	 *	{
	 *		"glsl/box": ["in_Vertex", "in_TexCoord"]
	 *		"glsl/sphere": ["in_Vertex", "in_Normal", "in_TexCoord"]
	 *	}
	 *
	 * In this example we assumed there is a `glsl` directory containing two
	 * shaders pairs: `box.vert`, `box.frag`, `sphere.vert` and `sphere.frag`.
	 * Besides, the `box` shaders use two attribute arrays which are `in_Vertex`
	 * and `in_TexCoord`, while the `sphere` shaders use three attribute arrays
	 * which are `in_Vertex`, `in_Normal` and `in_TexCoord`.
	 * @example
	 *	TODO
	 */
	this.queuePrograms = function (map) {
		for (var id in map) {
			if (map.hasOwnProperty(id)) {
				thisObject.queue((function (id, attributes) {
					return function (next) {
						programs[id] = new context.AjaxProgram(id, attributes, next);
					};
				})(id, map[id]));
			}
		}
		return thisObject;
	};

	/**
	 * Retrieves a {{#crossLink "context.Program"}}Program{{/crossLink}} object
	 * loaded from the shader pair identified by the specified URL.
	 *
	 * @method getProgram
	 * @param id {String} The URL of a previously loaded shader pair, not
	 * including the filename extension.
	 * @return {context.Program} A `Program` object representing the GL program.
	 * @example
	 *	TODO
	 */
	this.getProgram = function (id) {
		if (programs.hasOwnProperty(id)) {
			return programs[id];
		}
	};

	/**
	 * Queues a task that loads a file of the specified type via AJAX.
	 *
	 * @method queueData
	 * @param id {String} The URL of the file.
	 * @param [data] {Object} An optional object containing parameters to be
	 * passed to the server in the AJAX request. It specified directly to the
	 * {{#crossLink "OOGL.Ajax/get"}}OOGL.Ajax.get{{/crossLink}} method.
	 * @param type {String} The data type, specified directly to the
	 * {{#crossLink "OOGL.Ajax/get"}}OOGL.Ajax.get{{/crossLink}} method.
	 * @example
	 *	TODO
	 */
	this.queueData = function (id, data, type) {
		if (arguments.length > 2) {
			return thisObject.queue(function (next) {
				OOGL.Ajax.get(id, data, function () {
					next();
				}, type);
			});
		} else {
			type = data;
			return thisObject.queue(function (next) {
				OOGL.Ajax.get(id, function () {
					next();
				}, type);
			});
		}
	};

	/**
	 * Queues a task that loads JSON data via AJAX.
	 *
	 * @method queueJSON
	 * @param id {String} The URL of the JSON data.
	 * @param [data] {Object} An optional object containing parameters to be
	 * passed to the server in the AJAX request. It specified directly to the
	 * {{#crossLink "OOGL.Ajax/get"}}OOGL.Ajax.getJSON{{/crossLink}} method.
	 * @example
	 *	TODO
	 */
	this.queueJSON = function (id, data) {
		if (arguments.length > 1) {
			return thisObject.queue(function (next) {
				OOGL.Ajax.getJSON(id, data, function () {
					next();
				});
			});
		} else {
			return thisObject.queue(function (next) {
				OOGL.Ajax.getJSON(id, function () {
					next();
				});
			});
		}
	};

	/**
	 * Retrieves data previously loaded via the
	 * {{#crossLink "context.Loader/queueData"}}queueData{{/crossLink}} or
	 * {{#crossLink "context.Loader/queueJSON"}}queueJSON{{/crossLink}} method.
	 *
	 * @method getData
	 * @param id {String} The URL of the requested data.
	 * @return {Object} The requested data.
	 * @example
	 *	TODO
	 */
	this.getData = function (id) {
		if (data.hasOwnProperty(id)) {
			return data[id];
		}
	};
};
<commit_msg>Bugfix<commit_after>/*global OOGL: false */
/*global context: false */

/**
 * @module context
 */

/**
 * Manages asynchronous asset loading with progress feedback.
 *
 * @class context.Loader
 * @extends OOGL.TaskQueue
 * @constructor
 * @param tasks* {Function} Zero or more asynchronous tasks to queue. See the
 * {{#crossLink "OOGL.TaskQueue"}}TaskQueue{{/crossLink}} description for more
 * information.
 * @example
 *	TODO
 */
context.Loader = function () {
	OOGL.TaskQueue.apply(this, arguments);
	var thisObject = this;

	var textures = {};
	var programs = {};
	var data = {};

	/**
	 * Queues an asynchronous task that loads and creates a texture given its
	 * URL.
	 *
	 * Internally the `queueTexture` method uses the
	 * {{#crossLink "context.AsyncTexture"}}AsyncTexture{{/crossLink}} class.
	 *
	 * After being loaded, the texture can be retrieved via the
	 * {{#crossLink "OOGL.Loader/getTexture"}}getTexture{{/crossLink}} method as
	 * a {{#crossLink "context.Texture2D"}}Texture2D{{/crossLink}} object.
	 *
	 * @method queueTexture
	 * @chainable
	 * @param id {String} The URL of the texture image to load.
	 * @param [magFilter=gl.LINEAR] {Number} An optional value for the
	 * magnifying filter. See
	 * {{#crossLink "context.AsyncTexture"}}AsyncTexture{{/crossLink}} for more
	 * information.
	 * @param [minFilter=gl.LINEAR] {Number} An optional value for the minifying
	 * filter. See
	 * {{#crossLink "context.AsyncTexture"}}AsyncTexture{{/crossLink}} for more
	 * information.
	 * @example
	 *	TODO
	 */
	this.queueTexture = function (id, minFilter, magFilter) {
		if (arguments.length < 2) {
			magFilter = context.LINEAR;
			minFilter = context.LINEAR;
		} else if (arguments.length < 3) {
			minFilter = context.LINEAR;
		}
		return thisObject.queue(function (next) {
			textures[id] = new context.AsyncTexture(id, next, minFilter, magFilter);
		});
	};

	/**
	 * Queues asynchronous tasks that load and create zero or more textures
	 * given their URLs.
	 *
	 * Internally the `queueTextures` method uses the
	 * {{#crossLink "context.AsyncTexture"}}AsyncTexture{{/crossLink}} class.
	 *
	 * After being loaded, the textures can be retrieved via the
	 * {{#crossLink "OOGL.Loader/getTexture"}}getTexture{{/crossLink}} method as
	 * {{#crossLink "context.Texture2D"}}Texture2D{{/crossLink}} objects.
	 *
	 * @method queueTextures
	 * @chainable
	 * @param ids {String[]} An array of image URLs.
	 * @example
	 *	TODO
	 */
	this.queueTextures = function (ids) {
		return thisObject.queue.apply(thisObject, ids.map(function (id) {
			return function (next) {
				textures[id] = new context.AsyncTexture(id, next);
			};
		}));
	};

	/**
	 * Retrieves a {{#crossLink "context.Texture2D"}}Texture2D{{/crossLink}}
	 * object loaded from the image identified by the specified URL.
	 *
	 * @method getTexture
	 * @param id {String} The URL of a previously loaded texture image.
	 * @return {context.Texture2D} A `Texture2D` object representing the GL
	 * texture.
	 * @example
	 *	TODO
	 */
	this.getTexture = function (id) {
		if (textures.hasOwnProperty(id)) {
			return textures[id];
		}
	};

	/**
	 * Queues an asynchronous task that loads, compiles and links the specified
	 * shader pair, and returns it as a
	 * {{#crossLink "context.Program"}}Program{{/crossLink}} object.
	 *
	 * Internally the `queueProgram` method uses the
	 * {{#crossLink "context.AjaxProgram"}}AjaxProgram{{/crossLink}} class.
	 *
	 * The loaded program can then be retrieved using the
	 * {{#crossLink "context.Loader/getProgram"}}getProgram{{/crossLink}}
	 * method.
	 *
	 * @method queueProgram
	 * @chainable
	 * @param id {String} The URL of the GLSL shaders to load, excluding the
	 * filename extension which is automatically appended (`.frag` for fragment
	 * shaders and `.vert` for vertex shaders).
	 * @param attributes {String[]} An array of attribute array variable names
	 * to associate to array indices. See the
	 * {{#crossLink "context.AjaxProgram"}}AjaxProgam{{/crossLink}} class for
	 * more information.
	 * @example
	 *	TODO
	 */
	this.queueProgram = function (id, attributes) {
		return thisObject.queue(function (next) {
			programs[id] = new context.AjaxProgram(id, attributes, next);
		});
	};

	/**
	 * Queues asynchronous tasks that load, compile and link zero or more shader
	 * pairs, and return them as
	 * {{#crossLink "context.Program"}}Program{{/crossLink}} objects.
	 *
	 * Internally the `queuePrograms` method uses the
	 * {{#crossLink "context.AjaxProgram"}}AjaxProgram{{/crossLink}} class to
	 * load each shader pair.
	 *
	 * The loaded programs can then be retrieved using the
	 * {{#crossLink "context.Loader/getProgram"}}getProgram{{/crossLink}}
	 * method.
	 *
	 * @method queuePrograms
	 * @chainable
	 * @param map {Object} A dictionary object that associates shader pair URLs
	 * to attribute arrays. For example:
	 *
	 *	{
	 *		"glsl/box": ["in_Vertex", "in_TexCoord"]
	 *		"glsl/sphere": ["in_Vertex", "in_Normal", "in_TexCoord"]
	 *	}
	 *
	 * In this example we assumed there is a `glsl` directory containing two
	 * shaders pairs: `box.vert`, `box.frag`, `sphere.vert` and `sphere.frag`.
	 * Besides, the `box` shaders use two attribute arrays which are `in_Vertex`
	 * and `in_TexCoord`, while the `sphere` shaders use three attribute arrays
	 * which are `in_Vertex`, `in_Normal` and `in_TexCoord`.
	 * @example
	 *	TODO
	 */
	this.queuePrograms = function (map) {
		for (var id in map) {
			if (map.hasOwnProperty(id)) {
				thisObject.queue((function (id, attributes) {
					return function (next) {
						programs[id] = new context.AjaxProgram(id, attributes, next);
					};
				})(id, map[id]));
			}
		}
		return thisObject;
	};

	/**
	 * Retrieves a {{#crossLink "context.Program"}}Program{{/crossLink}} object
	 * loaded from the shader pair identified by the specified URL.
	 *
	 * @method getProgram
	 * @param id {String} The URL of a previously loaded shader pair, not
	 * including the filename extension.
	 * @return {context.Program} A `Program` object representing the GL program.
	 * @example
	 *	TODO
	 */
	this.getProgram = function (id) {
		if (programs.hasOwnProperty(id)) {
			return programs[id];
		}
	};

	/**
	 * Queues a task that loads a file of the specified type via AJAX.
	 *
	 * @method queueData
	 * @param id {String} The URL of the file.
	 * @param [parameters] {Object} An optional object containing parameters to
	 * be passed to the server in the AJAX request. It specified directly to the
	 * {{#crossLink "OOGL.Ajax/get"}}OOGL.Ajax.get{{/crossLink}} method.
	 * @param type {String} The data type, specified directly to the
	 * {{#crossLink "OOGL.Ajax/get"}}OOGL.Ajax.get{{/crossLink}} method.
	 * @example
	 *	TODO
	 */
	this.queueData = function (id, parameters, type) {
		if (arguments.length > 2) {
			return thisObject.queue(function (next) {
				OOGL.Ajax.get(id, parameters, function (response) {
					data[id] = response;
					next();
				}, type);
			});
		} else {
			type = data;
			return thisObject.queue(function (next) {
				OOGL.Ajax.get(id, function (response) {
					data[id] = response;
					next();
				}, type);
			});
		}
	};

	/**
	 * Queues a task that loads JSON data via AJAX.
	 *
	 * @method queueJSON
	 * @param id {String} The URL of the JSON data.
	 * @param [parameters] {Object} An optional object containing parameters to
	 * be passed to the server in the AJAX request. It specified directly to the
	 * {{#crossLink "OOGL.Ajax/get"}}OOGL.Ajax.getJSON{{/crossLink}} method.
	 * @example
	 *	TODO
	 */
	this.queueJSON = function (id, parameters) {
		if (arguments.length > 1) {
			return thisObject.queue(function (next) {
				OOGL.Ajax.getJSON(id, parameters, function (response) {
					data[id] = response;
					next();
				});
			});
		} else {
			return thisObject.queue(function (next) {
				OOGL.Ajax.getJSON(id, function (response) {
					data[id] = response;
					next();
				});
			});
		}
	};

	/**
	 * Retrieves data previously loaded via the
	 * {{#crossLink "context.Loader/queueData"}}queueData{{/crossLink}} or
	 * {{#crossLink "context.Loader/queueJSON"}}queueJSON{{/crossLink}} method.
	 *
	 * @method getData
	 * @param id {String} The URL of the requested data.
	 * @return {Object} The requested data.
	 * @example
	 *	TODO
	 */
	this.getData = function (id) {
		if (data.hasOwnProperty(id)) {
			return data[id];
		}
	};
};
<|endoftext|>


<commit_before>fb_app_ecosol_store = {

  selected_empreendimento: null,

  init_autocomplete: function() {

      var self = this;

      var profilesSearch = new Bloodhound({
          datumTokenizer: Bloodhound.tokenizers.obj.whitespace('name'),
          queryTokenizer: Bloodhound.tokenizers.whitespace,
          remote: '/plugin/fb_app_ecosol_store/search?query=%QUERY'
      });

      profilesSearch.initialize();

      var input = jQuery('[name=fb_store]');

      input.typeahead(null, {
          name: 'fb_search_store',
          displayKey: 'name',
          source: profilesSearch.ttAdapter()
      });

      input.on('typeahead:selected typeahead:autocompleted', function(object, datum){
        self.selected_empreendimento = datum
      });

      input.on('keydown', function() {
          self.selected_empreendimento = null
      });

      jQuery('#fb_store_add_profile').on('click',this.add_empreendimento.bind(this));
  },

  add_empreendimento: function(evt) {
    if (evt != null && evt != void 0) { evt.preventDefault(); evt.stopPropagation();}
    if (this.selected_empreendimento != null) {
        empreendimentos.push(this.selected_empreendimento);
        this.selected_empreendimento = null;
        jQuery('[name=fb_store]').val('');
        this.redraw_tabela_empreendimentos();
    }
  },

  addJS: function(url) {
    var script = document.createElement('script'); script.type = 'text/javascript'; script.src = url;
    document.getElementsByTagName('head')[0].appendChild(script);
  },

  init_admin: function() {
    if (jQuery('#fb_store_admin_page').length > 0) {
        this.init_integration_type_selection();
        this.init_autocomplete();
        this.redraw_tabela_empreendimentos();
        //init commit button
        jQuery('#fb_commit_btn').on('click',this.commit.bind(this));
    }
  },

  init_integration_type_selection: function() {
      var func_on_radio_change;

      func_on_radio_change = function(evt) {
        var radio_elements = jQuery('[name=fb_integration_type]');

        radio_elements.each(function(key,elm) {
            if (elm.checked) {
                jQuery('#fb_integration_type_'+elm.value).show();
            } else {
                jQuery('#fb_integration_type_'+elm.value).hide();
            }
        })
      };

      jQuery('[name=fb_integration_type]').each(function(key,radio_elm) {
        var $radio_elm = jQuery(radio_elm);
        $radio_elm.on('change',func_on_radio_change);
        $radio_elm.trigger('change');
      })

      if (empreendimentos.length > 0) {
          jQuery('[name=fb_integration_type][value=profiles]').click()
      } else {
          jQuery('[name=fb_integration_type][value=query]').click()

      }
  },

  redraw_tabela_empreendimentos: function() {
      var i, tbody, tr_html, tr, remove_button, self;
      self = this;
      tbody = jQuery('#fb_tabela_empreendimentos');
      tbody.empty();
      for (i=0;i<empreendimentos.length;i++) {
          tr_html  = '
';
          tr_html += ''+empreendimentos[i].name+'	';
          tr_html += '	';
          tr_html += '
';
          tr = jQuery(tr_html);
          tbody.append(tr);
          remove_button = jQuery('<button data-profile-pos="'+i+'">Remover</button>');
          remove_button.on('click',function(evt) {
            var pos = parseInt(jQuery(evt.target).data('profile-pos'));
            empreendimentos.splice(pos,1);
            self.redraw_tabela_empreendimentos();
          });
          tr.find('.remove-btn-holder').append(remove_button);
      }
  },

  commit: function(evt) {
      if (evt != null && evt != void 0) { evt.preventDefault(); evt.stopPropagation();}

      var profile_ids = []
      for (var i=0;i<empreendimentos.length;i++) {
          profile_ids.push(empreendimentos[i].id)
      }

      var data = {
          integration_type: jQuery( "[name=fb_integration_type]:checked" ).val(),
          keyword: jQuery('[name=fb_keyword]').val(),
          profile_ids: profile_ids,
          page_id: window.page_id,
      }

      jQuery.ajax({
          url: '/plugin/fb_app_ecosol_store/admin',
          type: 'post',
          dataType: 'json',
          data: data,
          success: function(data) {
            alert('OK!')
          },
          error: function() {
              alert('Erro!')
          }
      });

  },

  fb: {
    id: '',
    page_tab_next: '',

    init: function(id, next) {
      this.id = id;
      this.page_tab_next = next;

      window.fbAsyncInit = function() {

        FB.init({
          appId      : id,
          status     : true,
          xfbml      : true
        });
      };

      (function(d, s, id){
        var js, fjs = d.getElementsByTagName(s)[0];
        if (d.getElementById(id)) {return;}
        js = d.createElement(s); js.id = id;
        js.src = "//connect.facebook.net/en_US/all.js";
        fjs.parentNode.insertBefore(js, fjs);
      }(document, 'script', 'facebook-jssdk'));

    },

    add_tab: function () {
      FB.ui({
        method: 'pagetab',
      }, function(response){
        var i=0;
        var url = fb_app_ecosol_store.base_url + "/admin?";
        jQuery.each(response.tabs_added, function(tab,v) {
          url += 'page_id['+(i++)+']='+tab+'&';
        });
        window.location.href = url;
      });
    },

    login: function() {
      FB.getLoginStatus(function(response) {
        if (response.status === 'connected') {
          fb_app_ecosol_store.fb.add_tab();
        } else {
          FB.login(function(response) {
            if (response.authResponse) {
              fb_app_ecosol_store.fb.add_tab();
            } else {
              alert('Infelizmente, sem sua autorização não poderemos inserir a loja do cirandas no seu facebook');
            }
          });
        }
      });
    },
  },
};

jQuery('document').ready(function(){
  fb_app_ecosol_store.init_admin();
});

fb_app_ecosol_store.addJS('http://dtygel.eita.org.br/lojacirandasfb/utils.js');

<commit_msg>Set auto grow<commit_after>fb_app_ecosol_store = {

  selected_empreendimento: null,

  init_autocomplete: function() {

      var self = this;

      var profilesSearch = new Bloodhound({
          datumTokenizer: Bloodhound.tokenizers.obj.whitespace('name'),
          queryTokenizer: Bloodhound.tokenizers.whitespace,
          remote: '/plugin/fb_app_ecosol_store/search?query=%QUERY'
      });

      profilesSearch.initialize();

      var input = jQuery('[name=fb_store]');

      input.typeahead(null, {
          name: 'fb_search_store',
          displayKey: 'name',
          source: profilesSearch.ttAdapter()
      });

      input.on('typeahead:selected typeahead:autocompleted', function(object, datum){
        self.selected_empreendimento = datum
      });

      input.on('keydown', function() {
          self.selected_empreendimento = null
      });

      jQuery('#fb_store_add_profile').on('click',this.add_empreendimento.bind(this));
  },

  add_empreendimento: function(evt) {
    if (evt != null && evt != void 0) { evt.preventDefault(); evt.stopPropagation();}
    if (this.selected_empreendimento != null) {
        empreendimentos.push(this.selected_empreendimento);
        this.selected_empreendimento = null;
        jQuery('[name=fb_store]').val('');
        this.redraw_tabela_empreendimentos();
    }
  },

  addJS: function(url) {
    var script = document.createElement('script'); script.type = 'text/javascript'; script.src = url;
    document.getElementsByTagName('head')[0].appendChild(script);
  },

  init_admin: function() {
    if (jQuery('#fb_store_admin_page').length > 0) {
        this.init_integration_type_selection();
        this.init_autocomplete();
        this.redraw_tabela_empreendimentos();
        //init commit button
        jQuery('#fb_commit_btn').on('click',this.commit.bind(this));
    }
  },

  init_integration_type_selection: function() {
      var func_on_radio_change;

      func_on_radio_change = function(evt) {
        var radio_elements = jQuery('[name=fb_integration_type]');

        radio_elements.each(function(key,elm) {
            if (elm.checked) {
                jQuery('#fb_integration_type_'+elm.value).show();
            } else {
                jQuery('#fb_integration_type_'+elm.value).hide();
            }
        })
      };

      jQuery('[name=fb_integration_type]').each(function(key,radio_elm) {
        var $radio_elm = jQuery(radio_elm);
        $radio_elm.on('change',func_on_radio_change);
        $radio_elm.trigger('change');
      })

      if (empreendimentos.length > 0) {
          jQuery('[name=fb_integration_type][value=profiles]').click()
      } else {
          jQuery('[name=fb_integration_type][value=query]').click()

      }
  },

  redraw_tabela_empreendimentos: function() {
      var i, tbody, tr_html, tr, remove_button, self;
      self = this;
      tbody = jQuery('#fb_tabela_empreendimentos');
      tbody.empty();
      for (i=0;i<empreendimentos.length;i++) {
          tr_html  = '
';
          tr_html += ''+empreendimentos[i].name+'	';
          tr_html += '	';
          tr_html += '
';
          tr = jQuery(tr_html);
          tbody.append(tr);
          remove_button = jQuery('<button data-profile-pos="'+i+'">Remover</button>');
          remove_button.on('click',function(evt) {
            var pos = parseInt(jQuery(evt.target).data('profile-pos'));
            empreendimentos.splice(pos,1);
            self.redraw_tabela_empreendimentos();
          });
          tr.find('.remove-btn-holder').append(remove_button);
      }
  },

  commit: function(evt) {
      if (evt != null && evt != void 0) { evt.preventDefault(); evt.stopPropagation();}

      var profile_ids = []
      for (var i=0;i<empreendimentos.length;i++) {
          profile_ids.push(empreendimentos[i].id)
      }

      var data = {
          integration_type: jQuery( "[name=fb_integration_type]:checked" ).val(),
          keyword: jQuery('[name=fb_keyword]').val(),
          profile_ids: profile_ids,
          page_id: window.page_id,
      }

      jQuery.ajax({
          url: '/plugin/fb_app_ecosol_store/admin',
          type: 'post',
          dataType: 'json',
          data: data,
          success: function(data) {
            alert('OK!')
          },
          error: function() {
              alert('Erro!')
          }
      });

  },

  fb: {
    id: '',
    page_tab_next: '',

    init: function(id, next) {
      this.id = id;
      this.page_tab_next = next;

      window.fbAsyncInit = function() {

        FB.init({
          appId      : id,
          status     : true,
          xfbml      : true
        });

        FB.Canvas.setAutoGrow();
      };

      (function(d, s, id){
        var js, fjs = d.getElementsByTagName(s)[0];
        if (d.getElementById(id)) {return;}
        js = d.createElement(s); js.id = id;
        js.src = "//connect.facebook.net/en_US/all.js";
        fjs.parentNode.insertBefore(js, fjs);
      }(document, 'script', 'facebook-jssdk'));

    },

    add_tab: function () {
      FB.ui({
        method: 'pagetab',
      }, function(response){
        var i=0;
        var url = fb_app_ecosol_store.base_url + "/admin?";
        jQuery.each(response.tabs_added, function(tab,v) {
          url += 'page_id['+(i++)+']='+tab+'&';
        });
        window.location.href = url;
      });
    },

    login: function() {
      FB.getLoginStatus(function(response) {
        if (response.status === 'connected') {
          fb_app_ecosol_store.fb.add_tab();
        } else {
          FB.login(function(response) {
            if (response.authResponse) {
              fb_app_ecosol_store.fb.add_tab();
            } else {
              alert('Infelizmente, sem sua autorização não poderemos inserir a loja do cirandas no seu facebook');
            }
          });
        }
      });
    },
  },
};

jQuery('document').ready(function(){
  fb_app_ecosol_store.init_admin();
});

fb_app_ecosol_store.addJS('http://dtygel.eita.org.br/lojacirandasfb/utils.js');

<|endoftext|>