<commit_before>core.factory("AbstractModel", function ($injector, $rootScope, $q, ModelCache, ModelUpdateService, ValidationStore, WsApi) {

    return function AbstractModel(repoName) {

        var abstractModel;

        var mapping;

        var entityName;

        var validations;

        var defer = $q.defer();

        var listenCallbacks = [];

        var shadow = {};

        var validationResults = {};

        var listening = false;

        var combinationOperation = 'extend';

        var beforeMethodBuffer = [];

        var repo;

        $rootScope.$on("$locationChangeSuccess", function () {
            listenCallbacks.length = 0;
        });

        this.updatePending = false;

        this.deletePending = false;

        this.updateRequested = false;

        this.deleteRequested = false;

        this.before = function (beforeMethod) {
            beforeMethodBuffer.push(beforeMethod);
        }

        this.fetch = function () {
            if (mapping.instantiate !== undefined) {

                var fetch = true;
                if (mapping.caching) {
                    var cachedModel = ModelCache.get(entityName);
                    if (cachedModel) {
                        setData(cachedModel);
                        fetch = false;
                    }
                }

                if (fetch) {
                    WsApi.fetch(mapping.instantiate).then(function (res) {
                        processResponse(res);
                    }, function (error) {
                        defer.reject(error);
                    });
                }

            }
        }

        this.init = function (data, apiMapping) {

            abstractModel = this;

            mapping = apiMapping;

            entityName = abstractModel.constructor.getName();

            if (mapping.validations && entityName !== undefined && entityName !== null && entityName.length > 0) {
                validations = ValidationStore.getValidations(entityName);
            }

            if (data) {
                setData(data);
            } else {
                if (!mapping.lazy) {
                    this.fetch();
                }
            }
            angular.forEach(beforeMethodBuffer, function (beforeMethod) {
                beforeMethod();
            });
            this.ready().then(function () {
                ModelUpdateService.register(abstractModel);
            });
        };

        this.enableMergeCombinationOperation = function () {
            combinationOperation = 'merge';
        };

        this.enableExtendCombinationOperation = function () {
            combinationOperation = 'extend';
        };

        this.getCombinationOperation = function () {
            return combinationOperation;
        };

        this.getEntityName = function () {
            return entityName;
        };

        this.getValidations = function () {
            return validations;
        };

        this.getMapping = function () {
            return mapping;
        };

        this.ready = function () {
            return defer.promise;
        };

        this.save = function () {
            if (injectRepo()) {
                return repo.save(this);
            } else {
                var model = this;
                var promise = $q(function (resolve) {
                    if (model.dirty()) {
                        angular.extend(mapping.update, {
                            data: model
                        });
                        WsApi.fetch(mapping.update).then(function (res) {
                            resolve(res);
                        });
                    } else {
                        var payload = {};
                        payload[model.constructor.name] = model;
                        resolve({
                            body: angular.toJson({
                                payload: payload,
                                meta: {
                                    type: "SUCCESS"
                                }
                            })
                        });
                    }
                });
                promise.then(function (res) {
                    var message = angular.fromJson(res.body);
                    if (message.meta.status === "INVALID") {
                        angular.extend(abstractRepo, message.payload);
                    }
                });
                return promise;
            }
        };

        this.delete = function () {
            if (injectRepo()) {
                return repo.delete(this);
            } else {
                var model = this;
                angular.extend(mapping.remove, {
                    data: model
                });
                var promise = WsApi.fetch(mapping.remove);
                promise.then(function (res) {
                    var message = angular.fromJson(res.body);
                    if (message.meta.status === "INVALID") {
                        angular.extend(abstractRepo, message.payload);
                    }
                });
                return promise;
            }
        };

        this.listen = function (cb) {
            listenCallbacks.push(cb);
        };

        this.clearListens = function () {
            listenCallbacks.length = 0;
        };

        this._syncShadow = function () {
            shadow = angular.copy(abstractModel);
        };

        this.acceptPendingUpdate = function () {
            console.warn("No update pending!");
        };

        this.acceptPendingDelete = function () {
            console.warn("No delete pending!");
        };

        this.refresh = function () {
            angular.extend(abstractModel, shadow);
        };

        this.dirty = function () {
            return compare(abstractModel, shadow);
        };

        this.setValidationResults = function (results) {
            angular.extend(validationResults, results);
            if (injectRepo()) {
                angular.extend(repo.ValidationResults, results);
            }
        };

        this.getValidationResults = function () {
            return validationResults;
        };

        this.clearValidationResults = function () {
            delete validationResults.messages;
        };

        this.update = function (data) {
            angular[combinationOperation](abstractModel, data);
            abstractModel._syncShadow();
        };

        this.extend = function (changes) {
            angular.extend(abstractModel, changes);
            abstractModel._syncShadow();
        };

        var compare = function (m, s) {
            if (typeof m === 'object') {
                if (typeof s === 'object') {
                    var diff = false;
                    for (var i in m) {
                        if (m.hasOwnProperty(i) && i !== '$$hashKey' && i !== '$$state') {
                            diff = compare(m[i], s[i]);
                            if (diff) {
                                break;
                            }
                        }
                    }
                    return diff;
                } else {
                    return false;
                }
            } else if (typeof m === 'function') {
                return false;
            } else {
                // console.log(m !== s, m, s);
                return m !== s;
            }
        };

        var injectRepo = function () {
            if (repo === undefined) {
                try {
                    repo = $injector.get(repoName);
                } catch (e) {
                    console.warn('Unable to inject ' + repoName);
                    return false;
                }
                return true;
            } else {
                return true;
            }
        };

        var setData = function (data) {

            angular[combinationOperation](abstractModel, data);

            abstractModel._syncShadow();
            if (!listening && mapping.modelListeners) {
                listen();
            }
            if (mapping.caching) {
                var cachedModel = ModelCache.get(entityName);
                if (cachedModel === undefined) {
                    ModelCache.set(entityName, abstractModel);
                } else {
                    // could possibly update cache here
                }
            }
            defer.resolve(abstractModel);
        };

        var listen = function () {
            if (abstractModel && mapping.listen) {
                if (abstractModel.id) {
                    angular.extend(mapping.listen, {
                        method: abstractModel.id
                    });
                }
                var notifyPromise = WsApi.listen(mapping.listen);
                notifyPromise.then(null, null, function (res) {
                    processResponse(res);
                    angular.forEach(listenCallbacks, function (cb) {
                        cb(res);
                    });
                });
                listening = true;
                return notifyPromise;
            }
        };

        var processResponse = function (res) {
            var resObj = angular.fromJson(res.body);
            if (resObj.meta.status !== 'ERROR') {
                if (combinationOperation === 'extend') {
                    angular.forEach(resObj.payload, function (datum) {
                        angular[combinationOperation](abstractModel, datum);
                    });
                }
                setData(abstractModel);
            } else {
                abstractModel.refresh();
            }
        };

        // additional core level model methods and variables

        return this;
    };

});<commit_msg>use weak comparison for dirty check<commit_after>core.factory("AbstractModel", function ($injector, $rootScope, $q, ModelCache, ModelUpdateService, ValidationStore, WsApi) {

    return function AbstractModel(repoName) {

        var abstractModel;

        var mapping;

        var entityName;

        var validations;

        var defer = $q.defer();

        var listenCallbacks = [];

        var shadow = {};

        var validationResults = {};

        var listening = false;

        var combinationOperation = 'extend';

        var beforeMethodBuffer = [];

        var repo;

        $rootScope.$on("$locationChangeSuccess", function () {
            listenCallbacks.length = 0;
        });

        this.updatePending = false;

        this.deletePending = false;

        this.updateRequested = false;

        this.deleteRequested = false;

        this.before = function (beforeMethod) {
            beforeMethodBuffer.push(beforeMethod);
        }

        this.fetch = function () {
            if (mapping.instantiate !== undefined) {

                var fetch = true;
                if (mapping.caching) {
                    var cachedModel = ModelCache.get(entityName);
                    if (cachedModel) {
                        setData(cachedModel);
                        fetch = false;
                    }
                }

                if (fetch) {
                    WsApi.fetch(mapping.instantiate).then(function (res) {
                        processResponse(res);
                    }, function (error) {
                        defer.reject(error);
                    });
                }

            }
        }

        this.init = function (data, apiMapping) {

            abstractModel = this;

            mapping = apiMapping;

            entityName = abstractModel.constructor.getName();

            if (mapping.validations && entityName !== undefined && entityName !== null && entityName.length > 0) {
                validations = ValidationStore.getValidations(entityName);
            }

            if (data) {
                setData(data);
            } else {
                if (!mapping.lazy) {
                    this.fetch();
                }
            }
            angular.forEach(beforeMethodBuffer, function (beforeMethod) {
                beforeMethod();
            });
            this.ready().then(function () {
                ModelUpdateService.register(abstractModel);
            });
        };

        this.enableMergeCombinationOperation = function () {
            combinationOperation = 'merge';
        };

        this.enableExtendCombinationOperation = function () {
            combinationOperation = 'extend';
        };

        this.getCombinationOperation = function () {
            return combinationOperation;
        };

        this.getEntityName = function () {
            return entityName;
        };

        this.getValidations = function () {
            return validations;
        };

        this.getMapping = function () {
            return mapping;
        };

        this.ready = function () {
            return defer.promise;
        };

        this.save = function () {
            if (injectRepo()) {
                return repo.save(this);
            } else {
                var model = this;
                var promise = $q(function (resolve) {
                    if (model.dirty()) {
                        angular.extend(mapping.update, {
                            data: model
                        });
                        WsApi.fetch(mapping.update).then(function (res) {
                            resolve(res);
                        });
                    } else {
                        var payload = {};
                        payload[model.constructor.name] = model;
                        resolve({
                            body: angular.toJson({
                                payload: payload,
                                meta: {
                                    type: "SUCCESS"
                                }
                            })
                        });
                    }
                });
                promise.then(function (res) {
                    var message = angular.fromJson(res.body);
                    if (message.meta.status === "INVALID") {
                        angular.extend(abstractRepo, message.payload);
                    }
                });
                return promise;
            }
        };

        this.delete = function () {
            if (injectRepo()) {
                return repo.delete(this);
            } else {
                var model = this;
                angular.extend(mapping.remove, {
                    data: model
                });
                var promise = WsApi.fetch(mapping.remove);
                promise.then(function (res) {
                    var message = angular.fromJson(res.body);
                    if (message.meta.status === "INVALID") {
                        angular.extend(abstractRepo, message.payload);
                    }
                });
                return promise;
            }
        };

        this.listen = function (cb) {
            listenCallbacks.push(cb);
        };

        this.clearListens = function () {
            listenCallbacks.length = 0;
        };

        this._syncShadow = function () {
            shadow = angular.copy(abstractModel);
        };

        this.acceptPendingUpdate = function () {
            console.warn("No update pending!");
        };

        this.acceptPendingDelete = function () {
            console.warn("No delete pending!");
        };

        this.refresh = function () {
            angular.extend(abstractModel, shadow);
        };

        this.dirty = function () {
            return compare(abstractModel, shadow);
        };

        this.setValidationResults = function (results) {
            angular.extend(validationResults, results);
            if (injectRepo()) {
                angular.extend(repo.ValidationResults, results);
            }
        };

        this.getValidationResults = function () {
            return validationResults;
        };

        this.clearValidationResults = function () {
            delete validationResults.messages;
        };

        this.update = function (data) {
            angular[combinationOperation](abstractModel, data);
            abstractModel._syncShadow();
        };

        this.extend = function (changes) {
            angular.extend(abstractModel, changes);
            abstractModel._syncShadow();
        };

        var compare = function (m, s) {
            if (typeof m === 'object') {
                if (typeof s === 'object') {
                    var diff = false;
                    for (var i in m) {
                        if (m.hasOwnProperty(i) && i !== '$$hashKey' && i !== '$$state') {
                            diff = compare(m[i], s[i]);
                            if (diff) {
                                // console.log(i)
                                break;
                            }
                        }
                    }
                    return diff;
                } else {
                    return false;
                }
            } else if (typeof m === 'function') {
                return false;
            } else {
                // console.log(m != s, m, s);
                return m != s;
            }
        };

        var injectRepo = function () {
            if (repo === undefined) {
                try {
                    repo = $injector.get(repoName);
                } catch (e) {
                    console.warn('Unable to inject ' + repoName);
                    return false;
                }
                return true;
            } else {
                return true;
            }
        };

        var setData = function (data) {

            angular[combinationOperation](abstractModel, data);

            abstractModel._syncShadow();
            if (!listening && mapping.modelListeners) {
                listen();
            }
            if (mapping.caching) {
                var cachedModel = ModelCache.get(entityName);
                if (cachedModel === undefined) {
                    ModelCache.set(entityName, abstractModel);
                } else {
                    // could possibly update cache here
                }
            }
            defer.resolve(abstractModel);
        };

        var listen = function () {
            if (abstractModel && mapping.listen) {
                if (abstractModel.id) {
                    angular.extend(mapping.listen, {
                        method: abstractModel.id
                    });
                }
                var notifyPromise = WsApi.listen(mapping.listen);
                notifyPromise.then(null, null, function (res) {
                    processResponse(res);
                    angular.forEach(listenCallbacks, function (cb) {
                        cb(res);
                    });
                });
                listening = true;
                return notifyPromise;
            }
        };

        var processResponse = function (res) {
            var resObj = angular.fromJson(res.body);
            if (resObj.meta.status !== 'ERROR') {
                if (combinationOperation === 'extend') {
                    angular.forEach(resObj.payload, function (datum) {
                        angular[combinationOperation](abstractModel, datum);
                    });
                }
                setData(abstractModel);
            } else {
                abstractModel.refresh();
            }
        };

        // additional core level model methods and variables

        return this;
    };

});<|endoftext|>


