<commit_before>if (jQuery) {
    (function($j) {

        String.prototype.format = String.prototype.f = function(arg) {
            var s = this,
                i = arg.length;
            while (i--) {
                s = s.replace(new RegExp('\\{' + i + '\\}', 'gm'), arg[i]);
            }
            return s;
        };

        var sales_assistant = window.sales_assistant = window.sales_assistant || {};
        var sale_assistant = window.sale_assistant = sales_assistant;
        sales_assistant.__gender_list = [{id:'M', text : "Male"}, {id:'F', text : "Female"}, {id:'U', text : "Unknown"}];

        sales_assistant.genders = function(){
            return sales_assistant.__gender_list;
        };

        sales_assistant.render_gender = function(record, index, col_index){
            var cellvalue = this.getCellValue(index, col_index);
            var html = "";
            var gs = sales_assistant.__gender_list;
            for (var p in gs) {
                if (gs[p].id === cellvalue){
                    html = sales_assistant.l(gs[p].text);
                    break;
                }
            }
            return html;
        };

        sales_assistant.renderLink = function(record, index, col_index){
            var cellvalue = this.getCellValue(index, col_index);
            var html = "";
            //Visit W3Schools.com!
            return "<a href='"+(cellvalue.indexOf('http') < 0 ? 'http://': '' )+cellvalue+"' target='_blank'>"+cellvalue+"";
        };

        sales_assistant.render_link  = sales_assistant.renderLink ;
        sales_assistant.error = function(d) {
            if (console) {
                console.error(d);
            }
        };
        sales_assistant.log = function(d) {
            if (console) {
                console.log(d);
            }
        };
        sales_assistant.debug = function(d) {
            if (console) {
                console.debug("*********");
                console.debug(d);
            }
        };

        sales_assistant.safeEval = function(sr){
            var fu = new Function(sr);
            fu();
        };

        sales_assistant.autoUpdate = function(data) {
            if (!data)
                return;
            var zones = data.contentRefreshZone;
            if (zones) {
                for (var z in zones) {
                    try {
                        $j("#" + z).innerHTML = zones[z];
                    } catch (ex) {
                        sales_assistant.log(z);
                    }
                }
            }
            var scripts = data.scriptRefreshZone;
            if (scripts) {
                for (var i = 0; i < scripts.length; i++) {
                    try {
                        sales_assistant.safeEval(scripts[i]);
                    } catch (ex) {
                        sales_assistant.log(ex);
                    }
                }
            }
            var clearZones = data.clearRefreshZone;
            if (clearZones) {
                for (var m = 0; m < clearZones.length; m++) {
                    try {
                        $(clearZones[m]).innerHTML = '';
                    } catch (e) {
                        sales_assistant.log(e);
                    }
                }
            }
            if (data.data) {
                sales_assistant.autoUpdate(data.data);
            }
        };

        sales_assistant.callbackFunction = function(func, transport) {
            if (func && typeof (func) === 'function') {
                var callBack = func;
                callBack(transport);
                return true;
            } else {
                return false;
            }
        };

        sales_assistant.queryString2JSON = function(qry) {
            var pairs = qry.split('&');
            var result = {};
            pairs.forEach(function(pair) {
                pair = pair.split('=');
                if (pair[1]) {
                    var va = decodeURIComponent(pair[1]);
                    var curr = result[pair[0]];
                    if (curr) {
                        if (Array.isArray(curr)) {
                            curr.push(va);
                        } else {
                            result[pair[0]] = [curr, va];
                        }
                    } else {
                        result[pair[0]] = decodeURIComponent(va);
                    }
                }
            });
            return JSON.parse(JSON.stringify(result));
        };

        sales_assistant.url = function(navUri, action, type){
            return navUri + '&' + sales_assistant.BTN_OPTION +'='+action+"&" + sales_assistant.REFRESH_TYPE +"=" + type;
        };

        sales_assistant.init = function(uri, butName, formName, refreshTypeName) {
            sales_assistant.baseURLWithButton = uri;
            sales_assistant.BTN_OPTION = butName;
            sales_assistant.form = formName;
            sales_assistant.REFRESH_TYPE = refreshTypeName;
        };

        sales_assistant.serialize = function(zone) {
            if(!zone){
                return "";
            }
            if(zone.indexOf("=") >= 0){
                return zone;
            }
            var zones = zone.split(/;|,/);
            var params = "";
            for (var index = 0; index < zones.length; index++) {
                var el = $j("#" + zones[index]);
                var serialized = el.serialize();
                if (!serialized) // not a form
                    serialized = el.
                      find('input,select,textarea').serialize();
                params += serialized;
                if (index !== zones.length - 1) {
                    params += "&";
                }
            }
            return params;
        };

        sales_assistant.get = function(action, paraZone, sucessCallback, errorCallback, autoUpdate, sync) {
            var paras =  $j.isPlainObject(paraZone) ? paraZone :  sales_assistant.serialize(paraZone);
            $j.ajax({
                url: sales_assistant.baseURLWithButton + action,
                header: {
                    "Access-Control-Allow-Origin": "*",
                    "Access-Control-Allow-Methods": "GET"
                },
                async: sync ? false : true,
                type: "GET",
                dataType: "json",
                contentType: "application/json; charset=utf-8",
                data: paras,
                success: function(data, state) {
                    sales_assistant.callbackFunction(sucessCallback, data, state);
                    if (autoUpdate) {
                        sales_assistant.autoUpdate(data);
                    }
                },
                error: function(data, state) {
                    sales_assistant.callbackFunction(errorCallback, data, state);
                }
            });
        };

        sales_assistant.post = function(action, paraZone, sucessCallback, errorCallback, autoUpdate) {
            var paras =  $j.isPlainObject(paraZone) ? paraZone :  sales_assistant.serialize(paraZone);
            alert(paras);
            $j.ajax({
                url: sales_assistant.baseURLWithButton + action,
                header: {
                    "Access-Control-Allow-Origin": "*",
                    "Access-Control-Allow-Methods": "POST"
                },
                async: true,
                type: "POST",
                dataType: "json",
                contentType: "application/json; charset=utf-8",
                data: paras,
                success: function(data, state) {
                    sales_assistant.log(data);
                    sales_assistant.log(state);
                    sales_assistant.callbackFunction(sucessCallback, data, state);
                    if (autoUpdate) {
                        sales_assistant.autoUpdate(data);
                    }
                },
                error: function(data, state) {
                    sales_assistant.error(data);
                    sales_assistant.error(state);
                    sales_assistant.callbackFunction(errorCallback, data, state);
                }
            });
        };

        sales_assistant.sumbit = function(action) {
            $j("#" + sales_assistant.actionButton).value = action;
            $j("#" + sales_assistant.form).submit();
        };

        sales_assistant.loadContent = function(url, title, navTier){

        };

        sales_assistant.runApp = function(et){
            if(!et) return;
            var url = et["data-url"];
            var title = et.text;
            var navTier = et.navTier;
            if(url && url !== '' && (navTier && navTier !== '' && navTier !== sales_assistant.currentNavTier) ){
                sales_assistant.currentNavTier = navTier;
                sales_assistant.log(url);
                document.title = title;
                sales_assistant.loadContent(url, title);
            }
        };

        sales_assistant.generateSidebarID = function(prefix, nav){
            if(nav && nav !== "" ){
                return prefix + "-" +  nav.replace(/_/g, '-');
            }
            return null;
        };

        sales_assistant.expandSidebar = function(id, parent){
            if(parent && parent.id && parent.parent){
                  window.w2ui[id].expand(parent.id);
                  if(parent.parent){
                      sales_assistant.expandSidebar(id, parent.parent);
                  }
            }
        };

        sales_assistant.selectSidebar = function (id, item){
            if(item){
                window.w2ui[id].select(item);
                var _sel = window.w2ui[id].get(item);
                if(_sel){
                    sales_assistant.expandSidebar(id ,_sel.parent);
                }
            }
        };

        sales_assistant.max_recid = {};
        sales_assistant.generateRecid = function(grid, app){
            var rs = grid.records;
            sales_assistant.max_recid[app]  = sales_assistant.max_recid[app] || 0;
            for(var i =0; i< rs.length; i++){
                var recid = rs[i].recid;
                if(sales_assistant.max_recid[app] < recid){
                    sales_assistant.max_recid[app] = recid;
                }
            }
            sales_assistant.max_recid[app] += 1;
            return sales_assistant.max_recid[app];
        };

        sales_assistant.initMaxRecId = function(grid, app){
            var rs = grid.records;
            sales_assistant.max_recid[app]  =  0;
            for(var i =0; i< rs.length; i++){
                var recid = rs[i].recid;
                if(sales_assistant.max_recid[app] < recid){
                    sales_assistant.max_recid[app] = recid;
                }
            }
            //sa.log(sales_assistant.max_recid[app]);
        };

        sales_assistant.eventData = function(evt){
            var data;
            try { data = $.parseJSON(evt.xhr.responseText) } catch (e) {}
            sa.log(data);
            return data;
        };

        sales_assistant.add_record = function(record){
            if (!$.isArray(record)) record = [record];
            var added = 0;
            for (var o in record) {
                if (!this.recid && typeof record[o].recid == 'undefined') record[o].recid = record[o][this.recid];
                if (record[o].recid == null || typeof record[o].recid == 'undefined') {
                    console.log('ERROR: Cannot add record without recid. (obj: '+ this.name +')');
                    continue;
                }
                this.records.unshift(record[o]);
                added++;
            }
            var url = (typeof this.url != 'object' ? this.url : this.url.get);
            if (!url) {
                this.total = this.records.length;
                this.localSort();
                this.localSearch();
            }
            this.refresh(); // ??  should it be reload?
            return added;

        }

    })(jQuery);
}<commit_msg>add leve=function(level, str) method<commit_after>if (jQuery) {
    (function($j) {

        String.prototype.format = String.prototype.f = function(arg) {
            var s = this,
                i = arg.length;
            while (i--) {
                s = s.replace(new RegExp('\\{' + i + '\\}', 'gm'), arg[i]);
            }
            return s;
        };

        var sales_assistant = window.sales_assistant = window.sales_assistant || {};
        var sale_assistant = window.sale_assistant = sales_assistant;
        sales_assistant.__gender_list = [{id:'M', text : "Male"}, {id:'F', text : "Female"}, {id:'U', text : "Unknown"}];

        sales_assistant.genders = function(){
            return sales_assistant.__gender_list;
        };

        sales_assistant.render_gender = function(record, index, col_index){
            var cellvalue = this.getCellValue(index, col_index);
            var html = "";
            var gs = sales_assistant.__gender_list;
            for (var p in gs) {
                if (gs[p].id === cellvalue){
                    html = sales_assistant.l(gs[p].text);
                    break;
                }
            }
            return html;
        };

        sales_assistant.renderLink = function(record, index, col_index){
            var cellvalue = this.getCellValue(index, col_index);
            var html = "";
            //Visit W3Schools.com!
            return "<a href='"+(cellvalue.indexOf('http') < 0 ? 'http://': '' )+cellvalue+"' target='_blank'>"+cellvalue+"";
        };

        sales_assistant.render_link  = sales_assistant.renderLink ;
        sales_assistant.error = function(d) {
            if (console) {
                console.error(d);
            }
        };
        sales_assistant.log = function(d) {
            if (console) {
                console.log(d);
            }
        };
        sales_assistant.debug = function(d) {
            if (console) {
                console.debug("*********");
                console.debug(d);
            }
        };

        sales_assistant.safeEval = function(sr){
            var fu = new Function(sr);
            fu();
        };

        sales_assistant.autoUpdate = function(data) {
            if (!data)
                return;
            var zones = data.contentRefreshZone;
            if (zones) {
                for (var z in zones) {
                    try {
                        $j("#" + z).innerHTML = zones[z];
                    } catch (ex) {
                        sales_assistant.log(z);
                    }
                }
            }
            var scripts = data.scriptRefreshZone;
            if (scripts) {
                for (var i = 0; i < scripts.length; i++) {
                    try {
                        sales_assistant.safeEval(scripts[i]);
                    } catch (ex) {
                        sales_assistant.log(ex);
                    }
                }
            }
            var clearZones = data.clearRefreshZone;
            if (clearZones) {
                for (var m = 0; m < clearZones.length; m++) {
                    try {
                        $(clearZones[m]).innerHTML = '';
                    } catch (e) {
                        sales_assistant.log(e);
                    }
                }
            }
            if (data.data) {
                sales_assistant.autoUpdate(data.data);
            }
        };

        sales_assistant.callbackFunction = function(func, transport) {
            if (func && typeof (func) === 'function') {
                var callBack = func;
                callBack(transport);
                return true;
            } else {
                return false;
            }
        };

        sales_assistant.level = function(level, ls){
          var x = "";
          for(var i = 1; i < level; i++){
              x += ls ;// " &n"
          }
          return x;
        };

        sales_assistant.queryString2JSON = function(qry) {
            var pairs = qry.split('&');
            var result = {};
            pairs.forEach(function(pair) {
                pair = pair.split('=');
                if (pair[1]) {
                    var va = decodeURIComponent(pair[1]);
                    var curr = result[pair[0]];
                    if (curr) {
                        if (Array.isArray(curr)) {
                            curr.push(va);
                        } else {
                            result[pair[0]] = [curr, va];
                        }
                    } else {
                        result[pair[0]] = decodeURIComponent(va);
                    }
                }
            });
            return JSON.parse(JSON.stringify(result));
        };

        sales_assistant.url = function(navUri, action, type){
            return navUri + '&' + sales_assistant.BTN_OPTION +'='+action+"&" + sales_assistant.REFRESH_TYPE +"=" + type;
        };

        sales_assistant.init = function(uri, butName, formName, refreshTypeName) {
            sales_assistant.baseURLWithButton = uri;
            sales_assistant.BTN_OPTION = butName;
            sales_assistant.form = formName;
            sales_assistant.REFRESH_TYPE = refreshTypeName;
        };

        sales_assistant.serialize = function(zone) {
            if(!zone){
                return "";
            }
            if(zone.indexOf("=") >= 0){
                return zone;
            }
            var zones = zone.split(/;|,/);
            var params = "";
            for (var index = 0; index < zones.length; index++) {
                var el = $j("#" + zones[index]);
                var serialized = el.serialize();
                if (!serialized) // not a form
                    serialized = el.
                      find('input,select,textarea').serialize();
                params += serialized;
                if (index !== zones.length - 1) {
                    params += "&";
                }
            }
            return params;
        };

        sales_assistant.get = function(action, paraZone, sucessCallback, errorCallback, autoUpdate, sync) {
            var paras =  $j.isPlainObject(paraZone) ? paraZone :  sales_assistant.serialize(paraZone);
            $j.ajax({
                url: sales_assistant.baseURLWithButton + action,
                header: {
                    "Access-Control-Allow-Origin": "*",
                    "Access-Control-Allow-Methods": "GET"
                },
                async: sync ? false : true,
                type: "GET",
                dataType: "json",
                contentType: "application/json; charset=utf-8",
                data: paras,
                success: function(data, state) {
                    sales_assistant.callbackFunction(sucessCallback, data, state);
                    if (autoUpdate) {
                        sales_assistant.autoUpdate(data);
                    }
                },
                error: function(data, state) {
                    sales_assistant.callbackFunction(errorCallback, data, state);
                }
            });
        };

        sales_assistant.post = function(action, paraZone, sucessCallback, errorCallback, autoUpdate) {
            var paras =  $j.isPlainObject(paraZone) ? paraZone :  sales_assistant.serialize(paraZone);
            alert(paras);
            $j.ajax({
                url: sales_assistant.baseURLWithButton + action,
                header: {
                    "Access-Control-Allow-Origin": "*",
                    "Access-Control-Allow-Methods": "POST"
                },
                async: true,
                type: "POST",
                dataType: "json",
                contentType: "application/json; charset=utf-8",
                data: paras,
                success: function(data, state) {
                    sales_assistant.log(data);
                    sales_assistant.log(state);
                    sales_assistant.callbackFunction(sucessCallback, data, state);
                    if (autoUpdate) {
                        sales_assistant.autoUpdate(data);
                    }
                },
                error: function(data, state) {
                    sales_assistant.error(data);
                    sales_assistant.error(state);
                    sales_assistant.callbackFunction(errorCallback, data, state);
                }
            });
        };

        sales_assistant.sumbit = function(action) {
            $j("#" + sales_assistant.actionButton).value = action;
            $j("#" + sales_assistant.form).submit();
        };

        sales_assistant.loadContent = function(url, title, navTier){

        };

        sales_assistant.runApp = function(et){
            if(!et) return;
            var url = et["data-url"];
            var title = et.text;
            var navTier = et.navTier;
            if(url && url !== '' && (navTier && navTier !== '' && navTier !== sales_assistant.currentNavTier) ){
                sales_assistant.currentNavTier = navTier;
                sales_assistant.log(url);
                document.title = title;
                sales_assistant.loadContent(url, title);
            }
        };

        sales_assistant.generateSidebarID = function(prefix, nav){
            if(nav && nav !== "" ){
                return prefix + "-" +  nav.replace(/_/g, '-');
            }
            return null;
        };

        sales_assistant.expandSidebar = function(id, parent){
            if(parent && parent.id && parent.parent){
                  window.w2ui[id].expand(parent.id);
                  if(parent.parent){
                      sales_assistant.expandSidebar(id, parent.parent);
                  }
            }
        };

        sales_assistant.selectSidebar = function (id, item){
            if(item){
                window.w2ui[id].select(item);
                var _sel = window.w2ui[id].get(item);
                if(_sel){
                    sales_assistant.expandSidebar(id ,_sel.parent);
                }
            }
        };

        sales_assistant.max_recid = {};
        sales_assistant.generateRecid = function(grid, app){
            var rs = grid.records;
            sales_assistant.max_recid[app]  = sales_assistant.max_recid[app] || 0;
            for(var i =0; i< rs.length; i++){
                var recid = rs[i].recid;
                if(sales_assistant.max_recid[app] < recid){
                    sales_assistant.max_recid[app] = recid;
                }
            }
            sales_assistant.max_recid[app] += 1;
            return sales_assistant.max_recid[app];
        };

        sales_assistant.initMaxRecId = function(grid, app){
            var rs = grid.records;
            sales_assistant.max_recid[app]  =  0;
            for(var i =0; i< rs.length; i++){
                var recid = rs[i].recid;
                if(sales_assistant.max_recid[app] < recid){
                    sales_assistant.max_recid[app] = recid;
                }
            }
            //sa.log(sales_assistant.max_recid[app]);
        };

        sales_assistant.eventData = function(evt){
            var data;
            try { data = $.parseJSON(evt.xhr.responseText) } catch (e) {}
            sa.log(data);
            return data;
        };

        sales_assistant.add_record = function(record){
            if (!$.isArray(record)) record = [record];
            var added = 0;
            for (var o in record) {
                if (!this.recid && typeof record[o].recid == 'undefined') record[o].recid = record[o][this.recid];
                if (record[o].recid == null || typeof record[o].recid == 'undefined') {
                    console.log('ERROR: Cannot add record without recid. (obj: '+ this.name +')');
                    continue;
                }
                this.records.unshift(record[o]);
                added++;
            }
            var url = (typeof this.url != 'object' ? this.url : this.url.get);
            if (!url) {
                this.total = this.records.length;
                this.localSort();
                this.localSearch();
            }
            this.refresh(); // ??  should it be reload?
            return added;

        }

    })(jQuery);
}<|endoftext|>


