<commit_before>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2016-2017 The Regents of the University of California
 * Author: Jim Robinson
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

var igv = (function (igv) {

    igv.UCSCServiceReader = function (config) {
        this.config = config;
    };

    igv.UCSCServiceReader.prototype.readFeatures = function (chr, start, end) {
        var self = this,
            url = this.config.url + '&table=' + this.config.tableName + '&chr=' + chr + '&start=' + start + '&end=' + end;

        return new Promise(function (fulfill, reject) {
            igv.xhr.loadJson(url, self.config)
                .then(function (data) {
                    if (data) {
                        fulfill(data);
                    } else {
                        fulfill(null);
                    }
                })
                .catch(function (error) {
                    reject(error);
                });
        });
    };

    return igv;
})(igv || {});
<commit_msg>add exons to ucsc gene track (#437)<commit_after>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2016-2017 The Regents of the University of California
 * Author: Jim Robinson
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

var igv = (function (igv) {

    igv.UCSCServiceReader = function (config) {
        this.config = config;
    };

    igv.UCSCServiceReader.prototype.readFeatures = function (chr, start, end) {
        var self = this,
            url = this.config.url + '&table=' + this.config.tableName + '&chr=' + chr + '&start=' + start + '&end=' + end;

        return new Promise(function (fulfill, reject) {
            igv.xhr.loadJson(url, self.config)
                .then(function (data) {
                    if (data) {
                        data.forEach(function(sample) {
                            if (sample.hasOwnProperty('exonStarts') && sample.hasOwnProperty('exonEnds') && sample.hasOwnProperty('exonCount')
                                && sample.hasOwnProperty('cdsStart') && sample.hasOwnProperty('cdsEnd')) {

                                addExons(sample);
                            }
                        });
                        fulfill(data);
                    } else {
                        fulfill(null);
                    }
                })
                .catch(function (error) {
                    reject(error);
                });
        });
    };

    function addExons(sample) {
        var exonCount, exonStarts, exonEnds, exons, eStart, eEnd;
        exonCount = sample['exonCount'];
        exonStarts = sample['exonStarts'].split(',');
        exonEnds = sample['exonEnds'].split(',');
        exons = [];

        for (var i = 0; i < exonCount; i++) {
            eStart = parseInt(exonStarts[i]);
            eEnd = parseInt(exonEnds[i]);
            var exon = {start: eStart, end: eEnd};

            if (sample.cdsStart > eEnd || sample.cdsEnd < sample.cdsStart) exon.utr = true;   // Entire exon is UTR
            if (sample.cdsStart >= eStart && sample.cdsStart <= eEnd) exon.cdStart = sample.cdsStart;
            if (sample.cdsEnd >= eStart && sample.cdsEnd <= eEnd) exon.cdEnd = sample.cdsEnd;

            exons.push(exon);
        }

        sample.exons = exons;
    }

    return igv;
})(igv || {});
<|endoftext|>


<commit_before>
/**
 * A component for modifying the configuration settings on the "current" server.
 *
 * TODO: This has turned into major poop. The actual config getting/setting should be moved
 *       to morpheus.neo4j, and be loaded by default when a server is loaded. This component
 *       should simply provide a UI to that.
 */
wa.components.config.Config = (function($, undefined) {

    var me = {};

    //
    // PRIVATE
    //

    me.basePage = $("

");
    me.ui = {};

    me.uiLoaded = false;
    me.uiNeedsUpdate = true;
    me.server = null;

    /**
     * Configuration lookup map for the server currently beeing operated on.
     */
    me.config = null;
    me.loadingConfig = false;

    /**
     * Pending requests for config data, these will be processed once data has loaded.
     */
    me.pendingConfigRequests = [];

    me.visible = false;

    me.getPage = function() {
        return me.basePage;
    };

    me.pageChanged = function(ev) {

        if(ev.data === "config") {

            me.visible = true;

            if( me.uiLoaded === false ) {
            	me.uiLoaded = true;
                me.basePage.setTemplateURL("templates/components/config/index.tp");
            }

            if ( me.config !== null ) {
            	me.render();
            }

        } else {
            me.visible = false;
        }
    };

    me.getUncommittedChanges = function(args) {
        var args = args || {};
        var changed = {};
        for( var key in me.config ) {
            if( me.config[key].newValue !== undefined && ( !args.excludeType || me.config[key].type !== args.excludeType)) {
                changed[key] = me.config[key].newValue;
            }
        }

        return changed;
    };

    /**
     * Set all changes committed.
     */
    me.allChangesCommitted = function(args) {
        var args = args || {};
        for( var key in me.config) {
            if( me.config[key].newValue !== undefined && ( !args.excludeType || me.config[key].type !== args.excludeType)) {
                me.config[key].value = me.config[key].newValue;
                delete(me.config[key].newValue);
            }
        }

        $(".mor_config_value",me.basePage).removeClass('uncommitted');
    };

    /**
     * Enable save button if there are uncommitted changes. Disable it otherwise.
     */
    me.updateSaveButtonState = function() {
        if( hasUncommittedChanges() ) {
            $("input#mor_setting_save",me.basePage).removeAttr('disabled');
        } else {
            $("input#mor_setting_save",me.basePage).attr('disabled', 'disabled');
        }
    };

    function hasUncommittedChanges () {
        for(var key in me.getUncommittedChanges({excludeType:"DB_CREATION_PROPERTY"})) {
            return true;
        }
        return false;
    }

    function loadConfig() {

    	me.loadingConfig = true;

    	getRemoteProperties(function(properties) {
            me.config = {};

            for( var index in properties ) {
                me.config[properties[index].key] = properties[index];
            }

            // Process pending config requests
            for( var index in me.pendingConfigRequests ) {
                me.pendingConfigRequests[index].cb( me.config[me.pendingConfigRequests[index].key] );
            }

            me.render();

            me.loadingConfig = false;
        });

    };

    me.render = function() {
    	if( me.uiLoaded ) {
	    	var config = [], advanced_config = [], jvm_config = [], general_config = [];

	        for( var index in me.config ) {
	            if(me.config[index].type === "DB_CREATION_PROPERTY") {
	                advanced_config.push(me.config[index]);
	            } else if(me.config[index].type === "JVM_ARGUMENT") {
	                jvm_config.push(me.config[index]);
	            } else if(me.config[index].type === "GENERAL_PROPERTY") {
	                general_config.push(me.config[index]);
	            } else {
	                config.push(me.config[index]);
	            }
	        }

	    	me.basePage.processTemplate({
	            config : config,
	            jvm_config: jvm_config,
	            advanced_config : advanced_config,
	            server : me.server
	        });
    	}
    };

    me.configValueChanged = function(ev){
        var el = $(ev.target);

        var key = el.attr('name');

        if( me.config[key] !== undefined ) {

        	// Handle checkboxes
	        if( el.attr("type") === "checkbox" ) {
	        	if(el.attr('checked')) {
	        		var value = me.config[key].definition.values[0].value;
	        	} else {
	        		if(me.config[key].definition.values.length > 1) {
		        		// Toggle between two values
	        			var value = me.config[key].definition.values[1].value;
	        		} else {
	        			// Toggle between some value and no value at all
	        			var value = "";
	        		}
	        	}
	        } else {
	        	var value = el.val();
	        }

	        // Handle everything else
            if( value !== me.config[key].value ) {
                me.config[key].newValue = value;
                el.addClass("uncommitted");
            } else {
                delete(me.config[key].newValue);
                el.removeClass("uncommitted");
            }

            me.updateSaveButtonState();
        }
    };

    function getRemoteProperties(callback) {
        wa.Servers.getCurrentServer().manage.config.getProperties(callback);
    }

    function setRemoteProperties(settings, callback) {
        wa.Servers.getCurrentServer().manage.config.setProperties(settings, callback);
    }

    //
    // CONSTRUCT
    //

    /**
     * Hook event listeners to the UI.
     */
    $('.mor_config_value').live('keyup',me.configValueChanged);
    $('.mor_config_value').live('change',me.configValueChanged);
    $('.mor_config_value').live('click',me.configValueChanged);

    /**
     * Saving changes to normal config settings.
     */
    $("#mor_setting_save").live('click',function() {

        if(hasUncommittedChanges()) {

            // Find all settings that are changed
            var changed = me.getUncommittedChanges({excludeType:"DB_CREATION_PROPERTY"});

            // Commit changes to server
            wa.ui.Loading.show("Hold on..", "Waiting for changes to be applied..");

            // Disable controls while saving
            $("input",me.basePage).attr('disabled', 'disabled');

            setRemoteProperties(changed,function(data){
                setTimeout(function() {
                    wa.Servers.getCurrentServer().heartbeat.waitForPulse(function() {
                        wa.ui.Loading.hide();
                        window.location.reload();
                    });
                }, 100);
            });
        }
    });

    wa.bind("servers.current.changed", loadConfig);

    //
    // PUBLIC INTERFACE
    //

    return {
        getPage : me.getPage,
        pageChanged : me.pageChanged
    };

})(jQuery);

//
// REGISTER STUFF
//

wa.ui.Pages.add("config",wa.components.config.Config);
wa.ui.MainMenu.add({ label : "Configuration", pageKey:"config", index:3, requiredServices:['config'], perspectives:['server']});

wa.bind("ui.page.changed", wa.components.config.Config.pageChanged);<commit_msg>Stopped showing hidden settings in client config UI.<commit_after>
/**
 * A component for modifying the configuration settings on the "current" server.
 *
 * TODO: This has turned into major poop. The actual config getting/setting should be moved
 *       to morpheus.neo4j, and be loaded by default when a server is loaded. This component
 *       should simply provide a UI to that.
 */
wa.components.config.Config = (function($, undefined) {

    var me = {};

    //
    // PRIVATE
    //

    me.basePage = $("

");
    me.ui = {};

    me.uiLoaded = false;
    me.uiNeedsUpdate = true;
    me.server = null;

    /**
     * Configuration lookup map for the server currently beeing operated on.
     */
    me.config = null;
    me.loadingConfig = false;

    /**
     * Pending requests for config data, these will be processed once data has loaded.
     */
    me.pendingConfigRequests = [];

    me.visible = false;

    me.getPage = function() {
        return me.basePage;
    };

    me.pageChanged = function(ev) {

        if(ev.data === "config") {

            me.visible = true;

            if( me.uiLoaded === false ) {
            	me.uiLoaded = true;
                me.basePage.setTemplateURL("templates/components/config/index.tp");
            }

            if ( me.config !== null ) {
            	me.render();
            }

        } else {
            me.visible = false;
        }
    };

    me.getUncommittedChanges = function(args) {
        var args = args || {};
        var changed = {};
        for( var key in me.config ) {
            if( me.config[key].newValue !== undefined && ( !args.excludeType || me.config[key].type !== args.excludeType)) {
                changed[key] = me.config[key].newValue;
            }
        }

        return changed;
    };

    /**
     * Set all changes committed.
     */
    me.allChangesCommitted = function(args) {
        var args = args || {};
        for( var key in me.config) {
            if( me.config[key].newValue !== undefined && ( !args.excludeType || me.config[key].type !== args.excludeType)) {
                me.config[key].value = me.config[key].newValue;
                delete(me.config[key].newValue);
            }
        }

        $(".mor_config_value",me.basePage).removeClass('uncommitted');
    };

    /**
     * Enable save button if there are uncommitted changes. Disable it otherwise.
     */
    me.updateSaveButtonState = function() {
        if( hasUncommittedChanges() ) {
            $("input#mor_setting_save",me.basePage).removeAttr('disabled');
        } else {
            $("input#mor_setting_save",me.basePage).attr('disabled', 'disabled');
        }
    };

    function hasUncommittedChanges () {
        for(var key in me.getUncommittedChanges({excludeType:"DB_CREATION_PROPERTY"})) {
            return true;
        }
        return false;
    }

    function loadConfig() {

    	me.loadingConfig = true;

    	getRemoteProperties(function(properties) {
            me.config = {};

            for( var index in properties ) {
                me.config[properties[index].key] = properties[index];
            }

            // Process pending config requests
            for( var index in me.pendingConfigRequests ) {
                me.pendingConfigRequests[index].cb( me.config[me.pendingConfigRequests[index].key] );
            }

            me.render();

            me.loadingConfig = false;
        });

    };

    me.render = function() {
    	if( me.uiLoaded ) {
	    	var config = [], advanced_config = [], jvm_config = [], general_config = [];

	        for( var index in me.config ) {
	            if( me.config[index].hidden !== true ) {
    	            if(me.config[index].type === "DB_CREATION_PROPERTY") {
    	                advanced_config.push(me.config[index]);
    	            } else if(me.config[index].type === "JVM_ARGUMENT") {
    	                jvm_config.push(me.config[index]);
    	            } else if(me.config[index].type === "GENERAL_PROPERTY") {
    	                general_config.push(me.config[index]);
    	            } else {
    	                config.push(me.config[index]);
    	            }
                }
	        }

	    	me.basePage.processTemplate({
	            config : config,
	            jvm_config: jvm_config,
	            advanced_config : advanced_config,
	            server : me.server
	        });
    	}
    };

    me.configValueChanged = function(ev){
        var el = $(ev.target);

        var key = el.attr('name');

        if( me.config[key] !== undefined ) {

        	// Handle checkboxes
	        if( el.attr("type") === "checkbox" ) {
	        	if(el.attr('checked')) {
	        		var value = me.config[key].definition.values[0].value;
	        	} else {
	        		if(me.config[key].definition.values.length > 1) {
		        		// Toggle between two values
	        			var value = me.config[key].definition.values[1].value;
	        		} else {
	        			// Toggle between some value and no value at all
	        			var value = "";
	        		}
	        	}
	        } else {
	        	var value = el.val();
	        }

	        // Handle everything else
            if( value !== me.config[key].value ) {
                me.config[key].newValue = value;
                el.addClass("uncommitted");
            } else {
                delete(me.config[key].newValue);
                el.removeClass("uncommitted");
            }

            me.updateSaveButtonState();
        }
    };

    function getRemoteProperties(callback) {
        wa.Servers.getCurrentServer().manage.config.getProperties(callback);
    }

    function setRemoteProperties(settings, callback) {
        wa.Servers.getCurrentServer().manage.config.setProperties(settings, callback);
    }

    //
    // CONSTRUCT
    //

    /**
     * Hook event listeners to the UI.
     */
    $('.mor_config_value').live('keyup',me.configValueChanged);
    $('.mor_config_value').live('change',me.configValueChanged);
    $('.mor_config_value').live('click',me.configValueChanged);

    /**
     * Saving changes to normal config settings.
     */
    $("#mor_setting_save").live('click',function() {

        if(hasUncommittedChanges()) {

            // Find all settings that are changed
            var changed = me.getUncommittedChanges({excludeType:"DB_CREATION_PROPERTY"});

            // Commit changes to server
            wa.ui.Loading.show("Hold on..", "Waiting for changes to be applied..");

            // Disable controls while saving
            $("input",me.basePage).attr('disabled', 'disabled');

            setRemoteProperties(changed,function(data){
                setTimeout(function() {
                    wa.Servers.getCurrentServer().heartbeat.waitForPulse(function() {
                        wa.ui.Loading.hide();
                        window.location.reload();
                    });
                }, 100);
            });
        }
    });

    wa.bind("servers.current.changed", loadConfig);

    //
    // PUBLIC INTERFACE
    //

    return {
        getPage : me.getPage,
        pageChanged : me.pageChanged
    };

})(jQuery);

//
// REGISTER STUFF
//

wa.ui.Pages.add("config",wa.components.config.Config);
wa.ui.MainMenu.add({ label : "Configuration", pageKey:"config", index:3, requiredServices:['config'], perspectives:['server']});

wa.bind("ui.page.changed", wa.components.config.Config.pageChanged);<|endoftext|>


<commit_before>Ext.ux.ErrorHandler.on('error', function(ex) {
    // zeitweise kommt aus ein fehler von chrome://skype_ff_toolbar_win/content/injection_graph_func.js:1
    // der hier ignoriert wird. (falsche / nicht mehr verfügbare toolbar?)
    // gefunden bei 2F Stargate
    if (ex.url && ex.url.substr(0, 9) == 'chrome:/'+'/') {
        return;
    }
    if (Vps.Debug.displayErrors) {
        throw ex;
    }
    Ext.Ajax.request({
        url: '/vps/error/error/json-mail',
        ignoreErrors: true,
        params: {
            url: ex.url,
            lineNumber: ex.lineNumber,
            stack: Ext.encode(ex.stack),
            message: ex.message,
            location: location.href,
            referrer: document.referrer
        }
    });
});

if (!Vps.Debug.displayErrors) {
    Ext.ux.ErrorHandler.init();
}

/**
 * message
 * title
 * mail bool
 * url
 * errorText nur für tests glaub ich, WTF?
 * checkRetry bool
 * mail bool
 * retry function
 * abort function
 */
Vps.handleError = function(error) {

    if (typeof error == 'string') error = { message: error };
    if (arguments[1]) error.title = arguments[1];
    if (arguments[2]) error.mail = arguments[2];
    if (!error.url) error.url = '';

    if ((error.checkRetry || Vps.Debug.displayErrors) && error.retry) {
        if (Vps.Debug.displayErrors) {
            title = error.title;
            msg = error.message;
        } else if (error.errorText) {
            title = error.errorText;
            msg = error.errorText;
        } else {
            title = (trlVps('Error'));
            msg = trlVps("A Server failure occured.");
            if (error.mail || (typeof error.mail == 'undefined')) {
                Ext.Ajax.request({
                    url: '/vps/error/error/json-mail',
                    params: {
                        url: error.url,
                        message: error.message,
                        location: location.href,
                        referrer: document.referrer
                    },
                    ignoreErrors: true
                });
            }
        }

            var win = new Ext.Window({
                    autoCreate : true,
                    title:title,
                    resizable:true,
                    constrain:true,
                    constrainHeader:true,
                    minimizable : false,
                    maximizable : false,
                    stateful: false,
                    modal: true,
                    shim:true,
                    buttonAlign:"center",
                    width:600,
                    minHeight: 300,
                    plain:true,
                    footer:true,
                    closable:false,
                    html: msg,
                    buttons: [{
                        text     : trlVps('Retry'),
                        handler  : function(){
                            error.retry.call(error.scope || window);
                            win.close();
                        }
                    },{
                        text     : trlVps('Abort'),
                        handler  : function(){
                            error.abort.call(error.scope || window);
                            win.close();
                        }
                }]

                });
                win.show();
    } else if (Vps.Debug.displayErrors) {
        Ext.Msg.show({
            title: error.title,
            msg: error.message,
            buttons: Ext.Msg.OK,
            modal: true,
            width: 800
        });
    } else {
        Ext.Msg.alert(trlVps('Error'), trlVps("A Server failure occured."));
        if (error.mail || (typeof error.mail == 'undefined')) {
            Ext.Ajax.request({
                url: '/vps/error/error/json-mail',
                params: {
                    url: error.url,
                    message: error.message,
                    location: location.href,
                    referrer: document.referrer
                }
            });
        }
    }
};
<commit_msg>fix indentation<commit_after>Ext.ux.ErrorHandler.on('error', function(ex) {
    // zeitweise kommt aus ein fehler von chrome://skype_ff_toolbar_win/content/injection_graph_func.js:1
    // der hier ignoriert wird. (falsche / nicht mehr verfügbare toolbar?)
    // gefunden bei 2F Stargate
    if (ex.url && ex.url.substr(0, 9) == 'chrome:/'+'/') {
        return;
    }
    if (Vps.Debug.displayErrors) {
        throw ex;
    }
    Ext.Ajax.request({
        url: '/vps/error/error/json-mail',
        ignoreErrors: true,
        params: {
            url: ex.url,
            lineNumber: ex.lineNumber,
            stack: Ext.encode(ex.stack),
            message: ex.message,
            location: location.href,
            referrer: document.referrer
        }
    });
});

if (!Vps.Debug.displayErrors) {
    Ext.ux.ErrorHandler.init();
}

/**
 * message
 * title
 * mail bool
 * url
 * errorText nur für tests glaub ich, WTF?
 * checkRetry bool
 * mail bool
 * retry function
 * abort function
 */
Vps.handleError = function(error) {

    if (typeof error == 'string') error = { message: error };
    if (arguments[1]) error.title = arguments[1];
    if (arguments[2]) error.mail = arguments[2];
    if (!error.url) error.url = '';

    if ((error.checkRetry || Vps.Debug.displayErrors) && error.retry) {
        if (Vps.Debug.displayErrors) {
            title = error.title;
            msg = error.message;
        } else if (error.errorText) {
            title = error.errorText;
            msg = error.errorText;
        } else {
            title = (trlVps('Error'));
            msg = trlVps("A Server failure occured.");
            if (error.mail || (typeof error.mail == 'undefined')) {
                Ext.Ajax.request({
                    url: '/vps/error/error/json-mail',
                    params: {
                        url: error.url,
                        message: error.message,
                        location: location.href,
                        referrer: document.referrer
                    },
                    ignoreErrors: true
                });
            }
        }

        var win = new Ext.Window({
                autoCreate : true,
                title:title,
                resizable:true,
                constrain:true,
                constrainHeader:true,
                minimizable : false,
                maximizable : false,
                stateful: false,
                modal: true,
                shim:true,
                buttonAlign:"center",
                width:600,
                minHeight: 300,
                plain:true,
                footer:true,
                closable:false,
                html: msg,
                buttons: [{
                    text     : trlVps('Retry'),
                    handler  : function(){
                        error.retry.call(error.scope || window);
                        win.close();
                    }
                },{
                    text     : trlVps('Abort'),
                    handler  : function(){
                        error.abort.call(error.scope || window);
                        win.close();
                    }
            }]
        });
        win.show();
    } else if (Vps.Debug.displayErrors) {
        Ext.Msg.show({
            title: error.title,
            msg: error.message,
            buttons: Ext.Msg.OK,
            modal: true,
            width: 800
        });
    } else {
        Ext.Msg.alert(trlVps('Error'), trlVps("A Server failure occured."));
        if (error.mail || (typeof error.mail == 'undefined')) {
            Ext.Ajax.request({
                url: '/vps/error/error/json-mail',
                params: {
                    url: error.url,
                    message: error.message,
                    location: location.href,
                    referrer: document.referrer
                }
            });
        }
    }
};
<|endoftext|>


<commit_before>import yaml from 'js-yaml';
import { Map, fromJS } from 'immutable';
import { trimStart, get } from 'lodash';
import { authenticateUser } from 'Actions/auth';
import * as publishModes from 'Constants/publishModes';
import { validateConfig } from 'Constants/configSchema';

export const CONFIG_REQUEST = 'CONFIG_REQUEST';
export const CONFIG_SUCCESS = 'CONFIG_SUCCESS';
export const CONFIG_FAILURE = 'CONFIG_FAILURE';
export const CONFIG_MERGE = 'CONFIG_MERGE';

const getConfigUrl = () => {
  const validTypes = { 'text/yaml': 'yaml', 'application/x-yaml': 'yaml' };
  const configLinkEl = document.querySelector('link[rel="cms-config-url"]');
  const isValidLink = configLinkEl && validTypes[configLinkEl.type] && get(configLinkEl, 'href');
  if (isValidLink) {
    const link = get(configLinkEl, 'href');
    console.log(`Using config file path: "${link}"`);
    return link;
  }
  return 'config.yml';
};

const defaults = {
  publish_mode: publishModes.SIMPLE,
};

export function applyDefaults(config) {
  return Map(defaults)
    .mergeDeep(config)
    .withMutations(map => {
      /**
       * Use media_folder as default public_folder.
       */
      const defaultPublicFolder = `/${trimStart(map.get('media_folder'), '/')}`;
      if (!map.get('public_folder')) {
        map.set('public_folder', defaultPublicFolder);
      }
    });
}

function mergePreloadedConfig(preloadedConfig, loadedConfig) {
  const map = fromJS(loadedConfig) || Map();
  return preloadedConfig ? preloadedConfig.mergeDeep(map) : map;
}

function parseConfig(data) {
  const config = yaml.safeLoad(data);
  if (typeof CMS_ENV === 'string' && config[CMS_ENV]) {
    Object.keys(config[CMS_ENV]).forEach(key => {
      config[key] = config[CMS_ENV][key];
    });
  }
  return config;
}

async function getConfig(file, isPreloaded) {
  const response = await fetch(file, { credentials: 'same-origin' });
  if (response.status !== 200) {
    if (isPreloaded) return parseConfig('');
    throw new Error(`Failed to load config.yml (${response.status})`);
  }
  const contentType = response.headers.get('Content-Type') || 'Not-Found';
  const isYaml = contentType.indexOf('yaml') !== -1;
  if (!isYaml) {
    console.log(`Response for ${file} was not yaml. (Content-Type: ${contentType})`);
    if (isPreloaded) return parseConfig('');
  }
  return parseConfig(await response.text());
}

export function configLoaded(config) {
  return {
    type: CONFIG_SUCCESS,
    payload: config,
  };
}

export function configLoading() {
  return {
    type: CONFIG_REQUEST,
  };
}

export function configFailed(err) {
  return {
    type: CONFIG_FAILURE,
    error: 'Error loading config',
    payload: err,
  };
}

export function configDidLoad(config) {
  return dispatch => {
    dispatch(configLoaded(config));
  };
}

export function mergeConfig(config) {
  return { type: CONFIG_MERGE, payload: config };
}

export function loadConfig() {
  if (window.CMS_CONFIG) {
    return configDidLoad(fromJS(window.CMS_CONFIG));
  }
  return async (dispatch, getState) => {
    dispatch(configLoading());

    try {
      const preloadedConfig = getState().config;
      const configUrl = getConfigUrl();
      const loadedConfig = await getConfig(configUrl, preloadedConfig && preloadedConfig.size > 1);

      /**
       * Merge any existing configuration so the result can be validated.
       */
      const mergedConfig = mergePreloadedConfig(preloadedConfig, loadedConfig);
      validateConfig(mergedConfig.toJS());

      const config = applyDefaults(mergedConfig);

      dispatch(configDidLoad(config));
      dispatch(authenticateUser());
    } catch (err) {
      dispatch(configFailed(err));
      throw err;
    }
  };
}
<commit_msg>fix(config): stop throwing on config.yml 404, fix manual init (#1801)<commit_after>import yaml from 'js-yaml';
import { Map, fromJS } from 'immutable';
import { trimStart, get } from 'lodash';
import { authenticateUser } from 'Actions/auth';
import * as publishModes from 'Constants/publishModes';
import { validateConfig } from 'Constants/configSchema';

export const CONFIG_REQUEST = 'CONFIG_REQUEST';
export const CONFIG_SUCCESS = 'CONFIG_SUCCESS';
export const CONFIG_FAILURE = 'CONFIG_FAILURE';
export const CONFIG_MERGE = 'CONFIG_MERGE';

const getConfigUrl = () => {
  const validTypes = { 'text/yaml': 'yaml', 'application/x-yaml': 'yaml' };
  const configLinkEl = document.querySelector('link[rel="cms-config-url"]');
  const isValidLink = configLinkEl && validTypes[configLinkEl.type] && get(configLinkEl, 'href');
  if (isValidLink) {
    const link = get(configLinkEl, 'href');
    console.log(`Using config file path: "${link}"`);
    return link;
  }
  return 'config.yml';
};

const defaults = {
  publish_mode: publishModes.SIMPLE,
};

export function applyDefaults(config) {
  return Map(defaults)
    .mergeDeep(config)
    .withMutations(map => {
      /**
       * Use media_folder as default public_folder.
       */
      const defaultPublicFolder = `/${trimStart(map.get('media_folder'), '/')}`;
      if (!map.get('public_folder')) {
        map.set('public_folder', defaultPublicFolder);
      }
    });
}

function mergePreloadedConfig(preloadedConfig, loadedConfig) {
  const map = fromJS(loadedConfig) || Map();
  return preloadedConfig ? preloadedConfig.mergeDeep(map) : map;
}

function parseConfig(data) {
  const config = yaml.safeLoad(data);
  if (typeof CMS_ENV === 'string' && config[CMS_ENV]) {
    Object.keys(config[CMS_ENV]).forEach(key => {
      config[key] = config[CMS_ENV][key];
    });
  }
  return config;
}

async function getConfig(file, isPreloaded) {
  const response = await fetch(file, { credentials: 'same-origin' }).catch(err => err);
  if (response instanceof Error || response.status !== 200) {
    if (isPreloaded) return parseConfig('');
    throw new Error(`Failed to load config.yml (${response.status || response})`);
  }
  const contentType = response.headers.get('Content-Type') || 'Not-Found';
  const isYaml = contentType.indexOf('yaml') !== -1;
  if (!isYaml) {
    console.log(`Response for ${file} was not yaml. (Content-Type: ${contentType})`);
    if (isPreloaded) return parseConfig('');
  }
  return parseConfig(await response.text());
}

export function configLoaded(config) {
  return {
    type: CONFIG_SUCCESS,
    payload: config,
  };
}

export function configLoading() {
  return {
    type: CONFIG_REQUEST,
  };
}

export function configFailed(err) {
  return {
    type: CONFIG_FAILURE,
    error: 'Error loading config',
    payload: err,
  };
}

export function configDidLoad(config) {
  return dispatch => {
    dispatch(configLoaded(config));
  };
}

export function mergeConfig(config) {
  return { type: CONFIG_MERGE, payload: config };
}

export function loadConfig() {
  if (window.CMS_CONFIG) {
    return configDidLoad(fromJS(window.CMS_CONFIG));
  }
  return async (dispatch, getState) => {
    dispatch(configLoading());

    try {
      const preloadedConfig = getState().config;
      const configUrl = getConfigUrl();
      const loadedConfig = await getConfig(configUrl, preloadedConfig && preloadedConfig.size > 1);

      /**
       * Merge any existing configuration so the result can be validated.
       */
      const mergedConfig = mergePreloadedConfig(preloadedConfig, loadedConfig);
      validateConfig(mergedConfig.toJS());

      const config = applyDefaults(mergedConfig);

      dispatch(configDidLoad(config));
      dispatch(authenticateUser());
    } catch (err) {
      dispatch(configFailed(err));
      throw err;
    }
  };
}
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import classnames from 'classnames';
import { deSig, uxToHex } from '/lib/util';
import { Route, Link } from "react-router-dom";

import { ChatTabBar } from '/components/lib/chat-tabbar';
import SidebarSwitcher from './lib/icons/icon-sidebar-switch';

export class SettingsScreen extends Component {
  constructor(props) {
    super(props);

    this.state = {
      isLoading: false,
      title: "",
      description: "",
      color: ""
    };

    this.renderDelete = this.renderDelete.bind(this);
    this.changeTitle = this.changeTitle.bind(this);
    this.changeDescription = this.changeDescription.bind(this);
    this.changeColor = this.changeColor.bind(this);
  }

  componentDidMount() {
    if ((this.props.association) && (this.props.association.metadata)) {
      this.setState({
        title: this.props.association.metadata.title,
        description: this.props.association.metadata.description,
        color: uxToHex(this.props.association.metadata.color)
      });
    }
  }

  componentDidUpdate(prevProps, prevState) {
    const { props, state } = this;
    if (!!state.isLoading && !(props.station in props.inbox)) {
      this.setState({
        isLoading: false
      }, () => {
        props.api.setSpinner(false);
        props.history.push('/~chat');
      });
    }

    if ((this.state.title === "") && (prevProps !== this.props)) {
      if ((props.association) && (props.association.metadata))
      this.setState({
        title: props.association.metadata.title,
        description: props.association.metadata.description,
        color: uxToHex(props.association.metadata.color)});
    }
  }

  changeTitle() {
    this.setState({title: event.target.value})
  }

  changeDescription() {
    this.setState({description: event.target.value});
  }

  changeColor() {
    this.setState({color: event.target.value});
  }

  deleteChat() {
    const { props, state } = this;

    props.api.chatView.delete(props.station);
    props.api.setSpinner(true);

    this.setState({
      isLoading: true
    });
  }

  renderDelete() {
    const { props, state } = this;

    let chatOwner = (deSig(props.match.params.ship) === window.ship);

    let deleteButtonClasses = (chatOwner) ? 'b--red2 red2 pointer bg-gray0-d' : 'b--grey3 grey3 bg-gray0-d c-default';
    let leaveButtonClasses = (!chatOwner) ? "pointer" : "c-default";

    return (

Leave Chat

Remove this chat from your chat list. You will need to request for access again.

        Leave this chat

Delete Chat

Permenantly delete this chat. (All current members will no longer see this chat)

          Delete this chat

    );
  }

  renderMetadataSettings() {
    const { props, state } = this;

    let chatOwner = (deSig(props.match.params.ship) === window.ship);

    let association = ((props.association) && (props.association.metadata))
      ? props.association : {};

    return(

Rename

Change the name of this chat

          <input
            className={"f8 ba b--gray3 b--gray2-d bg-gray0-d white-d " +
            "focus-b--black focus-b--white-d pa3 db w-100 flex-auto mr3"}
            value={this.state.title}
            disabled={!chatOwner}
            onChange={this.changeTitle}
          />
           {
              if (chatOwner) {
                props.api.setSpinner(true);
                props.api.metadataAdd(
                  association['app-path'],
                  association['group-path'],
                  this.state.title,
                  association.metadata.description,
                  association.metadata['date-created'],
                  uxToHex(association.metadata.color)
                ).then(() => {
                  this.refs.rename.innerText = "Saved";
                  props.api.setSpinner(false);
                })
              }
            }}>
            Save

Change description

Change the description of this chat

            <input
              className={"f8 ba b--gray3 b--gray2-d bg-gray0-d white-d " +
                "focus-b--black focus-b--white-d pa3 db w-100 flex-auto mr3"}
              value={this.state.description}
              disabled={!chatOwner}
              onChange={this.changeDescription}
            />
             {
                if (chatOwner) {
                  props.api.setSpinner(true);
                  props.api.metadataAdd(
                    association['app-path'],
                    association['group-path'],
                    association.metadata.title,
                    this.state.description,
                    association.metadata['date-created'],
                    uxToHex(association.metadata.color)
                  ).then(() => {
                    this.refs.description.innerText = "Saved";
                    props.api.setSpinner(false);
                  })
                }
              }}>
              Save

Change color

Give this chat a color when viewing group channels

            <input
              className={"f8 ba b--gray3 b--gray2-d bg-gray0-d white-d " +
                "focus-b--black focus-b--white-d pa3 db w-100 flex-auto mr3"}
              value={this.state.color}
              disabled={!chatOwner}
              onChange={this.changeColor}
            />
             {
                if ((chatOwner) && (this.state.color.match(/[0-9A-F]{6}/i))) {
                  props.api.setSpinner(true);
                  props.api.metadataAdd(
                    association['app-path'],
                    association['group-path'],
                    association.metadata.title,
                    association.metadata.description,
                    association.metadata['date-created'],
                    this.state.color
                  ).then(() => {
                    this.refs.color.innerText = "Saved";
                    props.api.setSpinner(false);
                  })
                }
              }}>
              Save

    )
  }

  render() {
    const { props, state } = this;
    const isinPopout = this.props.popout ? "popout/" : "";

    let writeGroup = Array.from(props.group.values());

    if (!!state.isLoading) {
      let text = "Deleting...";
      if (deSig(props.match.params.ship) !== window.ship) {
        text = "Leaving...";
      }

      return (

            <Link to="/~chat/">{"⟵ All Chats"}</Link>

            <SidebarSwitcher
              sidebarShown={this.props.sidebarShown}
              popout={this.props.popout}
            />
            <Link to={`/~chat/` + isinPopout + `room` + props.station}
            className="pt2 white-d">

                {props.station.substr(1)}

            </Link>
            <ChatTabBar
              {...props}
              station={props.station}
              numPeers={writeGroup.length}
            />

{text}

      );
    }

    return (

          <Link to="/~chat/">{"⟵ All Chats"}</Link>

          <SidebarSwitcher
            sidebarShown={this.props.sidebarShown}
            popout={this.props.popout}
          />
          <Link to={`/~chat/` + isinPopout + `room` + props.station}
          className="pt2">

              {props.station.substr(1)}

          </Link>
          <ChatTabBar
            {...props}
            station={props.station}
            numPeers={writeGroup.length}
            isOwner={deSig(props.match.params.ship) === window.ship}
            popout={this.props.popout}
          />

Chat Settings

Share

Share a shortcode to join this chat

              <input
                className="f8 mono ba b--gray3 b--gray2-d bg-gray0-d white-d pa3 db w-100 flex-auto mr3"
                disabled={true}
                value={props.station.substr(1)}
              />
               {
                  navigator.clipboard.writeText(props.station.substr(1));
                  this.refs.copy.innerText = "Copied";
                }}>
                Copy

          {this.renderDelete()}
          {this.renderMetadataSettings()}

    );
  }
}
<commit_msg>chat: spell 'permanent' correctly<commit_after>import React, { Component } from 'react';
import classnames from 'classnames';
import { deSig, uxToHex } from '/lib/util';
import { Route, Link } from "react-router-dom";