<commit_before>/**
 * CCHelper v1.0 - https://github.com/hhromic/midi-leds-js
 * A MIDI Control Change (CC) helper for the MidiLeds and MidiColors classes.
 * MIT license
 * Hugo Hromic - http://github.com/hhromic
/*jslint nomen: true*/

;(function () {
    'use strict';

    /**
     * Represents a MIDI CC helper.
     *
     * @public
     * @constructor
     * @param {object} midiLeds - the MidiLeds class instance to manipulate.
     * @param {object} midiColors - the MidiColors class instance to manipulate.
     */
    function CCHelper(midiLeds, midiColors) {
        this._midiLeds = midiLeds;
        this._midiColors = midiColors;
        this._timeRange = 5000;
    }

    // Shortcuts to improve speed and size
    var proto = CCHelper.prototype;

    /**
     * Gets the current time range to use for scaling times.
     *
     * @public
     * @returns {number} - the current time range.
     */
    proto.getTimeRange = function () {
        return this._timeRange;
    }

    /**
     * Sets the time range to use for scaling times.
     *
     * @public
     * @param {number} timeRange - the time range to set.
     */
    proto.setTimeRange = function (timeRange) {
        this._timeRange = timeRange;
    }

    /**
     * Processes a MIDI Control Change (CC) message.
     *
     * @param {number} _channel - the channel of the CC message.
     * @param {number} _control - the control number of the CC message.
     * @param {number} _value - the control value of the CC message.
     */
    proto.controlChange = function (_channel, _control, _value) {
        var channel = _channel & 0xF;
        var control = _control & 0x7F;
        var value = _value & 0x7F;
        switch (control) {
            case 0x14: // Change Palette
                this._midiColors.setPalette(channel, value);
                return;
            case 0x15: // Change Color Map
                this._midiColors.setColorMap(channel, value);
                return;
            case 0x16: // Change Fixed Hue
                this._midiColors.setFixedHue(channel, Math.round(0xFF * (value / 0x7F)));
                return;
            case 0x17: // Change Attack Time
                this._midiLeds.setAttackTime(channel, Math.round(this._timeRange * (value / 0x7F)));
                return;
            case 0x18: // Change Decay Time
                this._midiLeds.setDecayTime(channel, Math.round(this._timeRange * (value / 0x7F)));
                return;
            case 0x19: // Change Sustain Level
                this._midiLeds.setSustainLevel(channel, 1.0 * (value / 0x7F));
                return;
            case 0x1A: // Change Release Time
                this._midiLeds.setReleaseTime(channel, Math.round(this._timeRange * (value / 0x7F)));
                return;
            case 0x1B: // Change Ignore Velocity
                this._midiColors.setIgnoreVelocity(channel, value < 0x40 ? false : true);
                return;
            case 0x1C: // Change Base Brightness
                this._midiLeds.setBaseBrightness(channel, value);
                return;
            case 0x1D: // Change Enabled State
                this._midiLeds.setEnabled(channel, value < 0x40 ? false : true);
                return;
        }
    }

    // Expose either via AMD, CommonJS or the global object
    if (typeof define === 'function' && define.amd) {
        define(function () {
            return CCHelper;
        });
    }
    else if (typeof module === 'object' && module.exports) {
        module.exports = CCHelper;
    }
    else {
        this.CCHelper = CCHelper;
    }
}.call(this));
<commit_msg>Normalised function parameter names<commit_after>/**
 * CCHelper v1.0 - https://github.com/hhromic/midi-leds-js
 * A MIDI Control Change (CC) helper for the MidiLeds and MidiColors classes.
 * MIT license
 * Hugo Hromic - http://github.com/hhromic
/*jslint nomen: true*/

;(function () {
    'use strict';

    /**
     * Represents a MIDI CC helper.
     *
     * @public
     * @constructor
     * @param {object} midiLeds - the MidiLeds class instance to manipulate.
     * @param {object} midiColors - the MidiColors class instance to manipulate.
     */
    function CCHelper(midiLeds, midiColors) {
        this._midiLeds = midiLeds;
        this._midiColors = midiColors;
        this._timeRange = 5000;
    }

    // Shortcuts to improve speed and size
    var proto = CCHelper.prototype;

    /**
     * Gets the current time range to use for scaling times.
     *
     * @public
     * @returns {number} - the current time range.
     */
    proto.getTimeRange = function () {
        return this._timeRange;
    }

    /**
     * Sets the time range to use for scaling times.
     *
     * @public
     * @param {number} timeRange - the time range to set.
     */
    proto.setTimeRange = function (timeRange) {
        this._timeRange = timeRange;
    }

    /**
     * Processes a MIDI Control Change (CC) message.
     *
     * @param {number} channel - the channel of the CC message.
     * @param {number} control - the control number of the CC message.
     * @param {number} value - the control value of the CC message.
     */
    proto.controlChange = function (channel, control, value) {
        var _channel = channel & 0xF;
        var _value = value & 0x7F;
        switch (control & 0x7F) {
            case 0x14: // Change Palette
                this._midiColors.setPalette(_channel, _value);
                return;
            case 0x15: // Change Color Map
                this._midiColors.setColorMap(_channel, _value);
                return;
            case 0x16: // Change Fixed Hue
                this._midiColors.setFixedHue(_channel, Math.round(0xFF * (_value / 0x7F)));
                return;
            case 0x17: // Change Attack Time
                this._midiLeds.setAttackTime(_channel, Math.round(this._timeRange * (_value / 0x7F)));
                return;
            case 0x18: // Change Decay Time
                this._midiLeds.setDecayTime(_channel, Math.round(this._timeRange * (_value / 0x7F)));
                return;
            case 0x19: // Change Sustain Level
                this._midiLeds.setSustainLevel(_channel, 1.0 * (_value / 0x7F));
                return;
            case 0x1A: // Change Release Time
                this._midiLeds.setReleaseTime(_channel, Math.round(this._timeRange * (_value / 0x7F)));
                return;
            case 0x1B: // Change Ignore Velocity
                this._midiColors.setIgnoreVelocity(_channel, _value < 0x40 ? false : true);
                return;
            case 0x1C: // Change Base Brightness
                this._midiLeds.setBaseBrightness(_channel, _value);
                return;
            case 0x1D: // Change Enabled State
                this._midiLeds.setEnabled(_channel, _value < 0x40 ? false : true);
                return;
        }
    }

    // Expose either via AMD, CommonJS or the global object
    if (typeof define === 'function' && define.amd) {
        define(function () {
            return CCHelper;
        });
    }
    else if (typeof module === 'object' && module.exports) {
        module.exports = CCHelper;
    }
    else {
        this.CCHelper = CCHelper;
    }
}.call(this));
<|endoftext|>


