<commit_before>import {LitElement, html} from 'lit-element';
import {nothing} from 'lit-html';
import '@polymer/iron-icon';
import style from '../css/elements/chromedash-schedule.css';

const TEMPLATE_CONTENT = {
  stable: {
    channelLabel: 'Stable',
    h1Class: '',
    downloadUrl: 'https://www.google.com/chrome/browser/desktop/index.html',
    downloadTitle: 'Download Chrome stable',
    dateText: 'was',
    featureHeader: 'Features in this release',
  },
  beta: {
    channelLabel: 'Next up',
    h1Class: 'chrome_version--beta',
    downloadUrl: 'https://www.google.com/chrome/browser/beta.html',
    downloadTitle: 'Download Chrome Beta',
    dateText: 'between',
    featureHeader: 'Features planned in this release',
  },
  dev: {
    channelLabel: 'Dev',
    h1Class: 'chrome_version--dev',
    downloadUrl: 'https://www.google.com/chrome/browser/canary.html',
    downloadTitle: 'Download Chrome Canary',
    dateText: 'coming',
    featureHeader: 'Features planned in this release',
  },
};

const REMOVED_STATUS = ['Removed'];
const DEPRECATED_STATUS = ['Deprecated', 'No longer pursuing'];

class ChromedashSchedule extends LitElement {
  static styles = style;

  static get properties() {
    return {
      channels: {attribute: false}, // Assigned in schedule.js, value from Django
      hideBlink: {attribute: false}, // Edited in schedule.js
    };
  }

  _objKeys(obj) {
    if (!obj) {
      return [];
    }
    return Object.keys(obj).sort();
  }

  _computeDaysUntil(dateStr) {
    const today = new Date();
    const diff = this._dateDiffInDays(new Date(dateStr), today);
    const prefix = diff.future ? 'in' : '';
    const days = diff.days > 1 ? 's' : '';
    const ago = !diff.future ? 'ago' : '';
    return `${prefix} ${diff.days} day${days} ${ago}`;
  }

  _computeDate(dateStr) {
    const opts = {/* weekday: 'short', */month: 'short', day: 'numeric'};
    const date = new Date(dateStr);
    return new Intl.DateTimeFormat('en-US', opts).format(date);
  }

  /**
   *  Returns the number of days between a and b.
   *  @param {!Date} a
   *  @param {!Date} b
   *  @return {!{days: number, future: boolean}}
   */
  _dateDiffInDays(a, b) {
    // Discard time and time-zone information.
    const utc1 = Date.UTC(a.getFullYear(), a.getMonth(), a.getDate());
    const utc2 = Date.UTC(b.getFullYear(), b.getMonth(), b.getDate());

    const MS_PER_DAY = 1000 * 60 * 60 * 24;
    const daysDiff = Math.floor((utc2 - utc1) / MS_PER_DAY);
    return {days: Math.abs(daysDiff), future: daysDiff < 1};
  }

  _subscribeToFeature(e) {
    e.preventDefault();
    e.stopPropagation();

    const iconEl = e.target;
    const featureId = iconEl.dataset.featureId;
    const receivePush = iconEl.icon !== 'chromestatus:notifications';
    iconEl.icon = receivePush ? 'chromestatus:notifications' : 'chromestatus:notifications-off';

    if (receivePush) {
      window.PushNotifications.subscribeToFeature(featureId);
    } else {
      window.PushNotifications.unsubscribeFromFeature(featureId);
    }
  }

  render() {
    if (!this.channels) {
      return html``;
    }
    return html`
      ${['stable', 'beta', 'dev'].map((type) => html`

${TEMPLATE_CONTENT[type].channelLabel}

              Chrome ${this.channels[type].version}

              Beta ${TEMPLATE_CONTENT[type].dateText}
              ${this._computeDate(this.channels[type].earliest_beta)} - ${this._computeDate(this.channels[type].latest_beta)}

              Stable ${this._computeDaysUntil(this.channels[type].stable_date)}
              ( ${this._computeDate(this.channels[type].stable_date)} )

${TEMPLATE_CONTENT[type].featureHeader}:

            ${this._objKeys(this.channels[type].components).map((componentName) => html`

${componentName}

                ${this.channels[type].components[componentName].map((f) => html`

                    ${f.name}

                      ${f.browsers.chrome.origintrial ? html`

                          <iron-icon icon="chromestatus:extension" class="experimental" data-tooltip></iron-icon>

                        ` : nothing}
                      ${f.browsers.chrome.intervention ? html`

                          <iron-icon icon="chromestatus:pan-tool" class="intervention" data-tooltip></iron-icon>

                        ` : nothing}
                      ${REMOVED_STATUS.includes(f.browsers.chrome.status.text) ? html`

                          <iron-icon icon="chromestatus:cancel" class="remove" data-tooltip></iron-icon>

                        ` : nothing}
                      ${DEPRECATED_STATUS.includes(f.browsers.chrome.status.text) ? html`

                          <iron-icon icon="chromestatus:warning" class="deprecated" data-tooltip></iron-icon>

                        ` : nothing}
                      ${window.PushNotifier && window.PushNotifier.SUPPORTS_NOTIFICATIONS ? html`

                          <iron-icon icon="chromestatus:notifications-off"
                                     class="pushicon ${window.PushNotifier && window.PushNotifier.GRANTED_ACCESS ? nothing : 'disabled'}"
                                     data-feature-id="${f.id}"
                                     @click="${this._subscribeToFeature}"></iron-icon>

                      ` : nothing}

                  `)}

              `)}

        `)}
    `;
  }
}

customElements.define('chromedash-schedule', ChromedashSchedule);
<commit_msg>Disable display of release dates.<commit_after>import {LitElement, html} from 'lit-element';
import {nothing} from 'lit-html';
import '@polymer/iron-icon';
import style from '../css/elements/chromedash-schedule.css';

const TEMPLATE_CONTENT = {
  stable: {
    channelLabel: 'Stable',
    h1Class: '',
    downloadUrl: 'https://www.google.com/chrome/browser/desktop/index.html',
    downloadTitle: 'Download Chrome stable',
    dateText: 'was',
    featureHeader: 'Features in this release',
  },
  beta: {
    channelLabel: 'Next up',
    h1Class: 'chrome_version--beta',
    downloadUrl: 'https://www.google.com/chrome/browser/beta.html',
    downloadTitle: 'Download Chrome Beta',
    dateText: 'between',
    featureHeader: 'Features planned in this release',
  },
  dev: {
    channelLabel: 'Dev',
    h1Class: 'chrome_version--dev',
    downloadUrl: 'https://www.google.com/chrome/browser/canary.html',
    downloadTitle: 'Download Chrome Canary',
    dateText: 'coming',
    featureHeader: 'Features planned in this release',
  },
};

const REMOVED_STATUS = ['Removed'];
const DEPRECATED_STATUS = ['Deprecated', 'No longer pursuing'];
const SHOW_DATES = false;

class ChromedashSchedule extends LitElement {
  static styles = style;

  static get properties() {
    return {
      channels: {attribute: false}, // Assigned in schedule.js, value from Django
      hideBlink: {attribute: false}, // Edited in schedule.js
    };
  }

  _objKeys(obj) {
    if (!obj) {
      return [];
    }
    return Object.keys(obj).sort();
  }

  _computeDaysUntil(dateStr) {
    const today = new Date();
    const diff = this._dateDiffInDays(new Date(dateStr), today);
    const prefix = diff.future ? 'in' : '';
    const days = diff.days > 1 ? 's' : '';
    const ago = !diff.future ? 'ago' : '';
    return `${prefix} ${diff.days} day${days} ${ago}`;
  }

  _computeDate(dateStr) {
    const opts = {/* weekday: 'short', */month: 'short', day: 'numeric'};
    const date = new Date(dateStr);
    return new Intl.DateTimeFormat('en-US', opts).format(date);
  }

  /**
   *  Returns the number of days between a and b.
   *  @param {!Date} a
   *  @param {!Date} b
   *  @return {!{days: number, future: boolean}}
   */
  _dateDiffInDays(a, b) {
    // Discard time and time-zone information.
    const utc1 = Date.UTC(a.getFullYear(), a.getMonth(), a.getDate());
    const utc2 = Date.UTC(b.getFullYear(), b.getMonth(), b.getDate());

    const MS_PER_DAY = 1000 * 60 * 60 * 24;
    const daysDiff = Math.floor((utc2 - utc1) / MS_PER_DAY);
    return {days: Math.abs(daysDiff), future: daysDiff < 1};
  }

  _subscribeToFeature(e) {
    e.preventDefault();
    e.stopPropagation();

    const iconEl = e.target;
    const featureId = iconEl.dataset.featureId;
    const receivePush = iconEl.icon !== 'chromestatus:notifications';
    iconEl.icon = receivePush ? 'chromestatus:notifications' : 'chromestatus:notifications-off';

    if (receivePush) {
      window.PushNotifications.subscribeToFeature(featureId);
    } else {
      window.PushNotifications.unsubscribeFromFeature(featureId);
    }
  }

  render() {
    if (!this.channels) {
      return html``;
    }
    return html`
      ${['stable', 'beta', 'dev'].map((type) => html`

${TEMPLATE_CONTENT[type].channelLabel}

              Chrome ${this.channels[type].version}

          ${SHOW_DATES ? html`

                Beta ${TEMPLATE_CONTENT[type].dateText}
                ${this._computeDate(this.channels[type].earliest_beta)} - ${this._computeDate(this.channels[type].latest_beta)}

                Stable ${this._computeDaysUntil(this.channels[type].stable_date)}
                ( ${this._computeDate(this.channels[type].stable_date)} )

          ` : nothing}

${TEMPLATE_CONTENT[type].featureHeader}:

            ${this._objKeys(this.channels[type].components).map((componentName) => html`

${componentName}

                ${this.channels[type].components[componentName].map((f) => html`

                    ${f.name}

                      ${f.browsers.chrome.origintrial ? html`

                          <iron-icon icon="chromestatus:extension" class="experimental" data-tooltip></iron-icon>

                        ` : nothing}
                      ${f.browsers.chrome.intervention ? html`

                          <iron-icon icon="chromestatus:pan-tool" class="intervention" data-tooltip></iron-icon>

                        ` : nothing}
                      ${REMOVED_STATUS.includes(f.browsers.chrome.status.text) ? html`

                          <iron-icon icon="chromestatus:cancel" class="remove" data-tooltip></iron-icon>

                        ` : nothing}
                      ${DEPRECATED_STATUS.includes(f.browsers.chrome.status.text) ? html`

                          <iron-icon icon="chromestatus:warning" class="deprecated" data-tooltip></iron-icon>

                        ` : nothing}
                      ${window.PushNotifier && window.PushNotifier.SUPPORTS_NOTIFICATIONS ? html`

                          <iron-icon icon="chromestatus:notifications-off"
                                     class="pushicon ${window.PushNotifier && window.PushNotifier.GRANTED_ACCESS ? nothing : 'disabled'}"
                                     data-feature-id="${f.id}"
                                     @click="${this._subscribeToFeature}"></iron-icon>

                      ` : nothing}

                  `)}

              `)}

        `)}
    `;
  }
}

customElements.define('chromedash-schedule', ChromedashSchedule);
<|endoftext|>


<commit_before>var fs = require('fs');