<commit_before>var grunt = require('grunt'),
    Promise = require('core-js/library/es6/promise'),
    debug = require('debug')('merge-meta'),
    glob = require('glob'),
    getFileHash = require('./file-hash'),
    getDirLabel = require('./dir-label'),
    hashPath = require('./hash-path'),
    join = require('path').join,
    eol = require('os').EOL

var META_DIR = '.meta',
    LIST_FILE = 'files.json';

var LIST_IGNORE = ['.ds_store'];

module.exports = {
    merge: function(dest, src, options) {
        'use strict';

        debug(
            '- init' + eol +
            '\t' + 'src: [' + src + ']' + eol +
            '\t' + 'dest: [' + dest + ']'
        );

        var getHash = options && options.getHash || getFileHash,
            getLabel = options && options.getLabel || getDirLabel,
            getFileId = options && options.getFileId || hashPath;

        var destMeta = join(dest, META_DIR, LIST_FILE);
        var srcMeta = join(src, META_DIR);

        var label;
        var files = [];
        var meta = {
            labels: [],
            files: {}
        };
        var toCopy = [];

        return new Promise( function mergePromise(resolve, reject) {

            debug(
                '- preconditions' + eol +
                '\t' + 'src: [' + src + ']' + eol +
                '\t' + 'dest: [' + dest + ']'
            );

            // verify that dest and src are folders
            if (grunt.file.exists(dest)) {
                if (!grunt.file.isDir(dest)) {
                    reject(new Error('Invalid dest folder'));
                }
            }
            if (!grunt.file.isDir(src)) {
                reject(new Error('Invalid src folder'));
            }

            // get folder label
            label = getLabel(src);
            if (typeof label !== 'string') {
                reject(new Error('Invalid label'));
            }

            // right now there is no support for merging folders
            // with meta info
            if (grunt.file.isDir(srcMeta)) {
                reject(new Error('Meta folder in ' + src));
            }

            // test if meta is available
            if (grunt.file.exists(destMeta)) {
                meta = grunt.file.readJSON(destMeta);
            } else {
                grunt.file.mkdir(join(dest, META_DIR));
            }

            // label must not already have been added
            if (meta.labels.indexOf(label) !== -1) {
                reject(new Error('Label already indexed'));
            }

            // get file list
            files = glob.sync('**', {
                cwd: src,
                dot: true,
                nodir: true
            }).filter(function removeIgnored(relPath) {
                // ignore system files
                var match = LIST_IGNORE
                    .some(function searchIgnoredFile(ignored) {
                        return relPath
                            .toLowerCase()
                            .indexOf(ignored.toLowerCase()) !== -1;
                    });
                return !match;
            }).map(function getStats(relPath) {
                return {
                    relSrc: relPath,
                    src: join(src, relPath)
                };
            });

            resolve();
        })

        .then( function addHashInfo() {

            debug(
                '- addHashInfo' + eol +
                '\t' + 'src: [' + src + ']' + eol +
                '\t' + 'dest: [' + dest + ']'
            );

            return Promise.all(
                files.map( function doHash(file) {
                    return getHash(file.src);
                })
            ).then( function(hashList) {
                files.forEach( function(file, index) {
                    var hash = hashList[index];
                    file.hash = hash;
                    file.dest = join(dest, getFileId(file.relSrc, hash));
                });
            });
        })

        .then( function evaluateFiles() {

            debug(
                '- evaluateFiles' + eol +
                '\t' + 'src: [' + src + ']' + eol +
                '\t' + 'dest: [' + dest + ']'
            );

            // add files to meta index, marking them for copy when needed
            files.forEach( function (file) {
                if (file.relSrc in meta.files) {
                    var hashMap = meta.files[file.relSrc];
                    if (file.hash in hashMap) {
                        // file with same hash already indexed, adding
                        // label to list
                        hashMap[file.hash].push(label);
                    } else {
                        // a new version of the file, add file and
                        // update hash list
                        hashMap[file.hash] = [label];
                        toCopy.push(file);
                    }
                } else {
                    // file never indexed, add file and update
                    // hash list
                    meta.files[file.relSrc] = {};
                    meta.files[file.relSrc][file.hash] = [label];
                    toCopy.push(file);
                }
            });
        })

        .then( function() {

            debug(
                '- postconditions' + eol +
                '\t' + 'src: [' + src + ']' + eol +
                '\t' + 'dest: [' + dest + ']'
            );

            // right now, folders that don't provide new files
            // are not welcome
            if (!toCopy.length) {
                reject(new Error('No new files from ' + src));
            }

            // if combining hash in file name unluckily collides with
            // another file, give up
            toCopy.forEach(function testExists(file) {
                if (grunt.file.exists(file.dest)) {
                    reject(new Error('Generated file already exists: ' + file.dest));
                }
            });
        })

        .then( function writeMerge() {

            debug(
                '- writeMerge' + eol +
                '\t' + 'src: [' + src + ']' + eol +
                '\t' + 'dest: [' + dest + ']'
            );

            // if we reached here, everything is ready for merging

            // save meta file
            meta.labels.push(label);
            grunt.file.write(destMeta, JSON.stringify(meta));

            // copy new files
            toCopy.forEach(function copyFile(mapping) {
                grunt.file.copy(mapping.src, mapping.dest);
            });

        });
    }
};
<commit_msg>chore(labeled-merge): fix other jshint warnings<commit_after>var grunt = require('grunt'),
    Promise = require('core-js/library/es6/promise'),
    debug = require('debug')('merge-meta'),
    glob = require('glob'),
    getFileHash = require('./file-hash'),
    getDirLabel = require('./dir-label'),
    hashPath = require('./hash-path'),
    join = require('path').join,
    eol = require('os').EOL;