<commit_before>/**
* Independent code, not REDIPS related.
* Read JSON from server (LOCALHOST or Calm4)via AJAX
**/

/* enable strict mode */
//"use strict";
var urlJSON   = "http://localhost/json/students.json";
var tableId   = "table1";
var colorEven = "#eee";
var colorOdd  = "#e0e0e0";
var rowLetters = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N'];
var hallCols = { "DIPA" : 8, "MAHI" : 7, "NERU" : 4, "PADHANA" : 6, "SACCA" : 8, "SOBHANA" : 6};
var sp = {};
var legend = {};
var emptyId = "000000";
var last_layout = [["000000"],["OM0002"],["OM0003"],["OM0004"],["000000"],["OM0006"],["OM0007"],["OM0008"],["000000"],["NM0002"],["000000"],["NM0004"],["NM0005"],["NM0008"],["NM0006"],["NM0007"],["000000"],["NM0001"],["OM0001"],["NM0003"],["OM0005"]];

/**
* HTML+CSS output for each student
**/
function prettyPrintStd(std){
	var cushion = "";
	if(std.id === emptyId)
		cushion = prettyPrintEmpty();
	else{
		cushion  = "
";
		cushion += "
" + std.id + "
";
		cushion += "
" + std.gname + " " + std.fname + "
";
		cushion += "
" + std.nSat + "/" + std.nSv + "
";
		//<textarea cols='20' rows='5'>this is textarea</textarea>
		cushion += "
";
	}
	return cushion;
}
/**
* HTML+CSS output for empty seats
**/
function prettyPrintEmpty(){
	var cushion = "
EMPTY
";
	return cushion;
}
/**
* Returns an HTML table compliying with REDIPS library.
* When arrStd.length and ncols values are such that there are not enough students to fill up the table (ajax from CALM),
* empty places will be populated. When loading students from a persisted-table, then everything should be square
* @param arrStd Array of students. Should be ordered top-down (from oldest to newest)
* @param colWidth Width in px for each col (This value will depend on the A4 proportions and number of cols of Dhamma Hall)
* @param ncols
**/
function createTable(arrStd, ncols){
	var numStd = arrStd.length;
	var numStdInLastRow = numStd % ncols; //Num students on last row
	var numEmptiesInLastRow = ncols - numStdInLastRow; //Never used when reading from persisted array. Only used
	//when reading first time from CALM ajax
	var nrows = Math.floor(numStd / ncols); //Num FULL rows
	var color = colorEven;
	var table = colgroup = tfoot = "";

	//COLGROUP + TFOOT
	colgroup = "<colgroup><col width=\"20\" />";
	tfoot    = "

	";

	for( var i =0; i<ncols; i++){
		colgroup += "<col width='100'/>"; //colWidth HARDCODED
		tfoot += "" + (i+1) + "	\n";
	}
	colgroup += "</colgroup>";
	tfoot += "

";

	//ACTUAL CELLs DATA
	for(i=0; i<nrows; i++){ //EACH ROW
		var tr = "
\n";
		tr += "" + rowLetters[i] + "	\n";
		var tmp = arrStd.splice(0,ncols);
		for( var j=0; j< ncols; j++) { //EACH SEAT OF CURRENT ROW
			tr += "" + prettyPrintStd(tmp[j]) + "	\n"; //Students
		}
		table = tr + "
\n" + table;
	}
	//Last row: Filled with remaining students + fillinp up with EMPTYes
	if(numStdInLastRow != 0){
		var tr = "
\n";
		tr += "" + rowLetters[nrows] + "	\n";
		for(i=0; i < numStdInLastRow; i++){//arrStd: Leftover students
			tr += "" + prettyPrintStd(arrStd[i]) + "	\n"; //Last row on the back
		}
		for(i=0; i< numEmptiesInLastRow; i++){ //EMPTYes
			tr += "" + prettyPrintEmpty() + "	\n"; 	//Empty
		}
		table = tr + "
\n" + table;
	}
	table = "
" + colgroup + tfoot + table + "
"; //id HARDCODED
	return table;
}
/**
* New unknown dhamma venue. Send email to start enquiring around the number of columns, and meanwhile return defaultHallCols
* @return defaultHallCols
*/
function setVenue(newVenue){  //TODO codigo ya disponible en feedback.js
	//Ask user for information (nCols) about this new unknown venue and upload it to GoogleForm
	return  $('input[name=ncol]:checked', '#cols').val() ;
}

/**
* Method executed whenever user performs drag&drop and cell contents are actually switched
* It's passed to script.js via asingEventHandlerForSwitch(persist)
*/
function persist(){

	alert("metodo persist en din_table.js");
}

function reload(){  //Uses global sp
	g = $('input[name=gender]:checked', '#genders').val(); //"MALE" || "FEMALE"
	var vOld = sp[g]["OLD"].sort(function(a, b) { return b.nSat - a.nSat; }); //order by sat courses
	var vNew = sp[g]["NEW"];
	var vGender = vOld.concat(vNew); //old+new students, just one gender
	vGender = orderByLastLayout(vGender);

	legend.gender = g;
	legend.venue = sp.VENUE;
	legend.cols = $('input[name=ncol]:checked', '#cols').val();
	legend.start = sp.START;
	legend.numOld = vOld.length;
	legend.numNew = vNew.length;
	legend.rows = Math.ceil(vGender.length / legend.cols);

	//renderLegend(legend); //TODO
	var table = createTable(vGender, legend.cols );
	$('#entry_point').text(''); //Clean previous table
	$('#entry_point').html(table); //Inject table into html label:
	$('#redips-drag').width($('#table1').width()); //http://www.redips.net/javascript/redips-drag-documentation-appendix-a/#redips_drag
	$('#redips-drag').height($('#table1').height()); //http://www.redips.net/javascript/redips-drag-documentation-appendix-a/#redips_drag
	alert("din_table: antes REDIPS.drag.init(persist)");
	 //REDIPS has to reload dinamically generated tables
	REDIPS.drag.init();
	var json_text = save("json"); //"save" saves array as text!  <----- QUE SCRIPT.JS GUARDE EL ARRAY EN CADA DRAG&DROP (EVENTO SWITCH!!!!!)
	last_layout = jQuery.parseJSON(json_text); //this is a kind of  eval('(' + json_text + ')');
}
/* TODO improve this function description
* Whenever user modifies the SP, the changes get stored (online DB, extension persistence, etc)
* Meanwhile CALM4 database may have changed (cancelled students/added students)
* getLastLayout
*/
function getLastLayout(){
	//last_layout = [["NM0006"],["000000"],["OM0003"],["OM0004"],["OM0001"],["OM0006"],["OM0007"],["OM0008"],["OM0002"],["NM0002"],["000000"],["NM0004"],["NM0005"],["NM0008"],["000000"],["NM0007"],["000000"],["NM0001"],["000000"],["NM0003"],["OM0005"]];
	//last_layout = [["NM0006"],["000000"],["OM0003"],["000000"],["OM0001"],["OM0006"],["OM0007"],["OM0008"],["OM0002"],["NM0002"],["OM0004"],["NM0004"],["NM0005"],["NM0008"],["000000"],["NM0007"],["000000"],["NM0001"],["000000"],["NM0003"],["OM0005"]];
	return last_layout;
}
/* Recalls the last used layout for this course and sort the students array according to it.
* Default order to asign sits to students is by "nSat top->down". However, any modification by the user was recorded,
* so now we can rebuild the last used layout.
* TODO improve explanation
* Whenever user modifies the SP, the changes get stored (online DB, extension persistence, etc)
* Meanwhile CALM4 database may have changed (cancelled students/added students)
*/
function orderByLastLayout(students){
	var zig = getLastLayout();
	var ordered_result = []; //Ordered according to persisted user's last arrangement
	for(var i=0;i< zig.length; i++){ //ZIG is the ordered pattern (user's last arrangement)
		var j = 0;
		var found = false;
		while(j < students.length && !found){ //to match against unsorted student's array
			if(zig[i][0] === emptyId){ //"000000"
				ordered_result.push({"id": emptyId});
				found = true;
			}
			else if(zig[i][0] === students[j]['id'] ){
				var std = students.splice(j,1) //students.length decreases by 1
				ordered_result.push(std[0]);
				found = true;
			}
			j++;
		}
	}
	console.table(ordered_result);
	return ordered_result;
}

function getStudentsJSON(urlJSON){
	$.ajax({ url: urlJSON, type: "GET", dataType: "json",
		complete: function (data_response) {
			sp = JSON.parse(data_response.responseText); //sp GLOBAL
			//$('input:radio[name="gender"]').filter('[value="Male"]').attr('checked', true);
			//Pre-select the number of cols according to the center name:
			$('input[name=ncol]', '#cols').filter('[value="' +hallCols[sp.VENUE.toUpperCase()] + '"]').attr('checked', true);
			setMode(document.querySelector('input[name="drop_option"]:checked')); //Activate the selected drag&drop mode
			reload();
			//console.log(document.documentElement.outerHTML);
		},
		error: function (error) {
			alert(JSON.stringify(error));
		}
	});
}

//EXEC:
getStudentsJSON(urlJSON);
asingEventHandlerForSwitch(persist); //only once is enough :)
<commit_msg>Added localStorage for table layout<commit_after>/**
* Independent code, not REDIPS related.
* Read JSON from server (LOCALHOST or Calm4)via AJAX
**/

/* enable strict mode */
"use strict";
var urlJSON   = "http://localhost/json/students.json";
var tableId   = "table1";
var colorEven = "#eee";
var colorOdd  = "#e0e0e0";
var rowLetters = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N'];
var hallCols = { "DIPA" : 8, "MAHI" : 7, "NERU" : 4, "PADHANA" : 6, "SACCA" : 8, "SOBHANA" : 6};
var sp = {};
var legend = {};
var emptyId = "000000";
var last_layout = [["000000"],["OM0002"],["OM0003"],["OM0004"],["000000"],["OM0006"],["OM0007"],["OM0008"],["000000"],["NM0002"],["000000"],["NM0004"],["NM0005"],["NM0008"],["NM0006"],["NM0007"],["000000"],["NM0001"],["OM0001"],["NM0003"],["OM0005"]];