module.exports = function(config) {

  // Use ENV vars on Drone and sauce.json locally to get credentials
  if (!process.env.SAUCE_USERNAME) {
    if (!fs.existsSync('sauce.json')) {
      console.log('Create a sauce.json with your credentials based on the sauce-sample.json file.');
      process.exit(1);
    } else {
      process.env.SAUCE_USERNAME = require('./sauce').username;
      process.env.SAUCE_ACCESS_KEY = require('./sauce').accessKey;
    }
  }

  // Browsers to run on Sauce Labs
  var customLaunchers = {
    'SL_Chrome': {
      base: 'SauceLabs',
      browserName: 'chrome'
    },
    'SL_FireFox': {
      base: 'SauceLabs',
      browserName: 'firefox',
    },
    'SL_Safari': {
      base: 'SauceLabs',
      browserName: 'safari',
    },
    'SL_Opera': {
      base: 'SauceLabs',
      browserName: 'opera',
    },
    'SL_InternetExplorer': {
      base: 'SauceLabs',
      browserName: 'internet explorer',
      version: '10'
    },
    'SL_IPhone': {
      base: 'SauceLabs',
      browserName: 'iPhone'
    },
    'SL_Android': {
      base: 'SauceLabs',
      browserName: 'android'
    },
    'SL_IPad': {
      base: 'SauceLabs',
      browserName: 'iPad'
    }
  };

  config.set({

    sauceLabs: {
      testName: 'ClientJS'
    },

    // base path that will be used to resolve all patterns (eg. files, exclude)
    basePath: '',

    // frameworks to use
    // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
    frameworks: ['jasmine'],

    // list of files / patterns to load in the browser
    files: [
      'src/**/*.js',
      'specs/**/*.js'
    ],

    // test results reporter to use
    // possible values: 'dots', 'progress'
    // available reporters: https://npmjs.org/browse/keyword/karma-reporter
    reporters: ['progress', 'coverage', 'saucelabs'],
    preprocessors: {
      'src/*.js': ['coverage']
    },

    coverageReporter: {
      // specify a common output directory
      dir: 'coverage',
      reporters: [
        // reporters supporting the `file` property, use `subdir` to directly
        // output them in the `dir` directory
        { type: 'lcovonly', subdir: '.', file: 'lcov.info' }
      ]
    },

    // web server port
    port: 9876,

    colors: true,

    // level of logging
    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
    logLevel: onfig.LOG_WARN,

    captureTimeout: 180000,
    customLaunchers: customLaunchers,

    // start these browsers
    // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
    browsers: Object.keys(customLaunchers),
    singleRun: true
  });
};
<commit_msg>fixing karma config typo<commit_after>var fs = require('fs');

module.exports = function(config) {

  // Use ENV vars on Drone and sauce.json locally to get credentials
  if (!process.env.SAUCE_USERNAME) {
    if (!fs.existsSync('sauce.json')) {
      console.log('Create a sauce.json with your credentials based on the sauce-sample.json file.');
      process.exit(1);
    } else {
      process.env.SAUCE_USERNAME = require('./sauce').username;
      process.env.SAUCE_ACCESS_KEY = require('./sauce').accessKey;
    }
  }

  // Browsers to run on Sauce Labs
  var customLaunchers = {
    'SL_Chrome': {
      base: 'SauceLabs',
      browserName: 'chrome'
    },
    'SL_FireFox': {
      base: 'SauceLabs',
      browserName: 'firefox',
    },
    'SL_Safari': {
      base: 'SauceLabs',
      browserName: 'safari',
    },
    'SL_Opera': {
      base: 'SauceLabs',
      browserName: 'opera',
    },
    'SL_InternetExplorer': {
      base: 'SauceLabs',
      browserName: 'internet explorer',
      version: '10'
    },
    'SL_IPhone': {
      base: 'SauceLabs',
      browserName: 'iPhone'
    },
    'SL_Android': {
      base: 'SauceLabs',
      browserName: 'android'
    },
    'SL_IPad': {
      base: 'SauceLabs',
      browserName: 'iPad'
    }
  };

  config.set({

    sauceLabs: {
      testName: 'ClientJS'
    },

    // base path that will be used to resolve all patterns (eg. files, exclude)
    basePath: '',

    // frameworks to use
    // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
    frameworks: ['jasmine'],

    // list of files / patterns to load in the browser
    files: [
      'src/**/*.js',
      'specs/**/*.js'
    ],

    // test results reporter to use
    // possible values: 'dots', 'progress'
    // available reporters: https://npmjs.org/browse/keyword/karma-reporter
    reporters: ['progress', 'coverage', 'saucelabs'],
    preprocessors: {
      'src/*.js': ['coverage']
    },

    coverageReporter: {
      // specify a common output directory
      dir: 'coverage',
      reporters: [
        // reporters supporting the `file` property, use `subdir` to directly
        // output them in the `dir` directory
        { type: 'lcovonly', subdir: '.', file: 'lcov.info' }
      ]
    },

    // web server port
    port: 9876,

    colors: true,

    // level of logging
    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
    logLevel: config.LOG_WARN,

    captureTimeout: 180000,
    customLaunchers: customLaunchers,

    // start these browsers
    // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
    browsers: Object.keys(customLaunchers),
    singleRun: true
  });
};
<|endoftext|>


<commit_before>var configFactory = require('./configFactory')

module.exports = configFactory(function (defaultConfig) {
  var customConfig = Object.assign({}, defaultConfig, {
    port: 9877,

    proxies: {
      '/script.html': `/base/test/script.html`
    },
    // list of files / patterns to load in the browser
    // all modules including in tests should be included here
    files: [
      /* targets */
      { pattern: 'src/tracker/trackers/*/*[!(.d)].ts' },
      { pattern: 'src/tracker/private/*[!(.d)].ts' },

      /* dependencies */
      { pattern: 'src/tracker/trackers/utils.ts' },
      { pattern: 'src/tracker/public/*[!(.d)].ts' },

      /* static */
      { pattern: 'test/script.html', served: true, included: false },

      /* tests */
      { pattern: 'test/tracker/trackers/*.ts' }
    ],
    // preprocess matching files before serving them to the browser
    // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
    preprocessors: {
      /* targets */
      'src/tracker/trackers/*/*[!(.d)].ts': ['karma-typescript', 'coverage'],
      'src/tracker/private/*[!(.d)].ts': ['karma-typescript', 'coverage'],

      /* dependencies */
      'src/tracker/trackers/utils.ts': ['karma-typescript'],
      'src/tracker/public/*[!(.d)].ts': ['karma-typescript'],

      /* tests */
      'test/tracker/trackers/*.ts': ['karma-typescript']
    }
  })
  customConfig.karmaTypescriptConfig.reports.html = 'coverage/tracker'

  return customConfig
})
<commit_msg>[refine] add trackers/utils to coverage report<commit_after>var configFactory = require('./configFactory')

module.exports = configFactory(function (defaultConfig) {
  var customConfig = Object.assign({}, defaultConfig, {
    port: 9877,

    proxies: {
      '/script.html': `/base/test/script.html`
    },
    // list of files / patterns to load in the browser
    // all modules including in tests should be included here
    files: [
      /* targets */
      { pattern: 'src/tracker/trackers/*/*[!(.d)].ts' },
      { pattern: 'src/tracker/trackers/utils.ts' },
      { pattern: 'src/tracker/private/*[!(.d)].ts' },

      /* dependencies */
      { pattern: 'src/tracker/public/*[!(.d)].ts' },

      /* static */
      { pattern: 'test/script.html', served: true, included: false },

      /* tests */
      { pattern: 'test/tracker/trackers/*.ts' }
    ],
    // preprocess matching files before serving them to the browser
    // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
    preprocessors: {
      /* targets */
      'src/tracker/trackers/*/*[!(.d)].ts': ['karma-typescript', 'coverage'],
      'src/tracker/trackers/utils.ts': ['karma-typescript', 'coverage'],
      'src/tracker/private/*[!(.d)].ts': ['karma-typescript', 'coverage'],

      /* dependencies */
      'src/tracker/public/*[!(.d)].ts': ['karma-typescript'],

      /* tests */
      'test/tracker/trackers/*.ts': ['karma-typescript']
    }
  })
  customConfig.karmaTypescriptConfig.reports.html = 'coverage/tracker'

  return customConfig
})
<|endoftext|>


<commit_before>/**
 * Created by apple on 14-5-29.
 */
var libs = require("../core/normal_libs");

var output = "signed_egret.apk";
var async = require("../core/async.js");
var fs = require("fs");

function run(current, arg, opt) {

    if (opt["-config"]) {
        var content = "{\n" +
            "  \"project\":\"\",\n" +
            "  \"keyfile\":\"\",\n" +
            "  \"password\":\"\",\n" +
            "  \"egret-native\":\"EgretFrameworkNative\"\n" +
            "}";
        fs.writeFileSync("package_to_native.config", content, "utf-8");
        return;
    }
    var config = libs.getConfig(arg[0]);
    var keystore = config.keystore;
    var keypass = config.password;
    var project = config.project;
    var native_folder = config["egret-native"];
    var join = require("path").join;

    async.series(
        [

            function (callback) {
                libs.copy(join(project,"bin-debug"), join(native_folder,"assets/js/bin-debug"));
                libs.copy(join(project,"launcher"), join(native_folder,"assets/js/launcher"));
                libs.copy(join(project,"resources"), join(native_folder,"assets/js/resources"));
                callback();
            },

            function (callback) {
                var cmd = "apktool b " + native_folder + " a.apk";
                executeCommand(callback, cmd);
            },

            function (callback) {
                var cmd = "jarsigner -verbose -keystore  " + keystore + " -storepass " + keypass + " -signedjar " + output + " a.apk " + keystore;
                executeCommand(callback, cmd);
            },

            function (callback) {
                fs.unlinkSync("a.apk");
                var cmd = "adb uninstall org.egret.egretframeworknative";
                executeCommand(callback, cmd)
                callback();
            },

            function (callback) {
                var cmd = "adb install " + output;
                executeCommand(callback, cmd);
            }

        ]

    )
}

function executeCommand(callback, script) {
    var cp_exec = require("child_process").exec;
    var cmd = cp_exec(script);
    cmd.stderr.on("data", function (data) {
        console.log(data);
    })

    cmd.on('exit', function (code) {

        if (code == 0) {
            callback();
        }
        else {
            libs.log("脚本执行失败");
        }

    });
}

exports.run = run;<commit_msg>修改文件属性名keyfile为keystore<commit_after>/**
 * Created by apple on 14-5-29.
 */
var libs = require("../core/normal_libs");

var output = "signed_egret.apk";
var async = require("../core/async.js");
var fs = require("fs");

function run(current, arg, opt) {

    if (opt["-config"]) {
        var content = "{\n" +
            "  \"project\":\"\",\n" +
            "  \"keystore\":\"\",\n" +
            "  \"password\":\"\",\n" +
            "  \"egret-native\":\"EgretFrameworkNative\"\n" +
            "}";
        fs.writeFileSync("package_to_native.config", content, "utf-8");
        return;
    }
    var config = libs.getConfig(arg[0]);
    var keystore = config.keystore;
    var keypass = config.password;
    var project = config.project;
    var native_folder = config["egret-native"];
    var join = require("path").join;

    async.series(
        [

            function (callback) {
                libs.copy(join(project,"bin-debug"), join(native_folder,"assets/js/bin-debug"));
                libs.copy(join(project,"launcher"), join(native_folder,"assets/js/launcher"));
                libs.copy(join(project,"resources"), join(native_folder,"assets/js/resources"));
                callback();
            },

            function (callback) {
                var cmd = "apktool b " + native_folder + " a.apk";
                executeCommand(callback, cmd);
            },

            function (callback) {
                var cmd = "jarsigner -verbose -keystore  " + keystore + " -storepass " + keypass + " -signedjar " + output + " a.apk " + keystore;
                executeCommand(callback, cmd);
            },

            function (callback) {
                fs.unlinkSync("a.apk");
                var cmd = "adb uninstall org.egret.egretframeworknative";
                executeCommand(callback, cmd)
                callback();
            },

            function (callback) {
                var cmd = "adb install " + output;
                executeCommand(callback, cmd);
            }

        ]

    )
}

function executeCommand(callback, script) {
    var cp_exec = require("child_process").exec;
    var cmd = cp_exec(script);
    cmd.stderr.on("data", function (data) {
        console.log(data);
    })

    cmd.on('exit', function (code) {

        if (code == 0) {
            callback();
        }
        else {
            libs.log("脚本执行失败");
        }

    });
}