var META_DIR = '.meta',
    LIST_FILE = 'files.json';

var LIST_IGNORE = ['.ds_store'];

module.exports = {
    merge: function(dest, src, options) {
        'use strict';

        debug(
            '- init' + eol +
            '\t' + 'src: [' + src + ']' + eol +
            '\t' + 'dest: [' + dest + ']'
        );

        var getHash = options && options.getHash || getFileHash,
            getLabel = options && options.getLabel || getDirLabel,
            getFileId = options && options.getFileId || hashPath;

        var destMeta = join(dest, META_DIR, LIST_FILE);
        var srcMeta = join(src, META_DIR);

        var label;
        var files = [];
        var meta = {
            labels: [],
            files: {}
        };
        var toCopy = [];

        return new Promise( function mergePromise(resolve) {

            debug(
                '- preconditions' + eol +
                '\t' + 'src: [' + src + ']' + eol +
                '\t' + 'dest: [' + dest + ']'
            );

            // verify that dest and src are folders
            if (grunt.file.exists(dest)) {
                if (!grunt.file.isDir(dest)) {
                    throw new Error('Invalid dest folder');
                }
            }
            if (!grunt.file.isDir(src)) {
                throw new Error('Invalid src folder');
            }

            // get folder label
            label = getLabel(src);
            if (typeof label !== 'string') {
                throw new Error('Invalid label');
            }

            // right now there is no support for merging folders
            // with meta info
            if (grunt.file.isDir(srcMeta)) {
                throw new Error('Meta folder in ' + src);
            }

            // test if meta is available
            if (grunt.file.exists(destMeta)) {
                meta = grunt.file.readJSON(destMeta);
            } else {
                grunt.file.mkdir(join(dest, META_DIR));
            }

            // label must not already have been added
            if (meta.labels.indexOf(label) !== -1) {
                throw new Error('Label already indexed');
            }

            // get file list
            files = glob.sync('**', {
                cwd: src,
                dot: true,
                nodir: true
            }).filter(function removeIgnored(relPath) {
                // ignore system files
                var match = LIST_IGNORE
                    .some(function searchIgnoredFile(ignored) {
                        return relPath
                            .toLowerCase()
                            .indexOf(ignored.toLowerCase()) !== -1;
                    });
                return !match;
            }).map(function getStats(relPath) {
                return {
                    relSrc: relPath,
                    src: join(src, relPath)
                };
            });

            resolve();
        })

        .then( function addHashInfo() {

            debug(
                '- addHashInfo' + eol +
                '\t' + 'src: [' + src + ']' + eol +
                '\t' + 'dest: [' + dest + ']'
            );

            return Promise.all(
                files.map( function doHash(file) {
                    return getHash(file.src);
                })
            ).then( function(hashList) {
                files.forEach( function(file, index) {
                    var hash = hashList[index];
                    file.hash = hash;
                    file.dest = join(dest, getFileId(file.relSrc, hash));
                });
            });
        })

        .then( function evaluateFiles() {

            debug(
                '- evaluateFiles' + eol +
                '\t' + 'src: [' + src + ']' + eol +
                '\t' + 'dest: [' + dest + ']'
            );

            // add files to meta index, marking them for copy when needed
            files.forEach( function (file) {
                if (file.relSrc in meta.files) {
                    var hashMap = meta.files[file.relSrc];
                    if (file.hash in hashMap) {
                        // file with same hash already indexed, adding
                        // label to list
                        hashMap[file.hash].push(label);
                    } else {
                        // a new version of the file, add file and
                        // update hash list
                        hashMap[file.hash] = [label];
                        toCopy.push(file);
                    }
                } else {
                    // file never indexed, add file and update
                    // hash list
                    meta.files[file.relSrc] = {};
                    meta.files[file.relSrc][file.hash] = [label];
                    toCopy.push(file);
                }
            });
        })

        .then( function() {

            debug(
                '- postconditions' + eol +
                '\t' + 'src: [' + src + ']' + eol +
                '\t' + 'dest: [' + dest + ']'
            );

            // right now, folders that don't provide new files
            // are not welcome
            if (!toCopy.length) {
                throw new Error('No new files from ' + src);
            }

            // if combining hash in file name unluckily collides with
            // another file, give up
            toCopy.forEach(function testExists(file) {
                if (grunt.file.exists(file.dest)) {
                    throw new Error('Generated file already exists: ' + file.dest);
                }
            });
        })

        .then( function writeMerge() {

            debug(
                '- writeMerge' + eol +
                '\t' + 'src: [' + src + ']' + eol +
                '\t' + 'dest: [' + dest + ']'
            );

            // if we reached here, everything is ready for merging

            // save meta file
            meta.labels.push(label);
            grunt.file.write(destMeta, JSON.stringify(meta));

            // copy new files
            toCopy.forEach(function copyFile(mapping) {
                grunt.file.copy(mapping.src, mapping.dest);
            });

        });
    }
};
<|endoftext|>