import { ChatTabBar } from '/components/lib/chat-tabbar';
import SidebarSwitcher from './lib/icons/icon-sidebar-switch';

export class SettingsScreen extends Component {
  constructor(props) {
    super(props);

    this.state = {
      isLoading: false,
      title: "",
      description: "",
      color: ""
    };

    this.renderDelete = this.renderDelete.bind(this);
    this.changeTitle = this.changeTitle.bind(this);
    this.changeDescription = this.changeDescription.bind(this);
    this.changeColor = this.changeColor.bind(this);
  }

  componentDidMount() {
    if ((this.props.association) && (this.props.association.metadata)) {
      this.setState({
        title: this.props.association.metadata.title,
        description: this.props.association.metadata.description,
        color: uxToHex(this.props.association.metadata.color)
      });
    }
  }

  componentDidUpdate(prevProps, prevState) {
    const { props, state } = this;
    if (!!state.isLoading && !(props.station in props.inbox)) {
      this.setState({
        isLoading: false
      }, () => {
        props.api.setSpinner(false);
        props.history.push('/~chat');
      });
    }

    if ((this.state.title === "") && (prevProps !== this.props)) {
      if ((props.association) && (props.association.metadata))
      this.setState({
        title: props.association.metadata.title,
        description: props.association.metadata.description,
        color: uxToHex(props.association.metadata.color)});
    }
  }