exports.run = run;<|endoftext|>


<commit_before>
	app_page.features.push(function(app){
		tc.util.log('Give A Minute: User Account oAuth');

		if(app.app_page.data.facebook_connected){
			$(".connect-facebook").closest(".social-networking").addClass("facebook-connected");
			$(".disconnect-facebook").click(function(event){
				event.preventDefault();
				$.getJSON("/disconnect_facebook", function(data){
					if(data.success) {
						$(".connect-facebook").closest(".social-networking").removeClass("facebook-connected");
					}
				});
			});
		} else {
			$(".connect-facebook").click(function(event){
				event.preventDefault();
				F.connect();
			});
		}

		if(app.app_page.data.twitter_connected){
			$(".connect-twitter").closest(".social-networking").addClass("twitter-connected");
			$(".disconnect-twitter").click(function(event){
				event.preventDefault();
				$.getJSON("/disconnect_twitter", function(data){
					if(data.success) {
						$(".connect-twitter").closest(".social-networking").removeClass("twitter-connected");
					}
				});
			});
		}

	});

	app_page.features.push(function(app){
		tc.util.log('Give A Minute: User Account');
		var offset;

		offset = 0;
		if(app.app_page.data.user_activity && app.app_page.data.user_activity.messages){
			offset = app.app_page.data.user_activity.messages.length - 1;
		}

		app.components.user_page_merlin = new tc.merlin(app,{
			name: "user-account",
			dom:tc.jQ('.midlands.merlin'),
			first_step:'activity',
			steps:{
				'activity':{
					selector:'.activity-view',
					init:function(merlin,dom){
						tc.jQ('ul.tabs li').removeClass('active').filter(".activity").addClass("active");
						tc.jQ('.addphoto').hide();

						dom.find('a.remove-idea').unbind("click").bind('click', {app:app}, function(e){
							e.preventDefault();
							e.data.app.components.modal.show({
								app:e.data.app,
								source_element:tc.jQ('.modal-content.remove-idea'),
								submit:function(){
									tc.jQ.ajax({
										type:'POST',
										url:'/idea/remove',
										data:{
											idea_id: e.target.hash.split(',')[1]
										},
										context: tc.jQ(e.target),
										dataType:'text',
										success:function(data,ts,xhr){
											var item;
											if(data == 'False'){
												return false;
											}
											item = this.parents("li").eq(0);

											(function(counter, n) {
												counter.text(n);
											}(this.parents(".box").eq(0).children(".hd").find(".counter"), item.siblings().length));

											item.remove();

											tc.jQ('ul.idea-cards > li').removeClass('every-third').filter(function(index) {
									 			return index % 3 == 2;
											}).addClass('every-third');
										}
									});
								}
							});
						});
						// add official resource tags
						//tc.addOfficialResourceTags(tc.jQ('table.resources-list'));
					}
				},
				'messages':{
					selector:'.messages-view',
					current_offset: offset,
					n_to_fetch:5,
					has_run_init: false,
					init:function(merlin,dom){
						tc.jQ('ul.tabs li').removeClass('active').filter(".messages").addClass("active");
						tc.jQ('.addphoto').hide();

						if (!merlin.current_step.has_run_init) {
							(function() {
								var checklist, pref;
								checklist = tc.jQ(".messages-view .preferences .checklist");
								pref = app.app_page.user.email_notification;
								switch (pref) {
									case "digest":
										checklist.find("label[for='pref-emails-daily']").click();
										break;
									case "none":
										checklist.find("label[for='pref-no-emails']").click();
								}
							}());
							merlin.current_step.has_run_init = true;
						}

						//Click event to handle "prettyCheckbox" custom check boxes
						//This event is namespaced to avoid collisions with the click events bound by $.fn.prettyCheckboxes()
						dom.find(".preferences .checklist .prettyCheckbox.radio").unbind("click.message_prefs")
						   .bind("click.message_prefs", {merlin:merlin}, function(e, d) {
								var pref;
								switch (tc.jQ(this).attr("for")) {
									case "pref-emails-daily":
										pref = "digest";
										break;
									case "pref-no-emails":
										pref = "none";
										break;
								}
								if (!pref) {
									tc.util.log("error parsing email pref", "warn");
									return false;
								}
								tc.jQ.ajax({
									type:"POST",
									url:"/useraccount/messageprefs",
									data:{
										pref: pref
									},
									context:e.data.merlin,
									dataType:"text",
									success: function(data, ts, xhr) {
										if (data == "False") {
											return false;
										}
									}
								});
							});

						dom.find(".load-more a").unbind("click").bind("click", {merlin:merlin, dom:dom}, function(e, d) {
							var $t;
							$t = tc.jQ(e.target);
							$t.parent().addClass("loading");
							e.preventDefault();
							tc.jQ.ajax({
								type:"POST",
								url:"/useraccount/messages",
								data:{
									n_messages: e.data.merlin.current_step.n_to_fetch,
									offset: e.data.merlin.current_step.current_offset
								},
								context:merlin,
								dataType:"text",
								success: function(data, ts, xhr) {
									var d, dom_stack;
									$t.parent().removeClass("loading");
									try {
										d = tc.jQ.parseJSON(data);
									} catch(e) {
										tc.util.log("/useraccount/messages: json parsing error", "warn");
										return;
									}
									if (!d.length) {
										$t.hide();
										return;
									}
									dom_stack = e.data.dom.find("ol.message-stack");
									tc.jQ.each(d, function(i, message) {
										var template;
										switch (message.message_type) {
											case "member_comment":
												template = tc.jQ(".template-content.message-item.member-comment").clone().removeClass("template-content");
												if (message.owner.image_id) {
													template.find(".thumb img").attr("src", app_page.media_root + "images/"+ message.owner.image_id % 10 +"/"+ message.owner.image_id +".png").attr("alt", message.owner.name);
												} else {
													template.find(".thumb img").attr("src", "/static/images/thumb_genAvatar50.png").attr("alt", message.owner.name);
												}
												template.find(".sender").html(""+ message.owner.name+ "").next().text(message.created).time_since();
												template.find(".excerpt p").text(message.body);
												break;
											case "join":
												template = tc.jQ(".template-content.message-item.join-notification").clone().removeClass("template-content");
												template.find(".title").text(message.body);
												template.find(".controls > a").attr("href", "/project/"+ message.project_id +"#show,members");
												break;
											case "invite":
												template = tc.jQ(".template-content.message-item.user-notification").clone().removeClass("template-content");
												template.find(".title").text(message.body);
												template.find(".controls > a").attr("href", ("/project/"+ message.project_id + ""));
												break;
											default:
												break;
										}
										if (template) {
											dom_stack.append(template);
										}
									});
									this.current_step.current_offset += d.length;
								}
							});
						});
					}
				},
				'account':{
					selector:'.account-view',
					init:function(merlin,dom){
						tc.jQ('ul.tabs li').removeClass('active').filter(".account").addClass("active");
						window.location.hash = "account-info,edit-account-details";
						tc.jQ('.addphoto').show();
					}
				},
				'resources':{
					selector:'.resources-view',
					init:function(merlin,dom){
						tc.jQ('ul.tabs li').removeClass('active').filter(".resources").addClass("active");
						window.location.hash = "resources-info";
						tc.jQ('.addphoto').hide();
					}
				}
			}
		});

		//commented out because it is breaking the page.
		tc.gam.ideas_invite(app, tc.jQ("a.invite"));

		// random note-card backgrounds
		var ideasList = tc.jQ('.idea-cards li');
		for (i=0; i < ideasList.length; i++) {
			ideasList.eq(i).children('.note-card').addClass('card' + (Math.floor(Math.random()*4) + 1));
		}

	});
	<commit_msg>fixes #1740<commit_after>
	app_page.features.push(function(app){
		tc.util.log('Give A Minute: User Account oAuth');

		if(app.app_page.data.facebook_connected){
			$(".connect-facebook").closest(".social-networking").addClass("facebook-connected");
			$(".disconnect-facebook").click(function(event){
				event.preventDefault();
				$.getJSON("/facebook/disconnect", function(data){
					if(data.success) {
						$(".connect-facebook").closest(".social-networking").removeClass("facebook-connected");
					}
				});
			});
		} else {
			$(".connect-facebook").click(function(event){
				event.preventDefault();
				F.connect();
			});
		}

		if(app.app_page.data.twitter_connected){
			$(".connect-twitter").closest(".social-networking").addClass("twitter-connected");
			$(".disconnect-twitter").click(function(event){
				event.preventDefault();
				$.getJSON("/twitter/disconnect", function(data){
					if(data.success) {
						$(".connect-twitter").closest(".social-networking").removeClass("twitter-connected");
					}
				});
			});
		}

	});

	app_page.features.push(function(app){
		tc.util.log('Give A Minute: User Account');
		var offset;

		offset = 0;
		if(app.app_page.data.user_activity && app.app_page.data.user_activity.messages){
			offset = app.app_page.data.user_activity.messages.length - 1;
		}

		app.components.user_page_merlin = new tc.merlin(app,{
			name: "user-account",
			dom:tc.jQ('.midlands.merlin'),
			allow_hash_override_onload:true,
			first_step:'activity',
			steps:{
				'activity':{
					selector:'.activity-view',
					init:function(merlin,dom){
						tc.jQ('ul.tabs li').removeClass('active').filter(".activity").addClass("active");
						tc.jQ('.addphoto').hide();

						dom.find('a.remove-idea').unbind("click").bind('click', {app:app}, function(e){
							e.preventDefault();
							e.data.app.components.modal.show({
								app:e.data.app,
								source_element:tc.jQ('.modal-content.remove-idea'),
								submit:function(){
									tc.jQ.ajax({
										type:'POST',
										url:'/idea/remove',
										data:{
											idea_id: e.target.hash.split(',')[1]
										},
										context: tc.jQ(e.target),
										dataType:'text',
										success:function(data,ts,xhr){
											var item;
											if(data == 'False'){
												return false;
											}
											item = this.parents("li").eq(0);

											(function(counter, n) {
												counter.text(n);
											}(this.parents(".box").eq(0).children(".hd").find(".counter"), item.siblings().length));

											item.remove();

											tc.jQ('ul.idea-cards > li').removeClass('every-third').filter(function(index) {
									 			return index % 3 == 2;
											}).addClass('every-third');
										}
									});
								}
							});
						});
						// add official resource tags
						//tc.addOfficialResourceTags(tc.jQ('table.resources-list'));
					}
				},
				'messages':{
					selector:'.messages-view',
					current_offset: offset,
					n_to_fetch:5,
					has_run_init: false,
					init:function(merlin,dom){
						tc.jQ('ul.tabs li').removeClass('active').filter(".messages").addClass("active");
						tc.jQ('.addphoto').hide();

						if (!merlin.current_step.has_run_init) {
							(function() {
								var checklist, pref;
								checklist = tc.jQ(".messages-view .preferences .checklist");
								pref = app.app_page.user.email_notification;
								switch (pref) {
									case "digest":
										checklist.find("label[for='pref-emails-daily']").click();
										break;
									case "none":
										checklist.find("label[for='pref-no-emails']").click();
								}
							}());
							merlin.current_step.has_run_init = true;
						}

						//Click event to handle "prettyCheckbox" custom check boxes
						//This event is namespaced to avoid collisions with the click events bound by $.fn.prettyCheckboxes()
						dom.find(".preferences .checklist .prettyCheckbox.radio").unbind("click.message_prefs")
						   .bind("click.message_prefs", {merlin:merlin}, function(e, d) {
								var pref;
								switch (tc.jQ(this).attr("for")) {
									case "pref-emails-daily":
										pref = "digest";
										break;
									case "pref-no-emails":
										pref = "none";
										break;
								}
								if (!pref) {
									tc.util.log("error parsing email pref", "warn");
									return false;
								}
								tc.jQ.ajax({
									type:"POST",
									url:"/useraccount/messageprefs",
									data:{
										pref: pref
									},
									context:e.data.merlin,
									dataType:"text",
									success: function(data, ts, xhr) {
										if (data == "False") {
											return false;
										}
									}
								});
							});

						dom.find(".load-more a").unbind("click").bind("click", {merlin:merlin, dom:dom}, function(e, d) {
							var $t;
							$t = tc.jQ(e.target);
							$t.parent().addClass("loading");
							e.preventDefault();
							tc.jQ.ajax({
								type:"POST",
								url:"/useraccount/messages",
								data:{
									n_messages: e.data.merlin.current_step.n_to_fetch,
									offset: e.data.merlin.current_step.current_offset
								},
								context:merlin,
								dataType:"text",
								success: function(data, ts, xhr) {
									var d, dom_stack;
									$t.parent().removeClass("loading");
									try {
										d = tc.jQ.parseJSON(data);
									} catch(e) {
										tc.util.log("/useraccount/messages: json parsing error", "warn");
										return;
									}
									if (!d.length) {
										$t.hide();
										return;
									}
									dom_stack = e.data.dom.find("ol.message-stack");
									tc.jQ.each(d, function(i, message) {
										var template;
										switch (message.message_type) {
											case "member_comment":
												template = tc.jQ(".template-content.message-item.member-comment").clone().removeClass("template-content");
												if (message.owner.image_id) {
													template.find(".thumb img").attr("src", app_page.media_root + "images/"+ message.owner.image_id % 10 +"/"+ message.owner.image_id +".png").attr("alt", message.owner.name);
												} else {
													template.find(".thumb img").attr("src", "/static/images/thumb_genAvatar50.png").attr("alt", message.owner.name);
												}
												template.find(".sender").html(""+ message.owner.name+ "").next().text(message.created).time_since();
												template.find(".excerpt p").text(message.body);
												break;
											case "join":
												template = tc.jQ(".template-content.message-item.join-notification").clone().removeClass("template-content");
												template.find(".title").text(message.body);
												template.find(".controls > a").attr("href", "/project/"+ message.project_id +"#show,members");
												break;
											case "invite":
												template = tc.jQ(".template-content.message-item.user-notification").clone().removeClass("template-content");
												template.find(".title").text(message.body);
												template.find(".controls > a").attr("href", ("/project/"+ message.project_id + ""));
												break;
											default:
												break;
										}
										if (template) {
											dom_stack.append(template);
										}
									});
									this.current_step.current_offset += d.length;
								}
							});
						});
					}
				},
				'account':{
					selector:'.account-view',
					init:function(merlin,dom){
						tc.jQ('ul.tabs li').removeClass('active').filter(".account").addClass("active");
						window.location.hash = "account-info,edit-account-details";
						tc.jQ('.addphoto').show();
					}
				},
				'resources':{
					selector:'.resources-view',
					init:function(merlin,dom){
						tc.jQ('ul.tabs li').removeClass('active').filter(".resources").addClass("active");
						window.location.hash = "resources-info";
						tc.jQ('.addphoto').hide();
					}
				}
			}
		});

		//commented out because it is breaking the page.
		tc.gam.ideas_invite(app, tc.jQ("a.invite"));

		// random note-card backgrounds
		var ideasList = tc.jQ('.idea-cards li');
		for (i=0; i < ideasList.length; i++) {
			ideasList.eq(i).children('.note-card').addClass('card' + (Math.floor(Math.random()*4) + 1));
		}

	});
	<|endoftext|>