<commit_before>/**
 * Copyright (c) 2014, Facebook, Inc. All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 */
'use strict';

var FileFinder = require('node-find-files');
var fs = require('fs');
var os = require('os');
var path = require('path');
var q = require('q');
var utils = require('./lib/utils');
var WorkerPool = require('node-worker-pool');

var TEST_WORKER_PATH = require.resolve('./TestWorker');

var DEFAULT_OPTIONS = {
  /**
   * The maximum number of workers to run tests concurrently with.
   *
   * It's probably good to keep this at something close to the number of cores
   * on the machine that's running the test.
   */
  maxWorkers: os.cpus().length - 1,

  /**
   * The path to the executable node binary.
   *
   * This is used in the process of booting each of the workers.
   */
  nodePath: process.execPath,

  /**
   * The args to be passed to the node binary executable.
   *
   * this is used in the process of booting each of the workers.
   */
  nodeArgv: process.execArgv.filter(function(arg) {
    // Passing --debug off to child processes can screw with socket connections
    // of the parent process.
    return arg !== '--debug';
  })
};

var HIDDEN_FILE_RE = /\/\.[^\/]*$/;

function _serializeConsoleArguments(type, args) {
  return {
    type: type,
    args: Array.prototype.map.call(args, function(arg) {
      return utils.serializeConsoleArgValue(arg);
    })
  };
}

/**
 * A class that takes a project's test config and provides various utilities for
 * executing its tests.
 *
 * @param config The jest configuration
 * @param options See DEFAULT_OPTIONS for descriptions on the various options
 *                and their defaults.
 */
function TestRunner(config, options) {
  this._config = config;
  this._configDeps = null;
  this._moduleLoaderResourceMap = null;
  this._testPathDirsRegExp = new RegExp(
    config.testPathDirs
      .map(function(dir) {
        return utils.escapeStrForRegex(dir);
      })
      .join('|')
  );

  this._nodeHasteTestRegExp = new RegExp(
    '/' + utils.escapeStrForRegex(config.testDirectoryName) + '/' +
    '.*\\.(' +
      config.testFileExtensions.map(function(ext) {
        return utils.escapeStrForRegex(ext);
      })
      .join('|') +
    ')$'
  );
  this._opts = Object.create(DEFAULT_OPTIONS);
  if (options) {
    for (var key in options) {
      this._opts[key] = options[key];
    }
  }
}

TestRunner.prototype._constructModuleLoader = function(environment, customCfg) {
  var config = customCfg || this._config;
  var ModuleLoader = this._loadConfigDependencies().ModuleLoader;
  return this._getModuleLoaderResourceMap().then(function(resourceMap) {
    return new ModuleLoader(config, environment, resourceMap);
  });
};

TestRunner.prototype._getModuleLoaderResourceMap = function() {
  var ModuleLoader = this._loadConfigDependencies().ModuleLoader;
  if (this._moduleLoaderResourceMap === null) {
    if (this._opts.useCachedModuleLoaderResourceMap) {
      this._moduleLoaderResourceMap =
        ModuleLoader.loadResourceMapFromCacheFile(this._config);
    } else {
      this._moduleLoaderResourceMap =
        ModuleLoader.loadResourceMap(this._config);
    }
  }
  return this._moduleLoaderResourceMap;
};

TestRunner.prototype._isTestFilePath = function(filePath) {
  var testPathIgnorePattern =
    this._config.testPathIgnorePatterns
    ? new RegExp(this._config.testPathIgnorePatterns.join('|'))
    : null;

  return (
    this._nodeHasteTestRegExp.test(filePath)
    && !HIDDEN_FILE_RE.test(filePath)
    && (!testPathIgnorePattern || !testPathIgnorePattern.test(filePath))
    && this._testPathDirsRegExp.test(filePath)
  );
};

TestRunner.prototype._loadConfigDependencies = function() {
  var config = this._config;
  if (this._configDeps === null) {
    this._configDeps = {
      ModuleLoader: require(config.moduleLoader),
      testEnvironment: require(config.testEnvironment),
      testRunner: require(config.testRunner).bind(null)
    };
  }
  return this._configDeps;
};

/**
 * Given a list of paths to modules or tests, find all tests that are related to
 * any of those paths. For a test to be considered "related" to a path, the test
 * must depend on that path (either directly, or indirectly through one of its
 * direct dependencies).
 *
 * @param {Array<String>} paths A list of path strings to find related tests for
 * @return {Promise<Array<String>>} Fulfilled with a list of testPaths once the
 *                                  search has completed.
 */
TestRunner.prototype.findTestsRelatedTo = function(paths) {
  var testRunner = this;
  return this._constructModuleLoader().then(function(moduleLoader) {
    var discoveredModules = {};

    // If a path to a test file is given, make sure we consider that test as
    // related to itself...
    //
    // (If any of the supplied paths aren't tests, it's ok because we filter
    //  non-tests out at the end)
    paths.forEach(function(path) {
      discoveredModules[path] = true;
    });

    var modulesToSearch = [].concat(paths);
    while (modulesToSearch.length > 0) {
      var modulePath = modulesToSearch.shift();
      var depPaths = moduleLoader.getDependentsFromPath(modulePath);

      /* jshint loopfunc:true */
      depPaths.forEach(function(depPath) {
        if (!discoveredModules.hasOwnProperty(depPath)) {
          discoveredModules[depPath] = true;
          modulesToSearch.push(depPath);
        }
      });
    }

    return Object.keys(discoveredModules).filter(function(path) {
      return testRunner._isTestFilePath(path) && fs.existsSync(path);
    });
  });
};

/**
 * Given a path pattern, find the absolute paths for all tests that match the
 * pattern.
 *
 * @param {RegExp} pathPattern
 * @param {Function} onTestFound Callback called immediately when a test is
 *                               found.
 *
 *                               Ideally this function should return a
 *                               stream, but I don't personally understand all
 *                               the variations of "node streams" that exist in
 *                               the world (and their various compatibilities
 *                               with various node versions), so I've opted to
 *                               forgo that for now.
 * @return {Promise<Array<String>>} Fulfilled with a list of testPaths once the
 *                                  search has completed.
 */
TestRunner.prototype.findTestPathsMatching = function(
  pathPattern, onTestFound) {

  var config = this._config;
  var deferred = q.defer();

  var foundPaths = [];
  function _onMatcherMatch(pathStr) {
    foundPaths.push(pathStr);
    try {
      onTestFound && onTestFound(pathStr);
    } catch (e) {
      deferred.reject(e);
    }
  }

  var numMatchers = config.testPathDirs.length;
  function _onMatcherEnd() {
    numMatchers--;
    if (numMatchers === 0) {
      deferred.resolve(foundPaths);
    }
  }

  function _onMatcherError(err) {
    deferred.reject(err);
  }

  config.testPathDirs.forEach(function(scanDir) {
    var finder = new FileFinder({
      rootFolder: scanDir,
      filterFunction: function(pathStr) {
        return this._isTestFilePath(pathStr) && pathPattern.test(pathStr);
      }.bind(this)
    });
    finder.on('error', _onMatcherError);
    finder.on('match', _onMatcherMatch);
    finder.on('complete', _onMatcherEnd);
    finder.startSearch();
  }, this);

  return deferred.promise;
};

