<commit_before>/*
 * Sift
 *
 * Copryright 2011, Craig Condon
 * Licensed under MIT
 *
 * Inspired by mongodb's query language
 */

(function() {

  "use strict";

  //traversable statements
  var TRAV_OP = {
    $and  : true,
    $or   : true,
    $nor  : true,
    $trav : true,
    $not  : true
  };

  var _testers = {

    /**
     */

    $eq: function(a, b) {
      return a.test(b);
    },

    /**
     */

    $ne: function(a, b) {
      return !a.test(b);
    },

    /**
     */

    $lt: function(a, b) {
      return a > b;
    },

    /**
     */

    $gt: function(a, b) {
      return a < b;
    },

    /**
     */

    $lte: function(a, b) {
      return a >= b;
    },

    /**
     */

    $gte: function(a, b) {
      return a <= b;
    },

    /**
     */

    $exists: function(a, b) {
      return a === (b != null);
    },

    /**
     */

    $in: function(a, b) {

      //intersecting an array
      if (b instanceof Array) {

        for (var i = b.length; i--;) {
          if (~a.indexOf(b[i])) return true;
        }

      } else {
        return ~a.indexOf(b);
      }

      return false;
    },

    /**
     */

    $not: function(a, b) {
      if (!a.test) throw new Error("$not test should include an expression, not a value. Use $ne instead.");
      return !a.test(b);
    },

    /**
     */

    $type: function(a, b, org) {
      //instanceof doesn't work for strings / boolean. instanceof works with inheritance
      return org != null ? org instanceof a || org.constructor == a : false;
    },

    /**
     */

    $nin: function(a, b) {
      return !_testers.$in(a, b);
    },

    /**
     */

    $mod: function(a, b) {
      return b % a[0] == a[1];
    },

    /**
     */

    $all: function(a, b) {
      if (!b) b = [];
      for (var i = a.length; i--;) {
        var a1 = a[i];
        var indexInB = ~b.indexOf(a1);
        if (!indexInB) return false;
      }

      return true;
    },

    /**
     */

    $size: function(a, b) {
      return b ? a === b.length : false;
    },

    /**
     */

    $or: function(a, b) {

      var i = a.length;
      var n = i;

      for (; i--;) {
        if (test(a[i], b)) {
          return true;
        }
      }

      return n === 0;
    },

    /**
     */

    $nor: function(a, b) {

      var i = a.length;

      for (; i--;) {
        if (test(a[i], b)) {
          return false;
        }
      }

      return true;
    },

    /**
     */

    $and: function(a, b) {

      for (var i = a.length; i--;) {
        if (!test(a[i], b)) {
          return false;
        }
      }

      return true;
    },

    /**
     */

    $trav: function(a, b) {

      if (b instanceof Array) {

        for (var i = b.length; i--;) {
          var subb = b[i];
          if (subb[a.k] && test(a, subb[a.k])) return true;
        }

        return false;
      }

      //continue to traverse even if there isn't a value - this is needed for
      //something like name:{$exists:false}
      return test(a, b ? b[a.k] : void 0);
    },

    /**
     */

    $regex: function(a, b) {
      var aRE = new RegExp(a);
      return aRE.test(b);
    },

    /**
     */

    $where: function(a, b) {
      return a.call(b, b);
    },

    /**
     */

    $elemMatch: function(a, b) {
      return a.test(b);
    }
  };

  var _prepare = {

    /**
     */

    $eq: function(a) {

      var fn;

      if (a instanceof RegExp) {
        return a;
      } else if (a instanceof Function) {
        fn = a;
      } else {
        fn = function(b) {
          if (b instanceof Array) {
            return ~b.indexOf(a);
          } else {
            return a === b;
          }
        };
      }

      return {
        test: fn
      };
    },

    /**
     */

    $ne: function(a) {
      return _prepare.$eq(a);
    },

    /**
     */

    $where: function(a) {

      if (typeof a === "string") {
        return new Function("obj", "return " + a);
      }

      return a;
    },

    /**
     */

    $elemMatch: function(a) {
      return parse(a);
    }
  };

  /**
   */

  function getExpr(type, key, value) {

    var v = comparable(value);

    return {

      //k key
      k: key,

      //v value
      v: _prepare[type] ? _prepare[type](v) : v,

      //e eval
      e: _testers[type]
    };
  }

  /**
   * tests against data
   */

  function test(statement, data) {

    var exprs    = statement.exprs;

    //generally, expressions are ordered from least efficient, to most efficient.
    for (var i = 0, n = exprs.length; i < n; i++) {

      var expr = exprs[i];

      if (!expr.e(expr.v, comparable(data), data)) return false;

    }

    return true;
  }

  /**
   * parses a statement into something evaluable
   */

  function parse(statement, key) {

    //fixes sift(null, []) issue
    if (!statement) statement = { $eq: statement };

    var testers = [];

    //if the statement is an object, then we're looking at something like: { key: match }
    if (statement && isObject(statement)) {

      for (var k in statement) {

        //find the apropriate operator. If one doesn't exist and the key does not start
        //with a $ character, then it's a property, which means we create a new statement
        //(traversing)
        var operator;
        if (!!_testers[k]) {
          operator = k;

        // $ == 36
        } else if (k.charCodeAt(0) !== 36) {
          operator = "$trav";
        } else {
          throw new Error("Unknown operator " + k + ".");
        }

        //value of given statement (the match)
        var value = statement[k];

        //default = match
        var exprValue = value;

        //if we're working with a traversable operator, then set the expr value
        if (TRAV_OP[operator]) {

          //using dot notation? convert into a sub-object
          if (~k.indexOf(".")) {
            var keyParts = k.split(".");
            k = keyParts.shift(); //we're using the first key, so remove it

            exprValue = value = convertDotToSubObject(keyParts, value);
          }

          //*if* the value is an array, then we're dealing with something like: $or, $and
          if (value instanceof Array) {

            exprValue = [];

            for (var i = value.length; i--;) {
              exprValue.push(parse(value[i]));
            }

          //otherwise we're dealing with $trav
          } else {
            exprValue = parse(value, k);
          }
        }

        testers.push(getExpr(operator, k, exprValue));
      }

    //otherwise we're comparing a particular value, so set to eq
    } else {
      testers.push(getExpr("$eq", key, statement));
    }

    var stmt =  {
      exprs: testers,
      k: key,
      test: function(value) {
        return test(stmt, value);
      }
    };

    return stmt;
  }

  /**
   */

  function comparable(value) {
    if (value instanceof Date) {
      return value.getTime();
    } else if (value instanceof Array) {
      return value.map(comparable);
    } else {
      return value;
    }
  }

  /**
   */

  function convertDotToSubObject(keyParts, value) {

    var subObject    = {};
    var currentValue = subObject;

    for (var i = 0, n = keyParts.length - 1; i < n; i++) {
      currentValue = currentValue[keyParts[i]] = {};
    }

    currentValue[keyParts[i]] = value;

    return subObject;
  }

  /**
   */

  function getSelector(selector) {

    if (!selector) {

      return function(value) {
        return value;
      };

    } else if (typeof selector == "function") {
      return selector;
    }

    throw new Error("Unknown sift selector " + selector);
  }

  /**
   * sifts the given function
   * @param query the mongodb query
   * @param target the target array
   * @param rawSelector the selector for plucking data from the given target
   */

  function sift(query, target, rawSelector) {

    //must be an array
    if (typeof target != "object") {
      rawSelector = target;
      target = void 0;
    }

    var selector = getSelector(rawSelector);

    //build the filter for the sifter
    var sifter = parse(query);

    function filter(value) {
      return sifter.test(selector(value));
    }

    if (target) return target.filter(filter);

    filter.query = query;

    return filter;
  }

  sift.use = function(options) {
    if (options.operators) sift.useOperators(options.operators);
    if (typeof options === "function") options(sift);
  };

  sift.useOperators = function(operators) {
    for (var key in operators) {
      sift.useOperator(key, operators[key]);
    }
  };

  sift.useOperator = function(operator, optionsOrFn) {

    var options = {};

    if (typeof optionsOrFn == "object") {
      options = optionsOrFn;
    } else {
      options = { test: optionsOrFn };
    }

    var key = "$" + operator;
    _testers[key] = options.test;

    if (options.traversable || options.traverse) {
      TRAV_OP[key] = true;
    }
  };

  sift.indexOf = function(query, target, rawSelector) {
    var selector = getSelector(rawSelector);

    //build the filter for the sifter
    var sifter = parse(query);

    for (var i = 0; i < target.length; i++) {
      if (sifter.test(selector(target[i]))) {
        return i;
      }
    }

    return -1;
  };

  function isObject(value) {
    // function appropriated from lodash. The check for obj.constructor === Object fails in a custom REPL for node.
    // Avoid a V8 JIT bug in Chrome 19-20.
    // See https://code.google.com/p/v8/issues/detail?id=2291 for more details.
    var type = typeof value;
    return !!value && (type === 'object' || type === 'function');
  }

  /* istanbul ignore next */
  if ((typeof module != "undefined") && (typeof module.exports != "undefined")) {
    module.exports = sift;
  } else
  if (typeof window != "undefined") {
    window.sift = sift;
  }
})();
<commit_msg>switch to double quotes per the style requirements.<commit_after>/*
 * Sift
 *
 * Copryright 2011, Craig Condon
 * Licensed under MIT
 *
 * Inspired by mongodb's query language
 */