<commit_before>
environment = 'node';

require(process.env.SUGAR || '../../../lib/sugar.js');

// Test suite
require('../../javascripts/setup.js');
require('../../javascripts/date_helper.js');
require('../../javascripts/object_helper.js');

// Tests
require('../sugar/object.js');
require('../sugar/string.js');
require('../sugar/array.js');
require('../sugar/number.js');
require('../sugar/date.js');
require('../sugar/regexp.js');
require('../sugar/function.js');
require('../sugar/es5.js');
require('../sugar/date_zh_cn.js')
require('../sugar/date_zh_tw.js')
require('../sugar/date_ko.js')
require('../sugar/date_ru.js')
require('../sugar/date_es.js')
require('../sugar/date_pt.js')
require('../sugar/date_fr.js')
require('../sugar/date_it.js')
require('../sugar/date_de.js')
require('../sugar/date_ja.js')

syncTestsFinished();
<commit_msg>adding equals tests to node<commit_after>
environment = 'node';

require(process.env.SUGAR || '../../../lib/sugar.js');

// Test suite
require('../../javascripts/setup.js');
require('../../javascripts/date_helper.js');
require('../../javascripts/object_helper.js');

// Tests
require('../sugar/object.js');
require('../sugar/string.js');
require('../sugar/array.js');
require('../sugar/number.js');
require('../sugar/date.js');
require('../sugar/regexp.js');
require('../sugar/function.js');
require('../sugar/es5.js');
require('../sugar/equals.js');
require('../sugar/date_zh_cn.js')
require('../sugar/date_zh_tw.js')
require('../sugar/date_ko.js')
require('../sugar/date_ru.js')
require('../sugar/date_es.js')
require('../sugar/date_pt.js')
require('../sugar/date_fr.js')
require('../sugar/date_it.js')
require('../sugar/date_de.js')
require('../sugar/date_ja.js')

syncTestsFinished();
<|endoftext|>


<commit_before>/* -*- Mode: Javascript; indent-tabs-mode:nil; js-indent-level: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */

/*************************************************************
 *
 *  MathJax/extensions/CHTML-preview.js
 *
 *  Implements a fast preview using the Common-HTML output jax
 *  and then a slower update to the more accurate HTML-CSS output
 *  (or whatever the user has selected).
 *
 *  ---------------------------------------------------------------------
 *
 *  Copyright (c) 2014-2015 The MathJax Consortium
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

(function (HUB,HTML) {

  var SETTINGS = HUB.config.menuSettings;

  var CHTMLpreview = MathJax.Extension["CHTML-preview"] = {
    version: "2.5.0",

    //
    //  Configuration for the chunking of the main output
    //  after the previews have been created, and other configuration.
    //
    config: HUB.CombineConfig("CHTML-preview",{
      Chunks: {EqnChunk: 10000, EqnChunkFactor: 1, EqnChunkDelay: 0},
      color: "inherit!important",
      updateTime: 30, updateDelay: 6,
      messageStyle: "none",
      disabled: false
    }),

    //
    //  Ajust the chunking of the output jax
    //
    Config: function () {
      HUB.Config({
        "HTML-CSS": this.config.Chunks,
        SVG: this.config.Chunks
      });
      MathJax.Ajax.Styles({".MathJax_Preview .MJXc-math":{color:this.config.color}});
      var update, delay, style, done, saved;
      var config = this.config;

      if (!config.disabled && SETTINGS.CHTMLpreview == null)
        HUB.Config({menuSettings:{CHTMLpreview:true}});
      HUB.Register.MessageHook("Begin Math Output",function () {
        if (!done && SETTINGS.CHTMLpreview && SETTINGS.renderer !== "CommonHTML") {
          update = HUB.processUpdateTime; delay = HUB.processUpdateDelay;
          style = HUB.config.messageStyle;
          HUB.processUpdateTime = config.updateTime;
          HUB.processUpdateDelay = config.updateDelay;
          HUB.Config({messageStyle: config.messageStyle});
          MathJax.Message.Clear(0,0);
          saved = true;
        }
      });
      HUB.Register.MessageHook("End Math Output",function () {
        if (!done && saved) {
          HUB.processUpdateTime = update;
          HUB.processUpdateDelay = delay;
          HUB.Config({messageStyle: style});
          done = true;
        }
      });
    },

    //
    //  Insert a preview span, if there isn't one already,
    //  and call the CommonHTML output jax to create the preview
    //
    Preview: function (data) {
      if (!SETTINGS.CHTMLpreview || SETTINGS.renderer === "CommonHTML") return;
      var preview = data.script.MathJax.preview || data.script.previousSibling;
      if (!preview || preview.className !== MathJax.Hub.config.preRemoveClass) {
        preview = HTML.Element("span",{className:MathJax.Hub.config.preRemoveClass});
        data.script.parentNode.insertBefore(preview,data.script);
        data.script.MathJax.preview = preview;
      }
      preview.innerHTML = ""; preview.style.color = "inherit";
      return this.postFilter(preview,data);
    },
    postFilter: function (preview,data) {
      //
      //  Load the CommonHTML jax if it is not already loaded
      //
      if (!data.math.root.toCommonHTML) {
        var queue = MathJax.Callback.Queue();
        queue.Push(
          ["Require",MathJax.Ajax,"[MathJax]/jax/output/CommonHTML/config.js"],
          ["Require",MathJax.Ajax,"[MathJax]/jax/output/CommonHTML/jax.js"]
        );
        HUB.RestartAfter(queue.Push({}));
      }
      data.math.root.toCommonHTML(preview);
    },

    //
    //  Hook into the input jax postFilter to create the previews as
    //  the input jax are processed.
    //
    Register: function (name) {
      HUB.Register.StartupHook(name+" Jax Require",function () {
        var jax = MathJax.InputJax[name];
        jax.postfilterHooks.Add(["Preview",MathJax.Extension["CHTML-preview"]],50);
      });
    }
  }

  //
  //  Hook into each input jax
  //
  CHTMLpreview.Register("TeX");
  CHTMLpreview.Register("MathML");
  CHTMLpreview.Register("AsciiMath");

  HUB.Register.StartupHook("End Config",["Config",CHTMLpreview]);

  HUB.Startup.signal.Post("CHTML-preview Ready");

})(MathJax.Hub,MathJax.HTML);

MathJax.Ajax.loadComplete("[MathJax]/extensions/CHTML-preview.js");

<commit_msg>for #1137: disable CHTMLpreview.js on IE<8<commit_after>/* -*- Mode: Javascript; indent-tabs-mode:nil; js-indent-level: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */

/*************************************************************
 *
 *  MathJax/extensions/CHTML-preview.js
 *
 *  Implements a fast preview using the Common-HTML output jax
 *  and then a slower update to the more accurate HTML-CSS output
 *  (or whatever the user has selected).
 *
 *  ---------------------------------------------------------------------
 *
 *  Copyright (c) 2014-2015 The MathJax Consortium
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

(function (HUB,HTML,BROWSER) {

  var SETTINGS = HUB.config.menuSettings;

  var CHTMLpreview = MathJax.Extension["CHTML-preview"] = {
    version: "2.5.0",

    //
    //  Configuration for the chunking of the main output
    //  after the previews have been created, and other configuration.
    //
    config: HUB.CombineConfig("CHTML-preview",{
      Chunks: {EqnChunk: 10000, EqnChunkFactor: 1, EqnChunkDelay: 0},
      color: "inherit!important",
      updateTime: 30, updateDelay: 6,
      messageStyle: "none",
      disabled: ((BROWSER.isMSIE && BROWSER.version < 8) ? true :  false)
    }),

    //
    //  Ajust the chunking of the output jax
    //
    Config: function () {
      HUB.Config({
        "HTML-CSS": this.config.Chunks,
        SVG: this.config.Chunks
      });
      MathJax.Ajax.Styles({".MathJax_Preview .MJXc-math":{color:this.config.color}});
      var update, delay, style, done, saved;
      var config = this.config;

      if (!config.disabled && SETTINGS.CHTMLpreview == null)
        HUB.Config({menuSettings:{CHTMLpreview:true}});
      HUB.Register.MessageHook("Begin Math Output",function () {
        if (!done && SETTINGS.CHTMLpreview && SETTINGS.renderer !== "CommonHTML") {
          update = HUB.processUpdateTime; delay = HUB.processUpdateDelay;
          style = HUB.config.messageStyle;
          HUB.processUpdateTime = config.updateTime;
          HUB.processUpdateDelay = config.updateDelay;
          HUB.Config({messageStyle: config.messageStyle});
          MathJax.Message.Clear(0,0);
          saved = true;
        }
      });
      HUB.Register.MessageHook("End Math Output",function () {
        if (!done && saved) {
          HUB.processUpdateTime = update;
          HUB.processUpdateDelay = delay;
          HUB.Config({messageStyle: style});
          done = true;
        }
      });
    },

    //
    //  Insert a preview span, if there isn't one already,
    //  and call the CommonHTML output jax to create the preview
    //
    Preview: function (data) {
      if (!SETTINGS.CHTMLpreview || SETTINGS.renderer === "CommonHTML") return;
      var preview = data.script.MathJax.preview || data.script.previousSibling;
      if (!preview || preview.className !== MathJax.Hub.config.preRemoveClass) {
        preview = HTML.Element("span",{className:MathJax.Hub.config.preRemoveClass});
        data.script.parentNode.insertBefore(preview,data.script);
        data.script.MathJax.preview = preview;
      }
      preview.innerHTML = ""; preview.style.color = "inherit";
      return this.postFilter(preview,data);
    },
    postFilter: function (preview,data) {
      //
      //  Load the CommonHTML jax if it is not already loaded
      //
      if (!data.math.root.toCommonHTML) {
        var queue = MathJax.Callback.Queue();
        queue.Push(
          ["Require",MathJax.Ajax,"[MathJax]/jax/output/CommonHTML/config.js"],
          ["Require",MathJax.Ajax,"[MathJax]/jax/output/CommonHTML/jax.js"]
        );
        HUB.RestartAfter(queue.Push({}));
      }
      data.math.root.toCommonHTML(preview);
    },

    //
    //  Hook into the input jax postFilter to create the previews as
    //  the input jax are processed.
    //
    Register: function (name) {
      HUB.Register.StartupHook(name+" Jax Require",function () {
        var jax = MathJax.InputJax[name];
        jax.postfilterHooks.Add(["Preview",MathJax.Extension["CHTML-preview"]],50);
      });
    }
  }

  //
  //  Hook into each input jax
  //
  CHTMLpreview.Register("TeX");
  CHTMLpreview.Register("MathML");
  CHTMLpreview.Register("AsciiMath");

  HUB.Register.StartupHook("End Config",["Config",CHTMLpreview]);

  HUB.Startup.signal.Post("CHTML-preview Ready");

})(MathJax.Hub,MathJax.HTML,MathJax.Hub.Browser);

MathJax.Ajax.loadComplete("[MathJax]/extensions/CHTML-preview.js");

<|endoftext|>


<commit_before>// @flow
import React from 'react'
import {View} from 'react-native'
import {connect} from 'react-redux'
import {updateMenuFilters} from '../../../flux'
import type {TopLevelViewPropsType} from '../../types'
import type momentT from 'moment'
import type {MenuItemType, MasterCorIconMapType, StationMenuType, CorIconType} from '../types'
import {tracker} from '../../../analytics'
import type {FilterType} from '../../components/filter'
import {applyFiltersToItem} from '../../components/filter'
import groupBy from 'lodash/groupBy'
import sortBy from 'lodash/sortBy'
import fromPairs from 'lodash/fromPairs'
import filter from 'lodash/filter'
import map from 'lodash/map'
import {FilterMenuToolbar} from './filter-menu-toolbar'
import {MenuListView} from './menu'
import DietaryFilters from '../../../images/dietary-filters'

type FancyMenuPropsType = TopLevelViewPropsType & {
  applyFilters: (filters: FilterType[], item: MenuItemType) => boolean,
  now: momentT,
  name: string,
  filters: FilterType[],
  foodItems: MenuItemType[],
  menuLabel?: string,
  menuCorIcons: MasterCorIconMapType,
  stationMenus: StationMenuType[],
  onFiltersChange: (f: FilterType[]) => any,
};

function getDetailsFromDietaryFilter(item: CorIconType, filterKey: string, givenFilters: any, localFilters=DietaryFilters): {title: string, image: any, detail: string} {
  // check if we've got local info for the filter
  const ourInfo = localFilters[filterKey]

  if (!ourInfo) {
    // if not, fall back to the version from bonapp
    tracker.trackEvent('menus', 'unknown-dietary-filter', {label: item.label})
    return {
      title: item.label,
      image: item.image ? {uri: item.image} : null,
      detail: item.description ? item.description.trim() : '',
    }
  }

  // otherwise, return our info
  return {
    title: ourInfo.label,
    image: ourInfo.icon,
    detail: ourInfo.description,
  }
}

class FancyMenuView extends React.Component {
  static defaultProps = {
    applyFilters: applyFiltersToItem,
  }

  componentWillMount() {
    let {foodItems, menuCorIcons, filters, stationMenus} = this.props

    // prevent ourselves from overwriting the filters from redux on mount
    if (filters.length) {
      return
    }

    let stations = stationMenus.map(m => m.label)
    filters = this.buildFilters({foodItems, stations, corIcons: menuCorIcons})
    this.props.onFiltersChange(filters)
  }

  buildFilters({foodItems, stations, corIcons}: {foodItems: MenuItemType[], stations: string[], corIcons: MasterCorIconMapType}): FilterType[] {
    // Format the items for the stations filter
    const allStations = map(stations, name => ({title: name}))

    // Grab the labels of the COR icons
    const allDietaryRestrictions = map(corIcons, getDetailsFromDietaryFilter)

    // Check if there is at least one special in order to show the specials-only filter
    const stationNames = allStations.map(s => s.title)
    const shouldShowSpecials = filter(foodItems, item =>
      item.special && stationNames.includes(item.station)).length >= 1

    return [
      {
        type: 'toggle',
        key: 'specials',
        enabled: shouldShowSpecials,
        spec: {
          label: 'Only Show Specials',
          caption: 'Allows you to either see only the "specials" for today, or everything the location has to offer (e.g., condiments.)',
        },
        apply: {
          key: 'special',
        },
      },
      {
        type: 'list',
        key: 'stations',
        enabled: false,
        spec: {
          title: 'Stations',
          options: allStations,
          mode: 'OR',
          selected: allStations,
        },
        apply: {
          key: 'station',
        },
      },
      {
        type: 'list',
        key: 'dietary-restrictions',
        enabled: false,
        spec: {
          title: 'Dietary Restrictions',
          showImages: true,
          options: allDietaryRestrictions,
          mode: 'AND',
          selected: [],
        },
        apply: {
          key: 'cor_icon',
        },
      },
    ]
  }

  props: FancyMenuPropsType;

  openFilterView = () => {
    this.props.navigator.push({
      id: 'FilterView',
      index: this.props.route.index + 1,
      title: 'Filter',
      sceneConfig: 'fromBottom',
      onDismiss: (route: any, navigator: any) => navigator.pop(),
      props: {
        pathToFilters: ['menus', this.props.name],
        onChange: filters => this.props.onFiltersChange(filters),
      },
    })
  }

  render() {
    const {
      applyFilters,
      filters,
      foodItems,
      menuLabel,
      now,
      stationMenus,
    } = this.props

    const stationNotes = fromPairs(stationMenus.map(m => [m.label, m.note]))
    const stationsSort = stationMenus.map(m => m.label)

    // only show items that the menu lists today
    const filteredByMenu = filter(foodItems, item => stationsSort.includes(item.station))
    // apply the selected filters
    const filtered = filter(filteredByMenu, item => applyFilters(filters, item))
    // sort the remaining items by station
    const sortedByStation = sortBy(filtered, item => stationsSort.indexOf(item.station))
    // group them for the ListView
    const grouped = groupBy(sortedByStation, item => item.station)

    const specialsFilterEnabled = Boolean(filters.find(f =>
      f.enabled && f.type === 'toggle' && f.spec.label === 'Only Show Specials'))

    let message = ''
    if (specialsFilterEnabled && sortedByStation.length === 0) {
      message = 'No items to show. There may be no specials today. Try changing the filters.'
    }

    return (
      <View style={{flex: 1}}>
        <FilterMenuToolbar
          date={now}
          title={menuLabel}
          filters={filters}
          onPress={this.openFilterView}
        />
        <MenuListView
          data={grouped}
          stationNotes={stationNotes}
          message={message}
          // We can't conditionally show the star – wierd things happen, like
          // the first two items having a star and none of the rest.
          //badgeSpecials={!specialsFilterEnabled}
          badgeSpecials
        />
      </View>
    )
  }
}

function mapStateToProps(state, actualProps: FancyMenuPropsType) {
  return {
    filters: state.menus[actualProps.name] || [],
  }
}

function mapDispatchToProps(dispatch, actualProps: FancyMenuPropsType) {
  return {
    onFiltersChange: (filters: FilterType[]) => dispatch(updateMenuFilters(actualProps.name, filters)),
  }
}

export const FancyMenu = connect(mapStateToProps, mapDispatchToProps)(FancyMenuView)
<commit_msg>strip html from bonapp's descriptions (#637)<commit_after>// @flow
import React from 'react'
import {View} from 'react-native'
import {connect} from 'react-redux'
import {updateMenuFilters} from '../../../flux'
import type {TopLevelViewPropsType} from '../../types'
import type momentT from 'moment'
import type {MenuItemType, MasterCorIconMapType, StationMenuType, CorIconType} from '../types'
import {tracker} from '../../../analytics'
import type {FilterType} from '../../components/filter'
import {applyFiltersToItem} from '../../components/filter'
import {fastGetTrimmedText} from '../../../lib/html'
import groupBy from 'lodash/groupBy'
import sortBy from 'lodash/sortBy'
import fromPairs from 'lodash/fromPairs'
import filter from 'lodash/filter'
import map from 'lodash/map'
import {FilterMenuToolbar} from './filter-menu-toolbar'
import {MenuListView} from './menu'
import DietaryFilters from '../../../images/dietary-filters'

type FancyMenuPropsType = TopLevelViewPropsType & {
  applyFilters: (filters: FilterType[], item: MenuItemType) => boolean,
  now: momentT,
  name: string,
  filters: FilterType[],
  foodItems: MenuItemType[],
  menuLabel?: string,
  menuCorIcons: MasterCorIconMapType,
  stationMenus: StationMenuType[],
  onFiltersChange: (f: FilterType[]) => any,
};

function getDetailsFromDietaryFilter(item: CorIconType, filterKey: string, givenFilters: any, localFilters=DietaryFilters): {title: string, image: any, detail: string} {
  // check if we've got local info for the filter
  const ourInfo = localFilters[filterKey]

  if (!ourInfo) {
    // if not, fall back to the version from bonapp
    tracker.trackEvent('menus', 'unknown-dietary-filter', {label: item.label})
    return {
      title: item.label,
      image: item.image ? {uri: item.image} : null,
      detail: item.description ? fastGetTrimmedText(item.description) : '',
    }
  }

  // otherwise, return our info
  return {
    title: ourInfo.label,
    image: ourInfo.icon,
    detail: ourInfo.description,
  }
}

class FancyMenuView extends React.Component {
  static defaultProps = {
    applyFilters: applyFiltersToItem,
  }

  componentWillMount() {
    let {foodItems, menuCorIcons, filters, stationMenus} = this.props

    // prevent ourselves from overwriting the filters from redux on mount
    if (filters.length) {
      return
    }

    let stations = stationMenus.map(m => m.label)
    filters = this.buildFilters({foodItems, stations, corIcons: menuCorIcons})
    this.props.onFiltersChange(filters)
  }

  buildFilters({foodItems, stations, corIcons}: {foodItems: MenuItemType[], stations: string[], corIcons: MasterCorIconMapType}): FilterType[] {
    // Format the items for the stations filter
    const allStations = map(stations, name => ({title: name}))

    // Grab the labels of the COR icons
    const allDietaryRestrictions = map(corIcons, getDetailsFromDietaryFilter)

    // Check if there is at least one special in order to show the specials-only filter
    const stationNames = allStations.map(s => s.title)
    const shouldShowSpecials = filter(foodItems, item =>
      item.special && stationNames.includes(item.station)).length >= 1

    return [
      {
        type: 'toggle',
        key: 'specials',
        enabled: shouldShowSpecials,
        spec: {
          label: 'Only Show Specials',
          caption: 'Allows you to either see only the "specials" for today, or everything the location has to offer (e.g., condiments.)',
        },
        apply: {
          key: 'special',
        },
      },
      {
        type: 'list',
        key: 'stations',
        enabled: false,
        spec: {
          title: 'Stations',
          options: allStations,
          mode: 'OR',
          selected: allStations,
        },
        apply: {
          key: 'station',
        },
      },
      {
        type: 'list',
        key: 'dietary-restrictions',
        enabled: false,
        spec: {
          title: 'Dietary Restrictions',
          showImages: true,
          options: allDietaryRestrictions,
          mode: 'AND',
          selected: [],
        },
        apply: {
          key: 'cor_icon',
        },
      },
    ]
  }

  props: FancyMenuPropsType;

  openFilterView = () => {
    this.props.navigator.push({
      id: 'FilterView',
      index: this.props.route.index + 1,
      title: 'Filter',
      sceneConfig: 'fromBottom',
      onDismiss: (route: any, navigator: any) => navigator.pop(),
      props: {
        pathToFilters: ['menus', this.props.name],
        onChange: filters => this.props.onFiltersChange(filters),
      },
    })
  }

  render() {
    const {
      applyFilters,
      filters,
      foodItems,
      menuLabel,
      now,
      stationMenus,
    } = this.props

    const stationNotes = fromPairs(stationMenus.map(m => [m.label, m.note]))
    const stationsSort = stationMenus.map(m => m.label)

    // only show items that the menu lists today
    const filteredByMenu = filter(foodItems, item => stationsSort.includes(item.station))
    // apply the selected filters
    const filtered = filter(filteredByMenu, item => applyFilters(filters, item))
    // sort the remaining items by station
    const sortedByStation = sortBy(filtered, item => stationsSort.indexOf(item.station))
    // group them for the ListView
    const grouped = groupBy(sortedByStation, item => item.station)

    const specialsFilterEnabled = Boolean(filters.find(f =>
      f.enabled && f.type === 'toggle' && f.spec.label === 'Only Show Specials'))

    let message = ''
    if (specialsFilterEnabled && sortedByStation.length === 0) {
      message = 'No items to show. There may be no specials today. Try changing the filters.'
    }

    return (
      <View style={{flex: 1}}>
        <FilterMenuToolbar
          date={now}
          title={menuLabel}
          filters={filters}
          onPress={this.openFilterView}
        />
        <MenuListView
          data={grouped}
          stationNotes={stationNotes}
          message={message}
          // We can't conditionally show the star – wierd things happen, like
          // the first two items having a star and none of the rest.
          //badgeSpecials={!specialsFilterEnabled}
          badgeSpecials
        />
      </View>
    )
  }
}

function mapStateToProps(state, actualProps: FancyMenuPropsType) {
  return {
    filters: state.menus[actualProps.name] || [],
  }
}

function mapDispatchToProps(dispatch, actualProps: FancyMenuPropsType) {
  return {
    onFiltersChange: (filters: FilterType[]) => dispatch(updateMenuFilters(actualProps.name, filters)),
  }
}

export const FancyMenu = connect(mapStateToProps, mapDispatchToProps)(FancyMenuView)
<|endoftext|>


<commit_before>// @flow

import React from 'react'
import {View, StyleSheet, Text} from 'react-native'
import type {BusLineType} from './types'
import getScheduleForNow from './get-schedule-for-now'
import getSetOfStopsForNow from './get-set-of-stops-for-now'
import zip from 'lodash/zip'
import head from 'lodash/head'
import last from 'lodash/last'
import moment from 'moment-timezone'
import * as c from '../../components/colors'

const TIME_FORMAT = 'h:mma'
const TIMEZONE = 'America/Winnipeg'

let styles = StyleSheet.create({
  container: {
    marginTop: 15,
  },
  rowSectionHeader: {
    paddingTop: 5,
    paddingBottom: 5,
    paddingLeft: 15,
    borderBottomWidth: 1,
    borderColor: '#ebebeb',
  },
  rowSectionHeaderText: {
    color: 'rgb(113, 113, 118)',
  },
  listContainer: {
    backgroundColor: '#ffffff',
    flex: 1,
    flexDirection: 'column',
  },
  busWillSkipStopTitle: {
    color: c.iosText,
  },
  busWillSkipStopDetail: {},
  busWillSkipStopDot: {
    backgroundColor: 'transparent',
    borderColor: 'transparent',
  },
  row: {
    flexDirection: 'row',
  },
  rowDetail: {
    flex: 1,
    marginLeft: 0,
    paddingRight: 10,
    paddingTop: 8,
    paddingBottom: 8,
    flexDirection: 'column',
  },
  notLastRowContainer: {
    borderBottomWidth: 1,
    borderBottomColor: '#ebebeb',
  },
  passedStopDetail: {

  },
  itemTitle: {
    color: c.black,
    paddingLeft: 0,
    paddingRight: 0,
    paddingBottom: 3,
    fontSize: 16,
    textAlign: 'left',
  },
  itemDetail: {
    color: c.iosText,
    paddingLeft: 0,
    paddingRight: 0,
    fontSize: 13,
    textAlign: 'left',
  },
  barContainer: {
    paddingRight: 5,
    width: 45,
    flexDirection: 'column',
    alignItems: 'center',
  },
  bar: {
    flex: 1,
    width: 5,
  },
  dot: {
    height: 15,
    width: 15,
    marginTop: -20,
    marginBottom: -20,
    borderRadius: 20,
    zIndex: 1,
  },
  passedStop: {
    height: 12,
    width: 12,
  },
  beforeStop: {
    borderWidth: 3,
    backgroundColor: 'white',
    height: 18,
    width: 18,
  },
  atStop: {
    height: 20,
    width: 20,
    borderColor: 'white',
    borderWidth: 3,
    backgroundColor: 'white',
  },
  atStopTitle: {
    fontWeight: 'bold',
  },
  passedStopTitle: {
    color: c.iosText,
  },
})

export default function BusLineView({line, style, now}: {line: BusLineType, style: Object|number, now: typeof moment}) {
  let schedule = getScheduleForNow(line.schedules, now)

  schedule.times = schedule.times.map(timeset => {
    return timeset.map(time =>
      time === false
        ? false
        : moment
          .tz(time, TIME_FORMAT, true, TIMEZONE)
          .dayOfYear(now.dayOfYear()))
  })

  let times = getSetOfStopsForNow(schedule, now)
  let timesIndex = schedule.times.indexOf(times)

  let pairs: [[string], [typeof moment]] = zip(schedule.stops, times)

  let barColor = c.salmon
  if (line.line === 'Blue') {
    barColor = c.steelBlue
  } else if (line.line === 'Express') {
    barColor = c.moneyGreen
  }

  let currentStopColor = c.brickRed
  if (line.line === 'Blue') {
    currentStopColor = c.midnightBlue
  } else if (line.line === 'Express') {
    currentStopColor = c.hollyGreen
  }

  let isLastBus = timesIndex === schedule.times.length - 1

  let lineTitle = line.line
  if (timesIndex === 0 && now.isBefore(head(times))) {
    lineTitle += ` — Starting ${head(times).format('h:mma')}`
  } else if (now.isAfter(last(times))) {
    lineTitle += ' — Over for Today'
  } else if (isLastBus) {
    lineTitle += ' — Last Bus'
  } else {
    lineTitle += ' — Running'
  }

  return (
    <View style={[styles.container, style]}>
      <View style={styles.rowSectionHeader}>
        <Text style={styles.rowSectionHeaderText}>
          {lineTitle.toUpperCase()}
        </Text>
      </View>
      <View style={[styles.listContainer]}>
        {pairs.map(([place, time], i) => {
          let afterStop = time && now.isAfter(time, 'minute')
          let atStop = time && now.isSame(time, 'minute')
          let beforeStop = !afterStop && !atStop && time !== false
          let skippingStop = time === false
          let isLastRow = i === pairs.length - 1

          return <View key={i} style={styles.row}>
            <View style={styles.barContainer}>
              <View style={[styles.bar, {backgroundColor: barColor}]} />
              <View
                style={[
                  styles.dot,
                  afterStop && [styles.passedStop, {borderColor: barColor, backgroundColor: barColor}],
                  beforeStop && [styles.beforeStop, {borderColor: barColor}],
                  atStop && [styles.atStop, {borderColor: currentStopColor}],
                  skippingStop && styles.busWillSkipStopDot,
                ]}
              />
              <View style={[styles.bar, {backgroundColor: barColor}]} />
            </View>
            <View style={[
              styles.rowDetail,
              !isLastRow && styles.notLastRowContainer,
            ]}>
              <Text style={[
                styles.itemTitle,
                skippingStop && styles.busWillSkipStopTitle,
                afterStop && styles.passedStopTitle,
                atStop && styles.atStopTitle,
              ]}>
                {place}
              </Text>
              <Text
                style={[
                  styles.itemDetail,
                  skippingStop && styles.busWillSkipStopDetail,
                ]}
                numberOfLines={1}
              >
                {schedule.times
                  .slice(timesIndex)
                  .map(timeSet => timeSet[i])
                  .map(time => time === false ? 'None' : time.format(TIME_FORMAT))
                  .join(' • ')}
              </Text>
            </View>
          </View>
        })}
      </View>
    </View>
  )
}
BusLineView.propTypes = {
  line: React.PropTypes.object.isRequired,
  now: React.PropTypes.instanceOf(moment).isRequired,
  style: React.PropTypes.object,
}
<commit_msg>make borders and border colors match iOS in BusLine<commit_after>// @flow

import React from 'react'
import {View, StyleSheet, Text} from 'react-native'
import type {BusLineType} from './types'
import getScheduleForNow from './get-schedule-for-now'
import getSetOfStopsForNow from './get-set-of-stops-for-now'
import zip from 'lodash/zip'
import head from 'lodash/head'
import last from 'lodash/last'
import moment from 'moment-timezone'
import * as c from '../../components/colors'

const TIME_FORMAT = 'h:mma'
const TIMEZONE = 'America/Winnipeg'

let styles = StyleSheet.create({
  container: {
    marginTop: 15,
    borderBottomWidth: StyleSheet.hairlineWidth,
    borderColor: '#c8c7cc',
  },
  rowSectionHeader: {
    paddingTop: 5,
    paddingBottom: 5,
    paddingLeft: 15,
    borderBottomWidth: StyleSheet.hairlineWidth,
    borderColor: '#c8c7cc',
  },
  rowSectionHeaderText: {
    color: 'rgb(113, 113, 118)',
  },
  listContainer: {
    backgroundColor: '#ffffff',
    flex: 1,
    flexDirection: 'column',
  },
  busWillSkipStopTitle: {
    color: c.iosText,
  },
  busWillSkipStopDetail: {},
  busWillSkipStopDot: {
    backgroundColor: 'transparent',
    borderColor: 'transparent',
  },
  row: {
    flexDirection: 'row',
  },
  rowDetail: {
    flex: 1,
    marginLeft: 0,
    paddingRight: 10,
    paddingTop: 8,
    paddingBottom: 8,
    flexDirection: 'column',
  },
  notLastRowContainer: {
    borderBottomWidth: StyleSheet.hairlineWidth,
    borderBottomColor: '#c8c7cc',
  },
  passedStopDetail: {

  },
  itemTitle: {
    color: c.black,
    paddingLeft: 0,
    paddingRight: 0,
    paddingBottom: 3,
    fontSize: 16,
    textAlign: 'left',
  },
  itemDetail: {
    color: c.iosText,
    paddingLeft: 0,
    paddingRight: 0,
    fontSize: 13,
    textAlign: 'left',
  },
  barContainer: {
    paddingRight: 5,
    width: 45,
    flexDirection: 'column',
    alignItems: 'center',
  },
  bar: {
    flex: 1,
    width: 5,
  },
  dot: {
    height: 15,
    width: 15,
    marginTop: -20,
    marginBottom: -20,
    borderRadius: 20,
    zIndex: 1,
  },
  passedStop: {
    height: 12,
    width: 12,
  },
  beforeStop: {
    borderWidth: 3,
    backgroundColor: 'white',
    height: 18,
    width: 18,
  },
  atStop: {
    height: 20,
    width: 20,
    borderColor: 'white',
    borderWidth: 3,
    backgroundColor: 'white',
  },
  atStopTitle: {
    fontWeight: 'bold',
  },
  passedStopTitle: {
    color: c.iosText,
  },
})

export default function BusLineView({line, style, now}: {line: BusLineType, style: Object|number, now: typeof moment}) {
  let schedule = getScheduleForNow(line.schedules, now)

  schedule.times = schedule.times.map(timeset => {
    return timeset.map(time =>
      time === false
        ? false
        : moment
          .tz(time, TIME_FORMAT, true, TIMEZONE)
          .dayOfYear(now.dayOfYear()))
  })

  let times = getSetOfStopsForNow(schedule, now)
  let timesIndex = schedule.times.indexOf(times)

  let pairs: [[string], [typeof moment]] = zip(schedule.stops, times)

  let barColor = c.salmon
  if (line.line === 'Blue') {
    barColor = c.steelBlue
  } else if (line.line === 'Express') {
    barColor = c.moneyGreen
  }

  let currentStopColor = c.brickRed
  if (line.line === 'Blue') {
    currentStopColor = c.midnightBlue
  } else if (line.line === 'Express') {
    currentStopColor = c.hollyGreen
  }

  let isLastBus = timesIndex === schedule.times.length - 1

  let lineTitle = line.line
  if (timesIndex === 0 && now.isBefore(head(times))) {
    lineTitle += ` — Starting ${head(times).format('h:mma')}`
  } else if (now.isAfter(last(times))) {
    lineTitle += ' — Over for Today'
  } else if (isLastBus) {
    lineTitle += ' — Last Bus'
  } else {
    lineTitle += ' — Running'
  }

  return (
    <View style={[styles.container, style]}>
      <View style={styles.rowSectionHeader}>
        <Text style={styles.rowSectionHeaderText}>
          {lineTitle.toUpperCase()}
        </Text>
      </View>
      <View style={[styles.listContainer]}>
        {pairs.map(([place, time], i) => {
          let afterStop = time && now.isAfter(time, 'minute')
          let atStop = time && now.isSame(time, 'minute')
          let beforeStop = !afterStop && !atStop && time !== false
          let skippingStop = time === false
          let isLastRow = i === pairs.length - 1

          return <View key={i} style={styles.row}>
            <View style={styles.barContainer}>
              <View style={[styles.bar, {backgroundColor: barColor}]} />
              <View
                style={[
                  styles.dot,
                  afterStop && [styles.passedStop, {borderColor: barColor, backgroundColor: barColor}],
                  beforeStop && [styles.beforeStop, {borderColor: barColor}],
                  atStop && [styles.atStop, {borderColor: currentStopColor}],
                  skippingStop && styles.busWillSkipStopDot,
                ]}
              />
              <View style={[styles.bar, {backgroundColor: barColor}]} />
            </View>
            <View style={[
              styles.rowDetail,
              !isLastRow && styles.notLastRowContainer,
            ]}>
              <Text style={[
                styles.itemTitle,
                skippingStop && styles.busWillSkipStopTitle,
                afterStop && styles.passedStopTitle,
                atStop && styles.atStopTitle,
              ]}>
                {place}
              </Text>
              <Text
                style={[
                  styles.itemDetail,
                  skippingStop && styles.busWillSkipStopDetail,
                ]}
                numberOfLines={1}
              >
                {schedule.times
                  .slice(timesIndex)
                  .map(timeSet => timeSet[i])
                  .map(time => time === false ? 'None' : time.format(TIME_FORMAT))
                  .join(' • ')}
              </Text>
            </View>
          </View>
        })}
      </View>
    </View>
  )
}
BusLineView.propTypes = {
  line: React.PropTypes.object.isRequired,
  now: React.PropTypes.instanceOf(moment).isRequired,
  style: React.PropTypes.object,
}
<|endoftext|>


<commit_before>var task = function (gulp, config) {
  'use strict';

  var manifest = require('gulp-manifest');

  gulp.task('manifest', function(){
    return gulp.src([config.dist.root + '/**/*'])
      .pipe(manifest({
        hash: true,
        prefix: '/',
        preferOnline: !global.isProd, // Prefer online in devmode
        network: ['*'],
        filename: 'app.manifest',
        exclude: ['**/app.manifest', 'assets/**/*']
       }))
      .pipe(gulp.dest(config.dist.approot));
  });
};