/**
 * For use by external users of TestRunner as a means of optimization.
 *
 * Imagine the following scenario executing in a child worker process:
 *
 * var runner = new TestRunner(config, {
 *   moduleLoaderResourceMap: serializedResourceMap
 * });
 * someOtherAyncProcess.then(function() {
 *   runner.runTestsParallel();
 * });
 *
 * Here we wouldn't start deserializing the resource map (passed to us from the
 * parent) until runner.runTestsParallel() is called. At the time of this
 * writing, resource map deserialization is slow and a bottleneck on running the
 * first test in a child.
 *
 * So this API gives scenarios such as the one above an optimization path to
 * potentially start deserializing the resource map while we wait on the
 * someOtherAsyncProcess to resolve (rather that doing it after it's resolved).
 */
TestRunner.prototype.preloadResourceMap = function() {
  this._getModuleLoaderResourceMap().done();
};

TestRunner.prototype.preloadConfigDependencies = function() {
  this._loadConfigDependencies();
};

/**
 * Run the given single test file path.
 * This just contains logic for running a single test given it's file path.
 *
 * @param {String} testFilePath
 * @return {Promise<Object>} Results of the test
 */
TestRunner.prototype.runTest = function(testFilePath) {
  // Using Object.create() lets us adjust the config object locally without
  // worrying about the external consequences of changing the config object for
  // needs that are local to this particular function call
  var config = Object.create(this._config);
  var configDeps = this._loadConfigDependencies();

  var env = new configDeps.testEnvironment();
  var testRunner = configDeps.testRunner;

  // Capture and serialize console.{log|warning|error}s so they can be passed
  // around (such as through some channel back to a parent process)
  var consoleMessages = [];
  env.global.console = {
    error: function() {
      consoleMessages.push(_serializeConsoleArguments('error', arguments));
    },

    group: function() {
      // TODO
    },

    groupCollapsed: function() {
      // TODO
    },

    groupEnd: function() {
      // TODO
    },

    log: function() {
      consoleMessages.push(_serializeConsoleArguments('log', arguments));
    },

    table: function() {
      // TODO
    },

    warn: function() {
      consoleMessages.push(_serializeConsoleArguments('warn', arguments));
    }
  };

  return this._constructModuleLoader(env, config).then(function(moduleLoader) {
    // This is a kind of janky way to ensure that we only collect coverage
    // information on modules that are immediate dependencies of the test file.
    //
    // Collecting coverage info on more than that is often not useful as
    // *usually*, when one is looking for coverage info, one is only looking
    // for coverage info on the files under test. Since a test file is just a
    // regular old module that can depend on whatever other modules it likes,
    // it's usually pretty hard to tell which of those dependencies is/are the
    // "module(s)" under test.
    //
    // I'm not super happy with having to inject stuff into the config object
    // mid-stream here, but it gets the job done.
    if (config.collectCoverage && !config.collectCoverageOnlyFrom) {
      config.collectCoverageOnlyFrom = {};
      moduleLoader.getDependenciesFromPath(testFilePath)
        .filter(function(depPath) {
          // Skip over built-in and node modules
          return /^\//.test(depPath);
        }).forEach(function(depPath) {
          config.collectCoverageOnlyFrom[depPath] = true;
        });
    }

    if (config.setupEnvScriptFile) {
      utils.runContentWithLocalBindings(
        env.runSourceText.bind(env),
        utils.readAndPreprocessFileContent(config.setupEnvScriptFile, config),
        config.setupEnvScriptFile,
        {
          __dirname: path.dirname(config.setupEnvScriptFile),
          __filename: config.setupEnvScriptFile,
          global: env.global,
          require: moduleLoader.constructBoundRequire(
            config.setupEnvScriptFile
          )
        }
      );
    }

    var testExecStats = {start: Date.now()};
    return testRunner(config, env, moduleLoader, testFilePath)
      .then(function(results) {
        testExecStats.end = Date.now();

        results.logMessages = consoleMessages;
        results.perfStats = testExecStats;
        results.testFilePath = testFilePath;
        results.coverage =
          config.collectCoverage
          ? moduleLoader.getAllCoverageInfo()
          : {};

        return results;
      });
  }).finally(function() {
    env.dispose();
  });
};

/**
 * Run all given test paths serially (in the current process).
 *
 * This is mostly useful for debugging issues with jest itself, but may also be
 * useful for scenarios where you don't want jest to start up a worker pool of
 * its own.
 *
 * @param {Array<String>} testPaths Array of paths to test files
 * @param {Function} onResult Callback called once for each test result
 * @return {Promise<Object>} Fulfilled with aggregate pass/fail information
 *                           about all tests that were run
 */
TestRunner.prototype.runTestsInBand = function(testPaths, onResult) {
  var config = this._config;

  var aggregatedResults = {
    numFailedTests: 0,
    numTotalTests: testPaths.length,
    startTime: Date.now(),
    endTime: null
  };

  var testSequence = q();
  testPaths.forEach(function(testPath) {
    testSequence = testSequence.then(this.runTest.bind(this, testPath))
      .then(function(testResult) {
        if (testResult.numFailingTests > 0) {
          aggregatedResults.numFailedTests++;
        }
        onResult && onResult(config, testResult);
      })
      .catch(function(err) {
        aggregatedResults.numFailedTests++;
        onResult && onResult(config, {
          testFilePath: testPath,
          testExecError: err,
          suites: {},
          tests: {},
          logMessages: []
        });
      });
  }, this);

  return testSequence.then(function() {
    aggregatedResults.endTime = Date.now();
    return aggregatedResults;
  });
};

/**
 * Run all given test paths in parallel using a worker pool.
 *
 * @param {Array<String>} testPaths Array of paths to test files
 * @param {Function} onResult Callback called once for each test result
 * @return {Promise<Object>} Fulfilled with aggregate pass/fail information
 *                           about all tests that were run
 */
TestRunner.prototype.runTestsParallel = function(testPaths, onResult) {
  var config = this._config;

  var aggregatedResults = {
    numFailedTests: 0,
    numTotalTests: testPaths.length,
    startTime: Date.now(),
    endTime: null
  };

  var workerPool = new WorkerPool(
    this._opts.maxWorkers,
    this._opts.nodePath,
    this._opts.nodeArgv.concat([
      '--harmony',
      TEST_WORKER_PATH,
      '--config=' + JSON.stringify(config)
    ])
  );

  return this._getModuleLoaderResourceMap()
    .then(function() {
      // Tell all workers that it's now safe to read the resource map from disk.
      return workerPool.sendMessageToAllWorkers({
        resourceMapWrittenToDisk: true
      });
    })
    .then(function() {
      return q.all(testPaths.map(function(testPath) {
        return workerPool.sendMessage({testFilePath: testPath})
          .then(function(testResult) {
            if (testResult.numFailingTests > 0) {
              aggregatedResults.numFailedTests++;
            }
            onResult && onResult(config, testResult);
          })
          .catch(function(err) {
            aggregatedResults.numFailedTests++;
            onResult(config, {
              testFilePath: testPath,
              testExecError: err,
              suites: {},
              tests: {},
              logMessages: []
            });
          });
      }));
    })
    .then(function() {
      return workerPool.destroy().then(function() {
        aggregatedResults.endTime = Date.now();
        return aggregatedResults;
      });
    });
};

module.exports = TestRunner;
<commit_msg>remove tailing spaces<commit_after>/**
 * Copyright (c) 2014, Facebook, Inc. All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 */
'use strict';

var FileFinder = require('node-find-files');
var fs = require('fs');
var os = require('os');
var path = require('path');
var q = require('q');
var utils = require('./lib/utils');
var WorkerPool = require('node-worker-pool');

var TEST_WORKER_PATH = require.resolve('./TestWorker');

var DEFAULT_OPTIONS = {
  /**
   * The maximum number of workers to run tests concurrently with.
   *
   * It's probably good to keep this at something close to the number of cores
   * on the machine that's running the test.
   */
  maxWorkers: os.cpus().length - 1,

  /**
   * The path to the executable node binary.
   *
   * This is used in the process of booting each of the workers.
   */
  nodePath: process.execPath,

  /**
   * The args to be passed to the node binary executable.
   *
   * this is used in the process of booting each of the workers.
   */
  nodeArgv: process.execArgv.filter(function(arg) {
    // Passing --debug off to child processes can screw with socket connections
    // of the parent process.
    return arg !== '--debug';
  })
};

var HIDDEN_FILE_RE = /\/\.[^\/]*$/;

function _serializeConsoleArguments(type, args) {
  return {
    type: type,
    args: Array.prototype.map.call(args, function(arg) {
      return utils.serializeConsoleArgValue(arg);
    })
  };
}

/**
 * A class that takes a project's test config and provides various utilities for
 * executing its tests.
 *
 * @param config The jest configuration
 * @param options See DEFAULT_OPTIONS for descriptions on the various options
 *                and their defaults.
 */