(function() {

  "use strict";

  //traversable statements
  var TRAV_OP = {
    $and  : true,
    $or   : true,
    $nor  : true,
    $trav : true,
    $not  : true
  };

  var _testers = {

    /**
     */

    $eq: function(a, b) {
      return a.test(b);
    },

    /**
     */

    $ne: function(a, b) {
      return !a.test(b);
    },

    /**
     */

    $lt: function(a, b) {
      return a > b;
    },

    /**
     */

    $gt: function(a, b) {
      return a < b;
    },

    /**
     */

    $lte: function(a, b) {
      return a >= b;
    },

    /**
     */

    $gte: function(a, b) {
      return a <= b;
    },

    /**
     */

    $exists: function(a, b) {
      return a === (b != null);
    },

    /**
     */

    $in: function(a, b) {

      //intersecting an array
      if (b instanceof Array) {

        for (var i = b.length; i--;) {
          if (~a.indexOf(b[i])) return true;
        }

      } else {
        return ~a.indexOf(b);
      }

      return false;
    },

    /**
     */

    $not: function(a, b) {
      if (!a.test) throw new Error("$not test should include an expression, not a value. Use $ne instead.");
      return !a.test(b);
    },

    /**
     */

    $type: function(a, b, org) {
      //instanceof doesn't work for strings / boolean. instanceof works with inheritance
      return org != null ? org instanceof a || org.constructor == a : false;
    },

    /**
     */

    $nin: function(a, b) {
      return !_testers.$in(a, b);
    },

    /**
     */

    $mod: function(a, b) {
      return b % a[0] == a[1];
    },

    /**
     */

    $all: function(a, b) {
      if (!b) b = [];
      for (var i = a.length; i--;) {
        var a1 = a[i];
        var indexInB = ~b.indexOf(a1);
        if (!indexInB) return false;
      }

      return true;
    },

    /**
     */

    $size: function(a, b) {
      return b ? a === b.length : false;
    },

    /**
     */

    $or: function(a, b) {

      var i = a.length;
      var n = i;

      for (; i--;) {
        if (test(a[i], b)) {
          return true;
        }
      }

      return n === 0;
    },

    /**
     */

    $nor: function(a, b) {

      var i = a.length;

      for (; i--;) {
        if (test(a[i], b)) {
          return false;
        }
      }

      return true;
    },

    /**
     */

    $and: function(a, b) {

      for (var i = a.length; i--;) {
        if (!test(a[i], b)) {
          return false;
        }
      }

      return true;
    },

    /**
     */

    $trav: function(a, b) {

      if (b instanceof Array) {

        for (var i = b.length; i--;) {
          var subb = b[i];
          if (subb[a.k] && test(a, subb[a.k])) return true;
        }

        return false;
      }

      //continue to traverse even if there isn't a value - this is needed for
      //something like name:{$exists:false}
      return test(a, b ? b[a.k] : void 0);
    },

    /**
     */

    $regex: function(a, b) {
      var aRE = new RegExp(a);
      return aRE.test(b);
    },

    /**
     */

    $where: function(a, b) {
      return a.call(b, b);
    },

    /**
     */

    $elemMatch: function(a, b) {
      return a.test(b);
    }
  };

  var _prepare = {

    /**
     */

    $eq: function(a) {

      var fn;

      if (a instanceof RegExp) {
        return a;
      } else if (a instanceof Function) {
        fn = a;
      } else {
        fn = function(b) {
          if (b instanceof Array) {
            return ~b.indexOf(a);
          } else {
            return a === b;
          }
        };
      }

      return {
        test: fn
      };
    },

    /**
     */

    $ne: function(a) {
      return _prepare.$eq(a);
    },

    /**
     */

    $where: function(a) {

      if (typeof a === "string") {
        return new Function("obj", "return " + a);
      }

      return a;
    },

    /**
     */

    $elemMatch: function(a) {
      return parse(a);
    }
  };

  /**
   */

  function getExpr(type, key, value) {

    var v = comparable(value);

    return {

      //k key
      k: key,

      //v value
      v: _prepare[type] ? _prepare[type](v) : v,

      //e eval
      e: _testers[type]
    };
  }

  /**
   * tests against data
   */

  function test(statement, data) {

    var exprs    = statement.exprs;

    //generally, expressions are ordered from least efficient, to most efficient.
    for (var i = 0, n = exprs.length; i < n; i++) {

      var expr = exprs[i];

      if (!expr.e(expr.v, comparable(data), data)) return false;

    }

    return true;
  }

  /**
   * parses a statement into something evaluable
   */

  function parse(statement, key) {

    //fixes sift(null, []) issue
    if (!statement) statement = { $eq: statement };

    var testers = [];

    //if the statement is an object, then we're looking at something like: { key: match }
    if (statement && isObject(statement)) {

      for (var k in statement) {

        //find the apropriate operator. If one doesn't exist and the key does not start
        //with a $ character, then it's a property, which means we create a new statement
        //(traversing)
        var operator;
        if (!!_testers[k]) {
          operator = k;

        // $ == 36
        } else if (k.charCodeAt(0) !== 36) {
          operator = "$trav";
        } else {
          throw new Error("Unknown operator " + k + ".");
        }

        //value of given statement (the match)
        var value = statement[k];

        //default = match
        var exprValue = value;

        //if we're working with a traversable operator, then set the expr value
        if (TRAV_OP[operator]) {

          //using dot notation? convert into a sub-object
          if (~k.indexOf(".")) {
            var keyParts = k.split(".");
            k = keyParts.shift(); //we're using the first key, so remove it

            exprValue = value = convertDotToSubObject(keyParts, value);
          }

          //*if* the value is an array, then we're dealing with something like: $or, $and
          if (value instanceof Array) {

            exprValue = [];

            for (var i = value.length; i--;) {
              exprValue.push(parse(value[i]));
            }

          //otherwise we're dealing with $trav
          } else {
            exprValue = parse(value, k);
          }
        }

        testers.push(getExpr(operator, k, exprValue));
      }

    //otherwise we're comparing a particular value, so set to eq
    } else {
      testers.push(getExpr("$eq", key, statement));
    }

    var stmt =  {
      exprs: testers,
      k: key,
      test: function(value) {
        return test(stmt, value);
      }
    };

    return stmt;
  }

  /**
   */

  function comparable(value) {
    if (value instanceof Date) {
      return value.getTime();
    } else if (value instanceof Array) {
      return value.map(comparable);
    } else {
      return value;
    }
  }

  /**
   */

  function convertDotToSubObject(keyParts, value) {

    var subObject    = {};
    var currentValue = subObject;

    for (var i = 0, n = keyParts.length - 1; i < n; i++) {
      currentValue = currentValue[keyParts[i]] = {};
    }

    currentValue[keyParts[i]] = value;

    return subObject;
  }

  /**
   */

  function getSelector(selector) {

    if (!selector) {

      return function(value) {
        return value;
      };

    } else if (typeof selector == "function") {
      return selector;
    }

    throw new Error("Unknown sift selector " + selector);
  }

  /**
   * sifts the given function
   * @param query the mongodb query
   * @param target the target array
   * @param rawSelector the selector for plucking data from the given target
   */

  function sift(query, target, rawSelector) {

    //must be an array
    if (typeof target != "object") {
      rawSelector = target;
      target = void 0;
    }

    var selector = getSelector(rawSelector);

    //build the filter for the sifter
    var sifter = parse(query);

    function filter(value) {
      return sifter.test(selector(value));
    }

    if (target) return target.filter(filter);

    filter.query = query;

    return filter;
  }

  sift.use = function(options) {
    if (options.operators) sift.useOperators(options.operators);
    if (typeof options === "function") options(sift);
  };

  sift.useOperators = function(operators) {
    for (var key in operators) {
      sift.useOperator(key, operators[key]);
    }
  };

  sift.useOperator = function(operator, optionsOrFn) {

    var options = {};

    if (typeof optionsOrFn == "object") {
      options = optionsOrFn;
    } else {
      options = { test: optionsOrFn };
    }

    var key = "$" + operator;
    _testers[key] = options.test;

    if (options.traversable || options.traverse) {
      TRAV_OP[key] = true;
    }
  };

  sift.indexOf = function(query, target, rawSelector) {
    var selector = getSelector(rawSelector);

    //build the filter for the sifter
    var sifter = parse(query);

    for (var i = 0; i < target.length; i++) {
      if (sifter.test(selector(target[i]))) {
        return i;
      }
    }

    return -1;
  };

  function isObject(value) {
    // function appropriated from lodash. The check for obj.constructor === Object fails in a custom REPL for node.
    // Avoid a V8 JIT bug in Chrome 19-20.
    // See https://code.google.com/p/v8/issues/detail?id=2291 for more details.
    var type = typeof value;
    return !!value && (type === "object" || type === "function");
  }

  /* istanbul ignore next */
  if ((typeof module != "undefined") && (typeof module.exports != "undefined")) {
    module.exports = sift;
  } else
  if (typeof window != "undefined") {
    window.sift = sift;
  }
})();
<|endoftext|>


<commit_before>const fs = require('fs-extra');
const os = require('os');
const rollup = require('rollup');
const rollupPluginCommonjs = require('rollup-plugin-commonjs');
const rollupPluginNodeResolve = require('rollup-plugin-node-resolve');
const rollupPluginReplace = require('rollup-plugin-replace');
const rollupPluginTypescript = require('rollup-plugin-typescript2');
const typescript = require('typescript');
const {getDestDir} = require('./paths');
const reload = require('./reload');
const {PORT} = reload;
const {createTask} = require('./task');

async function copyToFF({cwdPath, production}) {
    const destPath = `${getDestDir({production})}/${cwdPath}`;
    const ffDestPath = `${getDestDir({production, firefox: true})}/${cwdPath}`;
    await fs.copy(destPath, ffDestPath);
}

function replace(str, find, replace) {
    return str.split(find).join(replace);
}

function patchFirefoxJS(/** @type {string} */code) {
    code = replace(code, 'chrome.fontSettings.getFontList', `chrome['font' + 'Settings']['get' + 'Font' + 'List']`);
    code = replace(code, 'chrome.fontSettings', `chrome['font' + 'Settings']`);
    return code;
}

/**
 * @typedef JSEntry
 * @property {string} src
 * @property {string} dest
 * @property {string} reloadType
 * @property {({production}) => Promise<void>} postBuild
 * @property {string[]} watchFiles
 */

/** @type {JSEntry[]} */
const jsEntries = [
    {
        src: 'src/background/index.ts',
        dest: 'background/index.js',
        reloadType: reload.FULL,
        async postBuild({production}) {
            const destPath = `${getDestDir({production})}/${this.dest}`;
            const ffDestPath = `${getDestDir({production, firefox: true})}/${this.dest}`;
            const code = await fs.readFile(destPath, 'utf8');
            await fs.outputFile(ffDestPath, patchFirefoxJS(code));
        },
        watchFiles: null,
    },
    {
        src: 'src/inject/index.ts',
        dest: 'inject/index.js',
        reloadType: reload.FULL,
        async postBuild({production}) {
            await copyToFF({cwdPath: this.dest, production});
        },
        watchFiles: null,
    },
    {
        src: 'src/ui/devtools/index.tsx',
        dest: 'ui/devtools/index.js',
        reloadType: reload.UI,
        async postBuild({production}) {
            await copyToFF({cwdPath: this.dest, production});
        },
        watchFiles: null,
    },
    {
        src: 'src/ui/popup/index.tsx',
        dest: 'ui/popup/index.js',
        reloadType: reload.UI,
        async postBuild({production}) {
            await copyToFF({cwdPath: this.dest, production});
        },
        watchFiles: null,
    },
    {
        src: 'src/ui/stylesheet-editor/index.tsx',
        dest: 'ui/stylesheet-editor/index.js',
        reloadType: reload.UI,
        async postBuild({production}) {
            await copyToFF({cwdPath: this.dest, production});
        },
        watchFiles: null,
    },
];

async function bundleJS(/** @type {JSEntry} */entry, {production}) {
    const {src, dest} = entry;
    const bundle = await rollup.rollup({
        input: src,
        plugins: [
            rollupPluginNodeResolve(),
            rollupPluginCommonjs(),
            rollupPluginTypescript({
                typescript,
                tsconfig: 'src/tsconfig.json',
                tsconfigOverride: {
                    compilerOptions: {
                        removeComments: production ? true : false,
                    },
                },
                clean: production ? true : false,
                cacheRoot: production ? null : `${fs.realpathSync(os.tmpdir())}/darkreader_typescript_cache`,
            }),
            rollupPluginReplace({
                '__DEBUG__': production ? 'false' : 'true',
                '__PORT__': production ? '-1' : String(PORT),
            }),
        ].filter((x) => x)
    });
    entry.watchFiles = bundle.watchFiles;
    await bundle.write({
        file: `${getDestDir({production})}/${dest}`,
        strict: true,
        format: 'iife',
        sourcemap: production ? false : 'inline',
    });
    await entry.postBuild({production});
}

function getWatchFiles() {
    const watchFiles = new Set();
    jsEntries.forEach((entry) => {
        entry.watchFiles.forEach((file) => watchFiles.add(file));
    });
    return Array.from(watchFiles);
}

/** @type {string[]} */
let watchFiles;

module.exports = createTask(
    'bundle-js',
    async ({production}) => await Promise.all(
        jsEntries.map((entry) => bundleJS(entry, {production}))
    ),
).addWatcher(
    () => {
        watchFiles = getWatchFiles();
        return watchFiles;
    },
    async (changedFiles, watcher) => {
        const entries = jsEntries.filter((entry) => {
            return changedFiles.some((changed) => {
                return entry.watchFiles.includes(changed);
            });
        });
        await Promise.all(
            entries.map((e) => bundleJS(e, {production: false}))
        );

        const newWatchFiles = getWatchFiles();
        watcher.unwatch(
            watchFiles.filter((oldFile) => !newWatchFiles.includes(oldFile))
        );
        watcher.add(
            newWatchFiles.filter((newFile) => watchFiles.includes(newFile))
        );

        const isUIOnly = entries.every((entry) => entry.reloadType === reload.UI);
        reload({
            type: isUIOnly ? reload.UI : reload.FULL,
        });
    },
);
<commit_msg>Fixed generating source maps<commit_after>const fs = require('fs-extra');
const os = require('os');
const rollup = require('rollup');
const rollupPluginCommonjs = require('rollup-plugin-commonjs');
const rollupPluginNodeResolve = require('rollup-plugin-node-resolve');
const rollupPluginReplace = require('rollup-plugin-replace');
const rollupPluginTypescript = require('rollup-plugin-typescript2');
const typescript = require('typescript');
const {getDestDir} = require('./paths');
const reload = require('./reload');
const {PORT} = reload;
const {createTask} = require('./task');