module.exports = task;
<commit_msg>Cahce browser update script<commit_after>var task = function (gulp, config) {
  'use strict';

  var manifest = require('gulp-manifest');

  gulp.task('manifest', function(){
    return gulp.src([config.dist.root + '/**/*'])
      .pipe(manifest({
        hash: true,
        prefix: '/',
        preferOnline: !global.isProd, // Prefer online in devmode
        network: ['*'],
        filename: 'app.manifest',
        include: ['http://browser-update.org/update.min.js', 'https://browser-update.org/update.min.js'],
        exclude: ['**/app.manifest', 'assets/**/*']
       }))
      .pipe(gulp.dest(config.dist.approot));
  });
};

module.exports = task;
<|endoftext|>


<commit_before>/*
 * grunt-manifest-concat
 * https://github.com/mcavallo
 *
 * Copyright (c) 2015 Mariano Cavallo
 * Licensed under the MIT license.
 */

'use strict';

var fs = require('fs'),
    path = require('path'),
    chalk = require('chalk');

module.exports = function(grunt) {

  var options,
      manifestFile = require('./lib/manifestFile').init(grunt);

  grunt.registerMultiTask('manifest', 'Turn manifest files into concatenated files.', function () {

    var concat = grunt.config.get('concat') || {},
        tasks = [];

    // Load dependent tasks
    grunt.loadNpmTasks('grunt-contrib-concat');

    options = this.options({
      sourceMap: false,
      banner: false,
      extension: 'js',
      cwd: ''
    });

    this.files.forEach(function(f) {
      f.orig.src.forEach(function(src) {
        // Only proceed if the src is a directory
        if (!grunt.file.isDir(src))
          return grunt.log.writeln(chalk.yellow('SKIPPED ') + ' Specified source is not a directory.');

        // If dest is set, It should be a directory
        if (!grunt.file.isDir(f.orig.dest))
          return grunt.log.writeln(chalk.yellow('SKIPPED ') + ' Specified destination is not a directory.');

        // Scan the directory for manifest files
        grunt.file.expand(path.join(src, '*.json')).forEach(function(manifestPath) {
          var manifest = manifestFile.new({
            filePath: manifestPath,
            dest: f.orig.dest,
            options: options
          });

          // Skip manifest if It's invalid
          if (!manifest.isValid())
            return;

          // Create the concat subtask for each manifest file
          concat[manifest.taskName()] = manifest.taskSettings();
          tasks.push(manifest.taskName());
        });
      });
    });

    // Only proceed if any tasks were added
    if (!tasks.length)
      return;

    // Add module subtasks to the concat task in initConfig
    grunt.config.set('concat', concat);

    // Run all the defined tasks
    for (var i = 0, t = tasks.length; i < t; i++)
      grunt.task.run('concat:' + tasks[i]);
  });
};
<commit_msg>Load grunt-contrib-concat from the baseCwd to avoid issues if the base was changed during runtime.<commit_after>/*
 * grunt-manifest-concat
 * https://github.com/mcavallo
 *
 * Copyright (c) 2015 Mariano Cavallo
 * Licensed under the MIT license.
 */