  changeTitle() {
    this.setState({title: event.target.value})
  }

  changeDescription() {
    this.setState({description: event.target.value});
  }

  changeColor() {
    this.setState({color: event.target.value});
  }

  deleteChat() {
    const { props, state } = this;

    props.api.chatView.delete(props.station);
    props.api.setSpinner(true);

    this.setState({
      isLoading: true
    });
  }

  renderDelete() {
    const { props, state } = this;

    let chatOwner = (deSig(props.match.params.ship) === window.ship);

    let deleteButtonClasses = (chatOwner) ? 'b--red2 red2 pointer bg-gray0-d' : 'b--grey3 grey3 bg-gray0-d c-default';
    let leaveButtonClasses = (!chatOwner) ? "pointer" : "c-default";

    return (

Leave Chat

Remove this chat from your chat list. You will need to request for access again.

        Leave this chat

Delete Chat

Permanently delete this chat. All current members will no longer see this chat.

          Delete this chat

    );
  }

  renderMetadataSettings() {
    const { props, state } = this;

    let chatOwner = (deSig(props.match.params.ship) === window.ship);

    let association = ((props.association) && (props.association.metadata))
      ? props.association : {};

    return(

Rename

Change the name of this chat

          <input
            className={"f8 ba b--gray3 b--gray2-d bg-gray0-d white-d " +
            "focus-b--black focus-b--white-d pa3 db w-100 flex-auto mr3"}
            value={this.state.title}
            disabled={!chatOwner}
            onChange={this.changeTitle}
          />
           {
              if (chatOwner) {
                props.api.setSpinner(true);
                props.api.metadataAdd(
                  association['app-path'],
                  association['group-path'],
                  this.state.title,
                  association.metadata.description,
                  association.metadata['date-created'],
                  uxToHex(association.metadata.color)
                ).then(() => {
                  this.refs.rename.innerText = "Saved";
                  props.api.setSpinner(false);
                })
              }
            }}>
            Save

Change description

Change the description of this chat

            <input
              className={"f8 ba b--gray3 b--gray2-d bg-gray0-d white-d " +
                "focus-b--black focus-b--white-d pa3 db w-100 flex-auto mr3"}
              value={this.state.description}
              disabled={!chatOwner}
              onChange={this.changeDescription}
            />
             {
                if (chatOwner) {
                  props.api.setSpinner(true);
                  props.api.metadataAdd(
                    association['app-path'],
                    association['group-path'],
                    association.metadata.title,
                    this.state.description,
                    association.metadata['date-created'],
                    uxToHex(association.metadata.color)
                  ).then(() => {
                    this.refs.description.innerText = "Saved";
                    props.api.setSpinner(false);
                  })
                }
              }}>
              Save

Change color

Give this chat a color when viewing group channels