/**
* HTML+CSS output for each student
**/
function prettyPrintStd(std){
	var cushion = "";
	if(std.id === emptyId)
		cushion = prettyPrintEmpty();
	else{
		cushion  = "
";
		cushion += "
" + std.id + "
";
		cushion += "
" + std.gname + " " + std.fname + "
";
		cushion += "
" + std.nSat + "/" + std.nSv + "
";
		//<textarea cols='20' rows='5'>this is textarea</textarea>
		cushion += "
";
	}
	return cushion;
}
/**
* HTML+CSS output for empty seats
**/
function prettyPrintEmpty(){
	var cushion = "
EMPTY
";
	return cushion;
}
/**
* Returns an HTML table compliying with REDIPS library.
* When arrStd.length and ncols values are such that there are not enough students to fill up the table (ajax from CALM),
* empty places will be populated. When loading students from a persisted-table, then everything should be square
* @param arrStd Array of students. Should be ordered top-down (from oldest to newest)
* @param colWidth Width in px for each col (This value will depend on the A4 proportions and number of cols of Dhamma Hall)
* @param ncols
**/
function createTable(arrStd, ncols){
	var numStd = arrStd.length;
	var numStdInLastRow = numStd % ncols; //Num students on last row
	var numEmptiesInLastRow = ncols - numStdInLastRow; //Never used when reading from persisted array. Only used
	//when reading first time from CALM ajax
	var nrows = Math.floor(numStd / ncols); //Num FULL rows
	var color = colorEven;
	var table = "";
	var colgroup = "";
	var tfoot = "";

	//COLGROUP + TFOOT
	colgroup = "<colgroup><col width=\"20\" />";
	tfoot    = "

	";

	for( var i =0; i<ncols; i++){
		colgroup += "<col width='100'/>"; //colWidth HARDCODED
		tfoot += "" + (i+1) + "	\n";
	}
	colgroup += "</colgroup>";
	tfoot += "

";

	//ACTUAL CELLs DATA
	for(i=0; i<nrows; i++){ //EACH ROW
		var tr = "
\n";
		tr += "" + rowLetters[i] + "	\n";
		var tmp = arrStd.splice(0,ncols);
		for( var j=0; j< ncols; j++) { //EACH SEAT OF CURRENT ROW
			tr += "" + prettyPrintStd(tmp[j]) + "	\n"; //Students
		}
		table = tr + "
\n" + table;
	}
	//Last row: Filled with remaining students + fillinp up with EMPTYes
	if(numStdInLastRow != 0){
		var tr = "
\n";
		tr += "" + rowLetters[nrows] + "	\n";
		for(i=0; i < numStdInLastRow; i++){//arrStd: Leftover students
			tr += "" + prettyPrintStd(arrStd[i]) + "	\n"; //Last row on the back
		}
		for(i=0; i< numEmptiesInLastRow; i++){ //EMPTYes
			tr += "" + prettyPrintEmpty() + "	\n"; 	//Empty
		}
		table = tr + "
\n" + table;
	}
	table = "
" + colgroup + tfoot + table + "
"; //id HARDCODED
	return table;
}
/**
* New unknown dhamma venue. Send email to start enquiring around the number of columns, and meanwhile return defaultHallCols
* @return defaultHallCols
*/
function setVenue(newVenue){  //TODO codigo ya disponible en feedback.js
	//Ask user for information (nCols) about this new unknown venue and upload it to GoogleForm
	return  $('input[name=ncol]:checked', '#cols').val() ;
}

//--------- LOCAL STORAGE --------
/**
* @return Last saved table layout
*/
function getLastLayout(){
	var last_layout = JSON.parse(localStorage["last_layout"]);
	return last_layout;
}
/**
* @param Stores the current table layout
*/
function setLastLayout(){
	var json_text = save("json"); //"save" saves array as text!
	last_layout = jQuery.parseJSON(json_text); //turns text into Array - this is a kind of  eval('(' + json_text + ')');
	localStorage["last_layout"] = JSON.stringify(last_layout);
}
/** initializes LocalStorage object with empty array*/
function initLocalStorage(){
	if (!localStorage.last_layout) localStorage.last_layout = null;
}
//--------- END LOCAL STORAGE --------

function reload(){  //Uses global sp
	var g = $('input[name=gender]:checked', '#genders').val(); //"MALE" || "FEMALE"
	var vOld = sp[g]["OLD"].sort(function(a, b) { return b.nSat - a.nSat; }); //order by sat courses
	var vNew = sp[g]["NEW"];
	var vGender = vOld.concat(vNew); //old+new students, just one gender
	vGender = orderByLastLayout(vGender);

	legend.gender = g;
	legend.venue = sp.VENUE;
	legend.cols = $('input[name=ncol]:checked', '#cols').val();
	legend.start = sp.START;
	legend.numOld = vOld.length;
	legend.numNew = vNew.length;
	legend.rows = Math.ceil(vGender.length / legend.cols);

	//renderLegend(legend); //TODO
	var table = createTable(vGender, legend.cols );
	$('#entry_point').text(''); //Clean previous table
	$('#entry_point').html(table); //Inject table into html label:
	$('#redips-drag').width($('#table1').width()); //http://www.redips.net/javascript/redips-drag-documentation-appendix-a/#redips_drag
	$('#redips-drag').height($('#table1').height()); //http://www.redips.net/javascript/redips-drag-documentation-appendix-a/#redips_drag

	REDIPS.drag.init(); //REDIPS has to reload dinamically generated tables

}

/** Recalls the last used layout for this course and sort the students array according to it.
* Default order to asign sits to students is by "nSat top->down". However, any layout modification made by the user was persisted in the
* localStorage, so now we can rebuild the last used layout.
* @param students , which is a fresh batch of students. If a student is not present in 'students' array anymore (ie. cancelled course) just wont be
* added to 'ordered_result'. If a new student joined the course, it wont be present in the previous persisted data ('zig' array) so it will be
* left out in 'students' array. But still needs to be included on the new layout, that is why we return ordered_result.concat(students)
* @return Any previous student that still present on the course + any new students comming from last-second ajax call to calm-server
*/
function orderByLastLayout(students){
	var zig = getLastLayout();
	var ordered_result = []; //Ordered according to persisted user's last arrangement
	if(!zig){
		ordered_result = students; //No previous layout, so use default order of 'students' array
	}else {
		for(var i=0;i< zig.length; i++){ //ZIG is the ordered pattern (user's last arrangement)
			var j = 0;
			var found = false;
			while(j < students.length && !found){ //to match against unsorted student's array
				if(zig[i][0] === emptyId){ //"000000"
					ordered_result.push({"id": emptyId});
					found = true;
				}
				else if(zig[i][0] === students[j]['id'] ){
					var std = students.splice(j,1) //students.length decreases by 1
					ordered_result.push(std[0]);
					found = true;
				}
				j++;
			}
		}
		ordered_result = ordered_result.concat(students);
	}
	return ordered_result;
}

function getStudentsJSON(urlJSON){
	$.ajax({ url: urlJSON, type: "GET", dataType: "json",
		complete: function (data_response) {
			sp = JSON.parse(data_response.responseText); //sp GLOBAL
			//$('input:radio[name="gender"]').filter('[value="Male"]').attr('checked', true);
			//Pre-select the number of cols according to the center name:
			$('input[name=ncol]', '#cols').filter('[value="' +hallCols[sp.VENUE.toUpperCase()] + '"]').attr('checked', true);
			setMode(document.querySelector('input[name="drop_option"]:checked')); //Activate the selected drag&drop mode
			reload();
			//console.log(document.documentElement.outerHTML);
		},
		error: function (error) {
			alert(JSON.stringify(error));
		}
	});
}

//EXEC:
initLocalStorage();
getStudentsJSON(urlJSON);
asingEventHandlerForSwitch(setLastLayout); //when user switches cells, persist() method gets invoked <|endoftext|>


<commit_before>/*jslint indent: 2 */
/*global document: false, chrome: false, $: false, createLink: false, createProjectSelect: false*/

(function () {
  "use strict";
  var iframeRegex = /oauth2relay/, userData = null,
    selectedProjectId = null, selectedProjectBillable = false;

  function createTimerLink(taskName) {
    var link = createLink('toggl-button teambox');
    link.addEventListener("click", function (e) {
      chrome.extension.sendMessage({
        type: 'timeEntry',
        description: task,
        projectId: selectedProjectId,
        billable: selectedProjectBillable
      });
      link.innerHTML = "Started...";
      return false;
    });
    return link;
  }

  function addButton(e) {
    if (e.target.className === "taskName" || iframeRegex.test(e.target.name)) {
      var taskDescription = $(".property.description"),
        title = $("#details_pane_title_row textarea#details_property_sheet_title").value,
        projectSelect = createProjectSelect(userData, "toggl-select teambox");

      //make sure we init the values when switching between tasks
      selectedProjectId = null;
      selectedProjectBillable = false;

      projectSelect.onchange = function (event) {
        selectedProjectId = event.target.options[event.target.selectedIndex].value;
        if (selectedProjectId !== "default") {
          selectedProjectBillable = userData.projects.filter(function (elem, index, array) {
            return (elem.id === selectedProjectId);
          })[0].billable;
        } else {
          selectedProjectId = null;
          selectedProjectBillable = false;
        }
      };

      taskDescription.parentNode.insertBefore(createTimerLink(title), taskDescription.nextSibling);
      taskDescription.parentNode.insertBefore(projectSelect, taskDescription.nextSibling);
    }
  }

  chrome.extension.sendMessage({type: 'activate'}, function (response) {
    if (response.success) {
      console.log(response.user);
      userData = response.user;
      document.addEventListener("DOMNodeInserted", addButton);
    }
  });

}());
<commit_msg>Update teambox.js<commit_after>/*jslint indent: 2 */
/*global document: false, chrome: false, $: false, createLink: false, createProjectSelect: false*/