'use strict';

var fs = require('fs'),
    path = require('path'),
    chalk = require('chalk');

module.exports = function(grunt) {

  var options,
      baseCwd = process.cwd(),
      manifestFile = require('./lib/manifestFile').init(grunt);

  // Solves dependencies not loading if the base was changed on runtime
  var fromBaseCwd = function(callback) {
    var cwd = process.cwd();
    process.chdir(baseCwd);
    callback.call(this);
    process.chdir(cwd);
  }

  grunt.registerMultiTask('manifest', 'Turn manifest files into concatenated files.', function () {

    var concat = grunt.config.get('concat') || {},
        tasks = [];

    fromBaseCwd(function() {
      // Load dependent tasks
      grunt.loadNpmTasks('grunt-contrib-concat');
    });

    options = this.options({
      sourceMap: false,
      banner: false,
      extension: 'js',
      cwd: ''
    });

    this.files.forEach(function(f) {
      f.orig.src.forEach(function(src) {
        // Only proceed if the src is a directory
        if (!grunt.file.isDir(src))
          return grunt.log.writeln(chalk.yellow('SKIPPED ') + ' Specified source is not a directory.');

        // If dest is set, It should be a directory
        if (!grunt.file.isDir(f.orig.dest))
          return grunt.log.writeln(chalk.yellow('SKIPPED ') + ' Specified destination is not a directory.');

        // Scan the directory for manifest files
        grunt.file.expand(path.join(src, '*.json')).forEach(function(manifestPath) {
          var manifest = manifestFile.new({
            filePath: manifestPath,
            dest: f.orig.dest,
            options: options
          });

          // Skip manifest if It's invalid
          if (!manifest.isValid())
            return;

          // Create the concat subtask for each manifest file
          concat[manifest.taskName()] = manifest.taskSettings();
          tasks.push(manifest.taskName());
        });
      });
    });

    // Only proceed if any tasks were added
    if (!tasks.length)
      return;

    // Add module subtasks to the concat task in initConfig
    grunt.config.set('concat', concat);

    // Run all the defined tasks
    for (var i = 0, t = tasks.length; i < t; i++)
      grunt.task.run('concat:' + tasks[i]);
  });
};
<|endoftext|>


<commit_before>/*
 * grunt-contrib-nodeunit
 * http://gruntjs.com/
 *
 * Copyright (c) 2012 "Cowboy" Ben Alman, contributors
 * Licensed under the MIT license.
 */

'use strict';