            <input
              className={"f8 ba b--gray3 b--gray2-d bg-gray0-d white-d " +
                "focus-b--black focus-b--white-d pa3 db w-100 flex-auto mr3"}
              value={this.state.color}
              disabled={!chatOwner}
              onChange={this.changeColor}
            />
             {
                if ((chatOwner) && (this.state.color.match(/[0-9A-F]{6}/i))) {
                  props.api.setSpinner(true);
                  props.api.metadataAdd(
                    association['app-path'],
                    association['group-path'],
                    association.metadata.title,
                    association.metadata.description,
                    association.metadata['date-created'],
                    this.state.color
                  ).then(() => {
                    this.refs.color.innerText = "Saved";
                    props.api.setSpinner(false);
                  })
                }
              }}>
              Save

    )
  }

  render() {
    const { props, state } = this;
    const isinPopout = this.props.popout ? "popout/" : "";

    let writeGroup = Array.from(props.group.values());

    if (!!state.isLoading) {
      let text = "Deleting...";
      if (deSig(props.match.params.ship) !== window.ship) {
        text = "Leaving...";
      }

      return (

            <Link to="/~chat/">{"⟵ All Chats"}</Link>

            <SidebarSwitcher
              sidebarShown={this.props.sidebarShown}
              popout={this.props.popout}
            />
            <Link to={`/~chat/` + isinPopout + `room` + props.station}
            className="pt2 white-d">

                {props.station.substr(1)}

            </Link>
            <ChatTabBar
              {...props}
              station={props.station}
              numPeers={writeGroup.length}
            />

{text}

      );
    }

    return (

          <Link to="/~chat/">{"⟵ All Chats"}</Link>

          <SidebarSwitcher
            sidebarShown={this.props.sidebarShown}
            popout={this.props.popout}
          />
          <Link to={`/~chat/` + isinPopout + `room` + props.station}
          className="pt2">

              {props.station.substr(1)}

          </Link>
          <ChatTabBar
            {...props}
            station={props.station}
            numPeers={writeGroup.length}
            isOwner={deSig(props.match.params.ship) === window.ship}
            popout={this.props.popout}
          />

Chat Settings

Share

Share a shortcode to join this chat

              <input
                className="f8 mono ba b--gray3 b--gray2-d bg-gray0-d white-d pa3 db w-100 flex-auto mr3"
                disabled={true}
                value={props.station.substr(1)}
              />
               {
                  navigator.clipboard.writeText(props.station.substr(1));
                  this.refs.copy.innerText = "Copied";
                }}>
                Copy

          {this.renderDelete()}
          {this.renderMetadataSettings()}

    );
  }
}
<|endoftext|>