(function () {
  "use strict";
  var iframeRegex = /oauth2relay/, userData = null,
    selectedProjectId = null, selectedProjectBillable = false;

  function createTimerLink(taskName) {
    var link = createLink('toggl-button teambox');
    link.addEventListener("click", function (e) {
      chrome.extension.sendMessage({
        type: 'timeEntry',
        description: task,
        projectId: selectedProjectId,
        billable: selectedProjectBillable
      });
      link.innerHTML = "Started...";
      return false;
    });
    return link;
  }

  function addButton(e) {
    if (e.target.className === ".taskName" || iframeRegex.test(e.target.name)) {
      var taskDescription = $(".taskName"),
        title = $("#details_pane_title_row textarea#details_property_sheet_title").value,
        projectSelect = createProjectSelect(userData, "toggl-select teambox");

      //make sure we init the values when switching between tasks
      selectedProjectId = null;
      selectedProjectBillable = false;

      projectSelect.onchange = function (event) {
        selectedProjectId = event.target.options[event.target.selectedIndex].value;
        if (selectedProjectId !== "default") {
          selectedProjectBillable = userData.projects.filter(function (elem, index, array) {
            return (elem.id === selectedProjectId);
          })[0].billable;
        } else {
          selectedProjectId = null;
          selectedProjectBillable = false;
        }
      };

      taskDescription.parentNode.insertBefore(createTimerLink(title), taskDescription.nextSibling);
      taskDescription.parentNode.insertBefore(projectSelect, taskDescription.nextSibling);
    }
  }

  chrome.extension.sendMessage({type: 'activate'}, function (response) {
    if (response.success) {
      console.log(response.user);
      userData = response.user;
      document.addEventListener("DOMNodeInserted", addButton);
    }
  });

}());
<|endoftext|>


<commit_before>var path = require('path')
var port = process.env.port || 8000
var srcPath = path.join(__dirname, '/../src')
var HtmlWebpackPlugin = require("html-webpack-plugin")
var CopyWebpackPlugin = require('copy-webpack-plugin')

module.exports = {
  port: port,
  debug: true,
  context: path.join(__dirname, '..'),
  output: {
    path: path.join(__dirname, '/../dist'),
    filename: '[name].js',
  },
  entry: {
    app: [path.join(__dirname, '../src/components/run')],
    presentation: path.join(__dirname, '../src/components/show/presentation'),
    vendors: [
      "webpack-material-design-icons",
      'babel-polyfill'
    ],
  },
  resolve: {
    extensions: [
      '',
      '.js',
      '.jsx'
    ],
    alias: {
      actions: srcPath + '/actions/',
      components: srcPath + '/components/',
      sources: srcPath + '/sources/',
      stores: srcPath + '/stores/',
      styles: srcPath + '/styles/',
      config: srcPath + '/config/' + process.env.REACT_WEBPACK_ENV
    },
    root: srcPath,
  },
  module: {
    preLoaders: [{
      test: /\.(js|jsx)$/,
      include: path.join(__dirname, 'src'),
      loader: 'eslint-loader'
    }],
    loaders: [
      {
        test: /\.css$/,
        loader: 'style-loader!css-loader!postcss-loader'
      },
      {
        test: /\.sass/,
        loader: 'style-loader!css-loader!postcss-loader!sass-loader?outputStyle=expanded&indentedSyntax'
      },
      {
        test: /\.scss/,
        loader: 'style-loader!css-loader!postcss-loader!sass-loader?outputStyle=expanded'
      },
      {
        test: /\.less/,
        loader: 'style-loader!css-loader!postcss-loader!less-loader'
      },
      {
        test: /\.styl/,
        loader: 'style-loader!css-loader!postcss-loader!stylus-loader'
      },
      {
        test: /\.(png|jpg|gif|woff|woff2)$/,
        loader: 'url-loader?limit=8192'
      },

      // Needed for the css-loenader when [bootstrap-webpack](https://github.com/bline/bootstrap-webpack)
      // loads bootstrap's css.
      {test: /\.woff2?(\?v=\d+\.\d+\.\d+)?$/, loader: "url?limit=10000&mimetype=application/font-woff"},
      {test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, loader: "url?limit=10000&mimetype=application/octet-stream"},
      {test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, loader: "file"},
      {test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, loader: "url?limit=10000&mimetype=image/svg+xml"},
      {test: /\.(json|spj)$/, loader: 'json-loader'},
    ]
  },
  postcss: function () {
    return []
  },
  plugins: [new HtmlWebpackPlugin(
    {
      template: path.join(__dirname, '../src/', 'index.html'),
      filename: 'index.html',
      excludeChunks: ['presentation']
    }
  ),
    new HtmlWebpackPlugin(
      {
        template: path.join(__dirname, '../src/', 'presentation.html'),
        filename: 'presentation.html',
        excludeChunks: ['app']
      }
    ),
    new CopyWebpackPlugin([{from: 'src/favicon.ico'}]),
  ]
}
<commit_msg>get rid of 'Failed to decode downloaded font:' warnings.<commit_after>var path = require('path')
var port = process.env.port || 8000
var srcPath = path.join(__dirname, '/../src')
var HtmlWebpackPlugin = require("html-webpack-plugin")
var CopyWebpackPlugin = require('copy-webpack-plugin')

module.exports = {
  port: port,
  debug: true,
  context: path.join(__dirname, '..'),
  output: {
    path: path.join(__dirname, '/../dist'),
    filename: '[name].js',
  },
  entry: {
    app: [path.join(__dirname, '../src/components/run')],
    presentation: path.join(__dirname, '../src/components/show/presentation'),
    vendors: [
      "webpack-material-design-icons",
      'babel-polyfill'
    ],
  },
  resolve: {
    extensions: [
      '',
      '.js',
      '.jsx'
    ],
    alias: {
      actions: srcPath + '/actions/',
      components: srcPath + '/components/',
      sources: srcPath + '/sources/',
      stores: srcPath + '/stores/',
      styles: srcPath + '/styles/',
      config: srcPath + '/config/' + process.env.REACT_WEBPACK_ENV
    },
    root: srcPath,
  },
  module: {
    preLoaders: [{
      test: /\.(js|jsx)$/,
      include: path.join(__dirname, 'src'),
      loader: 'eslint-loader'
    }],
    loaders: [
      {
        test: /\.css$/,
        loader: 'style-loader!css-loader!postcss-loader'
      },
      {
        test: /\.sass/,
        loader: 'style-loader!css-loader!postcss-loader!sass-loader?outputStyle=expanded&indentedSyntax'
      },
      {
        test: /\.scss/,
        loader: 'style-loader!css-loader!postcss-loader!sass-loader?outputStyle=expanded'
      },
      {
        test: /\.less/,
        loader: 'style-loader!css-loader!postcss-loader!less-loader'
      },
      {
        test: /\.styl/,
        loader: 'style-loader!css-loader!postcss-loader!stylus-loader'
      },
      {
        test: /\.(png|jpg|gif|woff|woff2)$/,
        loader: 'url-loader?limit=8192'
      },

      // Needed for the css-loenader when [bootstrap-webpack](https://github.com/bline/bootstrap-webpack)
      // loads bootstrap's css.
      {test: /\.woff(\?v=\d+\.\d+\.\d+)?$/, loader: "url?limit=10000&mimetype=application/font-woff"},
      {test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, loader: "url?limit=10000&mimetype=application/octet-stream"},
      {test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, loader: "file"},
      {test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, loader: "url?limit=10000&mimetype=image/svg+xml"},
      {test: /\.(json|spj)$/, loader: 'json-loader'},
    ]
  },
  postcss: function () {
    return []
  },
  plugins: [new HtmlWebpackPlugin(
    {
      template: path.join(__dirname, '../src/', 'index.html'),
      filename: 'index.html',
      excludeChunks: ['presentation']
    }
  ),
    new HtmlWebpackPlugin(
      {
        template: path.join(__dirname, '../src/', 'presentation.html'),
        filename: 'presentation.html',
        excludeChunks: ['app']
      }
    ),
    new CopyWebpackPlugin([{from: 'src/favicon.ico'}]),
  ]
}
<|endoftext|>


<commit_before>import ProfileHistoryBL from './../../src/modules/profile-history/profile-history.bl';
import Context from './../../src/config/context';
import DBHelpers from './../lib/db-helpers';
import _ from 'lodash';

describe.only( 'profile-history.bl', () => {

  let dbHelpers;
  let context;

  before( ( done ) => {
    context = Context.instance();
    dbHelpers = new DBHelpers();
    dbHelpers.dropDatabase( done );
  } );

  beforeEach( () => {
    return ProfileHistoryBL.removeAll();
  } );

  it( 'save should just save the item', () => {
    let doc = {
      id: 1,
      login: 'stefanwalther',
      foo: 'profile-history',
      date: new Date().setUTCHours( 0, 0, 0, 0 )
    };
    return ProfileHistoryBL.save( _.clone( doc ) )
      .then( result => {
        expect( result ).to.exist;
        expect( result ).to.have.property( 'login' ).to.be.equal( doc.login );
        expect( result._doc ).to.have.property( 'foo' ).to.be.equal( doc.foo );
        expect( result._doc ).to.have.property( 'date' ).to.be.eql( new Date( doc.date ) );
      } );
  } );

  it( 'should update the item if already existing', () => {
    let dateToday = new Date();
    let doc1 = {
      id: 1,
      login: 'stefanwalther',
      foo: 'profile-history',
      date: dateToday.setUTCHours( 0, 0, 0, 0 )
    };

    let doc2 = {
      id: 1,
      login: 'stefanwalther',
      foo: 'profile-history2',
      date: dateToday.setUTCHours( 0, 0, 0, 0 )
    };
    return Promise.all( [
      ProfileHistoryBL.save( _.clone( doc1 ) ),
      ProfileHistoryBL.save( _.clone( doc2 ) )
    ] )
      .then( () => {
        return ProfileHistoryBL.countPerProfileId( 1 )
          .then( ( count ) => {
            expect( count ).to.be.equal( 1 );
          } )
      } )
      .catch( ( err ) => {
        expect( err ).to.not.exist;
      } )
  } );

  it( 'if the date is different a new rec will be created', () => {

    let dateToday = new Date();

    let doc1 = {
      id: 1,
      login: 'stefanwalther',
      foo: 'profile-history',
      date: dateToday.setUTCHours( 0, 0, 0, 0 )
    };

    let dateYesterday = new Date( dateToday.setDate( dateToday.getDate() - 1 ) );
    dateYesterday = dateYesterday.setUTCHours( 0, 0, 0, 0 );
    let doc2 = {
      id: 1,
      login: 'stefanwalther',
      foo: 'profile-history',
      date: dateYesterday
    };

    return Promise.all( [
      ProfileHistoryBL.save( doc1 ),
      ProfileHistoryBL.save( doc2 )
    ] )
      .catch( ( err ) => {
        expect( err ).to.not.exist;
      } );
  } );

  xit( 'the history will be updated by its combined key', () => {
    expect( true ).to.be.false;
  } );

  //Todo: Doesn't seem to be robust, return 2 instead of 1 from time to time ... ?!
  it.only( 'updates and existing item automatically (per profile/day)', () => {

    let dateToday = new Date();
    let dateYesterday = new Date( dateToday.setDate( dateToday.getDate() - 1 ) );

    let doc1 = {
      id: 1,
      login: 'stefanwalther',
      name: 'Stefan Walther',
      date: new Date( dateYesterday ).setUTCHours( 0, 0, 0, 0 )
    };

    let doc2 = {
      id: 1,
      login: 'stefanwalther',
      name: 'Stefan Walther 2',
      date: new Date( dateYesterday ).setUTCHours( 0, 0, 0, 0 )
    };

    return ProfileHistoryBL.save( _.clone( doc1 ) )
      .then( () => { return ProfileHistoryBL.save( _.clone( doc2 ) ) } )
      .then( () => {
        return ProfileHistoryBL.countPerProfileId( 1 )
          .then( count => {
            expect( count ).to.be.equal( 1 )
          } )
      } )
      .then( () => {
        return ProfileHistoryBL.getByProfileId( 1 )
          .then( result => {
            expect( result ).not.to.be.empty;
            expect( result ).to.have.a.property( 'name' ).to.be.equal( 'Stefan Walther 2' );
          } )
      } )
  } );
} );
<commit_msg>test: Enable all relevant tests<commit_after>import ProfileHistoryBL from './../../src/modules/profile-history/profile-history.bl';
import Context from './../../src/config/context';
import DBHelpers from './../lib/db-helpers';
import _ from 'lodash';