module.exports = function(grunt) {

 // Nodejs libs.
  var path = require('path');

  // External libs.
  var nodeunit = require('nodeunit');
  var nodeunitUtils = require('nodeunit/lib/utils');

  // ==========================================================================
  // CUSTOM NODEUNIT REPORTER
  // ==========================================================================

  // Keep track of the last-started module.
  var currentModule;
  // Keep track of the last-started test(s).
  var unfinished = {};

  // If Nodeunit explodes because a test was missing test.done(), handle it.
  process.on('exit', function() {
    var len = Object.keys(unfinished).length;
    // If there are unfinished tests, tell the user why Nodeunit killed grunt.
    if (len > 0) {
      grunt.log.muted = false;
      grunt.verbose.error().or.writeln('F'.red);
      grunt.log.error('Incomplete tests/setups/teardowns:');
      Object.keys(unfinished).forEach(grunt.log.error, grunt.log);
      grunt.fatal('A test was missing test.done(), so nodeunit exploded. Sorry!',
        Math.min(99, 90 + len));
    }
  });

  // Keep track of failed assertions for pretty-printing.
  var failedAssertions = [];
  function logFailedAssertions() {
    var assertion, stack;
    // Print each assertion error + stack.
    while (assertion = failedAssertions.shift()) {
      nodeunitUtils.betterErrors(assertion);
      grunt.verbose.or.error(assertion.testName);
      if (assertion.error.name === 'AssertionError' && assertion.message) {
        grunt.log.error('AssertionMessage: ' + assertion.message.magenta);
      }
      stack = assertion.error.stack.replace(/ {4}(at)/g, '  $1');
      stack = stack.replace(/:(.*?\n)/, '$1'.magenta);
      grunt.log.error(stack + '\n').writeln();
    }
  }

  // Define our own Nodeunit reporter.
  nodeunit.reporters.grunt = {
    info: 'Grunt reporter',
    run: function(files, options, callback) {
      var opts = {
        // No idea.
        testspec: undefined,
        // Executed when the first test in a file is run. If no tests exist in
        // the file, this doesn't execute.
        moduleStart: function(name) {
          // Keep track of this so that moduleDone output can be suppressed in
          // cases where a test file contains no tests.
          currentModule = name;
          grunt.verbose.subhead('Testing ' + name).or.write('Testing ' + name);
        },
        // Executed after a file is done being processed. This executes whether
        // tests exist in the file or not.
        moduleDone: function(name) {
          // Abort if no tests actually ran.
          if (name !== currentModule) { return; }
          // Print assertion errors here, if verbose mode is disabled.
          if (!grunt.option('verbose')) {
            if (failedAssertions.length > 0) {
              grunt.log.writeln();
              logFailedAssertions();
            } else {
              grunt.log.ok();
            }
          }
        },
        // Executed before each test is run.
        testStart: function(name) {
          // Keep track of the current test, in case test.done() was omitted
          // and Nodeunit explodes.
          unfinished[name] = name;
          grunt.verbose.write(name + '...');
          // Mute output, in cases where a function being tested logs through
          // grunt (for testing grunt internals).
          grunt.log.muted = true;
        },
        // Executed after each test and all its assertions are run.
        testDone: function(name, assertions) {
          delete unfinished[name];
          // Un-mute output.
          grunt.log.muted = false;
          // Log errors if necessary, otherwise success.
          if (assertions.failures()) {
            assertions.forEach(function(ass) {
              if (ass.failed()) {
                ass.testName = name;
                failedAssertions.push(ass);
              }
            });
            if (grunt.option('verbose')) {
              grunt.log.error();
              logFailedAssertions();
            } else {
              grunt.log.write('F'.red);
            }
          } else {
            grunt.verbose.ok().or.write('.');
          }
        },
        // Executed when everything is all done.
        done: function (assertions) {
          if (assertions.failures()) {
            grunt.warn(assertions.failures() + '/' + assertions.length +
              ' assertions failed (' + assertions.duration + 'ms)',
              Math.min(99, 90 + assertions.failures()));
          } else {
            grunt.verbose.writeln();
            grunt.log.ok(assertions.length + ' assertions passed (' +
              assertions.duration + 'ms)');
          }
          // Tell the task manager we're all done.
          callback(); // callback(assertions.failures() === 0);
        }
      };

      // Nodeunit needs absolute paths.
      var paths = files.map(function(filepath) {
        return path.resolve(filepath);
      });
      nodeunit.runFiles(paths, opts);
    }
  };

  // ==========================================================================
  // TASKS
  // ==========================================================================

  grunt.registerMultiTask('nodeunit', 'Run Nodeunit unit tests.', function() {
    // Clear all tests' cached require data, in case this task is run inside a
    // "watch" task loop. TODO: remove?
    grunt.file.clearRequireCache(this.file.src);
    // Run test(s)... asynchronously!
    nodeunit.reporters.grunt.run(this.file.src, {}, this.async());
  });

};
<commit_msg>Making output 866% more lovely. Closes gh-1.<commit_after>/*
 * grunt-contrib-nodeunit
 * http://gruntjs.com/
 *
 * Copyright (c) 2012 "Cowboy" Ben Alman, contributors
 * Licensed under the MIT license.
 */

'use strict';

module.exports = function(grunt) {

  // Nodejs libs.
  var path = require('path');
  var util = require('util');

  // External libs.
  var nodeunit = require('nodeunit');

  // ==========================================================================
  // BETTER ERROR DISPLAY
  // ==========================================================================

  // Much nicer error formatting than what comes with nodeunit.
  var betterErrors = function (assertion) {
    var e = assertion.error;
    if (!e || !e.actual || !e.expected) { return assertion; }

    // Temporarily override the global "inspect" property because logging
    // the entire global object is just silly.
    var globalInspect = global.inspect;
    global.inspect = function() { return '[object global]'; };

    e._message = e.message;

    // Pretty-formatted objects.
    var actual = util.inspect(e.actual, false, 10, true);
    var expected = util.inspect(e.expected, false, 10, true);

    var indent = function(str) {
      return str.split('\n').map(function(s) { return '  ' + s; }).join('\n');
    };

    var stack;
    var multiline = (actual + expected).indexOf('\n') !== -1;
    if (multiline) {
      stack = [
        'Actual:', indent(actual),
        'Operator:', indent(e.operator),
        'Expected:', indent(expected),
      ].join('\n');
    } else {
      stack = e.name + ': ' + actual + ' ' + e.operator + ' ' + expected;
    }

    e.stack = stack + '\n' + e.stack.split('\n').slice(1).join('\n');

    // Restore the global "inspect" property.
    global.inspect = globalInspect;
    return assertion;
  };

  // Reformat stack trace to remove nodeunit scripts, fix indentation, etc.
  var cleanStack = function(error) {
    error._stack = error.stack;
    // Match (/path/to/cwd for later removal.
    var cwdRe = new RegExp('(\\()' + process.cwd() + '/', 'g');
    // Show a full stack trace?
    var fullStack = grunt.option('verbose') || grunt.option('stack');
    // Reformat stack trace output.
    error.stack = error.stack.split('\n').map(function(line) {
      if (line[0] === ' ') {
        // Remove nodeunit script srcs from non-verbose stack trace.
        if (!fullStack && /node_modules\/nodeunit\//.test(line)) {
          return '';
        }
        // Remove leading spaces.
        line = line.replace(/^ {4}(?=at)/, '');
        // Remove cwd.
        line = line.replace(cwdRe, '$1');
      } else {
        line = line.replace(/Assertion(Error)/, '$1');
      }
      return line + '\n';
    }).join('');

    return error;
  };

  // ==========================================================================
  // CUSTOM NODEUNIT REPORTER
  // ==========================================================================

  // Keep track of the last-started module.
  var currentModule;
  // Keep track of the last-started test(s).
  var unfinished = {};

  // If Nodeunit explodes because a test was missing test.done(), handle it.
  process.on('exit', function() {
    var len = Object.keys(unfinished).length;
    // If there are unfinished tests, tell the user why Nodeunit killed grunt.
    if (len > 0) {
      grunt.log.muted = false;
      grunt.verbose.error().or.writeln('F'.red);
      grunt.log.error('Incomplete tests/setups/teardowns:');
      Object.keys(unfinished).forEach(grunt.log.error, grunt.log);
      grunt.fatal('A test was missing test.done(), so nodeunit exploded. Sorry!',
        Math.min(99, 90 + len));
    }
  });

  // Keep track of failed assertions for pretty-printing.
  var failedAssertions = [];
  function logFailedAssertions() {
    var assertion, stack;
    // Print each assertion error + stack.
    while (assertion = failedAssertions.shift()) {
      betterErrors(assertion);
      cleanStack(assertion.error);
      grunt.verbose.or.error(assertion.testName);
      if (assertion.error.name === 'AssertionError' && assertion.message) {
        grunt.log.error('Message: ' + assertion.message.magenta);
      }
      grunt.log.error(assertion.error.stack).writeln();
    }
  }

  // Define our own Nodeunit reporter.
  nodeunit.reporters.grunt = {
    info: 'Grunt reporter',
    run: function(files, options, callback) {
      var opts = {
        // No idea.
        testspec: undefined,
        // Executed when the first test in a file is run. If no tests exist in
        // the file, this doesn't execute.
        moduleStart: function(name) {
          // Keep track of this so that moduleDone output can be suppressed in
          // cases where a test file contains no tests.
          currentModule = name;
          grunt.verbose.subhead('Testing ' + name).or.write('Testing ' + name);
        },
        // Executed after a file is done being processed. This executes whether
        // tests exist in the file or not.
        moduleDone: function(name) {
          // Abort if no tests actually ran.
          if (name !== currentModule) { return; }
          // Print assertion errors here, if verbose mode is disabled.
          if (!grunt.option('verbose')) {
            if (failedAssertions.length > 0) {
              grunt.log.writeln();
              logFailedAssertions();
            } else {
              grunt.log.ok();
            }
          }
        },
        // Executed before each test is run.
        testStart: function(name) {
          // Keep track of the current test, in case test.done() was omitted
          // and Nodeunit explodes.
          unfinished[name] = name;
          grunt.verbose.write(name + '...');
          // Mute output, in cases where a function being tested logs through
          // grunt (for testing grunt internals).
          grunt.log.muted = true;
        },
        // Executed after each test and all its assertions are run.
        testDone: function(name, assertions) {
          delete unfinished[name];
          // Un-mute output.
          grunt.log.muted = false;
          // Log errors if necessary, otherwise success.
          if (assertions.failures()) {
            assertions.forEach(function(ass) {
              if (ass.failed()) {
                ass.testName = name;
                failedAssertions.push(ass);
              }
            });
            if (grunt.option('verbose')) {
              grunt.log.error();
              logFailedAssertions();
            } else {
              grunt.log.write('F'.red);
            }
          } else {
            grunt.verbose.ok().or.write('.');
          }
        },
        // Executed when everything is all done.
        done: function (assertions) {
          if (assertions.failures()) {
            grunt.warn(assertions.failures() + '/' + assertions.length +
              ' assertions failed (' + assertions.duration + 'ms)',
              Math.min(99, 90 + assertions.failures()));
          } else {
            grunt.verbose.writeln();
            grunt.log.ok(assertions.length + ' assertions passed (' +
              assertions.duration + 'ms)');
          }
          // Tell the task manager we're all done.
          callback(); // callback(assertions.failures() === 0);
        }
      };

      // Nodeunit needs absolute paths.
      var paths = files.map(function(filepath) {
        return path.resolve(filepath);
      });
      nodeunit.runFiles(paths, opts);
    }
  };

  // ==========================================================================
  // TASKS
  // ==========================================================================

  grunt.registerMultiTask('nodeunit', 'Run Nodeunit unit tests.', function() {
    // Clear all tests' cached require data, in case this task is run inside a
    // "watch" task loop. TODO: remove?
    grunt.file.clearRequireCache(this.file.src);
    // Run test(s)... asynchronously!
    nodeunit.reporters.grunt.run(this.file.src, {}, this.async());
  });

};
<|endoftext|>


<commit_before>const isAuthenticated = (bool) => {
	const token = window.localStorage.getItem('jwt.token');
    return (token !== undefined && token !== null);
};

export default isAuthenticated;<commit_msg>fix eslint<commit_after>/* global window */
const isAuthenticated = () => {
  const token = window.localStorage.getItem('jwt.token');

  return token !== undefined && token !== null;
};

export default isAuthenticated;
<|endoftext|>


<commit_before>// Web to Plex - Shana Project Plugin
// Aurthor(s) - @ephellon (2018)

let plugin = {
    "url": "*://*.shanaproject.com/*",

    "init": () => {
        let title = document.querySelector('#header_big .header_info_block').textContent.trim(),
            year = +document.querySelector('#header_big .header_info_block + *').textContent.trim().replace(/^.*(\d{4}).*$/m, '$1'),
            image = document.querySelector('#header_big .header_display_box').style['background-image'].trim().replace(/url\((.+)\)/i, '$1');

        return { type: 'show', title, year, image };
    }
};
<commit_msg>Update shana_project.js<commit_after>// Web to Plex - Shana Project Plugin
// Aurthor(s) - @ephellon (2018)

let plugin = {
    "url": "*://*.shanaproject.com/*",

    "init": () => {
        let title = document.querySelector('.overview i').textContent.trim(),
            year = +document.querySelector('#header_big .header_info_block + *').textContent.trim().replace(/^.*(\d{4}).*$/m, '$1'),
            image = document.querySelector('#header_big .header_display_box').style['background-image'].trim().replace(/url\((.+)\)/i, '$1');

        return { type: 'show', title, year, image };
    }
};
<|endoftext|>