<commit_before>function asNumber(value) {
  if (!isNaN(value)) {
    return value;
  } else {
    return 0;
  }
}

function getValue(id) {
  return asNumber(parseInt(document.getElementById(id).value));
}

function getInner(id) {
   return asNumber(parseInt(document.getElementById(id).innerHTML));
}

function updateValue(id, value) {
  document.getElementById(id).innerHTML = value;
}

function calcNineHoleTotal(nine, stat) {
  var nineHoleTotal = 0;
  if (nine == 'front') {
    for (i = 1; i < 10; i++) {
      nineHoleTotal += getValue('hole' + i + '_' + stat);
    }
    var otherNineTotal = getInner('total_back_9_' + stat);
  } else {
    for (i = 10; i < 19; i++) {
      nineHoleTotal += getValue('hole' + i + '_' + stat);
    }
    var otherNineTotal = getInner('total_front_9_' + stat);
  }
  updateValue('total_' + nine + '_9_' + stat, nineHoleTotal);
  updateValue('total_' + stat, nineHoleTotal + otherNineTotal);
}

function calcAllTotals() {
  calcNineHoleTotal('front', 'strokes');
  calcNineHoleTotal('front', 'putts');
  calcNineHoleTotal('back', 'strokes');
  calcNineHoleTotal('back', 'putts');
}
<commit_msg>change updatevalue to updateinner<commit_after>function asNumber(value) {
  if (!isNaN(value)) {
    return value;
  } else {
    return 0;
  }
}

function getValue(id) {
  return asNumber(parseInt(document.getElementById(id).value));
}

function getInner(id) {
   return asNumber(parseInt(document.getElementById(id).innerHTML));
}

function updateInner(id, value) {
  document.getElementById(id).innerHTML = value;
}

function calcNineHoleTotal(nine, stat) {
  var nineHoleTotal = 0;
  if (nine == 'front') {
    for (i = 1; i < 10; i++) {
      nineHoleTotal += getValue('hole' + i + '_' + stat);
    }
    var otherNineTotal = getInner('total_back_9_' + stat);
  } else {
    for (i = 10; i < 19; i++) {
      nineHoleTotal += getValue('hole' + i + '_' + stat);
    }
    var otherNineTotal = getInner('total_front_9_' + stat);
  }
  updateInner('total_' + nine + '_9_' + stat, nineHoleTotal);
  updateInner('total_' + stat, nineHoleTotal + otherNineTotal);
}

function calcAllTotals() {
  calcNineHoleTotal('front', 'strokes');
  calcNineHoleTotal('front', 'putts');
  calcNineHoleTotal('back', 'strokes');
  calcNineHoleTotal('back', 'putts');
}
<|endoftext|>


<commit_before>import DefineMap from "can-define/map/";
import DefineList from "can-define/list/";
import Observation from "can-observation";

import ContributionMonth from "./contribution-month";
import OSProject from "../os-project";

const MonthlyOSProject = DefineMap.extend("MonthlyOSProject", { seal: false }, {
  _id: {type: "string", identity: true},
  significance: "number",
  commissioned: "boolean",
  osProjectRef: OSProject.Ref,
  contributionMonth: {
    type: (data) => {
        if(!data) {
            return data;
        }
        let Constructor = ContributionMonth;
        if (Constructor.default) {
            Constructor = Constructor.default;
        }

        if (data instanceof Constructor) {
            return data;
        }

        return new Constructor(data);
    },
    serialize: false
  },
  getTotal() {
    // should this implementation be hidden and moved to contributionMonth: `contributionMonth.getOSProjectTotal( this )`?
    let calculations = this.contributionMonth &&
      this.contributionMonth.calculations;
    return calculations && calculations.osProjects[this.osProjectRef._id] || 0.0;
  },
  remove() {
    this.contributionMonth.removeMonthlyOSProject( this );
  }
});

MonthlyOSProject.List = DefineList.extend("MonthlyOSProjectList", {
  "#": {
    Type: MonthlyOSProject,
    added( items ) {
      this.contributionMonth && items.forEach( monthlyOSProject => {
        monthlyOSProject.contributionMonth = this.contributionMonth;
      } );
      return items;
    },
    removed( items ) {
      items.forEach( monthlyOSProject => {
        monthlyOSProject.contributionMonth = null;
      } );
      return items;
    }
  },
  contributionMonth: {
    set( contributionMonth ) {
      this.forEach( monthlyOSProject => {
        monthlyOSProject.contributionMonth = contributionMonth;
      });
      return contributionMonth;
    },
    type: (data) => {
        let Constructor = ContributionMonth;
        if (Constructor.default) {
            Constructor = Constructor.default;
        }

        if (data instanceof Constructor) {
            return data;
        }

        return new Constructor(data);
    },
    serialize: false
  },
  has( osProject ){
    return osProject._id in this.monthlyOSProjectIdMap;
  },
  getSignificance( osProjectRef ) {
    const monthlyOSProject = this.monthlyOSProjectIdMap[osProjectRef._id];
    return monthlyOSProject ? monthlyOSProject.significance : false;
  },
  get monthlyOSProjectIdMap() {
    const map = {};
    this.forEach( monthlyOSProject => {
      map[monthlyOSProject.osProjectRef._id] = monthlyOSProject;
    });
    return map;
  },
  get commissioned() {
    return this.filter({ commissioned: true });
  }
});

export default MonthlyOSProject;
<commit_msg>Bug: wrong totals for OS Projects<commit_after>import DefineMap from "can-define/map/";
import DefineList from "can-define/list/";
import Observation from "can-observation";

import ContributionMonth from "./contribution-month";
import OSProject from "../os-project";

const MonthlyOSProject = DefineMap.extend("MonthlyOSProject", { seal: false }, {
  _id: {type: "string"},
  significance: "number",
  commissioned: "boolean",
  osProjectRef: OSProject.Ref,
  contributionMonth: {
    type: (data) => {
        if(!data) {
            return data;
        }
        let Constructor = ContributionMonth;
        if (Constructor.default) {
            Constructor = Constructor.default;
        }

        if (data instanceof Constructor) {
            return data;
        }

        return new Constructor(data);
    },
    serialize: false
  },
  getTotal() {
    // should this implementation be hidden and moved to contributionMonth: `contributionMonth.getOSProjectTotal( this )`?
    let calculations = this.contributionMonth &&
      this.contributionMonth.calculations;
    return calculations && calculations.osProjects[this.osProjectRef._id] || 0.0;
  },
  remove() {
    this.contributionMonth.removeMonthlyOSProject( this );
  }
});

MonthlyOSProject.List = DefineList.extend("MonthlyOSProjectList", {
  "#": {
    Type: MonthlyOSProject,
    added( items ) {
      this.contributionMonth && items.forEach( monthlyOSProject => {
        monthlyOSProject.contributionMonth = this.contributionMonth;
      } );
      return items;
    },
    removed( items ) {
      items.forEach( monthlyOSProject => {
        monthlyOSProject.contributionMonth = null;
      } );
      return items;
    }
  },
  contributionMonth: {
    set( contributionMonth ) {
      this.forEach( monthlyOSProject => {
        monthlyOSProject.contributionMonth = contributionMonth;
      });
      return contributionMonth;
    },
    type: (data) => {
        let Constructor = ContributionMonth;
        if (Constructor.default) {
            Constructor = Constructor.default;
        }

        if (data instanceof Constructor) {
            return data;
        }

        return new Constructor(data);
    },
    serialize: false
  },
  has( osProject ){
    return osProject._id in this.monthlyOSProjectIdMap;
  },
  getSignificance( osProjectRef ) {
    const monthlyOSProject = this.monthlyOSProjectIdMap[osProjectRef._id];
    return monthlyOSProject ? monthlyOSProject.significance : false;
  },
  get monthlyOSProjectIdMap() {
    const map = {};
    this.forEach( monthlyOSProject => {
      map[monthlyOSProject.osProjectRef._id] = monthlyOSProject;
    });
    return map;
  },
  get commissioned() {
    return this.filter({ commissioned: true });
  }
});