async function copyToFF({cwdPath, production}) {
    const destPath = `${getDestDir({production})}/${cwdPath}`;
    const ffDestPath = `${getDestDir({production, firefox: true})}/${cwdPath}`;
    await fs.copy(destPath, ffDestPath);
}

function replace(str, find, replace) {
    return str.split(find).join(replace);
}

function patchFirefoxJS(/** @type {string} */code) {
    code = replace(code, 'chrome.fontSettings.getFontList', `chrome['font' + 'Settings']['get' + 'Font' + 'List']`);
    code = replace(code, 'chrome.fontSettings', `chrome['font' + 'Settings']`);
    return code;
}

/**
 * @typedef JSEntry
 * @property {string} src
 * @property {string} dest
 * @property {string} reloadType
 * @property {({production}) => Promise<void>} postBuild
 * @property {string[]} watchFiles
 */

/** @type {JSEntry[]} */
const jsEntries = [
    {
        src: 'src/background/index.ts',
        dest: 'background/index.js',
        reloadType: reload.FULL,
        async postBuild({production}) {
            const destPath = `${getDestDir({production})}/${this.dest}`;
            const ffDestPath = `${getDestDir({production, firefox: true})}/${this.dest}`;
            const code = await fs.readFile(destPath, 'utf8');
            await fs.outputFile(ffDestPath, patchFirefoxJS(code));
        },
        watchFiles: null,
    },
    {
        src: 'src/inject/index.ts',
        dest: 'inject/index.js',
        reloadType: reload.FULL,
        async postBuild({production}) {
            await copyToFF({cwdPath: this.dest, production});
        },
        watchFiles: null,
    },
    {
        src: 'src/ui/devtools/index.tsx',
        dest: 'ui/devtools/index.js',
        reloadType: reload.UI,
        async postBuild({production}) {
            await copyToFF({cwdPath: this.dest, production});
        },
        watchFiles: null,
    },
    {
        src: 'src/ui/popup/index.tsx',
        dest: 'ui/popup/index.js',
        reloadType: reload.UI,
        async postBuild({production}) {
            await copyToFF({cwdPath: this.dest, production});
        },
        watchFiles: null,
    },
    {
        src: 'src/ui/stylesheet-editor/index.tsx',
        dest: 'ui/stylesheet-editor/index.js',
        reloadType: reload.UI,
        async postBuild({production}) {
            await copyToFF({cwdPath: this.dest, production});
        },
        watchFiles: null,
    },
];

async function bundleJS(/** @type {JSEntry} */entry, {production}) {
    const {src, dest} = entry;
    const bundle = await rollup.rollup({
        input: src,
        plugins: [
            rollupPluginNodeResolve(),
            rollupPluginCommonjs(),
            rollupPluginTypescript({
                typescript,
                tsconfig: 'src/tsconfig.json',
                tsconfigOverride: {
                    compilerOptions: {
                        removeComments: production ? true : false,
                        sourceMap: production ? false : true,
                    },
                },
                clean: production ? true : false,
                cacheRoot: production ? null : `${fs.realpathSync(os.tmpdir())}/darkreader_typescript_cache`,
            }),
            rollupPluginReplace({
                '__DEBUG__': production ? 'false' : 'true',
                '__PORT__': production ? '-1' : String(PORT),
            }),
        ].filter((x) => x)
    });
    entry.watchFiles = bundle.watchFiles;
    await bundle.write({
        file: `${getDestDir({production})}/${dest}`,
        strict: true,
        format: 'iife',
        sourcemap: production ? false : 'inline',
    });
    await entry.postBuild({production});
}

function getWatchFiles() {
    const watchFiles = new Set();
    jsEntries.forEach((entry) => {
        entry.watchFiles.forEach((file) => watchFiles.add(file));
    });
    return Array.from(watchFiles);
}

/** @type {string[]} */
let watchFiles;

module.exports = createTask(
    'bundle-js',
    async ({production}) => await Promise.all(
        jsEntries.map((entry) => bundleJS(entry, {production}))
    ),
).addWatcher(
    () => {
        watchFiles = getWatchFiles();
        return watchFiles;
    },
    async (changedFiles, watcher) => {
        const entries = jsEntries.filter((entry) => {
            return changedFiles.some((changed) => {
                return entry.watchFiles.includes(changed);
            });
        });
        await Promise.all(
            entries.map((e) => bundleJS(e, {production: false}))
        );

        const newWatchFiles = getWatchFiles();
        watcher.unwatch(
            watchFiles.filter((oldFile) => !newWatchFiles.includes(oldFile))
        );
        watcher.add(
            newWatchFiles.filter((newFile) => watchFiles.includes(newFile))
        );

        const isUIOnly = entries.every((entry) => entry.reloadType === reload.UI);
        reload({
            type: isUIOnly ? reload.UI : reload.FULL,
        });
    },
);
<|endoftext|>


<commit_before>/*
 * grunt-diff-json
 * https://github.com/openknowl/grunt-diff-json
 *
 * Copyright (c) 2015 openknowl
 * Licensed under the MIT license.
 */

'use strict';

module.exports = function(grunt) {

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

  grunt.registerMultiTask('diff_json', 'Grunt task that compares two json files.', function() {
    // Merge task-specific and/or target-specific options with these defaults.
    var options = this.options({
      punctuation: '.',
      separator: ', '
    });

    // Iterate over all specified file groups.
    this.files.forEach(function(f) {
      // Concat specified files.
      var src = f.src.filter(function(filepath) {
        // Warn on and remove invalid source files (if nonull was set).
        if (!grunt.file.exists(filepath)) {
          grunt.log.warn('Source file "' + filepath + '" not found.');
          return false;
        } else {
          return true;
        }
      }).map(function(filepath) {
        // Read file source.
        return grunt.file.read(filepath);
      }).join(grunt.util.normalizelf(options.separator));

      // Handle options.
      src += options.punctuation;

      // Write the destination file.
      grunt.file.write(f.dest, src);

      // Print a success message.
      grunt.log.writeln('File "' + f.dest + '" created.');
    });
  });

};
<commit_msg>auto reindent<commit_after>/*
 * grunt-diff-json
 * https://github.com/openknowl/grunt-diff-json
 *
 * Copyright (c) 2015 openknowl
 * Licensed under the MIT license.
 */

'use strict';

module.exports = function (grunt) {

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

	grunt.registerMultiTask('diff_json', 'Grunt task that compares two json files.', function () {
		// Merge task-specific and/or target-specific options with these defaults.
		var options = this.options({
			punctuation: '.',
			separator: ', '
		});

		// Iterate over all specified file groups.
		this.files.forEach(function (f) {
			// Concat specified files.
			var src = f.src.filter(function (filepath) {
				// Warn on and remove invalid source files (if nonull was set).
				if (!grunt.file.exists(filepath)) {
					grunt.log.warn('Source file "' + filepath + '" not found.');
					return false;
				} else {
					return true;
				}
			}).map(function (filepath) {
				// Read file source.
				return grunt.file.read(filepath);
			}).join(grunt.util.normalizelf(options.separator));

			// Handle options.
			src += options.punctuation;

			// Write the destination file.
			grunt.file.write(f.dest, src);

			// Print a success message.
			grunt.log.writeln('File "' + f.dest + '" created.');
		});
	});

};<|endoftext|>


<commit_before>// ==UserScript==
// @name       Twitch Player Plus
// @namespace  http://twitch.tv/ehsankia
// @version    0.4
// @description  Various tweaks to the Twitch HTML5 player UI
// @match      http://www.twitch.tv/*
// @match      http://player.twitch.tv/*
// @copyright  2015+, Ehsan Kia
// ==/UserScript==

var qualityOptions;
var waitForPlayerReadyTimer = setInterval(function() {
    var html5Player = $('div.player');
    if (html5Player.length > 0) {
        setTimeout(applyFixes, 3000);
        clearInterval(waitForPlayerReadyTimer);
    }
}, 1000);

function applyFixes() {
    // Sticky volume slider
    $('.js-volume-container').css('width', '13em');

    // Move quality options to main bar and style appropriately
    qualityOptions = $(".js-quality");
    qualityOptions.insertAfter($('.js-quality-display-contain'));
    qualityOptions.css({
      float: "left",
      margin: "6px 4px",
      color: "white",
      fontWeight: "bold",
      background: "none",
      border: "none",
      boxShadow: "0 0 black",
      appearance: "none",
      "-moz-appearance": "none",
      "-webkit-appearance": "none",
      cursor: "pointer",
    });
    qualityOptions.mouseover(function() {
        $(this).css("color","#a991d4");
        $(this).find("> option").css({
            color: "white",
            background: "black",
            padding: "0 5px",
            marginRight: "-15px",
        });
    }).mouseout(function() {
        $(this).css("color","white");
    });

    // Remove remaining label
    $("span:contains('Video Quality:')").remove();

    // Hide options if there are no transcoders
    setInterval(checkForQualityOptions, 5000);
    // Remove initially, otherwise there's an empty space for a bit
    checkForQualityOptions()

    // Bind F key to fullscreen
    // document.addEventListener("keypress", function(e){
    //     if(e.which === 102) enterFS($('div.player')[0]);
    // });
}

function checkForQualityOptions() {
  var numQualityOptions = $(".js-quality > option").length;
  if (numQualityOptions > 1) {
    qualityOptions.css('display', 'block');
  } else {
    qualityOptions.css('display', 'none');
  }
}

function enterFS(elem) {
  if (elem.requestFullscreen) {
      elem.requestFullscreen();
  } else if (elem.msRequestFullscreen) {
      elem.msRequestFullscreen();
  } else if (elem.mozRequestFullScreen) {
      elem.mozRequestFullScreen();
  } else if (elem.webkitRequestFullscreen) {
      elem.webkitRequestFullscreen();
  }
}

GM_addStyle("select:-moz-focusring { outline: none; border: 0; }");
<commit_msg>Added F hotkey to toggle fullscreen<commit_after>// ==UserScript==
// @name       Twitch Player Plus
// @namespace  http://twitch.tv/ehsankia
// @version    0.5
// @description  Various tweaks to the Twitch HTML5 player UI
// @match      http://www.twitch.tv/*
// @match      http://player.twitch.tv/*
// @grant      GM_addStyle
// @copyright  2015+, Ehsan Kia
// ==/UserScript==

var html5Player;
var qualityOptions;
var waitForPlayerReadyTimer = setInterval(function() {
    html5Player = $('div.player');
    if (html5Player.length > 0) {
        setTimeout(applyFixes, 3000);
        clearInterval(waitForPlayerReadyTimer);
    }
}, 1000);

function applyFixes() {
    // Sticky volume slider
    $('.js-volume-container').css('width', '13em');

    // Move quality options to main bar and style appropriately
    qualityOptions = $(".js-quality");
    qualityOptions.insertAfter($('.js-quality-display-contain'));
    qualityOptions.css({
      float: "left",
      margin: "6px 4px",
      color: "white",
      fontWeight: "bold",
      background: "none",
      border: "none",
      boxShadow: "0 0 black",
      appearance: "none",
      "-moz-appearance": "none",
      "-webkit-appearance": "none",
      cursor: "pointer",
    });
    qualityOptions.mouseover(function() {
        $(this).css("color","#a991d4");
        $(this).find("> option").css({
            color: "white",
            background: "black",
            padding: "0 5px",
            marginRight: "-15px",
        });
    }).mouseout(function() {
        $(this).css("color","white");
    });

    // Remove remaining label
    $("span:contains('Video Quality:')").remove();

    // Hide options if there are no transcoders
    setInterval(checkForQualityOptions, 5000);
    // Remove initially, otherwise there's an empty space for a bit
    checkForQualityOptions()

    // Bind F key to toggle fullscreen
    html5Player.keypress(function(e){
        if(e.which === 102) $('.js-control-fullscreen').click();
    });
}