function TestRunner(config, options) {
  this._config = config;
  this._configDeps = null;
  this._moduleLoaderResourceMap = null;
  this._testPathDirsRegExp = new RegExp(
    config.testPathDirs
      .map(function(dir) {
        return utils.escapeStrForRegex(dir);
      })
      .join('|')
  );

  this._nodeHasteTestRegExp = new RegExp(
    '/' + utils.escapeStrForRegex(config.testDirectoryName) + '/' +
    '.*\\.(' +
      config.testFileExtensions.map(function(ext) {
        return utils.escapeStrForRegex(ext);
      })
      .join('|') +
    ')$'
  );
  this._opts = Object.create(DEFAULT_OPTIONS);
  if (options) {
    for (var key in options) {
      this._opts[key] = options[key];
    }
  }
}

TestRunner.prototype._constructModuleLoader = function(environment, customCfg) {
  var config = customCfg || this._config;
  var ModuleLoader = this._loadConfigDependencies().ModuleLoader;
  return this._getModuleLoaderResourceMap().then(function(resourceMap) {
    return new ModuleLoader(config, environment, resourceMap);
  });
};

TestRunner.prototype._getModuleLoaderResourceMap = function() {
  var ModuleLoader = this._loadConfigDependencies().ModuleLoader;
  if (this._moduleLoaderResourceMap === null) {
    if (this._opts.useCachedModuleLoaderResourceMap) {
      this._moduleLoaderResourceMap =
        ModuleLoader.loadResourceMapFromCacheFile(this._config);
    } else {
      this._moduleLoaderResourceMap =
        ModuleLoader.loadResourceMap(this._config);
    }
  }
  return this._moduleLoaderResourceMap;
};

TestRunner.prototype._isTestFilePath = function(filePath) {
  var testPathIgnorePattern =
    this._config.testPathIgnorePatterns
    ? new RegExp(this._config.testPathIgnorePatterns.join('|'))
    : null;

  return (
    this._nodeHasteTestRegExp.test(filePath)
    && !HIDDEN_FILE_RE.test(filePath)
    && (!testPathIgnorePattern || !testPathIgnorePattern.test(filePath))
    && this._testPathDirsRegExp.test(filePath)
  );
};

TestRunner.prototype._loadConfigDependencies = function() {
  var config = this._config;
  if (this._configDeps === null) {
    this._configDeps = {
      ModuleLoader: require(config.moduleLoader),
      testEnvironment: require(config.testEnvironment),
      testRunner: require(config.testRunner).bind(null)
    };
  }
  return this._configDeps;
};

/**
 * Given a list of paths to modules or tests, find all tests that are related to
 * any of those paths. For a test to be considered "related" to a path, the test
 * must depend on that path (either directly, or indirectly through one of its
 * direct dependencies).
 *
 * @param {Array<String>} paths A list of path strings to find related tests for
 * @return {Promise<Array<String>>} Fulfilled with a list of testPaths once the
 *                                  search has completed.
 */
TestRunner.prototype.findTestsRelatedTo = function(paths) {
  var testRunner = this;
  return this._constructModuleLoader().then(function(moduleLoader) {
    var discoveredModules = {};

    // If a path to a test file is given, make sure we consider that test as
    // related to itself...
    //
    // (If any of the supplied paths aren't tests, it's ok because we filter
    //  non-tests out at the end)
    paths.forEach(function(path) {
      discoveredModules[path] = true;
    });

    var modulesToSearch = [].concat(paths);
    while (modulesToSearch.length > 0) {
      var modulePath = modulesToSearch.shift();
      var depPaths = moduleLoader.getDependentsFromPath(modulePath);

      /* jshint loopfunc:true */
      depPaths.forEach(function(depPath) {
        if (!discoveredModules.hasOwnProperty(depPath)) {
          discoveredModules[depPath] = true;
          modulesToSearch.push(depPath);
        }
      });
    }

    return Object.keys(discoveredModules).filter(function(path) {
      return testRunner._isTestFilePath(path) && fs.existsSync(path);
    });
  });
};

/**
 * Given a path pattern, find the absolute paths for all tests that match the
 * pattern.
 *
 * @param {RegExp} pathPattern
 * @param {Function} onTestFound Callback called immediately when a test is
 *                               found.
 *
 *                               Ideally this function should return a
 *                               stream, but I don't personally understand all
 *                               the variations of "node streams" that exist in
 *                               the world (and their various compatibilities
 *                               with various node versions), so I've opted to
 *                               forgo that for now.
 * @return {Promise<Array<String>>} Fulfilled with a list of testPaths once the
 *                                  search has completed.
 */
TestRunner.prototype.findTestPathsMatching = function(
  pathPattern, onTestFound) {

  var config = this._config;
  var deferred = q.defer();

  var foundPaths = [];
  function _onMatcherMatch(pathStr) {
    foundPaths.push(pathStr);
    try {
      onTestFound && onTestFound(pathStr);
    } catch (e) {
      deferred.reject(e);
    }
  }

  var numMatchers = config.testPathDirs.length;
  function _onMatcherEnd() {
    numMatchers--;
    if (numMatchers === 0) {
      deferred.resolve(foundPaths);
    }
  }

  function _onMatcherError(err) {
    deferred.reject(err);
  }

  config.testPathDirs.forEach(function(scanDir) {
    var finder = new FileFinder({
      rootFolder: scanDir,
      filterFunction: function(pathStr) {
        return this._isTestFilePath(pathStr) && pathPattern.test(pathStr);
      }.bind(this)
    });
    finder.on('error', _onMatcherError);
    finder.on('match', _onMatcherMatch);
    finder.on('complete', _onMatcherEnd);
    finder.startSearch();
  }, this);

  return deferred.promise;
};

/**
 * For use by external users of TestRunner as a means of optimization.
 *
 * Imagine the following scenario executing in a child worker process:
 *
 * var runner = new TestRunner(config, {
 *   moduleLoaderResourceMap: serializedResourceMap
 * });
 * someOtherAyncProcess.then(function() {
 *   runner.runTestsParallel();
 * });
 *
 * Here we wouldn't start deserializing the resource map (passed to us from the
 * parent) until runner.runTestsParallel() is called. At the time of this
 * writing, resource map deserialization is slow and a bottleneck on running the
 * first test in a child.
 *
 * So this API gives scenarios such as the one above an optimization path to
 * potentially start deserializing the resource map while we wait on the
 * someOtherAsyncProcess to resolve (rather that doing it after it's resolved).
 */
TestRunner.prototype.preloadResourceMap = function() {
  this._getModuleLoaderResourceMap().done();
};

TestRunner.prototype.preloadConfigDependencies = function() {
  this._loadConfigDependencies();
};

/**
 * Run the given single test file path.
 * This just contains logic for running a single test given it's file path.
 *
 * @param {String} testFilePath
 * @return {Promise<Object>} Results of the test
 */
TestRunner.prototype.runTest = function(testFilePath) {
  // Using Object.create() lets us adjust the config object locally without
  // worrying about the external consequences of changing the config object for
  // needs that are local to this particular function call
  var config = Object.create(this._config);
  var configDeps = this._loadConfigDependencies();

  var env = new configDeps.testEnvironment();
  var testRunner = configDeps.testRunner;

  // Capture and serialize console.{log|warning|error}s so they can be passed
  // around (such as through some channel back to a parent process)
  var consoleMessages = [];
  env.global.console = {
    error: function() {
      consoleMessages.push(_serializeConsoleArguments('error', arguments));
    },

    group: function() {
      // TODO
    },

    groupCollapsed: function() {
      // TODO
    },

    groupEnd: function() {
      // TODO
    },

    log: function() {
      consoleMessages.push(_serializeConsoleArguments('log', arguments));
    },

    table: function() {
      // TODO
    },

    warn: function() {
      consoleMessages.push(_serializeConsoleArguments('warn', arguments));
    }
  };

  return this._constructModuleLoader(env, config).then(function(moduleLoader) {
    // This is a kind of janky way to ensure that we only collect coverage
    // information on modules that are immediate dependencies of the test file.
    //
    // Collecting coverage info on more than that is often not useful as
    // *usually*, when one is looking for coverage info, one is only looking
    // for coverage info on the files under test. Since a test file is just a
    // regular old module that can depend on whatever other modules it likes,
    // it's usually pretty hard to tell which of those dependencies is/are the
    // "module(s)" under test.
    //
    // I'm not super happy with having to inject stuff into the config object
    // mid-stream here, but it gets the job done.
    if (config.collectCoverage && !config.collectCoverageOnlyFrom) {
      config.collectCoverageOnlyFrom = {};
      moduleLoader.getDependenciesFromPath(testFilePath)
        .filter(function(depPath) {
          // Skip over built-in and node modules
          return /^\//.test(depPath);
        }).forEach(function(depPath) {
          config.collectCoverageOnlyFrom[depPath] = true;
        });
    }

    if (config.setupEnvScriptFile) {
      utils.runContentWithLocalBindings(
        env.runSourceText.bind(env),
        utils.readAndPreprocessFileContent(config.setupEnvScriptFile, config),
        config.setupEnvScriptFile,
        {
          __dirname: path.dirname(config.setupEnvScriptFile),
          __filename: config.setupEnvScriptFile,
          global: env.global,
          require: moduleLoader.constructBoundRequire(
            config.setupEnvScriptFile
          )
        }
      );
    }

    var testExecStats = {start: Date.now()};
    return testRunner(config, env, moduleLoader, testFilePath)
      .then(function(results) {
        testExecStats.end = Date.now();

        results.logMessages = consoleMessages;
        results.perfStats = testExecStats;
        results.testFilePath = testFilePath;
        results.coverage =
          config.collectCoverage
          ? moduleLoader.getAllCoverageInfo()
          : {};

        return results;
      });
  }).finally(function() {
    env.dispose();
  });
};