export default MonthlyOSProject;
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2015 Google Inc. All Rights Reserved.
 *
 * 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.
 */
CLASS({
  package: 'foam.browser.u2',
  name: 'StackView',
  extends: 'foam.u2.Element',

  imports: [
    'dynamic',
    'requestAnimationFrame',
    'setTimeout',
    'window'
  ],

  exports: [
    'as stack'
  ],

  constants: {
    VIEW_DESTROYED: ['view-destroyed']
  },

  properties: [
    {
      name: 'maxVisibleViews',
      type: 'Int',
      defaultValue: 2,
      documentation: 'Limits the number of views on screen at a time.',
    },
    {
      name: 'views_',
      documentation: 'Internal array of child views.',
      factory: function() {
        return [];
      }
    },
    {
      name: 'visibleStart_',
      documentation: 'The index of the leftmost visible element.',
      defaultValue: 0
    },
    {
      name: 'visibleEnd_',
      documentation: 'The index of the rightmost visible element.',
      defaultValue: 0
    },
    {
      name: 'noDecoration',
      type: 'Boolean',
      documentation: 'Set true to hide panel edges.',
      defaultValue: false
    },
    {
      model_: 'foam.core.types.StringEnumProperty',
      name: 'transition',
      documentation: 'Transition to use when adding or removing a view.',
      defaultValue: 'slide',
      choices: ['slide', 'fade']
    },
    {
      name: 'defaultMinWidth',
      documentation: 'If an item in the stack does not define a minWidth property, this value is used instead.',
      defaultValue: 300
    },
    {
      name: 'defaultPreferredWidth',
      documentation: 'If an item in the stack does not define a preferredWidth property, this value is used instead.',
      defaultValue: 400
    },
    {
      name: 'defaultMaxWidth',
      documentation: 'If an item in the stack does not define a maxWidth property, this value is used instead.',
      defaultValue: 10000
    },
  ],

  methods: [
    function init() {
      this.SUPER();
      this.window.addEventListener('resize', this.onResize);
    },
    {
      name: 'pushView',
      documentation: 'Default pushView that works at the top level. See ' +
          '$$DOC{ref:".pushView_"} for details.',
      code: function(view, hints) {
        this.pushView_(-1, view, hints);
      }
    },
    {
      name: 'popView',
      documentation: 'Default popView that works at the top level. See ' +
          '$$DOC{ref:".popView_"} for details.',
      code: function() {
        this.popView_(0);
      }
    },
    {
      name: 'popChildViews',
      documentation: 'Default popChildViews that works at the top level. See ' +
          '$$DOC{ref:".popChildViews_"} for details.',
      code: function() {
        this.popView_(1);
      }
    },
    function elementAnimationAdd_(e) {
      if (this.transition === 'slide') {
        e.style({
          width: '0px',
          left: '100%',
          transition: 'left 300ms ease'
        });
      } else if (this.transition === 'fade') {
        e.style({
          opacity: 0,
          transition: 'opacity 300ms ease'
        });
        this.requestAnimationFrame(function() { e.style({ opacity: 1 }); });
      }
    },
    function elementAnimationRemove_(e) {
      if (this.transition === 'slide') {
        e.style({ left: '100%' });
      } else if (this.transition === 'fade') {
        e.style({ opacity: 0 });
      }
    },
    function destroyChildViews_(index) {
      while (this.views_.length > index) {
        var obj = this.views_.pop();
        this.publish(this.VIEW_DESTROYED, obj.content);
        this.elementAnimationRemove_(obj.panel);
        this.resize();
        this.finishDestroy(obj.panel);
      }
    },
    function finishDestroy(e) {
      this.setTimeout(function() {
        // Clean up after the animation.
        // TODO(braden): Calling destroy() should cause an unload if needed.
        // Once that is true, the unload() call here can be removed.
        e.unload();
        e.destroy();
      }, 1000);
    },
    function renderChild(index) {
      var e = this.childE(index);
      this.views_[index].panel = e;
      this.add(e);
      this.elementAnimationAdd_(e);
    },
    function childE(index) {
      var obj = this.views_[index];
      var self = this;
      var e = this.E('div')
          .cls(this.myCls('panel'))
          .cls(this.dynamic(function(start, end) {
            return index < start - 1 || index > end + 1 ? self.myCls('hidden') : '';
          }, this.visibleStart_$, this.visibleEnd_$))
          .cls(this.dynamic(function(start, end) {
            return index < start || index > end ? self.myCls('no-input') : '';
          }, this.visibleStart_$, this.visibleEnd_$));

      e.add(obj.content);
      e.start('div')
          .cls(this.myCls('edge'))
          .cls(this.myCls('hidden'), this.noDecoration$)
          .end();
      return e;
    },
    function resize() {
      var width = this.id$el.offsetWidth;
      var index = this.views_.length - 1;
      while (index >= 0 && width >= (this.views_[index].content.minWidth || this.defaultMinWidth)) {
        width -= (this.views_[index].content.minWidth || this.defaultMinWidth);
        index--;
      }

      index = Math.min(index + 1, this.views_.length - 1);
      this.visibleStart_ = Math.max(index, this.views_.length - this.maxVisibleViews);
      // Currently visibleEnd_ is always the last view; it exists only to make
      // sure views that are replacing each other come and go lockstep in a
      // single frame.
      this.visibleEnd_ = this.views_.length - 1;

      // Now, we actually compute the sizes for the containing views.
      // The algorithm here is straightforward. They all have minWidth allocated
      // already. We try to assign each view some extra, up to its preferred
      // width. If there's still some left, we make a second pass, allowing each
      // view more space, up to its maxWidth. If there's still some left, the last
      // view gets it, no matter the maximum.

      if (this.visibleStart_ < 0) return;

      width = this.id$el.offsetWidth;
      var sizes = [];
      for (var i = this.visibleStart_; i <= this.visibleEnd_; i++) {
        var min = this.views_[i].content.minWidth || this.defaultMinWidth;
        sizes[i] = min; // This leaves blanks for hidden views. Oh well.
        width -= min;
      }

      for (i = this.visibleEnd_; width > 0 && i >= this.visibleStart_; i--) {
        var newSize = Math.min(sizes[i] + width, this.views_[i].content.preferredWidth || this.defaultMinWidth);
        width -= newSize - sizes[i];
        sizes[i] = newSize;
      }

      for (i = this.visibleEnd_; width > 0 && i >= this.visibleStart_; i--) {
        var newSize = Math.min(sizes[i] + width, this.views_[i].content.maxWidth || this.defaultMaxWidth);
        width -= newSize - sizes[i];
        sizes[i] = newSize;
      }

      if ( width !== 0 ) {
        // either the last view needs to suck up extra space, or only one view fit and it needs to shrink
        i = this.visibleEnd_;
        sizes[i] += width;
      }

      var foundTop = false;
      var pos = this.id$el.offsetWidth;
      for (i = this.visibleEnd_; i >= 0; i--) {
        var size = sizes[i] || 0; // size zero for buried items to the left
        pos -= size; // left edge
        var obj = this.views_[i];
        // ugly code to not animate width on the topmost visible view
        if ( this.transition == 'slide' ) {
          if ( ! foundTop ) {
            obj.panel.style({ transition: "left 300ms ease" });
            foundTop = true;
          } else {
            obj.panel.style({ transition: "left 300ms ease, width 300ms ease" });
          }
        }
        if (size > 0) {
          // Only set size for non-zero, buried panels will be overlapped.
          obj.panel.style({ width: size + 'px' });
        }
        obj.panel.style({
          'z-index': i, // z ordering ensures overlap
          left: pos + 'px',
          top: '0px'
        });
      }
    },
    function initE(opt_e) {
      opt_e.cls(this.myCls());
    },
    function load() {
      this.SUPER();
      this.onLoad();
    },
  ],

  templates: [
    function CSS() {/*
      $ {
        height: 100%;
        overflow-x: hidden;
        position: relative;
        width: 100%;
      }
      $-panel {
        background-color: #fff;
        display: flex;
        flex-direction: column;
        height: 100%;
        overflow: hidden;
        position: absolute;
      }
      $-edge {
        background-color: #000;
        height: 100%;
        opacity: 0.1;
        position: absolute;
        right: 0px;
        top: 0px;
        width: 1px;
        z-index: 100;
      }

      $-hidden {
        display: none;
      }
      $-no-input {
        pointer-events: none;
      }
    */},
  ],

  listeners: [
    function onLoad() {
      // Render and configure each child view that has already been loaded.
      for (var i = 0; i < this.views_.length; i++) {
        this.renderChild(i);
      }
      this.resize();
    },
    {
      name: 'onResize',
      isFramed: true,
      code: function() {
        this.resize();
      }
    },
    {
      name: 'pushView_',
      isFramed: true,
      documentation: function() {/*

The real implementation used to push new views onto the stack.
        The index specifies the level of the stack we're operating at.

        Generally this is not called directly. Instead, views use the stack in
        their contexts to push views. That method is a bound call to this function
        with the index set appropriately.

Hints

The last argument is an object containing hints. Should the switch
        be transitionless, or backwards? The supported hints are documented here.
        They take the form of an object with options rather than a confusing
        series of optional arguments.

instant: true

If this StackView is configured to use an animated transition,
          setting <tt>instant: true</tt> will skip the transition and instantly
          replace the view.

preview: true

Previews are optional pushes. This mode might be used to push and
          render details of the item under the cursor. Show the child view (the
          preview) if there's room for both the main and child views, but if
          there isn't room then keep showing the main view.

      */},
      // TODO(braden): Actually implement the preview hint. Instant is a no-op
      // until we actually have animated transitions to skip.
      code: function(index, view, hints) {
        if (!view) return;
        if (!hints) hints = {};

        // Pushing a new view with index i means we drop everything in the list
        // that's greater than i, and then add the new view back. There will be
        // i + 1 views in the stack after a pushView(i, v);
        this.destroyChildViews_(index + 1);

        var substack = {
          __proto__: this,
          pushView: this.pushView_.bind(this, this.views_.length),
          popView: this.popView_.bind(this, this.views_.length),
          popChildViews: this.popView_.bind(this, this.views_.length+1),
          replaceView: this.replaceView_.bind(this, this.views_.length)
        };

        // HACK: Replacing the values of properties on a child view is a hack
        // and dangerous. If we can think of a better way to make sure the child
        // view is talking to the right stack, then we should change this.
        if (view.stack)
          view.stack = substack;
        view.X.stack = substack;
        view.Y.stack = substack;

        this.views_.push({
          content: view
        });

        this.renderChild(index + 1);
        this.setTimeout(this.onResize, 100);
      }
    },
    {
      name: 'popView_',
      isFramed: true,
      code: function popView_(index) {
        // popView_(i) pops everything greater than and including i. Therefore,
        // a view that calls this.stack.popView() (on the substack object created
        // above) will be removed from the stack.
        if ( index >= this.views_.length ) return;

        this.visibleStart_ -= this.visibleEnd_ - index + 1;
        this.visibleEnd_ = index - 1;
        this.destroyChildViews_(index);
        this.setTimeout(this.onResize, 100);
      }
    },
    {
      name: 'replaceView_',
      isFramed: true,
      code: function(index, view, hints) {
        this.popView_(index);
        this.pushView_(index - 1, view, hints);
      }
    }
  ]
});
<commit_msg>Update StackView to work with initE interface change.<commit_after>/**
 * @license
 * Copyright 2015 Google Inc. All Rights Reserved.
 *
 * 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.
 */