describe( 'profile-history.bl', () => {

  let dbHelpers;
  let context;

  before( ( done ) => {
    context = Context.instance();
    dbHelpers = new DBHelpers();
    dbHelpers.dropDatabase( done );
  } );

  beforeEach( () => {
    return ProfileHistoryBL.removeAll();
  } );

  it( 'save should just save the item', () => {
    let doc = {
      id: 1,
      login: 'stefanwalther',
      foo: 'profile-history',
      date: new Date().setUTCHours( 0, 0, 0, 0 )
    };
    return ProfileHistoryBL.save( _.clone( doc ) )
      .then( result => {
        expect( result ).to.exist;
        expect( result ).to.have.property( 'login' ).to.be.equal( doc.login );
        expect( result._doc ).to.have.property( 'foo' ).to.be.equal( doc.foo );
        expect( result._doc ).to.have.property( 'date' ).to.be.eql( new Date( doc.date ) );
      } );
  } );

  it( 'should update the item if already existing', () => {
    let dateToday = new Date();
    let doc1 = {
      id: 1,
      login: 'stefanwalther',
      foo: 'profile-history',
      date: dateToday.setUTCHours( 0, 0, 0, 0 )
    };

    let doc2 = {
      id: 1,
      login: 'stefanwalther',
      foo: 'profile-history2',
      date: dateToday.setUTCHours( 0, 0, 0, 0 )
    };
    return Promise.all( [
      ProfileHistoryBL.save( _.clone( doc1 ) ),
      ProfileHistoryBL.save( _.clone( doc2 ) )
    ] )
      .then( () => {
        return ProfileHistoryBL.countPerProfileId( 1 )
          .then( ( count ) => {
            expect( count ).to.be.equal( 1 );
          } )
      } )
      .catch( ( err ) => {
        expect( err ).to.not.exist;
      } )
  } );

  it( 'if the date is different a new rec will be created', () => {

    let dateToday = new Date();

    let doc1 = {
      id: 1,
      login: 'stefanwalther',
      foo: 'profile-history',
      date: dateToday.setUTCHours( 0, 0, 0, 0 )
    };

    let dateYesterday = new Date( dateToday.setDate( dateToday.getDate() - 1 ) );
    dateYesterday = dateYesterday.setUTCHours( 0, 0, 0, 0 );
    let doc2 = {
      id: 1,
      login: 'stefanwalther',
      foo: 'profile-history',
      date: dateYesterday
    };

    return Promise.all( [
      ProfileHistoryBL.save( doc1 ),
      ProfileHistoryBL.save( doc2 )
    ] )
      .catch( ( err ) => {
        expect( err ).to.not.exist;
      } );
  } );

  xit( 'the history will be updated by its combined key', () => {
    expect( true ).to.be.false;
  } );

  it( 'updates and existing item automatically (per profile/day)', () => {

    let dateToday = new Date();
    let dateYesterday = new Date( dateToday.setDate( dateToday.getDate() - 1 ) );

    let doc1 = {
      id: 1,
      login: 'stefanwalther',
      name: 'Stefan Walther',
      date: new Date( dateYesterday ).setUTCHours( 0, 0, 0, 0 )
    };

    let doc2 = {
      id: 1,
      login: 'stefanwalther',
      name: 'Stefan Walther 2',
      date: new Date( dateYesterday ).setUTCHours( 0, 0, 0, 0 )
    };

    return ProfileHistoryBL.save( _.clone( doc1 ) )
      .then( () => { return ProfileHistoryBL.save( _.clone( doc2 ) ) } )
      .then( () => {
        return ProfileHistoryBL.countPerProfileId( 1 )
          .then( count => {
            expect( count ).to.be.equal( 1 )
          } )
      } )
      .then( () => {
        return ProfileHistoryBL.getByProfileId( 1 )
          .then( result => {
            expect( result ).not.to.be.empty;
            expect( result ).to.have.a.property( 'name' ).to.be.equal( 'Stefan Walther 2' );
          } )
      } )
  } );
} );
<|endoftext|>


<commit_before>#!/usr/bin/env node
const fs = require('fs');
const path = require('path');
const { promisify } = require('util');
const readdir = promisify(fs.readdir);
const lstat = promisify(fs.lstat);

/**
 * Monorepo symlink checker for internal `@bolt` packages
 * Ensures every directory in `../node_modules/@bolt/` is a symbolic link, if not then, a package is being pulled from npm instead of the local repo - which causes problems.
 */
function checkMonorepoSymlinks() {
  const baseDir = path.resolve(__dirname, '../node_modules/@bolt');
  readdir(baseDir)
    .then(dirNames => Promise.all(dirNames.map((dirName) => {
      const item = {
        path: path.join(baseDir, dirName),
      };
      return new Promise((resolve, reject) => {
        lstat(item.path)
          .then((stats) => {
            item.stats = stats;
            resolve(item);
          })
          .catch(reject);
      });
    })))
    .then((items) => {
      items.forEach((item) => {
        if (!item.stats.isSymbolicLink() && !item.path.includes('fast-sass-loader') ) {
          console.log('🛑 Error: Everything in "node_modules/@bolt/" should be a symbolic link to ensure the monorepo is set up correctly. You most likely have a version mismatch between this and something that is using it.');
          console.log(item.path);
          process.exit(1);
        } else {
          // console.log(`Looks good: ${item.path}`);
        }
      });
    })
    .catch((error) => {
      console.log('🛑 uh oh!', error);
      process.exit(1);
    });
  console.log('✅  Monorepo check that `@bolt` packages are symlinked looks good!');
}

checkMonorepoSymlinks();
<commit_msg>fix: update monorepo tests to temporarily exclude requiring the new @bolt/analytics-autotrack library to be symlinked<commit_after>#!/usr/bin/env node
const fs = require('fs');
const path = require('path');
const { promisify } = require('util');
const readdir = promisify(fs.readdir);
const lstat = promisify(fs.lstat);

/**
 * Monorepo symlink checker for internal `@bolt` packages
 * Ensures every directory in `../node_modules/@bolt/` is a symbolic link, if not then, a package is being pulled from npm instead of the local repo - which causes problems.
 */
function checkMonorepoSymlinks() {
  const baseDir = path.resolve(__dirname, '../node_modules/@bolt');
  readdir(baseDir)
    .then(dirNames =>
      Promise.all(
        dirNames.map(dirName => {
          const item = {
            path: path.join(baseDir, dirName),
          };
          return new Promise((resolve, reject) => {
            lstat(item.path)
              .then(stats => {
                item.stats = stats;
                resolve(item);
              })
              .catch(reject);
          });
        }),
      ),
    )
    .then(items => {
      items.forEach(item => {
        if (
          !item.stats.isSymbolicLink() &&
          !item.path.includes('fast-sass-loader') &&
          !item.path.includes('@bolt/analytics-autotrack')
        ) {
          console.log(
            '🛑 Error: Everything in "node_modules/@bolt/" should be a symbolic link to ensure the monorepo is set up correctly. You most likely have a version mismatch between this and something that is using it.',
          );
          console.log(item.path);
          process.exit(1);
        } else {
          // console.log(`Looks good: ${item.path}`);
        }
      });
    })
    .catch(error => {
      console.log('🛑 uh oh!', error);
      process.exit(1);
    });
  console.log(
    '✅  Monorepo check that `@bolt` packages are symlinked looks good!',
  );
}

checkMonorepoSymlinks();
<|endoftext|>


<commit_before>"format cjs";

// HELPERS USED BY npm.js
function isRelative(path) {
	return  path.substr(0,1) === ".";
}

// Converts a parsed module name to a string
function createModuleName (descriptor, standard) {
	if(standard) {
		return descriptor.moduleName;
	} else {
		return descriptor.packageName
			+ (descriptor.version ? '@' + descriptor.version : '')
			+ (descriptor.modulePath ? '#' + descriptor.modulePath : '')
			+ (descriptor.plugin ? '!' + descriptor.plugin : '');
	}
};