function checkForQualityOptions() {
  var numQualityOptions = $(".js-quality > option").length;
  if (numQualityOptions > 1) {
    qualityOptions.css('display', 'block');
  } else {
    qualityOptions.css('display', 'none');
  }
}

GM_addStyle("select:-moz-focusring { outline: none; }");
<|endoftext|>


<commit_before>var manifests = "./manifest.json";
jQuery(function() {
  if (typeof manifests === undefined || manifests === null || manifests === "") {
    manifests = "./manifest.json";
    // window.console.log(encodeURIComponent(manifests));
  }
});

$(function() {
  Mirador({
    "id": "mirador-viewer",
    "mainMenuSettings": {
      'show': true
    },
    "data": [
      { "manifestUri": manifests, "location": "University of Maryland" }
    ],
    'layout': '1x1',
    "windowObjects": [
      {
        "loadedManifest": manifests,
        "viewType": "ImageView",
        "displayLayout": false,
        "sidePanel" : true, //whether or not to make the side panel available in this window
        //control what is available in the side panel. if "sidePanel" is false, these options won't be applied
        "sidePanelOptions" : {
          "toc" : true,
          "annotations" : true
        },
        "bottomPanelVisible": true, //whether or not to make the bottom panel visible in this window on load. This setting is dependent on bottomPanel being true
        "canvasControls": { // The types of controls available to be displayed on a canvas
          "annotations": {
            "annotationLayer": true, //whether or not to make annotation layer available in this window
            "annotationCreation": false,
            /*whether or not to make annotation creation available in this window,
                         only valid if annotationLayer is set to True and an annotationEndpoint is defined.
                         This setting does NOT affect whether or not a user can edit an individual annotation that has already been created.*/
            "annotationState": 'on', //[_'off'_, 'on'] whether or not to turn on the annotation layer on window load
            // "annotationRefresh" : false, //whether or not to display the refresh icon for annotations
          },
          // "imageManipulation": {
          //   "manipulationLayer": false,
          // }
        }
      }
    ],

    'mainMenuSettings': {
      'show': true,
      'buttons' : {
        'bookmark' : false,
        'layout' : false,
        'options' : false,
        'fullScreenViewer': false
      }
      //'height': 25,
      //'width': '100%'
    },

    'workspacePanelSettings': {
      'maxRows': 3,
      'maxColumns': 3,
      'preserveWindows': true
    },

    'drawingToolsSettings': {
      // Additional tool settings.
      // 'Pin': {
      // },
      'selectedColor': 'yellow',
      // 'doubleClickReactionTime': 300,
      'strokeColor': 'rgba(255, 255, 255, 0)',
      'fillColor': 'yellow',
      'fillColorAlpha': 0.1,
      'hoverColor': 'rgba(255, 255, 255, 0)',
      'hoverFillColor': 'yellow',
      'hoverFillColorAlpha': 0.5,
      // 'shapeHandleSize':10,
      // 'fixedShapeSize':10,
      // 'newlyCreatedShapeStrokeWidthFactor': 0,
      'annotationTypeStyles': {
        'umd:searchResult': {
          'strokeColor': 'rgba(255, 255, 0, 0.6)',
          'fillColor': 'yellow',
          'fillColorAlpha': 0.4,
          'hoverColor': 'rgba(255, 255, 0, 0.6)',
          'hoverFillColor': 'yellow',
          'hoverFillColorAlpha': 0.6,
          'hideTooltip': true
        },
        'umd:articleSegment': {
          'strokeColor': 'rgba(255, 255, 255, 0.2)',
          'fillColor': 'green',
          'fillColorAlpha': 0.1,
          'hoverColor': 'rgba(255, 255, 255, 0.2)',
          'hoverFillColor': 'green',
          'hoverFillColorAlpha': 0.4,
          'hideTooltip': true
        },
        'umd:Article': {
          'strokeColor': 'rgba(255, 255, 255, 0)',
          'fillColor': 'green',
          'fillColorAlpha': 0.1,
          'hoverColor': 'rgba(255, 255, 255, 0.2)',
          'hoverFillColor': 'green',
          'hoverFillColorAlpha': 0.4
        },
        'umd:Line': {
          'strokeColor': 'rgba(255, 255, 0, 0.05)',
          'fillColor': 'yellow',
          'fillColorAlpha': 0.01,
          'hoverColor': 'rgba(255, 255, 0, 0.4)',
          'hoverFillColor': 'yellow',
          'hoverFillColorAlpha': 0.4
        }
      }
    },
  });
});
<commit_msg>Tweak Mirador settings.<commit_after>/* site */
$(function() {
  var manifest = "./manifest.json";

  Mirador({
    'id': 'mirador-viewer',
    'data': [
      { 'manifestUri': manifest, 'location': 'University of Maryland' }
    ],
    'layout': '1x1',
    'windowObjects': [
      {
        'loadedManifest': manifest,
        'canvasID': 'http://iiif-sandbox.lib.umd.edu/manifests/sn83045081/1902-01-15/2',
        'viewType': 'ImageView',
        'displayLayout': false,
        'sidePanel' : false, //whether or not to make the side panel available in this window
        //control what is available in the side panel. if 'sidePanel' is false, these options won't be applied
        // 'sidePanelOptions' : {
        //   'toc' : true,
        //   'annotations' : true
        // },
        'bottomPanelVisible': true, //whether or not to make the bottom panel visible in this window on load. This setting is dependent on bottomPanel being true
        'canvasControls': { // The types of controls available to be displayed on a canvas
          'annotations': {
            'annotationLayer': true, //whether or not to make annotation layer available in this window
            'annotationCreation': false,
            /*whether or not to make annotation creation available in this window,
                         only valid if annotationLayer is set to True and an annotationEndpoint is defined.
                         This setting does NOT affect whether or not a user can edit an individual annotation that has already been created.*/
            'annotationState': 'on', //[_'off'_, 'on'] whether or not to turn on the annotation layer on window load
            // 'annotationRefresh' : false, //whether or not to display the refresh icon for annotations
          },
          'imageManipulation': {
            'manipulationLayer': true,
          }
        }
      }
    ],

    'mainMenuSettings': {
      'show': false,
      'buttons' : {
        'bookmark' : false,
        'layout' : false,
        'options' : false,
        'fullScreenViewer': false
      }
      //'height': 25,
      //'width': '100%'
    },

    'workspacePanelSettings': {
      'maxRows': 3,
      'maxColumns': 3,
      'preserveWindows': true
    },

    'drawingToolsSettings': {
      'selectedColor': 'yellow',
      // 'doubleClickReactionTime': 300,
      'strokeColor': 'rgba(255, 255, 255, 0)',
      'fillColor': 'yellow',
      'fillColorAlpha': 0.1,
      'hoverColor': 'rgba(255, 255, 255, 0)',
      'hoverFillColor': 'yellow',
      'hoverFillColorAlpha': 0.5,
      //customize anno styling
      'annotationTypeStyles': {
        'umd:searchResult': {
          'strokeColor': 'rgba(255, 255, 0, 0.6)',
          'fillColor': 'yellow',
          'fillColorAlpha': 0.4,
          'hoverColor': 'rgba(255, 255, 0, 0.6)',
          'hoverFillColor': 'yellow',
          'hoverFillColorAlpha': 0.6,
          'hideTooltip': true
        },
        'umd:articleSegment': {
          'strokeColor': 'rgba(255, 255, 255, 0.2)',
          'fillColor': 'green',
          'fillColorAlpha': 0.1,
          'hoverColor': 'rgba(255, 255, 255, 0.2)',
          'hoverFillColor': 'green',
          'hoverFillColorAlpha': 0.4,
          'hideTooltip': true
        },
        'umd:Article': {
          'strokeColor': 'rgba(255, 255, 255, 0)',
          'fillColor': 'green',
          'fillColorAlpha': 0.1,
          'hoverColor': 'rgba(255, 255, 255, 0.2)',
          'hoverFillColor': 'green',
          'hoverFillColorAlpha': 0.4
        },
        'umd:Line': {
          'strokeColor': 'rgba(255, 255, 0, 0.05)',
          'fillColor': 'yellow',
          'fillColorAlpha': 0.01,
          'hoverColor': 'rgba(255, 255, 0, 0.4)',
          'hoverFillColor': 'yellow',
          'hoverFillColorAlpha': 0.4
        }
      }
    },
  });
});
<|endoftext|>


<commit_before>/*
 * grunt-file-info
 * https://github.com/hansifer/grunt-file-info
 *
 * Copyright (c) 2013 Hans Meyer
 * Licensed under the MIT license.
 */

'use strict';