CLASS({
  package: 'foam.browser.u2',
  name: 'StackView',
  extends: 'foam.u2.Element',

  imports: [
    'dynamic',
    'requestAnimationFrame',
    'setTimeout',
    'window'
  ],

  exports: [
    'as stack'
  ],

  constants: {
    VIEW_DESTROYED: ['view-destroyed']
  },

  properties: [
    {
      name: 'maxVisibleViews',
      type: 'Int',
      defaultValue: 2,
      documentation: 'Limits the number of views on screen at a time.',
    },
    {
      name: 'views_',
      documentation: 'Internal array of child views.',
      factory: function() {
        return [];
      }
    },
    {
      name: 'visibleStart_',
      documentation: 'The index of the leftmost visible element.',
      defaultValue: 0
    },
    {
      name: 'visibleEnd_',
      documentation: 'The index of the rightmost visible element.',
      defaultValue: 0
    },
    {
      name: 'noDecoration',
      type: 'Boolean',
      documentation: 'Set true to hide panel edges.',
      defaultValue: false
    },
    {
      model_: 'foam.core.types.StringEnumProperty',
      name: 'transition',
      documentation: 'Transition to use when adding or removing a view.',
      defaultValue: 'slide',
      choices: ['slide', 'fade']
    },
    {
      name: 'defaultMinWidth',
      documentation: 'If an item in the stack does not define a minWidth property, this value is used instead.',
      defaultValue: 300
    },
    {
      name: 'defaultPreferredWidth',
      documentation: 'If an item in the stack does not define a preferredWidth property, this value is used instead.',
      defaultValue: 400
    },
    {
      name: 'defaultMaxWidth',
      documentation: 'If an item in the stack does not define a maxWidth property, this value is used instead.',
      defaultValue: 10000
    },
  ],

  methods: [
    function init() {
      this.SUPER();
      this.window.addEventListener('resize', this.onResize);
    },
    {
      name: 'pushView',
      documentation: 'Default pushView that works at the top level. See ' +
          '$$DOC{ref:".pushView_"} for details.',
      code: function(view, hints) {
        this.pushView_(-1, view, hints);
      }
    },
    {
      name: 'popView',
      documentation: 'Default popView that works at the top level. See ' +
          '$$DOC{ref:".popView_"} for details.',
      code: function() {
        this.popView_(0);
      }
    },
    {
      name: 'popChildViews',
      documentation: 'Default popChildViews that works at the top level. See ' +
          '$$DOC{ref:".popChildViews_"} for details.',
      code: function() {
        this.popView_(1);
      }
    },
    function elementAnimationAdd_(e) {
      if (this.transition === 'slide') {
        e.style({
          width: '0px',
          left: '100%',
          transition: 'left 300ms ease'
        });
      } else if (this.transition === 'fade') {
        e.style({
          opacity: 0,
          transition: 'opacity 300ms ease'
        });
        this.requestAnimationFrame(function() { e.style({ opacity: 1 }); });
      }
    },
    function elementAnimationRemove_(e) {
      if (this.transition === 'slide') {
        e.style({ left: '100%' });
      } else if (this.transition === 'fade') {
        e.style({ opacity: 0 });
      }
    },
    function destroyChildViews_(index) {
      while (this.views_.length > index) {
        var obj = this.views_.pop();
        this.publish(this.VIEW_DESTROYED, obj.content);
        this.elementAnimationRemove_(obj.panel);
        this.resize();
        this.finishDestroy(obj.panel);
      }
    },
    function finishDestroy(e) {
      this.setTimeout(function() {
        // Clean up after the animation.
        // TODO(braden): Calling destroy() should cause an unload if needed.
        // Once that is true, the unload() call here can be removed.
        e.unload();
        e.destroy();
      }, 1000);
    },
    function renderChild(index) {
      var e = this.childE(index);
      this.views_[index].panel = e;
      this.add(e);
      this.elementAnimationAdd_(e);
    },
    function childE(index) {
      var obj = this.views_[index];
      var self = this;
      var e = this.E('div')
          .cls(this.myCls('panel'))
          .cls(this.dynamic(function(start, end) {
            return index < start - 1 || index > end + 1 ? self.myCls('hidden') : '';
          }, this.visibleStart_$, this.visibleEnd_$))
          .cls(this.dynamic(function(start, end) {
            return index < start || index > end ? self.myCls('no-input') : '';
          }, this.visibleStart_$, this.visibleEnd_$));

      e.add(obj.content);
      e.start('div')
          .cls(this.myCls('edge'))
          .cls(this.myCls('hidden'), this.noDecoration$)
          .end();
      return e;
    },
    function resize() {
      var width = this.id$el.offsetWidth;
      var index = this.views_.length - 1;
      while (index >= 0 && width >= (this.views_[index].content.minWidth || this.defaultMinWidth)) {
        width -= (this.views_[index].content.minWidth || this.defaultMinWidth);
        index--;
      }

      index = Math.min(index + 1, this.views_.length - 1);
      this.visibleStart_ = Math.max(index, this.views_.length - this.maxVisibleViews);
      // Currently visibleEnd_ is always the last view; it exists only to make
      // sure views that are replacing each other come and go lockstep in a
      // single frame.
      this.visibleEnd_ = this.views_.length - 1;

      // Now, we actually compute the sizes for the containing views.
      // The algorithm here is straightforward. They all have minWidth allocated
      // already. We try to assign each view some extra, up to its preferred
      // width. If there's still some left, we make a second pass, allowing each
      // view more space, up to its maxWidth. If there's still some left, the last
      // view gets it, no matter the maximum.

      if (this.visibleStart_ < 0) return;

      width = this.id$el.offsetWidth;
      var sizes = [];
      for (var i = this.visibleStart_; i <= this.visibleEnd_; i++) {
        var min = this.views_[i].content.minWidth || this.defaultMinWidth;
        sizes[i] = min; // This leaves blanks for hidden views. Oh well.
        width -= min;
      }

      for (i = this.visibleEnd_; width > 0 && i >= this.visibleStart_; i--) {
        var newSize = Math.min(sizes[i] + width, this.views_[i].content.preferredWidth || this.defaultMinWidth);
        width -= newSize - sizes[i];
        sizes[i] = newSize;
      }

      for (i = this.visibleEnd_; width > 0 && i >= this.visibleStart_; i--) {
        var newSize = Math.min(sizes[i] + width, this.views_[i].content.maxWidth || this.defaultMaxWidth);
        width -= newSize - sizes[i];
        sizes[i] = newSize;
      }

      if ( width !== 0 ) {
        // either the last view needs to suck up extra space, or only one view fit and it needs to shrink
        i = this.visibleEnd_;
        sizes[i] += width;
      }

      var foundTop = false;
      var pos = this.id$el.offsetWidth;
      for (i = this.visibleEnd_; i >= 0; i--) {
        var size = sizes[i] || 0; // size zero for buried items to the left
        pos -= size; // left edge
        var obj = this.views_[i];
        // ugly code to not animate width on the topmost visible view
        if ( this.transition == 'slide' ) {
          if ( ! foundTop ) {
            obj.panel.style({ transition: "left 300ms ease" });
            foundTop = true;
          } else {
            obj.panel.style({ transition: "left 300ms ease, width 300ms ease" });
          }
        }
        if (size > 0) {
          // Only set size for non-zero, buried panels will be overlapped.
          obj.panel.style({ width: size + 'px' });
        }
        obj.panel.style({
          'z-index': i, // z ordering ensures overlap
          left: pos + 'px',
          top: '0px'
        });
      }
    },
    function initE(opt_x, opt_e) {
      opt_e.cls(this.myCls());
    },
    function load() {
      this.SUPER();
      this.onLoad();
    },
  ],

  templates: [
    function CSS() {/*
      $ {
        height: 100%;
        overflow-x: hidden;
        position: relative;
        width: 100%;
      }
      $-panel {
        background-color: #fff;
        display: flex;
        flex-direction: column;
        height: 100%;
        overflow: hidden;
        position: absolute;
      }
      $-edge {
        background-color: #000;
        height: 100%;
        opacity: 0.1;
        position: absolute;
        right: 0px;
        top: 0px;
        width: 1px;
        z-index: 100;
      }

      $-hidden {
        display: none;
      }
      $-no-input {
        pointer-events: none;
      }
    */},
  ],

  listeners: [
    function onLoad() {
      // Render and configure each child view that has already been loaded.
      for (var i = 0; i < this.views_.length; i++) {
        this.renderChild(i);
      }
      this.resize();
    },
    {
      name: 'onResize',
      isFramed: true,
      code: function() {
        this.resize();
      }
    },
    {
      name: 'pushView_',
      isFramed: true,
      documentation: function() {/*

The real implementation used to push new views onto the stack.
        The index specifies the level of the stack we're operating at.

        Generally this is not called directly. Instead, views use the stack in
        their contexts to push views. That method is a bound call to this function
        with the index set appropriately.

Hints

The last argument is an object containing hints. Should the switch
        be transitionless, or backwards? The supported hints are documented here.
        They take the form of an object with options rather than a confusing
        series of optional arguments.

instant: true

If this StackView is configured to use an animated transition,
          setting <tt>instant: true</tt> will skip the transition and instantly
          replace the view.

preview: true

Previews are optional pushes. This mode might be used to push and
          render details of the item under the cursor. Show the child view (the
          preview) if there's room for both the main and child views, but if
          there isn't room then keep showing the main view.

      */},
      // TODO(braden): Actually implement the preview hint. Instant is a no-op
      // until we actually have animated transitions to skip.
      code: function(index, view, hints) {
        if (!view) return;
        if (!hints) hints = {};

        // Pushing a new view with index i means we drop everything in the list
        // that's greater than i, and then add the new view back. There will be
        // i + 1 views in the stack after a pushView(i, v);
        this.destroyChildViews_(index + 1);

        var substack = {
          __proto__: this,
          pushView: this.pushView_.bind(this, this.views_.length),
          popView: this.popView_.bind(this, this.views_.length),
          popChildViews: this.popView_.bind(this, this.views_.length+1),
          replaceView: this.replaceView_.bind(this, this.views_.length)
        };

        // HACK: Replacing the values of properties on a child view is a hack
        // and dangerous. If we can think of a better way to make sure the child
        // view is talking to the right stack, then we should change this.
        if (view.stack)
          view.stack = substack;
        view.X.stack = substack;
        view.Y.stack = substack;

        this.views_.push({
          content: view
        });

        this.renderChild(index + 1);
        this.setTimeout(this.onResize, 100);
      }
    },
    {
      name: 'popView_',
      isFramed: true,
      code: function popView_(index) {
        // popView_(i) pops everything greater than and including i. Therefore,
        // a view that calls this.stack.popView() (on the substack object created
        // above) will be removed from the stack.
        if ( index >= this.views_.length ) return;

        this.visibleStart_ -= this.visibleEnd_ - index + 1;
        this.visibleEnd_ = index - 1;
        this.destroyChildViews_(index);
        this.setTimeout(this.onResize, 100);
      }
    },
    {
      name: 'replaceView_',
      isFramed: true,
      code: function(index, view, hints) {
        this.popView_(index);
        this.pushView_(index - 1, view, hints);
      }
    }
  ]
});
<|endoftext|>