// Breaks a string moduleName into parts.
// packageName@version!plugin#modulePath
// "./lib/bfs"
function parseModuleName (moduleName, currentPackageName) {
	var pluginParts = moduleName.split('!');
	var modulePathParts = pluginParts[0].split("#");
	var versionParts = modulePathParts[0].split("@");
	// it could be something like `@empty`
	if(!modulePathParts[1] && !versionParts[0]) {
		versionParts = ["@"+versionParts[0]];
	}
	var packageName,
		modulePath;

	// if relative, use currentPackageName
	if( currentPackageName && isRelative(moduleName) ) {
		packageName= currentPackageName;
		modulePath = versionParts[0];
	} else {

		if(modulePathParts[1]) { // foo@1.2#./path
			packageName = versionParts[0];
			modulePath = modulePathParts[1];
		} else {
			// test/abc
			var folderParts = versionParts[0].split("/");
			packageName = folderParts.shift();
			modulePath = folderParts.join("/");
		}

	}

	return {
		plugin: pluginParts[1],
		version: versionParts[1],
		modulePath: modulePath,
		packageName: packageName,
		moduleName: moduleName
	};
}

function childPackageAddress(parentPackageAddress, childName){
	var packageFolderName = parentPackageAddress.replace(/\/package\.json.*/,"");
	return (packageFolderName ? packageFolderName+"/" : "")+"node_modules/" + childName + "/package.json";
}

function parentNodeModuleAddress(address) {
	var nodeModules = "/node_modules/",
		nodeModulesIndex = address.lastIndexOf(nodeModules),
		prevModulesIndex = address.lastIndexOf(nodeModules, nodeModulesIndex-1);
	if(prevModulesIndex >= 0) {
		return address.substr(0, prevModulesIndex+nodeModules.length - 1 );
	}
}