/**
 * Run all given test paths serially (in the current process).
 *
 * This is mostly useful for debugging issues with jest itself, but may also be
 * useful for scenarios where you don't want jest to start up a worker pool of
 * its own.
 *
 * @param {Array<String>} testPaths Array of paths to test files
 * @param {Function} onResult Callback called once for each test result
 * @return {Promise<Object>} Fulfilled with aggregate pass/fail information
 *                           about all tests that were run
 */
TestRunner.prototype.runTestsInBand = function(testPaths, onResult) {
  var config = this._config;

  var aggregatedResults = {
    numFailedTests: 0,
    numTotalTests: testPaths.length,
    startTime: Date.now(),
    endTime: null
  };

  var testSequence = q();
  testPaths.forEach(function(testPath) {
    testSequence = testSequence.then(this.runTest.bind(this, testPath))
      .then(function(testResult) {
        if (testResult.numFailingTests > 0) {
          aggregatedResults.numFailedTests++;
        }
        onResult && onResult(config, testResult);
      })
      .catch(function(err) {
        aggregatedResults.numFailedTests++;
        onResult && onResult(config, {
          testFilePath: testPath,
          testExecError: err,
          suites: {},
          tests: {},
          logMessages: []
        });
      });
  }, this);

  return testSequence.then(function() {
    aggregatedResults.endTime = Date.now();
    return aggregatedResults;
  });
};

/**
 * Run all given test paths in parallel using a worker pool.
 *
 * @param {Array<String>} testPaths Array of paths to test files
 * @param {Function} onResult Callback called once for each test result
 * @return {Promise<Object>} Fulfilled with aggregate pass/fail information
 *                           about all tests that were run
 */
TestRunner.prototype.runTestsParallel = function(testPaths, onResult) {
  var config = this._config;

  var aggregatedResults = {
    numFailedTests: 0,
    numTotalTests: testPaths.length,
    startTime: Date.now(),
    endTime: null
  };

  var workerPool = new WorkerPool(
    this._opts.maxWorkers,
    this._opts.nodePath,
    this._opts.nodeArgv.concat([
      '--harmony',
      TEST_WORKER_PATH,
      '--config=' + JSON.stringify(config)
    ])
  );

  return this._getModuleLoaderResourceMap()
    .then(function() {
      // Tell all workers that it's now safe to read the resource map from disk.
      return workerPool.sendMessageToAllWorkers({
        resourceMapWrittenToDisk: true
      });
    })
    .then(function() {
      return q.all(testPaths.map(function(testPath) {
        return workerPool.sendMessage({testFilePath: testPath})
          .then(function(testResult) {
            if (testResult.numFailingTests > 0) {
              aggregatedResults.numFailedTests++;
            }
            onResult && onResult(config, testResult);
          })
          .catch(function(err) {
            aggregatedResults.numFailedTests++;
            onResult(config, {
              testFilePath: testPath,
              testExecError: err,
              suites: {},
              tests: {},
              logMessages: []
            });
          });
      }));
    })
    .then(function() {
      return workerPool.destroy().then(function() {
        aggregatedResults.endTime = Date.now();
        return aggregatedResults;
      });
    });
};

module.exports = TestRunner;
<|endoftext|>


<commit_before>/*
 * {%= name %}
 * {%= homepage %}
 *
 * Copyright (c) {%= grunt.template.today('yyyy') %} {%= author_name %}
 * Licensed under the {%= licenses.join(', ') %} license{%= licenses.length === 1 ? '' : 's' %}.
 */

module.exports = function(grunt) {
  'use strict';

  // Please see the Grunt documentation for more information regarding task
  // creation: http://gruntjs.com/creating-tasks

  function MyTask(task) {
    this.options = task.options({
      separator: ', ',
      punctuation: "\n",
      localePlaceholder: '{locale}',
      localeName: 'i18n',
      jsonSpace: 2
    });

    this.task = task;
    this.done = task.async();

    // initialize the plugin we're extending
    this.basePlugin = this.initializeBasePlugin();

    if (grunt.util.kindOf(this[task.target]) === 'function') {
      this[task.target]();
    }
    else {
      this.passthru();
    }

  }

  grunt.registerMultiTask(
    'locales_extend',
    'Extends grunt-locales plugin',
    function () {
      return new MyTask(this);
    }
  );

  function extend(dst) {
    grunt.util.toArray(arguments).forEach(function (obj) {
      var key;
      if (obj && obj !== dst) {
        for (key in obj) {
          if (obj.hasOwnProperty(key)) {
            dst[key] = obj[key];
          }
        }
      }
    });
    return dst;
  }

  extend(MyTask.prototype, {
    extend: extend,

    /*
     * property names here correspond to task targets
     */

    initializeBasePlugin: function() {
        var basePluginName = this.options.basePlugin.pluginName,
          basePluginTask = this.options.basePlugin.pluginTask,
          basePluginConfig = grunt.config.get(this.task.name);

      var plugin = require(basePluginName);
      var pluginExports = plugin(grunt);
      grunt.config.set(basePluginTask, basePluginConfig);
      this.logObject(grunt.config.get());
      return pluginExports; // may not be useful, or even be defined ...
    },

    passthru: function() {
      var that = this,
        baseTask = this.options.basePlugin.pluginTask,
        baseTarget = baseTask + ':' + that.task.target;

      grunt.task.run(baseTarget);
      return that.done();

    },

    logObject: function (obj) {
      var theObject = obj;
      grunt.log.writeln('logObject: ' + JSON.stringify(
        theObject,
        this.options.jsonReplacer,
        this.options.jsonSpace
      ));
    },

    addExternalMessages: function () {
      var that = this,
        dest = this.getDestinationFilePath(),
        locale,
        externalMessages,
        messages = {};

      externalMessages = that.parseExternalMessages();
      Object.keys(externalMessages).forEach(function (locale) {
        var localeFile = dest.replace(that.options.localePlaceholder, locale),
          localFileExists = grunt.file.exists(localeFile),
          externalLocaleData = externalMessages[locale],
          dataId;
        if (localFileExists) {
          messages = grunt.file.readJSON(localeFile);
          grunt.log.writeln('Parsed locale messages from ' + localeFile.cyan + '.');
        }
        for (dataId in externalLocaleData) {
          if (messages.hasOwnProperty(dataId)) {
            messages[dataId].value = externalLocaleData[dataId].value;
            if (typeof externalLocaleData[dataId].files == 'array' && externalLocaleData[dataId].files.count > 0) {
              messages[dataId].files = externalLocaleData[dataId].files;
            }
          }
          else {
            messages[dataId] = externalLocaleData[dataId];
          }
        }
        grunt.file.write(localeFile, JSON.stringify(
          messages,
          that.options.jsonReplacer,
          that.options.jsonSpace
        ));
      });
      return that.done();
    },

    parseExternalMessages: function () {
      var that = this,
        externalMessages = {},
        localeData = {},
        key;

      this.getSourceFiles().forEach(function (file) {
        var dataId,
          externalMessages = grunt.file.readJSON(file);
        grunt.log.writeln('Parsed external messages from ' + file.cyan + '.');
        for (dataId in externalMessages) {
          var locales = externalMessages[dataId].locales,
            files = externalMessages[dataId].files || [];

          Object.keys(locales).forEach(function (locale) {
            var data = {};
            if (!localeData.hasOwnProperty(locale)) {
              localeData[locale] = {};
            }
            data = {
              value: locales[locale].value,
              files: files
            };
            localeData[locale][dataId] = data;
          });
        }
      });
      return localeData;
    },

    /*
     * Utility properties (functions)
     */

    getSourceFiles: function () {
      var files = this.task.filesSrc;
      if (this.task.args.length) {
        files = this.task.args;
      }
      return files.filter(function (file) {
        if (!grunt.file.exists(file)) {
          grunt.log.warn('Source file ' + file.cyan + ' not found.');
          return false;
        }
        return true;
      });
    },

    getDestinationFilePath: function () {
      var dest = this.task.files.length && this.task.files[0].dest;
      if (!dest) {
        grunt.fail.warn('Missing destination file path.');
        return this.done();
      }
      return dest;
    }

  });

};
<commit_msg>Needed to restore current task config after requiring base plugin<commit_after>/*
 * {%= name %}
 * {%= homepage %}
 *
 * Copyright (c) {%= grunt.template.today('yyyy') %} {%= author_name %}
 * Licensed under the {%= licenses.join(', ') %} license{%= licenses.length === 1 ? '' : 's' %}.
 */