module.exports = function(grunt) {

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

  // TODO: add support for optional 'find' option to allow specification of regexp of text to replace. In the absence of 'find' option, use 'text' to generate this regexp. In 'text' value, use '{{= pass(n) }}' to specify passthru values, where n is the index of a capturing group from the 'find' (regexp) value.
  grunt.registerMultiTask('file_info', 'Generate project file stats and automate template-driven updates of such to project documentation.', function() {
    var that = this;

    this.data.filesSrc = this.filesSrc;

    // add template functions
    this.data.size = size;
    this.data.gzipSize = gzipSize;
    this.data.sizeText = sizeText;
    this.data.spaceSavings = spaceSavings;
    this.data.modified = modified;
    this.data.modifiedAgo = modifiedAgo;
    this.data.filename = filename;
    this.data.filetype = filetype;
    this.data.pass = function(i) {
      if (!arguments.length) {
        i = that.data.i;
      }

      return (grunt.util._.isUndefined(i) ? '' : grunt.util._.trim(that.data.currentValues[i]));
    };

    grunt.template.addDelimiters('doubleBrace', '{{', '}}');

    if (this.options().inject) {
      var defaultInjectReport = !('injectReport' in this.options()) || !! this.options().injectReport;
      if (grunt.util._.isArray(this.options().inject)) {
        this.options().inject.forEach(function(injectConfig) {
          processInject.call(that, injectConfig, defaultInjectReport);
        });
      } else {
        processInject.call(this, this.options().inject, defaultInjectReport);
      }
    }

    if (!('stdout' in this.options()) || this.options().stdout === true) {
      grunt.log.writeln(grunt.util.linefeed + (('  ' + this.target + ' file sizes:').cyan) + grunt.util.linefeed);

      var colWidth = 0;
      this.filesSrc.forEach(function(filepath) {
        colWidth = Math.max(filepath.length, colWidth);
      });

      this.filesSrc.forEach(function(filepath) {
        grunt.log.writeln('  ' + grunt.util._.rpad(filepath, colWidth + 1).grey + sizeText(size(filepath), 8).grey + (' (' + sizeText(gzipSize(filepath)) + ' gzipped)').grey);
      });
    } else if (this.options().stdout) {
      grunt.log.write(grunt.template.process(this.options().stdout, {
        data: this.data,
        delimiters: 'doubleBrace'
      }));
    }
  });

  function processInject(injectConfig, report) {
    var that = this;

    if ('report' in injectConfig) {
      report = injectConfig.report;
    }

    if (grunt.util._.isArray(injectConfig.dest)) {
      injectConfig.dest.forEach(function(dest) {
        processInjectDest.call(that, dest, injectConfig.text, report);
      });
    } else {
      processInjectDest.call(this, injectConfig.dest, injectConfig.text, report);
    }
  }

  function processInjectDest(dest, text, report) {
    function writeStatus(str) {
      report && grunt.log.writeln(grunt.util.linefeed + '  ' + str);
    }

    var newValues, i, fileContents, output;
    var fieldDiffs = {}; // for values that either increased or decreased, add a property where name is fieldIndex and value is bytes delta (positive if up, negative if down)

    var reTemplateField = new RegExp('\\{\\{.*?\\}\\}', 'g');

    if (dest && text) {
      if (grunt.file.exists(dest)) {
        fileContents = grunt.file.read(dest);

        // var reText = '###Size[^]+?Original.*?\\|([^\\|]*).*?\\|([^\\|]*).*?$[^]+?Minified.*?\\|([^\\|]*).*?\\|([^\\|]*).*?$[^]+?Gzipped.*?\\|([^\\|]*).*?\\|([^\\|]*).*?$'
        var re = new RegExp( /*this.options().find || */ text.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&').replace(/\\{\\{.*?\\}\\}/g, '(.*)'));

        // console.log(re.source);

        if (this.data.currentValues = re.exec(fileContents)) { // first current field value is at index 1
          // console.log('Matched portion of file: ', '*' + this.data.currentValues[0] + '*');

          var templateFields = text.match(reTemplateField);
          // console.log(templateFields);

          // translate current values through grunt templating to gen new values

          newValues = [undefined]; // pad so that first new value is at index 1
          for (i = 0; i < templateFields.length; i++) {
            this.data.i = i + 1;
            newValues.push(grunt.template.process(templateFields[i], {
              data: this.data,
              delimiters: 'doubleBrace'
            }));
          }

          var delta;
          for (; i > 0; i--) {
            //  console.log(grunt.util._.lpad(grunt.util._.trim(this.data.currentValues[i]), 8), ' -->', grunt.util._.lpad(grunt.util._.trim(newValues[i]), 8));
            if (delta = fieldDiff(this.data.currentValues[i], newValues[i])) {
              fieldDiffs[i] = delta;
            }
          }

          if (Object.keys(fieldDiffs).length) {

            // output to file

            i = 1;
            output = text.replace(reTemplateField, function() {
              return newValues[i++];
            });
            grunt.file.write(dest, fileContents.replace(re, output));

            // output to command line

            if (report) {
              var reLF = new RegExp(grunt.util.linefeed + '|^', 'g');

              grunt.log.writeln();
              grunt.log.writeln(('  Updating ' + this.target + ' stats in ' + dest + ' from:').cyan);

              grunt.log.writeln(this.data.currentValues[0].replace(reLF, grunt.util.linefeed + '  ')); // indent output

              grunt.log.writeln();
              grunt.log.writeln('  to:'.cyan);

              // have to gen output string again for color-coded cli output

              i = 1;
              output = text.replace(reTemplateField, function() {
                var ret = newValues[i];

                if (fieldDiffs[i] > 0) {
                  ret = ret.red;
                } else if (fieldDiffs[i] < 0) {
                  ret = ret.green;
                }

                i++;

                return ret;
              });
              grunt.log.writeln(output.replace(reLF, grunt.util.linefeed + '  ')); // indent output
            }
          } else {
            writeStatus(('No changes detected in file ' + dest).green);
          }
        } else {
          writeStatus(('No matching text found in file ' + dest).red);
        }
      } else {
        delete this.data.i;
        grunt.file.write(dest, grunt.template.process(text, {
          data: this.data,
          delimiters: 'doubleBrace'
        }));

        writeStatus(('Created file ' + dest).green);
      }
    } else {
      writeStatus(('file_info config error for target ' + this.target + ': inject requires dest and text').red);
    }
  }

  function size(filepath) {
    return require('fs').lstatSync(filepath).size;
    // return grunt.file.read(filepath).length;
  }

  function gzipSize(filepath) {
    return require('zlib-browserify').gzipSync(grunt.file.read(filepath)).length;
  }

  // return left-padded integer-right-aligned size string

  function sizeText(bytes, lpadding) {
    var sizeStr;

    if (bytes > 999999) {
      sizeStr = '' + Math.round(bytes / 100000) / 10;

      if (lpadding) {
        if (/\./.test(sizeStr)) {
          lpadding += 2;
        }
        lpadding -= sizeStr.length;
        lpadding -= 4;
      }
      sizeStr += ' MB';
    } else if (bytes > 999) {
      sizeStr = '' + Math.round(bytes / 100) / 10;

      if (lpadding) {
        if (/\./.test(sizeStr)) {
          lpadding += 2;
        }
        lpadding -= sizeStr.length;
      }
      sizeStr += ' kB';
    } else {
      sizeStr = '' + bytes;

      if (lpadding) {
        lpadding -= sizeStr.length;
        lpadding += 4;
      }
      sizeStr += ' bytes';
    }

    return (lpadding ? grunt.util.repeat(lpadding, ' ') : '') + sizeStr;
  }

  function spaceSavings(filepath) {
    return Math.round((1 - gzipSize(filepath) / size(filepath)) * 10000) / 100;
  }

  function modified(filepath) {
    return require('fs').lstatSync(filepath).mtime;
  }

  function modifiedAgo(filepath) {
    return require('moment')(require('fs').lstatSync(filepath).mtime).fromNow();
  }

  function filename(filepath) {
    var arr = /\/?([^\/]+)$/.exec(filepath);

    if (arr && arr.length > 1) {
      return arr[1];
    }

    return '';
  }

  function filetype(filepath) {
    var arr = /\.([^\.]+)$/.exec(filename(filepath));

    if (arr && arr.length > 1) {
      return arr[1];
    }

    return '';
  }

  // if comparing file size fields, return file size delta in bytes. eg, fieldDiff('2 kB', '2 bytes') -- > -1998
  // else, return 1 if strings differ, 0 if same

  function fieldDiff(str1, str2) {
    var float1, float2;

    if (!grunt.util._.isUndefined(str1) && str1 !== str2) {
      if (isFileSize(str1) && isFileSize(str2)) {
        float1 = parseByteSize(str1);

        if (float1 || float1 === 0) {
          if (str2) {
            float2 = parseByteSize(str2);

            if (float2 || float2 === 0) {
              return float2 - float1;
            }
          }

          return float1;
        }
      }

      return 1;
    }

    return 0;
  }

  function isFileSize(str) {
    return (/[\d\s](bytes|kB|MB)$/i.test(str));
  }

  function parseByteSize(str) {
    var ret = parseFloat(str) || 0;

    if (/kB/i.test(str)) {
      ret = ret * 1000;
    } else if (/MB/i.test(str)) {
      ret = ret * 1000 * 1000;
    }

    return ret;
  }
};
<commit_msg>Change "file sizes" to "stats" in stdout header<commit_after>/*
 * grunt-file-info
 * https://github.com/hansifer/grunt-file-info
 *
 * Copyright (c) 2013 Hans Meyer
 * Licensed under the MIT license.
 */

'use strict';

module.exports = function(grunt) {

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

  // TODO: add support for optional 'find' option to allow specification of regexp of text to replace. In the absence of 'find' option, use 'text' to generate this regexp. In 'text' value, use '{{= pass(n) }}' to specify passthru values, where n is the index of a capturing group from the 'find' (regexp) value.
  grunt.registerMultiTask('file_info', 'Generate project file stats and automate template-driven updates of such to project documentation.', function() {
    var that = this;

    this.data.filesSrc = this.filesSrc;

    // add template functions
    this.data.size = size;
    this.data.gzipSize = gzipSize;
    this.data.sizeText = sizeText;
    this.data.spaceSavings = spaceSavings;
    this.data.modified = modified;
    this.data.modifiedAgo = modifiedAgo;
    this.data.filename = filename;
    this.data.filetype = filetype;
    this.data.pass = function(i) {
      if (!arguments.length) {
        i = that.data.i;
      }

      return (grunt.util._.isUndefined(i) ? '' : grunt.util._.trim(that.data.currentValues[i]));
    };

    grunt.template.addDelimiters('doubleBrace', '{{', '}}');

    if (this.options().inject) {
      var defaultInjectReport = !('injectReport' in this.options()) || !! this.options().injectReport;
      if (grunt.util._.isArray(this.options().inject)) {
        this.options().inject.forEach(function(injectConfig) {
          processInject.call(that, injectConfig, defaultInjectReport);
        });
      } else {
        processInject.call(this, this.options().inject, defaultInjectReport);
      }
    }

    if (!('stdout' in this.options()) || this.options().stdout === true) {
      grunt.log.writeln(grunt.util.linefeed + (('  ' + this.target + ' stats:').cyan) + grunt.util.linefeed);

      var colWidth = 0;
      this.filesSrc.forEach(function(filepath) {
        colWidth = Math.max(filepath.length, colWidth);
      });

      this.filesSrc.forEach(function(filepath) {
        grunt.log.writeln('  ' + grunt.util._.rpad(filepath, colWidth + 1).grey + sizeText(size(filepath), 8).grey + (' (' + sizeText(gzipSize(filepath)) + ' gzipped)').grey);
      });
    } else if (this.options().stdout) {
      grunt.log.write(grunt.template.process(this.options().stdout, {
        data: this.data,
        delimiters: 'doubleBrace'
      }));
    }
  });

  function processInject(injectConfig, report) {
    var that = this;

    if ('report' in injectConfig) {
      report = injectConfig.report;
    }

    if (grunt.util._.isArray(injectConfig.dest)) {
      injectConfig.dest.forEach(function(dest) {
        processInjectDest.call(that, dest, injectConfig.text, report);
      });
    } else {
      processInjectDest.call(this, injectConfig.dest, injectConfig.text, report);
    }
  }

  function processInjectDest(dest, text, report) {
    function writeStatus(str) {
      report && grunt.log.writeln(grunt.util.linefeed + '  ' + str);
    }

    var newValues, i, fileContents, output;
    var fieldDiffs = {}; // for values that either increased or decreased, add a property where name is fieldIndex and value is bytes delta (positive if up, negative if down)

    var reTemplateField = new RegExp('\\{\\{.*?\\}\\}', 'g');

    if (dest && text) {
      if (grunt.file.exists(dest)) {
        fileContents = grunt.file.read(dest);

        // var reText = '###Size[^]+?Original.*?\\|([^\\|]*).*?\\|([^\\|]*).*?$[^]+?Minified.*?\\|([^\\|]*).*?\\|([^\\|]*).*?$[^]+?Gzipped.*?\\|([^\\|]*).*?\\|([^\\|]*).*?$'
        var re = new RegExp( /*this.options().find || */ text.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&').replace(/\\{\\{.*?\\}\\}/g, '(.*)'));

        // console.log(re.source);

        if (this.data.currentValues = re.exec(fileContents)) { // first current field value is at index 1
          // console.log('Matched portion of file: ', '*' + this.data.currentValues[0] + '*');

          var templateFields = text.match(reTemplateField);
          // console.log(templateFields);

          // translate current values through grunt templating to gen new values

          newValues = [undefined]; // pad so that first new value is at index 1
          for (i = 0; i < templateFields.length; i++) {
            this.data.i = i + 1;
            newValues.push(grunt.template.process(templateFields[i], {
              data: this.data,
              delimiters: 'doubleBrace'
            }));
          }

          var delta;
          for (; i > 0; i--) {
            //  console.log(grunt.util._.lpad(grunt.util._.trim(this.data.currentValues[i]), 8), ' -->', grunt.util._.lpad(grunt.util._.trim(newValues[i]), 8));
            if (delta = fieldDiff(this.data.currentValues[i], newValues[i])) {
              fieldDiffs[i] = delta;
            }
          }

          if (Object.keys(fieldDiffs).length) {

            // output to file

            i = 1;
            output = text.replace(reTemplateField, function() {
              return newValues[i++];
            });
            grunt.file.write(dest, fileContents.replace(re, output));

            // output to command line

            if (report) {
              var reLF = new RegExp(grunt.util.linefeed + '|^', 'g');

              grunt.log.writeln();
              grunt.log.writeln(('  Updating ' + this.target + ' stats in ' + dest + ' from:').cyan);

              grunt.log.writeln(this.data.currentValues[0].replace(reLF, grunt.util.linefeed + '  ')); // indent output

              grunt.log.writeln();
              grunt.log.writeln('  to:'.cyan);

              // have to gen output string again for color-coded cli output

              i = 1;
              output = text.replace(reTemplateField, function() {
                var ret = newValues[i];

                if (fieldDiffs[i] > 0) {
                  ret = ret.red;
                } else if (fieldDiffs[i] < 0) {
                  ret = ret.green;
                }

                i++;

                return ret;
              });
              grunt.log.writeln(output.replace(reLF, grunt.util.linefeed + '  ')); // indent output
            }
          } else {
            writeStatus(('No changes detected in file ' + dest).green);
          }
        } else {
          writeStatus(('No matching text found in file ' + dest).red);
        }
      } else {
        delete this.data.i;
        grunt.file.write(dest, grunt.template.process(text, {
          data: this.data,
          delimiters: 'doubleBrace'
        }));

        writeStatus(('Created file ' + dest).green);
      }
    } else {
      writeStatus(('file_info config error for target ' + this.target + ': inject requires dest and text').red);
    }
  }

  function size(filepath) {
    return require('fs').lstatSync(filepath).size;
    // return grunt.file.read(filepath).length;
  }

  function gzipSize(filepath) {
    return require('zlib-browserify').gzipSync(grunt.file.read(filepath)).length;
  }

  // return left-padded integer-right-aligned size string

  function sizeText(bytes, lpadding) {
    var sizeStr;

    if (bytes > 999999) {
      sizeStr = '' + Math.round(bytes / 100000) / 10;

      if (lpadding) {
        if (/\./.test(sizeStr)) {
          lpadding += 2;
        }
        lpadding -= sizeStr.length;
        lpadding -= 4;
      }
      sizeStr += ' MB';
    } else if (bytes > 999) {
      sizeStr = '' + Math.round(bytes / 100) / 10;

      if (lpadding) {
        if (/\./.test(sizeStr)) {
          lpadding += 2;
        }
        lpadding -= sizeStr.length;
      }
      sizeStr += ' kB';
    } else {
      sizeStr = '' + bytes;

      if (lpadding) {
        lpadding -= sizeStr.length;
        lpadding += 4;
      }
      sizeStr += ' bytes';
    }

    return (lpadding ? grunt.util.repeat(lpadding, ' ') : '') + sizeStr;
  }

  function spaceSavings(filepath) {
    return Math.round((1 - gzipSize(filepath) / size(filepath)) * 10000) / 100;
  }

  function modified(filepath) {
    return require('fs').lstatSync(filepath).mtime;
  }

  function modifiedAgo(filepath) {
    return require('moment')(require('fs').lstatSync(filepath).mtime).fromNow();
  }

  function filename(filepath) {
    var arr = /\/?([^\/]+)$/.exec(filepath);

    if (arr && arr.length > 1) {
      return arr[1];
    }

    return '';
  }

  function filetype(filepath) {
    var arr = /\.([^\.]+)$/.exec(filename(filepath));

    if (arr && arr.length > 1) {
      return arr[1];
    }

    return '';
  }

  // if comparing file size fields, return file size delta in bytes. eg, fieldDiff('2 kB', '2 bytes') -- > -1998
  // else, return 1 if strings differ, 0 if same

  function fieldDiff(str1, str2) {
    var float1, float2;

    if (!grunt.util._.isUndefined(str1) && str1 !== str2) {
      if (isFileSize(str1) && isFileSize(str2)) {
        float1 = parseByteSize(str1);

        if (float1 || float1 === 0) {
          if (str2) {
            float2 = parseByteSize(str2);

            if (float2 || float2 === 0) {
              return float2 - float1;
            }
          }

          return float1;
        }
      }

      return 1;
    }

    return 0;
  }

  function isFileSize(str) {
    return (/[\d\s](bytes|kB|MB)$/i.test(str));
  }

  function parseByteSize(str) {
    var ret = parseFloat(str) || 0;

    if (/kB/i.test(str)) {
      ret = ret * 1000;
    } else if (/MB/i.test(str)) {
      ret = ret * 1000 * 1000;
    }

    return ret;
  }
};
<|endoftext|>


<commit_before>/**
 * @since 2017-04-15 13:59
 * @author chenyiqin
 */

var testObj = {
    a: 1,
    b: 2,
    c: 3,
    shiyunjie:29
};
<commit_msg>feat:修改cyq1<commit_after>/**
 * @since 2017-04-15 13:59
 * @author chenyiqin
 */

var testObj = {
    a: 1,
    b: 2,
    c: 3,
    shiyunjie:29,
    ttes:21
};
<|endoftext|>


<commit_before>'use strict';

var useragentToSauce = {
	'chrome/49': ['chrome', '49', 'Windows 7'],
	'chrome/42': ['chrome', '42', 'Windows 7'],
	'chrome/38': ['chrome', '38', 'OSX 10.11'],
	'chrome/35': ['chrome', '35', 'Windows 7'],
	'chrome/30': ['chrome', '30', 'Windows 7'],
	'firefox/42': ['firefox', '42', 'Linux'],
	'firefox/33': ['firefox', '33', 'Linux'],
	'firefox/30': ['firefox', '30', 'OSX 10.11'],
	'firefox/20': ['firefox', '20', 'Linux'],
	'ie/12': ['microsoftedge', '20.10240', 'Windows 10'],
	'ie/11': ['internet explorer', '11', 'Windows 10'],
	'ie/10': ['internet explorer', '10', 'Windows 7'],
	'ie/9': ['internet explorer', '9', 'Windows 7'],
	'ie/8': ['internet explorer', '8', 'Windows XP'],
	'ie/7': ['internet explorer', '7', 'Windows XP'],
	'ie/6': ['internet explorer', '6', 'Windows XP'],
	'safari/9': ['safari', '9.0', 'OSX 10.11'],
	'safari/8': ['safari', '8.0', 'OSX 10.10'],
	'safari/5.1': ['safari', '5.1', 'Windows 7'],
	'android/4.4': ['android', '4.4', 'linux', 'Android Emulator'],
	'android/4.3': ['android', '4.3', 'linux', 'Android Emulator'],
	'android/4.2': ['android', '4.2', 'linux', 'Android Emulator'],
	'android/4.1': ['android', '4.1', 'linux', 'Android Emulator'],
	'ios_saf/9.1': ['iphone', '9.1', 'OS X 10.10', 'iPhone 6'],
	'ios_saf/9.2': ['iphone', '9.2', 'OS X 10.10', 'iPhone 6 Plus']
};

var promiseTimer = function(time) {
	return new Promise(function (resolve) {
		setTimeout(function() {
			resolve();
		}, time);
	});
};

var toArray = function (obj) {
	if (!obj) return [];
	return Array.isArray(obj) ? obj : Object.keys(obj).map(function(k) { return obj[k]; });
};

module.exports = function(grunt) {

	var wd = require('wd');
	var Batch = require('batch');
	var SauceTunnel = require('sauce-tunnel');
	var mkdirp = require('mkdirp');
	var path = require('path');
	var fs = require('fs');
	var testResultsPath = path.join(__dirname, '../test/results');
	var UA = require('../lib/UA');

	var pollTick = 1000;
	var retryLimit = 50;
	var sauceConnectPort = 4445;

	grunt.registerMultiTask('saucelabs', 'Perform tests on Sauce', function() {
		var gruntDone = this.async();
		var nameSuffix = process.env.TRAVIS_BUILD_NUMBER ? "-travis-build:" + process.env.TRAVIS_BUILD_NUMBER : ':' + (new Date()).toUTCString();
		var tunnel;
		var tunnelId = 'SauceTunnel' + nameSuffix;

		if (!process.env.SAUCE_USER_NAME || !process.env.SAUCE_API_KEY) {
			gruntDone(new Error('SAUCE_USER_NAME and SAUCE_API_KEY must be set in the environment'));
		}

		var options = this.options({
			name: 'grunt-test' + nameSuffix,
			build: null,
			username: process.env.SAUCE_USER_NAME,
			key: process.env.SAUCE_API_KEY,
			browsers: [],
			url: {},
			concurrency: 3,
			tags: [],
			video: true,
			screenshots: true,
			cibuild: false
		});

		options.browsers = options.browsers.map(function(b) {
			var ua = b.split('/');
			var sauce = useragentToSauce[b];
			console.log(useragentToSauce, b, sauce);
			var def = {
				browserName: ua[0],
				browserVersion: ua[1],
				sauce: {
					browserName: sauce[0],
					version: sauce[1],
					platform: sauce[2]
				}
			};
			if (sauce[3]) def['sauce']['deviceName'] = sauce[3];
			return def;
		});

		var batch = new Batch();
		batch.concurrency(options.concurrency);
		batch.throws(false);

		// Create a new job to pass into the Batch runner, returns a function
		function newTestJob(url, urlName, ua) {
			return function testJob() {

				// Variadic like console.log(...);
				function log() {
					var leader = (new Date()).toString() + ' ' + ua.browserName + "/" + ua.browserVersion + ": ";
					var args = Array.prototype.slice.call(arguments);
					args.unshift(leader);
					grunt.log.writeln.apply(grunt.log, args);
				}

				var browser = wd.promiseRemote("127.0.0.1", sauceConnectPort, options.username, options.key);
				var conf = Object.assign({
					"name": options.name,
					"record-video": options.video,
					"record-screenshots": options.screenshots,
					"tunnel-identifier": tunnelId
				}, ua.sauce);
				if (options.tags && options.tags.length) conf['tags'] = options.tags;
				if (options.build) conf['build'] = options.build;

				function waitOnResults() {
					return browser.eval('window.global_test_results || window.remainingCount');
				}
				function processResults(data) {
					var remainingCount = null;
					var retryCount = 0;

					// Result polling code
					if (typeof data !== 'object' || data === null) {
						if (retryCount > retryLimit) {
							log('Timed out');
							browser.quit();
							throw new Error('Timeout waiting on ' + ua.browserName + '/' +ua.browserVersion);
						}

						if (typeof data === 'number' && data !== remainingCount) {
							remainingCount = data;
							retryCount = 0;
							log(remainingCount + ' test pages remaining');
						} else {
							retryCount++;
							log('no changes, waiting');
						}

						// Recurse waitOnResults and processResults
						return promiseTimer(pollTick).then(waitOnResults).then(processResults);
					}

					log("results received (" + data.passed + " passed, " + data.failed + " failed), closing remote browser");

					// Close the browser as soon as we have the
					// results.  No need to keep it hanging around
					// until the rest of the job is complete.
					browser.quit();
					log("Browser closing");

					return {
						id: browser.sessionID,
						ua: ua,
						urlName: urlName,
						results: data
					};
				}

				log("requested browser");

				return browser.init(conf).then(function() {
					log("browser launched");

					return browser.get(url).then(function() {
						return browser.refresh();
					}).then(function() {
						log("kicked");

						// Once refreshed, wait `pollTick` before continuing
						return promiseTimer(pollTick);
					}).then(waitOnResults).then(processResults);
				});
			};
		}

		function readResultsFile(cb) {
			var file = path.join(testResultsPath, 'results.json');
			mkdirp(testResultsPath, function(err) {
				if (err) {
					grunt.warn('Cannot write test results - '+testResultsPath+' cannot be written');
					cb({});
				} else {
					fs.readFile(file, function(err, filedata) {
						if (err) {
							// If ENOENT, continue as writeFile will create it later
							if (err.code !== 'ENOENT') {
								throw err;
							}
						}
						cb((filedata && filedata.length) ? JSON.parse(filedata) : {});
					});
				}
			});
		}

		function writeResultsFile(data) {
			var file = path.join(testResultsPath, 'results.json');
			var output = JSON.stringify(data, null, 2);
			console.log('Wrote '+output.length+' bytes to result file');
			fs.writeFile(file, output, function(err) {
				if (err) {
					grunt.warn(err);
					throw err;
				}
			});
		}

		grunt.log.writeln("travis_fold:start:Sauce test progress");
		grunt.log.writeln("Sauce job name: "+options.name);
		readResultsFile(function(testResults) {
			var noop = true;

			options.browsers.forEach(function(ua) {
				Object.keys(options.urls).forEach(function(urlName) {
					var url = options.urls[urlName], state;
					try {
						testResults[ua.browserName][ua.browserVersion][urlName].length;
						state = 'skipped - data already in result file';
					} catch(e) {
						batch.push(function(done) {
							var testJob = newTestJob(url, urlName, ua);
							testJob().then(function(data) {
								done(null, data);
							}).catch(function(e) {
								console.log(e.stack || e);
								done(e);
							});
						});

						state = 'queued';
						noop = false;
					}
					grunt.log.writeln(ua.browserName + '/' + ua.browserVersion + ' (' + urlName + ' mode): ' + state);
				});
			});

			if (noop) {
				grunt.log.writeln("Nothing to do");
				return gruntDone();
			}

			grunt.log.writeln('Opening tunnel to Sauce Labs');
			tunnel = new SauceTunnel(options.username, options.key, tunnelId, true);
			tunnel.start(function(status) {

				var cumFailCount = 0;

				grunt.log.writeln("Tunnel Started");
				if (status !== true)  {
					gruntDone(status);
				}

				grunt.log.writeln("Starting test jobs");

				batch.on('progress', function(e) {
					if (e.error) {
						throw e.error;
					}

					if (e.value.ua) {
						if (!testResults[e.value.ua.browserName]) {
							testResults[e.value.ua.browserName] = {};
						}

						if (!testResults[e.value.ua.browserName][e.value.ua.browserVersion]) {
							testResults[e.value.ua.browserName][e.value.ua.browserVersion]	= {};
						}

						testResults[e.value.ua.browserName][e.value.ua.browserVersion][e.value.urlName] = {
							passed: e.value.results.passed,
							failed: e.value.results.failed,
							failingSuites: e.value.results.failingSuites ? Object.keys(e.value.results.failingSuites) : [],
							testedSuites: toArray(e.value.results.testedSuites)
						};
						cumFailCount += e.value.results.failed;
						writeResultsFile(testResults);
					}

					// Pending count appears to have an off by one error
					grunt.log.writeln("Progress (browsers): " + e.complete + ' / ' + e.total + ' (' + (e.pending-1) + ' browsers remaining, ' + cumFailCount + ' test failures so far)');
				});

				batch.end(function(err, jobresults) {
					grunt.log.writeln('All jobs complete');
					tunnel.stop(function() {
						var passingUAs = [];
						var failed = false;
						grunt.log.writeln('Sauce tunnel stopped');
						grunt.log.writeln("travis_fold:end:Sauce test progress");
						grunt.log.writeln("Failed tests:");
						jobresults.forEach(function(job) {
							if (!job.results) {
								grunt.warn('No results reported for '+ job.ua.browserName+'/'+job.ua.browserVersion+' '+job.urlName);
								return true;
							}
							if (job.results.failed) {
								grunt.log.writeln(' - '+job.ua.browserName+' '+job.ua.browserVersion+' (Sauce results: https://saucelabs.com/tests/' + job.id+')');
								if (job.results.failingSuites) {
									Object.keys(job.results.failingSuites).forEach(function(feature) {
										var url = options.urls[job.urlName].replace(/test\/director/, 'test/tests')+'&feature='+feature;
										grunt.log.writeln('    -> '+feature);
										grunt.log.writeln('       '+url);
									});
								}
								failed = true;
							} else {
								passingUAs.push(job.ua.browserName+'/'+job.ua.browserVersion);
							}
						});
						if (!failed) {
							grunt.log.writeln(' - None!');
						}
						if (passingUAs.length) {
							grunt.log.writeln('Browsers tested with no failures: '+passingUAs.join(', '));
						}

						process.nextTick(function() {

							// Always report the grunt task as successful if not running as CI
							if (!options.cibuild) {
								gruntDone(null, true);
							} else {
								gruntDone(!failed, failed);
							}
						});
					});
				});
			});
		});
	});
};
<commit_msg>remove log statement<commit_after>'use strict';

var useragentToSauce = {
	'chrome/49': ['chrome', '49', 'Windows 7'],
	'chrome/42': ['chrome', '42', 'Windows 7'],
	'chrome/38': ['chrome', '38', 'OSX 10.11'],
	'chrome/35': ['chrome', '35', 'Windows 7'],
	'chrome/30': ['chrome', '30', 'Windows 7'],
	'firefox/42': ['firefox', '42', 'Linux'],
	'firefox/33': ['firefox', '33', 'Linux'],
	'firefox/30': ['firefox', '30', 'OSX 10.11'],
	'firefox/20': ['firefox', '20', 'Linux'],
	'ie/12': ['microsoftedge', '20.10240', 'Windows 10'],
	'ie/11': ['internet explorer', '11', 'Windows 10'],
	'ie/10': ['internet explorer', '10', 'Windows 7'],
	'ie/9': ['internet explorer', '9', 'Windows 7'],
	'ie/8': ['internet explorer', '8', 'Windows XP'],
	'ie/7': ['internet explorer', '7', 'Windows XP'],
	'ie/6': ['internet explorer', '6', 'Windows XP'],
	'safari/9': ['safari', '9.0', 'OSX 10.11'],
	'safari/8': ['safari', '8.0', 'OSX 10.10'],
	'safari/5.1': ['safari', '5.1', 'Windows 7'],
	'android/4.4': ['android', '4.4', 'linux', 'Android Emulator'],
	'android/4.3': ['android', '4.3', 'linux', 'Android Emulator'],
	'android/4.2': ['android', '4.2', 'linux', 'Android Emulator'],
	'android/4.1': ['android', '4.1', 'linux', 'Android Emulator'],
	'ios_saf/9.1': ['iphone', '9.1', 'OS X 10.10', 'iPhone 6'],
	'ios_saf/9.2': ['iphone', '9.2', 'OS X 10.10', 'iPhone 6 Plus']
};

var promiseTimer = function(time) {
	return new Promise(function (resolve) {
		setTimeout(function() {
			resolve();
		}, time);
	});
};

var toArray = function (obj) {
	if (!obj) return [];
	return Array.isArray(obj) ? obj : Object.keys(obj).map(function(k) { return obj[k]; });
};

module.exports = function(grunt) {

	var wd = require('wd');
	var Batch = require('batch');
	var SauceTunnel = require('sauce-tunnel');
	var mkdirp = require('mkdirp');
	var path = require('path');
	var fs = require('fs');
	var testResultsPath = path.join(__dirname, '../test/results');
	var UA = require('../lib/UA');

	var pollTick = 1000;
	var retryLimit = 50;
	var sauceConnectPort = 4445;

	grunt.registerMultiTask('saucelabs', 'Perform tests on Sauce', function() {
		var gruntDone = this.async();
		var nameSuffix = process.env.TRAVIS_BUILD_NUMBER ? "-travis-build:" + process.env.TRAVIS_BUILD_NUMBER : ':' + (new Date()).toUTCString();
		var tunnel;
		var tunnelId = 'SauceTunnel' + nameSuffix;

		if (!process.env.SAUCE_USER_NAME || !process.env.SAUCE_API_KEY) {
			gruntDone(new Error('SAUCE_USER_NAME and SAUCE_API_KEY must be set in the environment'));
		}

		var options = this.options({
			name: 'grunt-test' + nameSuffix,
			build: null,
			username: process.env.SAUCE_USER_NAME,
			key: process.env.SAUCE_API_KEY,
			browsers: [],
			url: {},
			concurrency: 3,
			tags: [],
			video: true,
			screenshots: true,
			cibuild: false
		});

		options.browsers = options.browsers.map(function(b) {
			var ua = b.split('/');
			var sauce = useragentToSauce[b];
			var def = {
				browserName: ua[0],
				browserVersion: ua[1],
				sauce: {
					browserName: sauce[0],
					version: sauce[1],
					platform: sauce[2]
				}
			};
			if (sauce[3]) def['sauce']['deviceName'] = sauce[3];
			return def;
		});

		var batch = new Batch();
		batch.concurrency(options.concurrency);
		batch.throws(false);

		// Create a new job to pass into the Batch runner, returns a function
		function newTestJob(url, urlName, ua) {
			return function testJob() {

				// Variadic like console.log(...);
				function log() {
					var leader = (new Date()).toString() + ' ' + ua.browserName + "/" + ua.browserVersion + ": ";
					var args = Array.prototype.slice.call(arguments);
					args.unshift(leader);
					grunt.log.writeln.apply(grunt.log, args);
				}

				var browser = wd.promiseRemote("127.0.0.1", sauceConnectPort, options.username, options.key);
				var conf = Object.assign({
					"name": options.name,
					"record-video": options.video,
					"record-screenshots": options.screenshots,
					"tunnel-identifier": tunnelId
				}, ua.sauce);
				if (options.tags && options.tags.length) conf['tags'] = options.tags;
				if (options.build) conf['build'] = options.build;

				function waitOnResults() {
					return browser.eval('window.global_test_results || window.remainingCount');
				}
				function processResults(data) {
					var remainingCount = null;
					var retryCount = 0;

					// Result polling code
					if (typeof data !== 'object' || data === null) {
						if (retryCount > retryLimit) {
							log('Timed out');
							browser.quit();
							throw new Error('Timeout waiting on ' + ua.browserName + '/' +ua.browserVersion);
						}

						if (typeof data === 'number' && data !== remainingCount) {
							remainingCount = data;
							retryCount = 0;
							log(remainingCount + ' test pages remaining');
						} else {
							retryCount++;
							log('no changes, waiting');
						}

						// Recurse waitOnResults and processResults
						return promiseTimer(pollTick).then(waitOnResults).then(processResults);
					}

					log("results received (" + data.passed + " passed, " + data.failed + " failed), closing remote browser");

					// Close the browser as soon as we have the
					// results.  No need to keep it hanging around
					// until the rest of the job is complete.
					browser.quit();
					log("Browser closing");

					return {
						id: browser.sessionID,
						ua: ua,
						urlName: urlName,
						results: data
					};
				}

				log("requested browser");

				return browser.init(conf).then(function() {
					log("browser launched");

					return browser.get(url).then(function() {
						return browser.refresh();
					}).then(function() {
						log("kicked");

						// Once refreshed, wait `pollTick` before continuing
						return promiseTimer(pollTick);
					}).then(waitOnResults).then(processResults);
				});
			};
		}

		function readResultsFile(cb) {
			var file = path.join(testResultsPath, 'results.json');
			mkdirp(testResultsPath, function(err) {
				if (err) {
					grunt.warn('Cannot write test results - '+testResultsPath+' cannot be written');
					cb({});
				} else {
					fs.readFile(file, function(err, filedata) {
						if (err) {
							// If ENOENT, continue as writeFile will create it later
							if (err.code !== 'ENOENT') {
								throw err;
							}
						}
						cb((filedata && filedata.length) ? JSON.parse(filedata) : {});
					});
				}
			});
		}

		function writeResultsFile(data) {
			var file = path.join(testResultsPath, 'results.json');
			var output = JSON.stringify(data, null, 2);
			console.log('Wrote '+output.length+' bytes to result file');
			fs.writeFile(file, output, function(err) {
				if (err) {
					grunt.warn(err);
					throw err;
				}
			});
		}

		grunt.log.writeln("travis_fold:start:Sauce test progress");
		grunt.log.writeln("Sauce job name: "+options.name);
		readResultsFile(function(testResults) {
			var noop = true;

			options.browsers.forEach(function(ua) {
				Object.keys(options.urls).forEach(function(urlName) {
					var url = options.urls[urlName], state;
					try {
						testResults[ua.browserName][ua.browserVersion][urlName].length;
						state = 'skipped - data already in result file';
					} catch(e) {
						batch.push(function(done) {
							var testJob = newTestJob(url, urlName, ua);
							testJob().then(function(data) {
								done(null, data);
							}).catch(function(e) {
								console.log(e.stack || e);
								done(e);
							});
						});

						state = 'queued';
						noop = false;
					}
					grunt.log.writeln(ua.browserName + '/' + ua.browserVersion + ' (' + urlName + ' mode): ' + state);
				});
			});

			if (noop) {
				grunt.log.writeln("Nothing to do");
				return gruntDone();
			}

			grunt.log.writeln('Opening tunnel to Sauce Labs');
			tunnel = new SauceTunnel(options.username, options.key, tunnelId, true);
			tunnel.start(function(status) {

				var cumFailCount = 0;

				grunt.log.writeln("Tunnel Started");
				if (status !== true)  {
					gruntDone(status);
				}

				grunt.log.writeln("Starting test jobs");

				batch.on('progress', function(e) {
					if (e.error) {
						throw e.error;
					}

					if (e.value.ua) {
						if (!testResults[e.value.ua.browserName]) {
							testResults[e.value.ua.browserName] = {};
						}

						if (!testResults[e.value.ua.browserName][e.value.ua.browserVersion]) {
							testResults[e.value.ua.browserName][e.value.ua.browserVersion]	= {};
						}

						testResults[e.value.ua.browserName][e.value.ua.browserVersion][e.value.urlName] = {
							passed: e.value.results.passed,
							failed: e.value.results.failed,
							failingSuites: e.value.results.failingSuites ? Object.keys(e.value.results.failingSuites) : [],
							testedSuites: toArray(e.value.results.testedSuites)
						};
						cumFailCount += e.value.results.failed;
						writeResultsFile(testResults);
					}

					// Pending count appears to have an off by one error
					grunt.log.writeln("Progress (browsers): " + e.complete + ' / ' + e.total + ' (' + (e.pending-1) + ' browsers remaining, ' + cumFailCount + ' test failures so far)');
				});

				batch.end(function(err, jobresults) {
					grunt.log.writeln('All jobs complete');
					tunnel.stop(function() {
						var passingUAs = [];
						var failed = false;
						grunt.log.writeln('Sauce tunnel stopped');
						grunt.log.writeln("travis_fold:end:Sauce test progress");
						grunt.log.writeln("Failed tests:");
						jobresults.forEach(function(job) {
							if (!job.results) {
								grunt.warn('No results reported for '+ job.ua.browserName+'/'+job.ua.browserVersion+' '+job.urlName);
								return true;
							}
							if (job.results.failed) {
								grunt.log.writeln(' - '+job.ua.browserName+' '+job.ua.browserVersion+' (Sauce results: https://saucelabs.com/tests/' + job.id+')');
								if (job.results.failingSuites) {
									Object.keys(job.results.failingSuites).forEach(function(feature) {
										var url = options.urls[job.urlName].replace(/test\/director/, 'test/tests')+'&feature='+feature;
										grunt.log.writeln('    -> '+feature);
										grunt.log.writeln('       '+url);
									});
								}
								failed = true;
							} else {
								passingUAs.push(job.ua.browserName+'/'+job.ua.browserVersion);
							}
						});
						if (!failed) {
							grunt.log.writeln(' - None!');
						}
						if (passingUAs.length) {
							grunt.log.writeln('Browsers tested with no failures: '+passingUAs.join(', '));
						}

						process.nextTick(function() {

							// Always report the grunt task as successful if not running as CI
							if (!options.cibuild) {
								gruntDone(null, true);
							} else {
								gruntDone(!failed, failed);
							}
						});
					});
				});
			});
		});
	});
};
<|endoftext|>


<commit_before>'use strict';

module.exports = function (grunt) {
  grunt.registerMultiTask('splitSems', function () {
    var options = this.options();

    var path = require('path');
    var _ = require('lodash');

    var acadYear = options.academicYear;
    var basePath = path.join(options.srcFolder, acadYear.replace('/', '-'));
    var joinedSemPath = path.join(basePath, grunt.config('normalize').options.destFileName);
    var modules = grunt.file.readJSON(joinedSemPath);

    var moduleList = [];
    var moduleInformation = [];

    modules.forEach(function (mod) {
      moduleList.push(_.extend(_.pick(mod, [
        'ModuleCode',
        'ModuleTitle'
      ]), {
        Semesters: _.pluck(mod.History, 'Semester')
      }));

      var info = _.pick(mod, [
        'ModuleCode',
        'ModuleTitle',
        'Department',
        'ModuleDescription',
        'CrossModule',
        'ModuleCredit',
        'Workload',
        'Prerequisite',
        'Preclusion',
        'Corequisite',
        'Types'
      ]);
      info.History = mod.History.map(function (history) {
        return _.omit(history, 'Timetable', 'IVLE');
      });
      grunt.file.write(
        path.join(basePath, grunt.config('split').options.destSubfolder, mod.ModuleCode, 'index.json'),
        JSON.stringify(info, null, options.jsonSpace)
      );
      moduleInformation.push(info);
    });

    grunt.file.write(
      path.join(basePath, grunt.config('split').options.destModuleList),
      JSON.stringify(moduleList, null, options.jsonSpace)
    );
    grunt.file.write(
      path.join(basePath, grunt.config('split').options.destModuleInformation),
      JSON.stringify(moduleInformation, null, options.jsonSpace)
    );
  });
};
<commit_msg>Split full individual modules json for combined semesters as well<commit_after>'use strict';

module.exports = function (grunt) {
  grunt.registerMultiTask('splitSems', function () {
    var options = this.options();

    var path = require('path');
    var _ = require('lodash');

    var acadYear = options.academicYear;
    var basePath = path.join(options.srcFolder, acadYear.replace('/', '-'));
    var joinedSemPath = path.join(basePath, grunt.config('normalize').options.destFileName);
    var modules = grunt.file.readJSON(joinedSemPath);

    var moduleList = [];
    var moduleInformation = [];

    modules.forEach(function (mod) {
      grunt.file.write(
        path.join(basePath, grunt.config('split').options.destSubfolder, mod.ModuleCode + '.json'),
        JSON.stringify(mod, null, options.jsonSpace)
      );

      moduleList.push(_.extend(_.pick(mod, [
        'ModuleCode',
        'ModuleTitle'
      ]), {
        Semesters: _.pluck(mod.History, 'Semester')
      }));

      var info = _.pick(mod, [
        'ModuleCode',
        'ModuleTitle',
        'Department',
        'ModuleDescription',
        'CrossModule',
        'ModuleCredit',
        'Workload',
        'Prerequisite',
        'Preclusion',
        'Corequisite',
        'Types'
      ]);
      info.History = mod.History.map(function (history) {
        return _.omit(history, 'Timetable', 'IVLE');
      });
      grunt.file.write(
        path.join(basePath, grunt.config('split').options.destSubfolder, mod.ModuleCode, 'index.json'),
        JSON.stringify(info, null, options.jsonSpace)
      );
      moduleInformation.push(info);
    });

    grunt.file.write(
      path.join(basePath, grunt.config('split').options.destModuleList),
      JSON.stringify(moduleList, null, options.jsonSpace)
    );
    grunt.file.write(
      path.join(basePath, grunt.config('split').options.destModuleInformation),
      JSON.stringify(moduleInformation, null, options.jsonSpace)
    );
  });
};
<|endoftext|>


<commit_before>(function() {
  var $ = require('jquery');
  var gui = require('nw.gui');

  // Get the current window
  var win = gui.Window.get();

  win.on('new-win-policy', function(frame, url, policy) {
    policy.ignore();
    gui.Shell.openExternal(url);
    //policy.forceNewWindow();
  });

  // FIXME: very hacky way to forcefully update font-family
  win.on('document-end', function() {
    var i = frames[0];
    var d = i.document;

    i.onload = function() {
        console.log('loaded');
        $(d.head).contents().append("<style>.panelFlyout {right: 0 !important;}</style>");
        // disable Meiryo hack once cause Mac font will be ugly
        //d.body.style.fontFamily = "Meiryo UI";

        var topbar = d.getElementById("pagelet_bluebar");
        if (topbar && topbar.style) {
          topbar.style.display = "none";
        }
        var sidebar = d.getElementById("pagelet_sidebar");
        if (sidebar && sidebar.style) {
          sidebar.style.display = "none";
        }
        var dock = d.getElementById("pagelet_dock");
        if (dock && dock.style) {
          dock.style.display = "none";
        }
        var ad = d.getElementById("rightCol");
        if (ad && ad.style) {
          ad.style.display = "none";
        }

        // observe message notification count on the blue bar
        var messageBadgeCountTag = d.querySelector('#mercurymessagesCountValue');
        if (messageBadgeCountTag) {
          var badgeCountObserver = new MutationObserver(function(mutations) {
            mutations.forEach(function(mutation) {
              var addedNodes = mutation.addedNodes;
              if (addedNodes && addedNodes.length ===1) {
                var count = parseInt(addedNodes[0].data);
                if (count) {
                  win.setBadgeLabel(count);
                } else {
                  win.setBadgeLabel("");
                }
              }
            });
          });
          badgeCountObserver.observe(messageBadgeCountTag, { childList: true });
        }

        // ready to show
        var root = document.getElementById("main");
        root.setAttribute("class", "visible");
    }
  });

  // Need to set the menu using shortcuts on osx
  // https://github.com/nwjs/nw.js/wiki/Menu#menucreatemacbuiltinappname
  var nativeMenuBar = new gui.Menu({type: "menubar"});
  if (nativeMenuBar && nativeMenuBar.createMacBuiltin) {
    nativeMenuBar.createMacBuiltin(gui.App.manifest.name);
    win.menu = nativeMenuBar;
  }
}).apply(this);
<commit_msg>apply better font for Win/JP<commit_after>(function() {
  var $ = require('jquery');
  var gui = require('nw.gui');

  // Get the current window
  var win = gui.Window.get();

  win.on('new-win-policy', function(frame, url, policy) {
    policy.ignore();
    gui.Shell.openExternal(url);
    //policy.forceNewWindow();
  });

  // FIXME: very hacky way to forcefully update font-family
  win.on('document-end', function() {
    var i = frames[0];
    var d = i.document;

    i.onload = function() {
        console.log('loaded');
        $(d.head).contents().append("<style>body {font-family: helvetica, arial,'lucida grande', '游ゴシック', YuGothic, 'Hiragino Kaku Gothic ProN', Meiryo, sans-serif !important;} .panelFlyout {right: 0 !important;}</style>");
        // disable Meiryo hack once cause Mac font will be ugly
        //d.body.style.fontFamily = "Meiryo UI";

        var topbar = d.getElementById("pagelet_bluebar");
        if (topbar && topbar.style) {
          topbar.style.display = "none";
        }
        var sidebar = d.getElementById("pagelet_sidebar");
        if (sidebar && sidebar.style) {
          sidebar.style.display = "none";
        }
        var dock = d.getElementById("pagelet_dock");
        if (dock && dock.style) {
          dock.style.display = "none";
        }
        var ad = d.getElementById("rightCol");
        if (ad && ad.style) {
          ad.style.display = "none";
        }

        // observe message notification count on the blue bar
        var messageBadgeCountTag = d.querySelector('#mercurymessagesCountValue');
        if (messageBadgeCountTag) {
          var badgeCountObserver = new MutationObserver(function(mutations) {
            mutations.forEach(function(mutation) {
              var addedNodes = mutation.addedNodes;
              if (addedNodes && addedNodes.length ===1) {
                var count = parseInt(addedNodes[0].data);
                if (count) {
                  win.setBadgeLabel(count);
                } else {
                  win.setBadgeLabel("");
                }
              }
            });
          });
          badgeCountObserver.observe(messageBadgeCountTag, { childList: true });
        }

        // ready to show
        var root = document.getElementById("main");
        root.setAttribute("class", "visible");
    }
  });

  // Need to set the menu using shortcuts on osx
  // https://github.com/nwjs/nw.js/wiki/Menu#menucreatemacbuiltinappname
  var nativeMenuBar = new gui.Menu({type: "menubar"});
  if (nativeMenuBar && nativeMenuBar.createMacBuiltin) {
    nativeMenuBar.createMacBuiltin(gui.App.manifest.name);
    win.menu = nativeMenuBar;
  }
}).apply(this);
<|endoftext|>