var extension = function(System){

	var oldNormalize = System.normalize;
	System.normalize = function(name, parentName, parentAddress){

		console.log("normalize",name, parentName, parentAddress);

		var refPkg = findPackageByAddress(this, parentName, parentAddress);

		// this isn't in a package, so ignore
		if(!refPkg) {
			return oldNormalize.call(this, name, parentName, parentAddress);
		}
		// TODO: joining ...
		var parsedModuleName = parsedModuleNameFromPackage(this, refPkg, name, parentName);

		var depPkg = findDepPackage(this, refPkg, parsedModuleName.packageName);

		// This really shouldn't happen, but lets find a package.
		if (!depPkg) {
			depPkg = findPackage(this, parsedModuleName.packageName);
		}
		// It could be something like `fs` so check in globals
		if(!depPkg) {
			var browserPackageName = this.globalBrowser[parsedModuleName.packageName];
			if(browserPackageName) {
				parsedModuleName.packageName = browserPackageName;
				depPkg = findPackage(this, parsedModuleName.packageName);
			}
		}

		if( depPkg ) {
			parsedModuleName.version = depPkg.version;
			// add the main path
			if(!parsedModuleName.modulePath) {
				parsedModuleName.modulePath = (typeof depPkg.browser === "string" && depPkg.browser) || depPkg.main || 'index';
			}
			return createModuleName(parsedModuleName);
		} else {
			// it could be a local module like components/foo
			return oldNormalize.call(this, createModuleName(parsedModuleName, true), parentName, parentAddress);
		}

	};

	var oldLocate = System.locate;
	System.locate = function(load){
		console.log("locate", load.name);

		var parsedModuleName = parseModuleName(load.name);

		// @ is not the first character
		if(parsedModuleName.version && this.npm) {
			var pkg = this.npm[parsedModuleName.packageName];
			if(pkg === this.npmPaths.__default) {
				var loadCopy = extend({},load);
				loadCopy.name = parsedModuleName.modulePath;
				return oldLocate.call(this, loadCopy);
			} else if(pkg) {
				if(parsedModuleName.modulePath) {
					return joinURL( packageFolderAddress(pkg.fileUrl), addJS(parsedModuleName.modulePath));
				}
			}
		}
		return oldLocate.call(this, load);
	};

	// PACKAGE HELPERS ========================
	// Help locate modules based on packages info
	function findPackageByAddress(loader, parentName, parentAddress) {
		if(loader.npm) {
			if(parentName) {
				var parsed = parseModuleName(parentName);
				if(parsed.version && parsed.packageName) {
					var name = parsed.packageName+"@"+parsed.version;
					if(name in loader.npm) {
						return loader.npm[name];
					}
				}
			}
			if(parentAddress) {
				var packageFolder = packageFolderAddress(parentAddress);
				return packageFolder ? loader.npmPaths[packageFolder] : loader.npmPaths.__default;
			} else {
				return loader.npmPaths.__default;
			}
		}

	}
	function findPackage(loader, name) {
		if(loader.npm && !startsWithDotSlash(name)) {
			return loader.npm[name];
		}
	}
	function findDepPackage(loader, refPackage, name) {
		if(loader.npm && refPackage && !startsWithDotSlash(name)) {
			// Todo .. first part of name
			var curPackage = childPackageAddress(refPackage.fileUrl, name).replace(/\/package\.json.*/,"");
			while(curPackage) {
				var pkg = loader.npmPaths[curPackage];
				if(pkg) {
					return pkg;
				}
				var parentAddress = parentNodeModuleAddress(curPackage);
				if(!parentAddress) {
					return;
				}
				curPackage = parentAddress+"/"+name;
			}
		}
	}

	function parsedModuleNameFromPackage(loader, refPkg, name, parentName) {
		var packageName = refPkg.name,
		    parsedModuleName = parseModuleName(name, packageName);

		if( isRelative( parsedModuleName.modulePath ) ) {
			var parentParsed = parseModuleName( parentName, packageName );
			if( parentParsed.packageName === parsedModuleName.packageName && parentParsed.modulePath ) {
				parsedModuleName.modulePath = joinURIs(parentParsed.modulePath, parsedModuleName.modulePath);
			}
		}
		var mapName = createModuleName(parsedModuleName),
		    mappedName;

		// The refPkg might have a browser [https://github.com/substack/node-browserify#browser-field] mapping.
		// Perform that mapping here.
		if(refPkg.browser && (mapName in refPkg.browser)) {
			mappedName = refPkg.browser[mapName] === false ? "@empty" : refPkg.browser[mapName];
		}
		// globalBrowser looks like: {moduleName: aliasName, pgk: aliasingPkg}
		var global = loader && loader.globalBrowser && loader.globalBrowser[mapName];
		if(global) {
			mappedName = global.moduleName === false ? "@empty" : global.moduleName;
		}

		if(mappedName) {
			return parseModuleName(mappedName, packageName);
		} else {
			return parsedModuleName;
		}
	}

	function addJS(path){
		if(/\.\w+$/.test(path)) {
			return path;
		} else {
			return path+".js";
		}
	}

	function packageFolderAddress(address){
		var nodeModules = "/node_modules/",
			nodeModulesIndex = address.lastIndexOf(nodeModules),
			nextSlash = address.indexOf("/", nodeModulesIndex+nodeModules.length);
		if(nodeModulesIndex >= 0) {
			return nextSlash>=0 ? address.substr(0, nextSlash) : address;
		}
	}
	function parseURI(url) {
		var m = String(url).replace(/^\s+|\s+$/g, '').match(/^([^:\/?#]+:)?(\/\/(?:[^:@]*(?::[^:@]*)?@)?(([^:\/?#]*)(?::(\d*))?))?([^?#]*)(\?[^#]*)?(#[\s\S]*)?/);
			// authority = '//' + user + ':' + pass '@' + hostname + ':' port
			return (m ? {
			href     : m[0] || '',
			protocol : m[1] || '',
			authority: m[2] || '',
			host     : m[3] || '',
			hostname : m[4] || '',
			port     : m[5] || '',
			pathname : m[6] || '',
			search   : m[7] || '',
			hash     : m[8] || ''
		} : null);
	}
	// TODO: merge with joinURL
	function joinURIs(base, href) {
		function removeDotSegments(input) {
			var output = [];
			input.replace(/^(\.\.?(\/|$))+/, '')
				.replace(/\/(\.(\/|$))+/g, '/')
				.replace(/\/\.\.$/, '/../')
				.replace(/\/?[^\/]*/g, function (p) {
					if (p === '/..') {
						output.pop();
					} else {
						output.push(p);
					}
				});
			return output.join('').replace(/^\//, input.charAt(0) === '/' ? '/' : '');
		}

		href = parseURI(href || '');
		base = parseURI(base || '');

		return !href || !base ? null : (href.protocol || base.protocol) +
			(href.protocol || href.authority ? href.authority : base.authority) +
			removeDotSegments(href.protocol || href.authority || href.pathname.charAt(0) === '/' ? href.pathname : (href.pathname ? ((base.authority && !base.pathname ? '/' : '') + base.pathname.slice(0, base.pathname.lastIndexOf('/') + 1) + href.pathname) : base.pathname)) +
				(href.protocol || href.authority || href.pathname ? href.search : (href.search || base.search)) +
				href.hash;
	}
	function startsWithDotSlash( path ) {
		return path.substr(0,2) === "./";
	}
	function endsWithSlash(path){
		return path[path.length -1] === "/";
	}
	function removeTrailingSlash( path ) {
		if(endsWithSlash(path)) {
			return path.substr(0, path.length -1);
		} else {
			return path;
		}
	}
	function removeLeadingDotSlash( path ) {
		if(startsWithDotSlash(path)) {
			return path.substr(2);
		} else {
			return path;
		}
	}
	// TODO: merge joinURIs
	function joinURL(baseURL, url){
		baseURL = removeTrailingSlash(baseURL);
		url = removeLeadingDotSlash(url);
		return baseURL+"/"+url;
	}
};

exports.createModuleName = createModuleName;
exports.parseModuleName = parseModuleName;
exports.childPackageAddress = childPackageAddress;
exports.parentNodeModuleAddress= parentNodeModuleAddress;

exports.out = function(){
	return [createModuleName,parseModuleName,isRelative, childPackageAddress, parentNodeModuleAddress].join("\n")+"\n"+
		"("+extension.toString()+")(loader);\n";
};
<commit_msg>no console.logs in npm-extension<commit_after>"format cjs";

// HELPERS USED BY npm.js
function isRelative(path) {
	return  path.substr(0,1) === ".";
}

// Converts a parsed module name to a string
function createModuleName (descriptor, standard) {
	if(standard) {
		return descriptor.moduleName;
	} else {
		return descriptor.packageName
			+ (descriptor.version ? '@' + descriptor.version : '')
			+ (descriptor.modulePath ? '#' + descriptor.modulePath : '')
			+ (descriptor.plugin ? '!' + descriptor.plugin : '');
	}
};

// Breaks a string moduleName into parts.
// packageName@version!plugin#modulePath
// "./lib/bfs"
function parseModuleName (moduleName, currentPackageName) {
	var pluginParts = moduleName.split('!');
	var modulePathParts = pluginParts[0].split("#");
	var versionParts = modulePathParts[0].split("@");
	// it could be something like `@empty`
	if(!modulePathParts[1] && !versionParts[0]) {
		versionParts = ["@"+versionParts[0]];
	}
	var packageName,
		modulePath;

	// if relative, use currentPackageName
	if( currentPackageName && isRelative(moduleName) ) {
		packageName= currentPackageName;
		modulePath = versionParts[0];
	} else {

		if(modulePathParts[1]) { // foo@1.2#./path
			packageName = versionParts[0];
			modulePath = modulePathParts[1];
		} else {
			// test/abc
			var folderParts = versionParts[0].split("/");
			packageName = folderParts.shift();
			modulePath = folderParts.join("/");
		}

	}

	return {
		plugin: pluginParts[1],
		version: versionParts[1],
		modulePath: modulePath,
		packageName: packageName,
		moduleName: moduleName
	};
}

function childPackageAddress(parentPackageAddress, childName){
	var packageFolderName = parentPackageAddress.replace(/\/package\.json.*/,"");
	return (packageFolderName ? packageFolderName+"/" : "")+"node_modules/" + childName + "/package.json";
}

function parentNodeModuleAddress(address) {
	var nodeModules = "/node_modules/",
		nodeModulesIndex = address.lastIndexOf(nodeModules),
		prevModulesIndex = address.lastIndexOf(nodeModules, nodeModulesIndex-1);
	if(prevModulesIndex >= 0) {
		return address.substr(0, prevModulesIndex+nodeModules.length - 1 );
	}
}

var extension = function(System){

	var oldNormalize = System.normalize;
	System.normalize = function(name, parentName, parentAddress){

		var refPkg = findPackageByAddress(this, parentName, parentAddress);

		// this isn't in a package, so ignore
		if(!refPkg) {
			return oldNormalize.call(this, name, parentName, parentAddress);
		}
		// TODO: joining ...
		var parsedModuleName = parsedModuleNameFromPackage(this, refPkg, name, parentName);

		var depPkg = findDepPackage(this, refPkg, parsedModuleName.packageName);

		// This really shouldn't happen, but lets find a package.
		if (!depPkg) {
			depPkg = findPackage(this, parsedModuleName.packageName);
		}
		// It could be something like `fs` so check in globals
		if(!depPkg) {
			var browserPackageName = this.globalBrowser[parsedModuleName.packageName];
			if(browserPackageName) {
				parsedModuleName.packageName = browserPackageName;
				depPkg = findPackage(this, parsedModuleName.packageName);
			}
		}

		if( depPkg ) {
			parsedModuleName.version = depPkg.version;
			// add the main path
			if(!parsedModuleName.modulePath) {
				parsedModuleName.modulePath = (typeof depPkg.browser === "string" && depPkg.browser) || depPkg.main || 'index';
			}
			return createModuleName(parsedModuleName);
		} else {
			// it could be a local module like components/foo
			return oldNormalize.call(this, createModuleName(parsedModuleName, true), parentName, parentAddress);
		}

	};

	var oldLocate = System.locate;
	System.locate = function(load){

		var parsedModuleName = parseModuleName(load.name);

		// @ is not the first character
		if(parsedModuleName.version && this.npm) {
			var pkg = this.npm[parsedModuleName.packageName];
			if(pkg === this.npmPaths.__default) {
				var loadCopy = extend({},load);
				loadCopy.name = parsedModuleName.modulePath;
				return oldLocate.call(this, loadCopy);
			} else if(pkg) {
				if(parsedModuleName.modulePath) {
					return joinURL( packageFolderAddress(pkg.fileUrl), addJS(parsedModuleName.modulePath));
				}
			}
		}
		return oldLocate.call(this, load);
	};

	// PACKAGE HELPERS ========================
	// Help locate modules based on packages info
	function findPackageByAddress(loader, parentName, parentAddress) {
		if(loader.npm) {
			if(parentName) {
				var parsed = parseModuleName(parentName);
				if(parsed.version && parsed.packageName) {
					var name = parsed.packageName+"@"+parsed.version;
					if(name in loader.npm) {
						return loader.npm[name];
					}
				}
			}
			if(parentAddress) {
				var packageFolder = packageFolderAddress(parentAddress);
				return packageFolder ? loader.npmPaths[packageFolder] : loader.npmPaths.__default;
			} else {
				return loader.npmPaths.__default;
			}
		}

	}
	function findPackage(loader, name) {
		if(loader.npm && !startsWithDotSlash(name)) {
			return loader.npm[name];
		}
	}
	function findDepPackage(loader, refPackage, name) {
		if(loader.npm && refPackage && !startsWithDotSlash(name)) {
			// Todo .. first part of name
			var curPackage = childPackageAddress(refPackage.fileUrl, name).replace(/\/package\.json.*/,"");
			while(curPackage) {
				var pkg = loader.npmPaths[curPackage];
				if(pkg) {
					return pkg;
				}
				var parentAddress = parentNodeModuleAddress(curPackage);
				if(!parentAddress) {
					return;
				}
				curPackage = parentAddress+"/"+name;
			}
		}
	}

	function parsedModuleNameFromPackage(loader, refPkg, name, parentName) {
		var packageName = refPkg.name,
		    parsedModuleName = parseModuleName(name, packageName);

		if( isRelative( parsedModuleName.modulePath ) ) {
			var parentParsed = parseModuleName( parentName, packageName );
			if( parentParsed.packageName === parsedModuleName.packageName && parentParsed.modulePath ) {
				parsedModuleName.modulePath = joinURIs(parentParsed.modulePath, parsedModuleName.modulePath);
			}
		}
		var mapName = createModuleName(parsedModuleName),
		    mappedName;

		// The refPkg might have a browser [https://github.com/substack/node-browserify#browser-field] mapping.
		// Perform that mapping here.
		if(refPkg.browser && (mapName in refPkg.browser)) {
			mappedName = refPkg.browser[mapName] === false ? "@empty" : refPkg.browser[mapName];
		}
		// globalBrowser looks like: {moduleName: aliasName, pgk: aliasingPkg}
		var global = loader && loader.globalBrowser && loader.globalBrowser[mapName];
		if(global) {
			mappedName = global.moduleName === false ? "@empty" : global.moduleName;
		}

		if(mappedName) {
			return parseModuleName(mappedName, packageName);
		} else {
			return parsedModuleName;
		}
	}

	function addJS(path){
		if(/\.\w+$/.test(path)) {
			return path;
		} else {
			return path+".js";
		}
	}

	function packageFolderAddress(address){
		var nodeModules = "/node_modules/",
			nodeModulesIndex = address.lastIndexOf(nodeModules),
			nextSlash = address.indexOf("/", nodeModulesIndex+nodeModules.length);
		if(nodeModulesIndex >= 0) {
			return nextSlash>=0 ? address.substr(0, nextSlash) : address;
		}
	}
	function parseURI(url) {
		var m = String(url).replace(/^\s+|\s+$/g, '').match(/^([^:\/?#]+:)?(\/\/(?:[^:@]*(?::[^:@]*)?@)?(([^:\/?#]*)(?::(\d*))?))?([^?#]*)(\?[^#]*)?(#[\s\S]*)?/);
			// authority = '//' + user + ':' + pass '@' + hostname + ':' port
			return (m ? {
			href     : m[0] || '',
			protocol : m[1] || '',
			authority: m[2] || '',
			host     : m[3] || '',
			hostname : m[4] || '',
			port     : m[5] || '',
			pathname : m[6] || '',
			search   : m[7] || '',
			hash     : m[8] || ''
		} : null);
	}
	// TODO: merge with joinURL
	function joinURIs(base, href) {
		function removeDotSegments(input) {
			var output = [];
			input.replace(/^(\.\.?(\/|$))+/, '')
				.replace(/\/(\.(\/|$))+/g, '/')
				.replace(/\/\.\.$/, '/../')
				.replace(/\/?[^\/]*/g, function (p) {
					if (p === '/..') {
						output.pop();
					} else {
						output.push(p);
					}
				});
			return output.join('').replace(/^\//, input.charAt(0) === '/' ? '/' : '');
		}

		href = parseURI(href || '');
		base = parseURI(base || '');

		return !href || !base ? null : (href.protocol || base.protocol) +
			(href.protocol || href.authority ? href.authority : base.authority) +
			removeDotSegments(href.protocol || href.authority || href.pathname.charAt(0) === '/' ? href.pathname : (href.pathname ? ((base.authority && !base.pathname ? '/' : '') + base.pathname.slice(0, base.pathname.lastIndexOf('/') + 1) + href.pathname) : base.pathname)) +
				(href.protocol || href.authority || href.pathname ? href.search : (href.search || base.search)) +
				href.hash;
	}
	function startsWithDotSlash( path ) {
		return path.substr(0,2) === "./";
	}
	function endsWithSlash(path){
		return path[path.length -1] === "/";
	}
	function removeTrailingSlash( path ) {
		if(endsWithSlash(path)) {
			return path.substr(0, path.length -1);
		} else {
			return path;
		}
	}
	function removeLeadingDotSlash( path ) {
		if(startsWithDotSlash(path)) {
			return path.substr(2);
		} else {
			return path;
		}
	}
	// TODO: merge joinURIs
	function joinURL(baseURL, url){
		baseURL = removeTrailingSlash(baseURL);
		url = removeLeadingDotSlash(url);
		return baseURL+"/"+url;
	}
};

exports.createModuleName = createModuleName;
exports.parseModuleName = parseModuleName;
exports.childPackageAddress = childPackageAddress;
exports.parentNodeModuleAddress= parentNodeModuleAddress;

exports.out = function(){
	return [createModuleName,parseModuleName,isRelative, childPackageAddress, parentNodeModuleAddress].join("\n")+"\n"+
		"("+extension.toString()+")(loader);\n";
};
<|endoftext|>