<commit_before>(function($)
{

  $.widget('ui.dmMarkdown',
  {
    _init: function()
    {
   this.markitup();

   this.selection = null;

   this.drop();

   this.saveSelection();
    },

  saveSelection: function()
  {
   var self = this;

   $('#dm_page_bar_toggler, #dm_media_bar_toggler').bind('click', function()
   {
      self.selection = self.element.getSelection();
   });
  },

  getSelection: function()
  {
   return (this.selection && this.selection.length) ? this.selection : this.element.getSelection();
  },

  markitup: function()
  {
      this.element.markItUp(dmMarkitupMarkdown);
  },

  drop: function()
  {
    var self = this;

    self.element.droppable({
      accept:       '#dm_page_bar li, #dm_media_bar li.file.image',
      activeClass:  'droppable_active',
      hoverClass:   'droppable_hover',
      //          tolerance:    'touch',
      drop: function(e, ui)
      {
        var selection = self.getSelection(),
        linkText = selection.text || $.trim(ui.draggable.find('>a').text()),
        scrollTop = self.element.scrollTop();

        if (ui.draggable.hasClass('file'))
        {
          var type = "media";
          var placeholder = "!["+linkText+"]("+type+":"+ui.draggable.attr('id').replace(/dmm/, '')+")"
        }
        else
        {
          var type = "page";
          var placeholder = "["+linkText+"]("+type+":"+ui.draggable.attr('id').replace(/dmp/, '')+")"
        }

        if (selection)
        {
          var val = self.element.val();
          self.element.val(val.substr(0, selection.start) + placeholder + val.substr(selection.end, val.length));
        }
        else
        {
          self.element.val($elem.val()+placeholder);
        }

        self.element.scrollTop(scrollTop);
      }
    });
  }
});

})(jQuery);<commit_msg>fixed uncaught javascript exception when dragging pages<commit_after>(function($)
{

  $.widget('ui.dmMarkdown',
  {
    _init: function()
    {
      this.markitup();

      this.selection = null;

      this.drop();

      this.saveSelection();
    },

  saveSelection: function()
  {
    var self = this;

    $('#dm_page_bar_toggler, #dm_media_bar_toggler').bind('click', function()
    {
      try
      {
        self.selection = self.element.getSelection();
      }
      catch(e){}
    });
  },

  getSelection: function()
  {
    return (this.selection && this.selection.length) ? this.selection : this.element.getSelection();
  },

  markitup: function()
  {
    this.element.markItUp(dmMarkitupMarkdown);
  },

  drop: function()
  {
    var self = this;

    self.element.droppable({
      accept:       '#dm_page_bar li, #dm_media_bar li.file.image',
      activeClass:  'droppable_active',
      hoverClass:   'droppable_hover',
      //          tolerance:    'touch',
      drop: function(e, ui)
      {
        var selection = self.getSelection(),
        linkText = selection.text || $.trim(ui.draggable.find('>a').text()),
        scrollTop = self.element.scrollTop();

        if (ui.draggable.hasClass('file'))
        {
          var type = "media";
          var placeholder = "!["+linkText+"]("+type+":"+ui.draggable.attr('id').replace(/dmm/, '')+")"
        }
        else
        {
          var type = "page";
          var placeholder = "["+linkText+"]("+type+":"+ui.draggable.attr('id').replace(/dmp/, '')+")"
        }

        if (selection)
        {
          var val = self.element.val();
          self.element.val(val.substr(0, selection.start) + placeholder + val.substr(selection.end, val.length));
        }
        else
        {
          self.element.val($elem.val()+placeholder);
        }

        self.element.scrollTop(scrollTop);
      }
    });
  }
});

})(jQuery);<|endoftext|>


<commit_before>Vps.Utils.Upload = {
    supportsHtml5Upload: function()
    {
        if (XMLHttpRequest) {
            var xhr = new XMLHttpRequest();
            if (xhr.upload) {
                return true;
            }
        }
        return false;
    },
    uploadFile: function(config)
    {
        var file = config.file;
        var xhr = new XMLHttpRequest();
        xhr.upload.addEventListener("progress", function(e) {
            if (e.lengthComputable && config.progress) {
                config.progress.call(config.scope, e);
            }
        }, false);
        xhr.onreadystatechange = function(e) {
            if (xhr.readyState == 4) {
                var errorMsg = false;
                if (xhr.status != 200) {
                        errorMsg = xhr.responseText;
                } else if (!xhr.responseText) {
                    errorMsg = 'response is empty';
                } else {
                    try {
                        var r = Ext.decode(xhr.responseText);
                    } catch(e) {
                        errorMsg = e.toString()+':
'+xhr.responseText;
                    }
                    if (!errorMsg && r.exception) {
                        errorMsg = '
'+r.exception+'
';
                    }
                }
                if (errorMsg) {
                    var sendMail = !r || !r.exception;
                    Vps.handleError({
                        url: '/vps/media/upload/json-upload',
                        message: errorMsg,
                        title: trlVps('Upload Error'),
                        mail: sendMail,
                        checkRetry: false
                    });
                    if (config.failure) {
                        config.failure.call(config.scope, r);
                    }
                    return;
                }

                if (!r.success) {
                    if (r.error) {
                        Ext.Msg.alert(trlVps('Error'), r.error);
                    } else {
                        Ext.Msg.alert(trlVps('Error'), trlVps("A Server failure occured."));
                    }
                    if (config.failure) {
                        config.failure.call(config.scope, r);
                    }
                    return;
                }

                if (config.success) {
                    config.success.call(config.scope, r, config);
                }
            }
        }
        xhr.open('POST', '/vps/media/upload/json-upload');
        xhr.setRequestHeader('X-Upload-Name', file.name);
        xhr.setRequestHeader('X-Upload-Size', file.size);
        xhr.setRequestHeader('X-Upload-Type', file.type);
        xhr.setRequestHeader('X-Upload-MaxResolution', config.maxResolution);
        xhr.overrideMimeType('text/plain; charset=x-user-defined-binary');
        xhr.send(file);

        return xhr;
    }
};<commit_msg>don't call overrideMimeType if it doesn't exist, required for IE<commit_after>Vps.Utils.Upload = {
    supportsHtml5Upload: function()
    {
        if (XMLHttpRequest) {
            var xhr = new XMLHttpRequest();
            if (xhr.upload) {
                return true;
            }
        }
        return false;
    },
    uploadFile: function(config)
    {
        var file = config.file;
        var xhr = new XMLHttpRequest();
        xhr.upload.addEventListener("progress", function(e) {
            if (e.lengthComputable && config.progress) {
                config.progress.call(config.scope, e);
            }
        }, false);
        xhr.onreadystatechange = function(e) {
            if (xhr.readyState == 4) {
                var errorMsg = false;
                if (xhr.status != 200) {
                        errorMsg = xhr.responseText;
                } else if (!xhr.responseText) {
                    errorMsg = 'response is empty';
                } else {
                    try {
                        var r = Ext.decode(xhr.responseText);
                    } catch(e) {
                        errorMsg = e.toString()+':
'+xhr.responseText;
                    }
                    if (!errorMsg && r.exception) {
                        errorMsg = '
'+r.exception+'
';
                    }
                }
                if (errorMsg) {
                    var sendMail = !r || !r.exception;
                    Vps.handleError({
                        url: '/vps/media/upload/json-upload',
                        message: errorMsg,
                        title: trlVps('Upload Error'),
                        mail: sendMail,
                        checkRetry: false
                    });
                    if (config.failure) {
                        config.failure.call(config.scope, r);
                    }
                    return;
                }

                if (!r.success) {
                    if (r.error) {
                        Ext.Msg.alert(trlVps('Error'), r.error);
                    } else {
                        Ext.Msg.alert(trlVps('Error'), trlVps("A Server failure occured."));
                    }
                    if (config.failure) {
                        config.failure.call(config.scope, r);
                    }
                    return;
                }

                if (config.success) {
                    config.success.call(config.scope, r, config);
                }
            }
        }
        xhr.open('POST', '/vps/media/upload/json-upload');
        xhr.setRequestHeader('X-Upload-Name', file.name);
        xhr.setRequestHeader('X-Upload-Size', file.size);
        xhr.setRequestHeader('X-Upload-Type', file.type);
        xhr.setRequestHeader('X-Upload-MaxResolution', config.maxResolution);
        if (xhr.overrideMimeType) xhr.overrideMimeType('text/plain; charset=x-user-defined-binary');
        xhr.send(file);

        return xhr;
    }
};
<|endoftext|>