module.exports = function(grunt) {
  'use strict';

  // Please see the Grunt documentation for more information regarding task
  // creation: http://gruntjs.com/creating-tasks

  function MyTask(task) {
    this.options = task.options({
      separator: ', ',
      punctuation: "\n",
      localePlaceholder: '{locale}',
      localeName: 'i18n',
      jsonSpace: 2
    });

    this.task = task;
    this.done = task.async();

    // initialize the plugin we're extending
    this.basePlugin = this.initializeBasePlugin();

    if (grunt.util.kindOf(this[task.target]) === 'function') {
      this[task.target]();
    }
    else {
      this.passthru();
    }

  }

  grunt.registerMultiTask(
    'locales_extend',
    'Extends grunt-locales plugin',
    function () {
      return new MyTask(this);
    }
  );

  function extend(dst) {
    grunt.util.toArray(arguments).forEach(function (obj) {
      var key;
      if (obj && obj !== dst) {
        for (key in obj) {
          if (obj.hasOwnProperty(key)) {
            dst[key] = obj[key];
          }
        }
      }
    });
    return dst;
  }

  extend(MyTask.prototype, {
    extend: extend,

    /*
     * property names here correspond to task targets
     */

    initializeBasePlugin: function() {
      var basePluginName = this.options.basePlugin.pluginName,
        basePluginTask = this.options.basePlugin.pluginTask,
        basePluginConfig = grunt.config.get(this.task.name);

      var plugin = require(basePluginName);
      var pluginExports = plugin(grunt);
      grunt.config.set(basePluginTask, basePluginConfig);
      // current task config is now gone ...
      grunt.config.set(this.task.name, basePluginConfig);
      this.logObject(grunt.config.get(this.task.name + ': '), this.task.name);
      return pluginExports; // may not be useful, or even be defined ...
    },

    passthru: function() {
      var that = this,
        baseTask = this.options.basePlugin.pluginTask,
        baseTarget = baseTask + ':' + that.task.target;

      grunt.task.run(baseTarget);
      return that.done();

    },

    logObject: function (obj, msg) {
      var theObject = obj,
        theMessage = msg|| 'logObject: ';
      grunt.log.writeln(theMessage + JSON.stringify(
        theObject,
        this.options.jsonReplacer,
        this.options.jsonSpace
      ));
    },

    addExternalMessages: function () {
      var that = this,
        dest = this.getDestinationFilePath(),
        locale,
        externalMessages,
        messages = {};

      externalMessages = that.parseExternalMessages();
      Object.keys(externalMessages).forEach(function (locale) {
        var localeFile = dest.replace(that.options.localePlaceholder, locale),
          localFileExists = grunt.file.exists(localeFile),
          externalLocaleData = externalMessages[locale],
          dataId;
        if (localFileExists) {
          messages = grunt.file.readJSON(localeFile);
          grunt.log.writeln('Parsed locale messages from ' + localeFile.cyan + '.');
        }
        for (dataId in externalLocaleData) {
          if (messages.hasOwnProperty(dataId)) {
            messages[dataId].value = externalLocaleData[dataId].value;
            if (typeof externalLocaleData[dataId].files == 'array' && externalLocaleData[dataId].files.count > 0) {
              messages[dataId].files = externalLocaleData[dataId].files;
            }
          }
          else {
            messages[dataId] = externalLocaleData[dataId];
          }
        }
        grunt.file.write(localeFile, JSON.stringify(
          messages,
          that.options.jsonReplacer,
          that.options.jsonSpace
        ));
      });
      return that.done();
    },

    parseExternalMessages: function () {
      var that = this,
        externalMessages = {},
        localeData = {},
        key;

      this.getSourceFiles().forEach(function (file) {
        var dataId,
          externalMessages = grunt.file.readJSON(file);
        grunt.log.writeln('Parsed external messages from ' + file.cyan + '.');
        for (dataId in externalMessages) {
          var locales = externalMessages[dataId].locales,
            files = externalMessages[dataId].files || [];

          Object.keys(locales).forEach(function (locale) {
            var data = {};
            if (!localeData.hasOwnProperty(locale)) {
              localeData[locale] = {};
            }
            data = {
              value: locales[locale].value,
              files: files
            };
            localeData[locale][dataId] = data;
          });
        }
      });
      return localeData;
    },

    /*
     * Utility properties (functions)
     */

    getSourceFiles: function () {
      var files = this.task.filesSrc;
      if (this.task.args.length) {
        files = this.task.args;
      }
      return files.filter(function (file) {
        if (!grunt.file.exists(file)) {
          grunt.log.warn('Source file ' + file.cyan + ' not found.');
          return false;
        }
        return true;
      });
    },

    getDestinationFilePath: function () {
      var dest = this.task.files.length && this.task.files[0].dest;
      if (!dest) {
        grunt.fail.warn('Missing destination file path.');
        return this.done();
      }
      return dest;
    }

  });

};
<|endoftext|>


<commit_before>function Thermostat() {
  this.resetTemp();
}

  Thermostat.prototype.increaseTemp = function() {
    if(this.psm === false && this.temperature < 32) return this.temperature += 1;
    else if(this.temperature < 25) return this.temperature +=1;
    return this.maxTemp();
  };

  Thermostat.prototype.decreaseTemp = function() {
    if(this.temperature >= 11) return this.temperature -= 1;
    else return this.temperature = 10;
  };

  Thermostat.prototype.maxTemp = function(){
  	if(this.psm) return 25;
  	else return 32;
  };

  Thermostat.prototype.resetTemp = function(){
    this.psm = true;
    return this.temperature = 20;
  };

  Thermostat.prototype.energyUsage = function(){
    if(this.temperature < 18) return 'Low';
    if(this.temperature < 25) return 'Medium';
    if(this.temperature >= 25) return 'High';
  };

  Thermostat.prototype.psmToggle = function(){
    if(this.psm === true) this.psm = false;
    else this.psm = true;
  };

<commit_msg>Update Thermostat.js<commit_after>function Thermostat() {
  this.resetTemp();
}

  Thermostat.prototype.increaseTemp = function() {
    if(this.psm === false && this.temperature < 32) return this.temperature += 1;
    else if(this.temperature < 25) return this.temperature +=1;
    return this.maxTemp();
  };

  Thermostat.prototype.decreaseTemp = function() {
    if(this.temperature >= 11) return this.temperature -= 1;
    else return this.temperature = 10;
  };

  Thermostat.prototype.maxTemp = function(){
  	if(this.psm) return 25;
  	else return 32;
  };

  Thermostat.prototype.resetTemp = function(){
    this.psm = true;
    return this.temperature = 20;
  };

  Thermostat.prototype.energyUsage = function(){
    if(this.temperature < 18) return 'Low';
    if(this.temperature < 25) return 'Medium';
    if(this.temperature >= 25) return 'High';
  };

  Thermostat.prototype.psmToggle = function(){
    (this.psm === true) ? this.psm = false : this.psm = true;
  };

<|endoftext|>


<commit_before>var handle = function(e) {
    // Find the relevant link
    var targetLink = e.currentTarget.href;

    // Find what we said about it
    var relevantItem = example_items.filter(function(item) { return item['url'] == targetLink});
    console.log(relevantItem);
    var
    desc = relevantItem[0]['description'],
    students = relevantItem[0]['students'],
    bugID = relevantItem[0]['id'],
    html = "";

    if (desc) {
        html = '
Notes by event organizers:

' + desc + '
';
    }

    if (Object.keys(students).length ==0) {
        html += '
No one claimed this ticket yet! Claim it by typing your name below:
<input type="text" id="new_name" value="Your name here"></input><input type="submit" value="Submit" id="claim_' + bugID + '" class="button"/>';
    }

    else {
        html += '
Currently being worked on by ' + students + '
';
    }

    html = html + '
View Open Task Here';

    var $dialog = $('

')
	.html(html)
	.dialog({
	    autoOpen: false,
	    title: 'Info about this task'
	});

    $dialog.dialog('open');

    $('#claim_' + bugID).on(
        'click',
        function(){
            $dialog.dialog('close');
            claim(bugID, $("#new_name").val()).done(
                function(data, status, jqxhr) {
                    $.getJSON('/tasks/', updateSettings);
                }
            );
        }
    );
    $("#new_name").val("");

    // prevent the default action, e.g., following a link
    return false;
};
<commit_msg>Fix typo<commit_after>var handle = function(e) {
    // Find the relevant link
    var targetLink = e.currentTarget.href;

    // Find what we said about it
    var relevantItem = example_items.filter(function(item) { return item['url'] == targetLink});
    console.log(relevantItem);
    var
    desc = relevantItem[0]['description'],
    students = relevantItem[0]['students'],
    bugID = relevantItem[0]['id'],
    html = "";

    if (desc) {
        html = '
Notes by event organizers:

' + desc + '
';
    }

    if (Object.keys(students).length ==0) {
        html += '
No one claimed this ticket yet! Claim it by typing your name below:
<input type="text" id="new_name" value="Your name here"></input><input type="submit" value="Submit" id="claim_' + bugID + '" class="button"/>';
    }

    else {
        html += '
Currently being worked on by ' + students + '
';
    }

    html = html + '
View Open Task Here';

    var $dialog = $('

')
	.html(html)
	.dialog({
	    autoOpen: false,
	    title: 'Info about this task'
	});

    $dialog.dialog('open');

    $('#claim_' + bugID).on(
        'click',
        function(){
            $dialog.dialog('close');
            claim(bugID, $("#new_name").val()).done(
                function(data, status, jqxhr) {
                    $.getJSON('/tasks/', updateSettings);
                }
            );
        }
    );
    $("#new_name").val("");

    // prevent the default action, e.g., following a link
    return false;
};
<|endoftext|>