<commit_before>(function(){
    var gulp = require('gulp');
    var $    = require('gulp-load-plugins')({lazy:false});
    var runSequence = require('run-sequence');
    var changed = require('gulp-changed');
    var plumber = require('gulp-plumber');
    var to5 = require('gulp-6to5');
    var sourcemaps = require('gulp-sourcemaps');
    var paths = require('../paths');
    var compilerOptions = require('../6to5-options');
    var assign = Object.assign || require('object.assign');

    var build = {
      source: {
            js   : buildSystem(paths.source.js, paths.output),
            html : buildHTML([paths.source.html, paths.source.jade], paths.output),
            style: buildCss(paths.source.style)
        },

      plugins:{
            js    : buildSystem(paths.plugins.js, paths.output)    ,
            html  : buildHTML([paths.plugins.html, paths.plugins.jade], paths.output)    ,
            fonts : buildFonts(paths.plugins.fonts)  ,
            styles: buildStylus(paths.plugins.styl.index)
        }
    }

    gulp
        .task('build',
              $.sequence( 'clean'
                         , 'build:source'
                         , 'build:plugins'
                         ))

    gulp
        .task('source:style' , build.source.css   )
        .task('source:html' , build.source.html )
        .task('source:js'   , build.source.js   )
        .task('build:source',
              $.sequence( 'source:js'
                        , 'source:html'
                        , 'source:style'
                        ))
    gulp
        .task('plugins:styles', build.plugins.styles )
        .task('plugins:fonts' , build.plugins.fonts  )
        .task('plugins:html'  , build.plugins.html   )
        .task('plugins:js'    , build.plugins.js     )
        .task('build:plugins' ,
              $.sequence( 'plugins:styles'
                        , 'plugins:html'
                        , 'plugins:js'
                        , 'plugins:fonts'
                        ))

    //////////////////////////

    // transpiles changed es6 files to SystemJS format

    // the plumber() call prevents 'pipe breaking' caused
    // by errors from other gulp plugins
    // https://www.npmjs.com/package/gulp-plumber
    function buildSystem(source, dest) {

        return function(){

            return gulp
              .src(source)
              .pipe($.flatten())
              .pipe(plumber())
              .pipe(changed(paths.output, {extension: '.js'}))
              .pipe(sourcemaps.init())
              .pipe(to5(assign({}, compilerOptions, {modules:'system'})))
              .pipe(sourcemaps.write({includeContent: false, sourceRoot: '/' + paths.root }))
              .pipe(gulp.dest(dest));

        }

    }

    // copies changed html files to the output directory

    // this task calls the clean task (located
    // in ./clean.js), then runs the build-system
    // and build-html tasks in parallel
    // https://www.npmjs.com/package/gulp-run-sequence
    function buildHTML(source, dest) {

        return function() {
            var jadeFilter = $.filter('**/*.jade')
            return gulp
              .src(source)
              .pipe(jadeFilter)
              .pipe($.plumber())
              .pipe($.jade())
              .pipe($.plumber.stop())
              .pipe(jadeFilter.restore())
              .pipe($.flatten())
              .pipe(changed(paths.output, {extension: '.html'}))
              .pipe(gulp.dest(dest));

        }

    }

    function buildFonts(source) {

        return function() {

           return gulp
              .src(source)
              .pipe(gulp.dest(paths.output + '/fonts'))

        }

    }

    function buildStylus(source) {

        return function() {

            return gulp
              .src(source)
              .pipe($.plumber())
              .pipe($.stylus())
              .pipe($.concat('ui-aurelia.css'))
              .pipe($.autoprefixer())
              .pipe($.plumber.stop())
              .pipe(gulp.dest(paths.output))

        }

    }

    function buildCss(source) {

        return function() {
            console.log(source)
            return gulp
              .src(source)
              .pipe($.concat('system.css'))
              .pipe($.autoprefixer())
              .pipe(gulp.dest(paths.output))

        }

    }

})();
<commit_msg>Update: Fix Issue with JS Sourcemaps<commit_after>(function(){
    var gulp = require('gulp');
    var $    = require('gulp-load-plugins')({lazy:false});
    var runSequence = require('run-sequence');
    var changed = require('gulp-changed');
    var plumber = require('gulp-plumber');
    var to5 = require('gulp-6to5');
    var sourcemaps = require('gulp-sourcemaps');
    var paths = require('../paths');
    var compilerOptions = require('../6to5-options');
    var assign = Object.assign || require('object.assign');

    var build = {
      source: {
            js   : buildSystem(paths.source.js, paths.output),
            html : buildHTML([paths.source.html, paths.source.jade], paths.output),
            style: buildCss(paths.source.style)
        },

      plugins:{
            js    : buildSystem(paths.plugins.js, paths.output)    ,
            html  : buildHTML([paths.plugins.html, paths.plugins.jade], paths.output)    ,
            fonts : buildFonts(paths.plugins.fonts)  ,
            styles: buildStylus(paths.plugins.styl.index)
        }
    }

    gulp
        .task('build',
              $.sequence( 'clean'
                         , 'build:source'
                         , 'build:plugins'
                         ))

    gulp
        .task('source:style' , build.source.css   )
        .task('source:html' , build.source.html )
        .task('source:js'   , build.source.js   )
        .task('build:source',
              $.sequence( 'source:js'
                        , 'source:html'
                        , 'source:style'
                        ))
    gulp
        .task('plugins:styles', build.plugins.styles )
        .task('plugins:fonts' , build.plugins.fonts  )
        .task('plugins:html'  , build.plugins.html   )
        .task('plugins:js'    , build.plugins.js     )
        .task('build:plugins' ,
              $.sequence( 'plugins:styles'
                        , 'plugins:html'
                        , 'plugins:js'
                        , 'plugins:fonts'
                        ))

    //////////////////////////

    // transpiles changed es6 files to SystemJS format

    // the plumber() call prevents 'pipe breaking' caused
    // by errors from other gulp plugins
    // https://www.npmjs.com/package/gulp-plumber
    function buildSystem(source, dest) {

        return function(){

            return gulp
              .src(source)
              .pipe($.flatten())
              .pipe(plumber())
              .pipe(changed(paths.output, {extension: '.js'}))
              .pipe(sourcemaps.init())
              .pipe(to5(assign({}, compilerOptions, {modules:'system'})))
              .pipe(sourcemaps.write({includeContent: false, sourceRoot: '/' + paths.output }))
              .pipe(gulp.dest(dest));

        }

    }

    // copies changed html files to the output directory

    // this task calls the clean task (located
    // in ./clean.js), then runs the build-system
    // and build-html tasks in parallel
    // https://www.npmjs.com/package/gulp-run-sequence
    function buildHTML(source, dest) {

        return function() {
            var jadeFilter = $.filter('**/*.jade')
            return gulp
              .src(source)
              .pipe(jadeFilter)
              .pipe($.plumber())
              .pipe($.jade())
              .pipe($.plumber.stop())
              .pipe(jadeFilter.restore())
              .pipe($.flatten())
              .pipe(changed(paths.output, {extension: '.html'}))
              .pipe(gulp.dest(dest));

        }

    }

    function buildFonts(source) {

        return function() {

           return gulp
              .src(source)
              .pipe(gulp.dest(paths.output + '/fonts'))

        }

    }

    function buildStylus(source) {

        return function() {

            return gulp
              .src(source)
              .pipe($.plumber())
              .pipe($.stylus())
              .pipe($.concat('ui-aurelia.css'))
              .pipe($.autoprefixer())
              .pipe($.plumber.stop())
              .pipe(gulp.dest(paths.output))

        }

    }

    function buildCss(source) {

        return function() {
            console.log(source)
            return gulp
              .src(source)
              .pipe($.concat('system.css'))
              .pipe($.autoprefixer())
              .pipe(gulp.dest(paths.output))

        }

    }

})();
<|endoftext|>


<commit_before>// whatever javascript files you place in this iFEF `folder will be concatenated to a single file in the js library

function test(arg){
	var output = arg || 'fire';
	console.log(output);
}

var AV = {};

AV.routes = Backbone.Router.extend({

	routes: {
		'': 'index',
	},

	initialize:function(){
		test('init');
		this.post = new AV.post();
		this.get = new AV.get();
		this.sources = new AV.sources();
		this.put = new AV.put();
		this.delete = new AV.delete();
		this.collection = new Backbone.Collection([
			this.post,
			this.get,
			this.sources,
			this.put,
			this.delete
			]);
		this.testerView = new AV.testerView({collection: this.collection});
	},

	index: function() {
		this.testerView.render();
	},

});

AV.testerView = Backbone.View.extend({

	el: '#verb-tests',
	initialize: function(){},
	events: {
		"click button#post" : 'post',
		"click button#get" : 'get',
		"click button#get-srcs" : 'getSRCS',
		"click button#put" : 'put',
		"click button#delete" : 'delete',
		"click button#clear" : 'clear'
	},
	render:function(){
		this.$el.html(
			'
'
			+ '
<button class="btn btn-success" id="post">post</button>
'
			+ '
<button class="btn btn-primary" id="get">get source</button>
'
			+ '
<button class="btn btn-primary" id="get-srcs">get sources</button>
'
			+ '
<button class="btn btn-warning" id="put">put</button>
'
			+ '
<button class="btn btn-danger" id="delete">delete</button>
'
			+ '
<button class="btn btn-default" id="clear">clear</button>
'
			+ '
');
		this.$el.find('ul li').css( {'list-style' : 'none', 'margin-bottom' : '5px' });
	},
	post:function(){

		test('post');
		this.collection.models[0].save({
			name: 'continuing the tests',
			data: 'good one'
		},
		{
			success:function() { test('success');},
			error:function(a,b,c) { test(a); test(b); test(c);}
		});
		location.delay(5000).reload(true);
	},

	get:function(){test('get');
		this.collection.models[1].fetch({
			success:function() { test('success');},
			error:function() { test('not so much');}
		});
		location.reload(true);
	},

	getSRCS:function(){test('get-srcs');
		test('get-ws');
		this.collection.models[2].fetch();
		location.reload(true);
	},

	put:function(){test('put');},

	delete:function(){test('delete');},

	clear:function(){
		$.post('redirect.php?clear=1', function() { test('clear'); });
		location.reload(true);
	}

});

AV.post = Backbone.Model.extend({
	url: 'redirect.php/juxta/source',

	defaults:{
		name: '',
		type: 'raw',
		contentType: 'txt',
		data: ''
	}
});

AV.get = Backbone.Model.extend({
	url: 'redirect.php/juxta/source/13.json'
});

AV.sources = Backbone.Model.extend({
	url: 'redirect.php/juxta/source.json'
});

AV.put = Backbone.Model.extend({});

AV.delete = Backbone.Model.extend({});

var readysetgo = new AV.routes();

$(function(){
  Backbone.history.start();
});
<commit_msg>fixed paths<commit_after>// whatever javascript files you place in this iFEF `folder will be concatenated to a single file in the js library

function test(arg){
	var output = arg || 'fire';
	console.log(output);
}

var AV = {};

AV.routes = Backbone.Router.extend({

	routes: {
		'': 'index',
	},

	initialize:function(){
		test('init');
		this.post = new AV.post();
		this.get = new AV.get();
		this.sources = new AV.sources();
		this.put = new AV.put();
		this.delete = new AV.delete();
		this.collection = new Backbone.Collection([
			this.post,
			this.get,
			this.sources,
			this.put,
			this.delete
			]);
		this.testerView = new AV.testerView({collection: this.collection});
	},

	index: function() {
		this.testerView.render();
	},

});

AV.testerView = Backbone.View.extend({

	el: '#verb-tests',
	initialize: function(){},
	events: {
		"click button#post" : 'post',
		"click button#get" : 'get',
		"click button#get-srcs" : 'getSRCS',
		"click button#put" : 'put',
		"click button#delete" : 'delete',
		"click button#clear" : 'clear'
	},
	render:function(){
		this.$el.html(
			'
'
			+ '
<button class="btn btn-success" id="post">post</button>
'
			+ '
<button class="btn btn-primary" id="get">get source</button>
'
			+ '
<button class="btn btn-primary" id="get-srcs">get sources</button>
'
			+ '
<button class="btn btn-warning" id="put">put</button>
'
			+ '
<button class="btn btn-danger" id="delete">delete</button>
'
			+ '
<button class="btn btn-default" id="clear">clear</button>
'
			+ '
');
		this.$el.find('ul li').css( {'list-style' : 'none', 'margin-bottom' : '5px' });
	},
	post:function(){

		test('post');
		this.collection.models[0].save({
			name: 'continuing the tests',
			data: 'good one'
		},
		{
			success:function() { test('success');},
			error:function(a,b,c) { test(a); test(b); test(c);}
		});
		location.reload(true);
	},

	get:function(){test('get');
		this.collection.models[1].fetch({
			success:function() { test('success');},
			error:function() { test('not so much');}
		});
		location.reload(true);
	},

	getSRCS:function(){test('get-srcs');
		test('get-ws');
		this.collection.models[2].fetch();
		location.reload(true);
	},

	put:function(){test('put');},

	delete:function(){test('delete');},

	clear:function(){
		$.post('redirect.php?clear=1', function() { test('clear'); });
		location.reload(true);
	}

});

AV.post = Backbone.Model.extend({
	url: '../../redirect.php/juxta/source',

	defaults:{
		name: '',
		type: 'raw',
		contentType: 'txt',
		data: ''
	}
});

AV.get = Backbone.Model.extend({
	url: '../../redirect.php/juxta/source/13.json'
});

AV.sources = Backbone.Model.extend({
	url: '../..redirect.php/juxta/source.json'
});

AV.put = Backbone.Model.extend({});

AV.delete = Backbone.Model.extend({});

var readysetgo = new AV.routes();

$(function(){
  Backbone.history.start();
});
<|endoftext|>


<commit_before>jQuery(() => {
  // Are we on a repo page?
  const [, user, repo] = document.location.pathname.match(/\/+([^/]*)\/([^(/|\?)]*)/) || [];
  if (!user) return

  // Does the repo have a package.json?
  if (!document.querySelector('.files [title="package.json"]')) return

  // Assemble API URL for fetching raw json from github
  const pkgUrl = 'https://raw.githubusercontent.com/' + user + '/' + repo + '/master/package.json'

  // Set up list containers and headings
  const $template = $('#readme').clone().empty().removeAttr('id');

  const $depsList = $("
");
  const $devDepsList = $("
");
  const $depsVisBtn = $("<button class='btn btn-sm viz-btn' style='float: right; margin: 5px 5px 0 0;' type='button'>Dependency tree visualization");

  $template.clone()
  .append($depsVisBtn)
  .append('
Dependencies', $depsList)
  .appendTo('.repository-content');

  $template.clone()
  .append('
Dev dependencies', $devDepsList)
  .appendTo('.repository-content');

  backgroundFetch(pkgUrl).then(pkg => {
    $depsVisBtn.wrap(`<a href="http://npm.anvaka.com/#/view/2d/${pkg.name}"></a>`);
    addDependencies(pkg.dependencies, $depsList);
    addDependencies(pkg.devDependencies, $devDepsList);
  });

  function addDependencies(dependencies, $list) {
    if (!dependencies) {
      return $list.append("
None found in package.json
");
    }

    const depNames = Object.keys(dependencies).forEach(name => {
      const depUrl = 'https://registry.npmjs.org/' + name
      const version = dependencies[name]

      const $dep = $("
" + name + ' ' + version + '
')
      $dep.appendTo($list);
      backgroundFetch(depUrl).then(dep => {
        $dep.append(dep.description)

        if (dep.repository) {
          $dep.append(" (repo)")
        }
      })
    });
  }
});
<commit_msg>No need to wait for domready<commit_after>const [, user, repo] = document.location.pathname.match(/\/+([^/]*)\/([^(/|\?)]*)/) || [];

// Are we on a repo page?
if (user) {

  // Does the repo have a package.json?
  if (!document.querySelector('.files [title="package.json"]')) return

  // Assemble API URL for fetching raw json from github
  const pkgUrl = 'https://raw.githubusercontent.com/' + user + '/' + repo + '/master/package.json'

  // Set up list containers and headings
  const $template = $('#readme').clone().empty().removeAttr('id');

  const $depsList = $("
");
  const $devDepsList = $("
");
  const $depsVisBtn = $("<button class='btn btn-sm viz-btn' style='float: right; margin: 5px 5px 0 0;' type='button'>Dependency tree visualization");

  $template.clone()
  .append($depsVisBtn)
  .append('
Dependencies', $depsList)
  .appendTo('.repository-content');

  $template.clone()
  .append('
Dev dependencies', $devDepsList)
  .appendTo('.repository-content');

  backgroundFetch(pkgUrl).then(pkg => {
    $depsVisBtn.wrap(`<a href="http://npm.anvaka.com/#/view/2d/${pkg.name}"></a>`);
    addDependencies(pkg.dependencies, $depsList);
    addDependencies(pkg.devDependencies, $devDepsList);
  });

  function addDependencies(dependencies, $list) {
    if (!dependencies) {
      return $list.append("
None found in package.json
");
    }

    const depNames = Object.keys(dependencies).forEach(name => {
      const depUrl = 'https://registry.npmjs.org/' + name
      const version = dependencies[name]

      const $dep = $("
" + name + ' ' + version + '
')
      $dep.appendTo($list);
      backgroundFetch(depUrl).then(dep => {
        $dep.append(dep.description)

        if (dep.repository) {
          $dep.append(" (repo)")
        }
      })
    });
  }
}
<|endoftext|>


<commit_before>/* global Node, chrome */

/**
 * This file is part of Random User-Agent Browser Extension
 * @link https://github.com/tarampampam/random-user-agent
 *
 * Copyright (C) 2016 tarampampam <github.com/tarampampam>
 *
 * Everyone is permitted to copy and distribute verbatim or modified copies of this license
 * document, and changing it is allowed as long as the name is changed.
 *
 * DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING,
 * DISTRIBUTION AND MODIFICATION
 *
 * 0. You just DO WHAT THE FUCK YOU WANT TO.
 */

"use strict";

/**
 * UI methods
 *
 * @type {object}
 */
var UI = {
  /**
   * Add event to DOM element
   *
   * @param   {object}       $el
   * @param   {string|array} name
   * @param   {callable}     callback
   * @returns {void}
   */
  addEvent: function ($el, name, callback) {
    var aE = function ($el, name, callback) {
      if ($el.addEventListener) {
        $el.addEventListener(name, callback, false);
      } else if ($el.attachEvent) {
        $el.attachEvent('on' + name, callback);
      } else {
        $el['on' + name] = callback;
      }
    };

    if (typeof name === 'string') {
      name = [name];
    }
    if (Object.prototype.toString.call(name) === '[object Array]') {
      for (var i = 0, len = name.length; i < len; i++) {
        switch (typeof $el) {
          case 'object':
            if ($el instanceof Node) {
              aE($el, name[i], callback);
            } else if ($el instanceof Array) {
              for (var j = $el.length - 1; j >= 0; j--) {
                aE($el[j], name[i], callback);
              }
            }
            break;
          default:
            throw new Error('Element must be object or instance of an array!');
        }
      }
    } else {
      throw new Error('Event name must be string or array');
    }
  },

  /**
   * Check for exists css class with element
   *
   * @param   {object} $el
   * @param   {string} css_class
   * @returns {boolean}
   */
  hasCssClass: function ($el, css_class) {
    return $el.className.split(' ').indexOf(css_class) > -1;
  },

  /**
   * Add element css class
   *
   * @param   {object} $el
   * @param   {string} css_class
   * @returns {void}
   */
  addCssClass: function ($el, css_class) {
    $el.className += ' ' + css_class.replace(new RegExp('  ', 'g'), ' ').trim();
  },

  /**
   * Remove element css class
   *
   * @param   {object} $el
   * @param   {string} css_class
   * @returns {void}
   */
  removeCssClass: function ($el, css_class) {
    $el.className = $el.className.replace(new RegExp(css_class, 'g'), '').replace(new RegExp('  ', 'g'), ' ').trim();
  },

  /**
   * Find all objects with some css class and execute callback
   *
   * @param   {string}   class_name
   * @param   {callable} callback
   * @returns {void}
   */
  forEachCssClass: function (class_name, callback) {
    if ((typeof class_name === 'string') && (class_name !== '') && (typeof callback === 'function')) {
      for (var elements = document.getElementsByClassName(class_name), i = 0, len = elements.length; i < len; i++) {
        callback.call(null, elements[i]);
      }
    }
  },

  /**
   * Get element by ID and execute callback if element is exists
   *
   * @param   {string}   element_id
   * @param   {callable} callback
   * @returns {void}
   */
  getElementById: function(element_id, callback) {
    if ((typeof element_id === 'string') && (element_id !== '') && (typeof callback === 'function')) {
      var $el = document.getElementById(element_id);
      typeof $el !== 'undefined' && callback.call(null, $el);
    }
  },

  /**
   * Test element - is textarea?
   *
   * @param   {object} $el
   * @returns {boolean}
   */
  isTextarea: function($el) {
    return (typeof $el !== 'undefined' && $el.tagName.toLowerCase() === 'textarea');
  },

  /**
   * Test element - is input?
   *
   * @param   {object} $el
   * @returns {boolean}
   */
  isInput: function($el) {
    return (typeof $el !== 'undefined' && $el.tagName.toLowerCase() === 'input');
  },

  /**
   * Test element - is inpit checkbox?
   *
   * @param   {object} $el
   * @returns {boolean}
   */
  isCheckbox: function($el) {
    return (this.isInput($el) && $el.type.toLowerCase() === 'checkbox');
  },

  /**
   * Test element - is inpit text?
   *
   * @param   {object} $el
   * @returns {boolean}
   */
  isInputText: function($el) {
    return (this.isInput($el) && $el.type.toLowerCase() === 'text');
  },

  /**
   * Test element - is inpit number?
   *
   * @param   {object} $el
   * @returns {boolean}
   */
  isInputNumber: function($el) {
    return (this.isInput($el) && $el.type.toLowerCase() === 'number');
  },

  /**
   * Change extension icon (based on state)
   *
   * @param   {string} state
   * @returns {Promise}
   */
  changeStateIcon: function(state) {
    var image_src;
    switch (state) {
      case 'active':
        image_src = '/img/48x48.png';
        break;
      case 'inactive':
        image_src = '/img/48x48g.png';
        break;
      default:
        image_src = '/img/48x48t.png';
        break;
    }
    return chrome.browserAction.setIcon({path: image_src}, function(){});
  },

  /**
   * Call callback for all elements with not empty attribute like 'data-%data_attrib_name%'
   *
   * @param   {string} data_attrib_name
   * @param   {callable} callback
   * @returns {void}
   */
  forEachWithDataAttrib: function(data_attrib_name, callback) {
    if ((typeof data_attrib_name === 'string') && (data_attrib_name !== '') && (typeof callback === 'function')) {
      for (var elements = document.body.getElementsByTagName('*'), i = 0, len = elements.length; i < len; i++) {
        var $el = elements[i], data_attrib_value = $el.getAttribute('data-' + data_attrib_name);
        if (data_attrib_value !== '' && data_attrib_value !== null) {
          callback.call(null, $el, data_attrib_value);
        }
      }
    }
  },

  /**
   * Locatize element content with 'chrome.i18n'
   *
   * @param   {object} $el
   * @param   {string} data_attrib_value
   * @returns {boolean}
   */
  localizeElement: function($el, data_attrib_value) {
    if (typeof $el !== 'undefined' && typeof data_attrib_value === 'string') {
      var localized_text = chrome.i18n.getMessage(data_attrib_value);
      if (typeof localized_text === 'string' && localized_text !== '') {
        $el.innerHTML = localized_text;
        return true;
      }
    }
    return false;
  }
};
<commit_msg>Fixing setIcon() call<commit_after>/* global Node, chrome */

/**
 * This file is part of Random User-Agent Browser Extension
 * @link https://github.com/tarampampam/random-user-agent
 *
 * Copyright (C) 2016 tarampampam <github.com/tarampampam>
 *
 * Everyone is permitted to copy and distribute verbatim or modified copies of this license
 * document, and changing it is allowed as long as the name is changed.
 *
 * DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING,
 * DISTRIBUTION AND MODIFICATION
 *
 * 0. You just DO WHAT THE FUCK YOU WANT TO.
 */

"use strict";

/**
 * UI methods
 *
 * @type {object}
 */
var UI = {
  /**
   * Add event to DOM element
   *
   * @param   {object}       $el
   * @param   {string|array} name
   * @param   {callable}     callback
   * @returns {void}
   */
  addEvent: function ($el, name, callback) {
    var aE = function ($el, name, callback) {
      if ($el.addEventListener) {
        $el.addEventListener(name, callback, false);
      } else if ($el.attachEvent) {
        $el.attachEvent('on' + name, callback);
      } else {
        $el['on' + name] = callback;
      }
    };

    if (typeof name === 'string') {
      name = [name];
    }
    if (Object.prototype.toString.call(name) === '[object Array]') {
      for (var i = 0, len = name.length; i < len; i++) {
        switch (typeof $el) {
          case 'object':
            if ($el instanceof Node) {
              aE($el, name[i], callback);
            } else if ($el instanceof Array) {
              for (var j = $el.length - 1; j >= 0; j--) {
                aE($el[j], name[i], callback);
              }
            }
            break;
          default:
            throw new Error('Element must be object or instance of an array!');
        }
      }
    } else {
      throw new Error('Event name must be string or array');
    }
  },

  /**
   * Check for exists css class with element
   *
   * @param   {object} $el
   * @param   {string} css_class
   * @returns {boolean}
   */
  hasCssClass: function ($el, css_class) {
    return $el.className.split(' ').indexOf(css_class) > -1;
  },

  /**
   * Add element css class
   *
   * @param   {object} $el
   * @param   {string} css_class
   * @returns {void}
   */
  addCssClass: function ($el, css_class) {
    $el.className += ' ' + css_class.replace(new RegExp('  ', 'g'), ' ').trim();
  },

  /**
   * Remove element css class
   *
   * @param   {object} $el
   * @param   {string} css_class
   * @returns {void}
   */
  removeCssClass: function ($el, css_class) {
    $el.className = $el.className.replace(new RegExp(css_class, 'g'), '').replace(new RegExp('  ', 'g'), ' ').trim();
  },

  /**
   * Find all objects with some css class and execute callback
   *
   * @param   {string}   class_name
   * @param   {callable} callback
   * @returns {void}
   */
  forEachCssClass: function (class_name, callback) {
    if ((typeof class_name === 'string') && (class_name !== '') && (typeof callback === 'function')) {
      for (var elements = document.getElementsByClassName(class_name), i = 0, len = elements.length; i < len; i++) {
        callback.call(null, elements[i]);
      }
    }
  },

  /**
   * Get element by ID and execute callback if element is exists
   *
   * @param   {string}   element_id
   * @param   {callable} callback
   * @returns {void}
   */
  getElementById: function(element_id, callback) {
    if ((typeof element_id === 'string') && (element_id !== '') && (typeof callback === 'function')) {
      var $el = document.getElementById(element_id);
      typeof $el !== 'undefined' && callback.call(null, $el);
    }
  },

  /**
   * Test element - is textarea?
   *
   * @param   {object} $el
   * @returns {boolean}
   */
  isTextarea: function($el) {
    return (typeof $el !== 'undefined' && $el.tagName.toLowerCase() === 'textarea');
  },

  /**
   * Test element - is input?
   *
   * @param   {object} $el
   * @returns {boolean}
   */
  isInput: function($el) {
    return (typeof $el !== 'undefined' && $el.tagName.toLowerCase() === 'input');
  },

  /**
   * Test element - is inpit checkbox?
   *
   * @param   {object} $el
   * @returns {boolean}
   */
  isCheckbox: function($el) {
    return (this.isInput($el) && $el.type.toLowerCase() === 'checkbox');
  },

  /**
   * Test element - is inpit text?
   *
   * @param   {object} $el
   * @returns {boolean}
   */
  isInputText: function($el) {
    return (this.isInput($el) && $el.type.toLowerCase() === 'text');
  },

  /**
   * Test element - is inpit number?
   *
   * @param   {object} $el
   * @returns {boolean}
   */
  isInputNumber: function($el) {
    return (this.isInput($el) && $el.type.toLowerCase() === 'number');
  },

  /**
   * Change extension icon (based on state)
   *
   * @param   {string} state
   * @returns {void}
   */
  changeStateIcon: function(state) {
    var image_src;
    switch (state) {
      case 'active':
        image_src = '/img/48x48.png';
        break;
      case 'inactive':
        image_src = '/img/48x48g.png';
        break;
      default:
        image_src = '/img/48x48t.png';
        break;
    }
    chrome.browserAction.setIcon({path: image_src});
  },

  /**
   * Call callback for all elements with not empty attribute like 'data-%data_attrib_name%'
   *
   * @param   {string} data_attrib_name
   * @param   {callable} callback
   * @returns {void}
   */
  forEachWithDataAttrib: function(data_attrib_name, callback) {
    if ((typeof data_attrib_name === 'string') && (data_attrib_name !== '') && (typeof callback === 'function')) {
      for (var elements = document.body.getElementsByTagName('*'), i = 0, len = elements.length; i < len; i++) {
        var $el = elements[i], data_attrib_value = $el.getAttribute('data-' + data_attrib_name);
        if (data_attrib_value !== '' && data_attrib_value !== null) {
          callback.call(null, $el, data_attrib_value);
        }
      }
    }
  },

  /**
   * Locatize element content with 'chrome.i18n'
   *
   * @param   {object} $el
   * @param   {string} data_attrib_value
   * @returns {boolean}
   */
  localizeElement: function($el, data_attrib_value) {
    if (typeof $el !== 'undefined' && typeof data_attrib_value === 'string') {
      var localized_text = chrome.i18n.getMessage(data_attrib_value);
      if (typeof localized_text === 'string' && localized_text !== '') {
        $el.innerHTML = localized_text;
        return true;
      }
    }
    return false;
  }
};
<|endoftext|>


<commit_before>class SomaPlayerPopup {
  constructor() {
    this.findElements()
    this.handleLinks()
    this.hookupMenu()
    this.applyTheme()
    this.fetchSomaStations()
    this.listenForPlayback()
  }

  hookupMenu() {
    this.stationMenuToggle.addEventListener('click', () => {
      if (this.stationMenuToggle.classList.contains('disabled')) {
        return
      }

      this.toggleStationMenu()
    })

    window.addEventListener('keydown', event => this.onKeydown(event))

    window.addEventListener('click', event => {
      const dropdown = event.target.closest('.dropdown')

      if (!dropdown) {
        this.closeStationMenu()
      }
    })
  }

  onKeydown(event) {
    switch (event.key) {
      case 'ArrowDown':
        this.focusStationListItem(1)
        this.openStationMenu()
        break
      case 'ArrowUp':
        this.focusStationListItem(-1)
        this.openStationMenu()
        break
      case 'Enter':
        this.handleEnter(event)
        break
      case ' ':
        this.handleSpace(event)
        break
    }
  }

  handleSpace(event) {
    event.preventDefault()

    chrome.runtime.sendMessage({ action: 'info' }, info => {
      if (info.paused) {
        this.play()
      } else {
        this.pause()
      }
    })
  }

  handleEnter(event) {
    if (this.stationMenuToggle.classList.contains('selected')) {
      event.preventDefault()

      const focusedItem = this.stationListEl.querySelector('.station-list-item.focused')
      if (focusedItem) {
        const button = focusedItem.querySelector('.station-button')
        this.playStationFromButton(button)
      }
    }
  }

  focusStationListItem(offset) {
    const focusedItem = this.stationListEl.querySelector('.station-list-item.focused')
    const listItems = Array.from(this.stationListEl.querySelectorAll('.station-list-item'))
    const firstListItem = listItems[0]
    const lastListItem = listItems[listItems.length - 1]
    let newFocusedItem

    if (focusedItem) {
      const index = listItems.indexOf(focusedItem)
      focusedItem.classList.remove('focused')

      if (offset > 0) {
        newFocusedItem = listItems[index + 1] || firstListItem
      } else {
        newFocusedItem = listItems[index - 1] || lastListItem
      }

    } else {
      if (offset > 0) {
        newFocusedItem = firstListItem
      } else {
        newFocusedItem = lastListItem
      }
    }

    if (newFocusedItem) {
      newFocusedItem.classList.add('focused')
      newFocusedItem.scrollIntoView()
    }
  }

  openStationMenu() {
    const container = this.stationMenuToggle.closest('.dropdown')
    container.classList.add('active')
    this.stationMenuToggle.classList.add('selected')

    const focusedItem = this.stationListEl.querySelector('.station-list-item.focused')
    if (focusedItem) {
      focusedItem.scrollIntoView()
    }
  }

  closeStationMenu() {
    const container = this.stationMenuToggle.closest('.dropdown')
    container.classList.remove('active')
    this.stationMenuToggle.classList.remove('selected')
  }

  toggleStationMenu() {
    if (this.stationMenuToggle.classList.contains('selected')) {
      this.closeStationMenu()
    } else {
      this.openStationMenu()
    }
  }

  listenForPlayback() {
    this.playButton.addEventListener('click', () => this.play())
    this.pauseButton.addEventListener('click', () => this.pause())
  }

  findElements() {
    this.stationMenuToggle = document.getElementById('station-menu-toggle')
    this.stationListEl = document.getElementById('station-list')
    this.stationListItemTpl = document.getElementById('station-list-item-template')
    this.playButton = document.getElementById('play')
    this.pauseButton = document.getElementById('pause')
    this.currentInfoEl = document.getElementById('currently-playing')
    this.trackListEl = document.getElementById('track-list')
    this.trackListItemTpl = document.getElementById('track-list-item-template')
    this.stationImg = document.getElementById('station-image')
  }

  insertStationOptions(stations) {
    const activeStationID = SomaLocalStorage.getCurrentStation()

    for (const data of stations) {
      const listItem = this.createStationListItem(data, data.id === activeStationID)
      this.stationListEl.appendChild(listItem)
    }

    this.stationMenuToggle.classList.remove('disabled')
    this.loadCurrentInfo()
  }

  loadDefaultStations() {
    console.debug('loading default station list')
    const url = chrome.extension.getURL('defaultStations.json')
    SomaPlayerUtil.getJSON(url).
      then(defaultStations => this.insertStationOptions(defaultStations))
  }

  fetchSomaStations() {
    chrome.runtime.sendMessage({ action: 'get_stations' }, stations => {
      if (!stations || stations.length < 1) {
        const msg = { action: 'refresh_stations' }

        chrome.runtime.sendMessage(msg, (stations, error) => {
          if (error) {
            console.error('failed to fetch stations, using defaults')
            this.loadDefaultStations()
          } else {
            console.debug('fetched stations', stations)
            this.insertStationOptions(stations)
          }
        })

      } else {
        console.debug('stations already stored', stations)
        this.insertStationOptions(stations)
      }
    })
  }

  emptyTrackList() {
    while (this.trackListEl.hasChildNodes()) {
      this.trackListEl.removeChild(this.trackListEl.lastChild);
    }
  }

  listTracks(tracks) {
    this.emptyTrackList()

    for (const track of tracks) {
      const listItem = this.createTrackListItem(track)
      this.trackListEl.appendChild(listItem)
    }
  }

  onStationButtonClick(event) {
    const button = event.currentTarget
    this.playStationFromButton(button)
  }

  playStationFromButton(button) {
    const stationID = button.value
    const stationName = button.querySelector('.station-name').textContent
    const focusedItem = this.stationListEl.querySelector('.station-list-item.focused')
    const listItem = button.closest('.station-list-item')

    if (focusedItem) {
      focusedItem.classList.remove('focused')
    }
    button.blur()
    this.toggleStationMenu()
    this.stationChanged(stationID, stationName)
    listItem.classList.add('focused')
  }

  createStationListItem(data, isActive) {
    const el = this.stationListItemTpl.content.cloneNode(true)
    const button = el.querySelector('.station-button')
    const nameEl = el.querySelector('.station-name')
    const imageEl = el.querySelector('.station-image')

    if (isActive) {
      const listItem = el.querySelector('.station-list-item')
      listItem.classList.add('focused')
    }

    imageEl.src = `station-images/${data.id}.png`
    button.value = data.id
    nameEl.textContent = data.title
    button.addEventListener('click', e => this.onStationButtonClick(e))

    return el
  }

  createTrackListItem(track) {
    const el = this.trackListItemTpl.content.cloneNode(true)
    const nameEl = el.querySelector('.track-name')
    const artistEl = el.querySelector('.artist')
    const timeEl = el.querySelector('.track-date')
    const date = new Date(track.date)

    nameEl.textContent = track.title
    artistEl.textContent = track.artist
    timeEl.textContent = this.prettyDate(date)

    return el
  }

  prettyDate(date) {
    const fullHours = date.getHours()
    const hours = fullHours - 12
    let minutes = date.getMinutes()
    if (minutes < 10) {
      minutes = `0${minutes}`
    }
    let amPM = 'am'
    if (fullHours >= 12) {
      amPM = 'pm'
    }
    return `${hours}:${minutes} ${amPM}`
  }

  hideTrackInfo() {
    this.emptyTrackList()
    this.trackListEl.classList.add('d-none')
  }

  loadCurrentInfo() {
    this.stationMenuToggle.classList.add('disabled')

    chrome.runtime.sendMessage({ action: 'info' }, info => {
      console.debug('finished info request, info', info);
      const currentStationButton = document.querySelector(`.station-button[value="${info.station}"]`)
      if (currentStationButton) {
        this.stationMenuToggle.value = info.station
        this.stationMenuToggle.textContent = currentStationButton.textContent
      }

      if (info.paused) {
        this.stationIsPaused()
      } else {
        this.play()
      }

      this.updateStationImage(info.station)
      this.stationMenuToggle.classList.remove('disabled')
      this.listTracks(info.tracks)
    })
  }

  stationIsPlaying() {
    this.pauseButton.classList.remove('d-none')
    this.playButton.classList.add('d-none')
    this.stationMenuToggle.focus()
  }

  stationIsPaused() {
    this.pauseButton.classList.add('d-none')
    this.playButton.classList.remove('d-none')
    this.playButton.disabled = false
    this.stationMenuToggle.focus()
  }

  play() {
    const station = this.stationMenuToggle.value
    if (!station || station.length < 1) {
      return
    }
    console.debug('play', station)
    this.updateStationImage(station)

    chrome.runtime.sendMessage({ action: 'play', station }, () => {
      this.stationIsPlaying()

      chrome.runtime.sendMessage({ action: 'info' }, info => {
        if (info.tracks && info.tracks.length > 0) {
          this.listTracks(info.tracks)
        } else {
          const api = new SomaAPI()
          api.getStationTracks(station).
            then(tracks => this.listTracks(tracks)).
            catch(error => console.error('failed to get tracks', error))
        }
      })
    })
  }

  pause() {
    const station = this.stationMenuToggle.value
    console.debug('pausing station', station)

    return new Promise(resolve => {
      chrome.runtime.sendMessage({ action: 'pause', station }, () => {
        this.stationIsPaused()
        this.stationMenuToggle.focus()
        resolve()
      })
    })
  }

  updateStationImage(station) {
    if (station && station.length > 0) {
      this.stationImg.src = `station-images/${station}.png`
      this.stationImg.classList.remove('d-none')
    } else {
      this.stationImg.classList.add('d-none')
    }
  }

  stationChanged(stationID, stationName) {
    if (stationID === '') {
      chrome.runtime.sendMessage({ action: 'clear' }, () => {
        this.playButton.disabled = true
        this.hideTrackInfo()
        this.pause()
      })
      return
    }

    this.stationMenuToggle.value = stationID
    this.stationMenuToggle.textContent = stationName
    this.closeStationMenu()

    chrome.runtime.sendMessage({ action: 'info' }, info => {
      const currentStation = info.station

      if (stationID !== currentStation) {
        console.debug(`station changed to ${stationID}`)
        this.playButton.disabled = false
        this.pause().then(() => this.play())
      }
    })
  }

  handleLinks() {
    const links = Array.from(document.querySelectorAll('a'));
    for (let i = 0; i < links.length; i++) {
      this.handleLink(links[i]);
    }
  }

  handleLink(link) {
    link.addEventListener('click', event => {
      event.preventDefault();
      let url;
      const href = link.href;
      const optionsSuffix = '#options';
      if (href.indexOf(optionsSuffix) === href.length - optionsSuffix.length) {
        url = chrome.extension.getURL('options.html');
      } else {
        url = href;
      }
      chrome.tabs.create({ url });
      return false;
    });
  }

  applyTheme() {
    SomaPlayerUtil.getOptions().then(opts => {
      const theme = opts.theme || 'light'
      document.body.classList.add(`theme-${theme}`)
    })
  }
}

document.addEventListener('DOMContentLoaded', () => {
  new SomaPlayerPopup();
});
<commit_msg>Delay listing tracks to fix popup not being tall enough on load<commit_after>class SomaPlayerPopup {
  constructor() {
    this.findElements()
    this.handleLinks()
    this.hookupMenu()
    this.applyTheme()
    this.fetchSomaStations()
    this.listenForPlayback()
  }

  hookupMenu() {
    this.stationMenuToggle.addEventListener('click', () => {
      if (this.stationMenuToggle.classList.contains('disabled')) {
        return
      }

      this.toggleStationMenu()
    })

    window.addEventListener('keydown', event => this.onKeydown(event))

    window.addEventListener('click', event => {
      const dropdown = event.target.closest('.dropdown')

      if (!dropdown) {
        this.closeStationMenu()
      }
    })
  }

  onKeydown(event) {
    switch (event.key) {
      case 'ArrowDown':
        this.focusStationListItem(1)
        this.openStationMenu()
        break
      case 'ArrowUp':
        this.focusStationListItem(-1)
        this.openStationMenu()
        break
      case 'Enter':
        this.handleEnter(event)
        break
      case ' ':
        this.handleSpace(event)
        break
    }
  }

  handleSpace(event) {
    event.preventDefault()

    chrome.runtime.sendMessage({ action: 'info' }, info => {
      if (info.paused) {
        this.play()
      } else {
        this.pause()
      }
    })
  }

  handleEnter(event) {
    if (this.stationMenuToggle.classList.contains('selected')) {
      event.preventDefault()

      const focusedItem = this.stationListEl.querySelector('.station-list-item.focused')
      if (focusedItem) {
        const button = focusedItem.querySelector('.station-button')
        this.playStationFromButton(button)
      }
    }
  }

  focusStationListItem(offset) {
    const focusedItem = this.stationListEl.querySelector('.station-list-item.focused')
    const listItems = Array.from(this.stationListEl.querySelectorAll('.station-list-item'))
    const firstListItem = listItems[0]
    const lastListItem = listItems[listItems.length - 1]
    let newFocusedItem

    if (focusedItem) {
      const index = listItems.indexOf(focusedItem)
      focusedItem.classList.remove('focused')

      if (offset > 0) {
        newFocusedItem = listItems[index + 1] || firstListItem
      } else {
        newFocusedItem = listItems[index - 1] || lastListItem
      }

    } else {
      if (offset > 0) {
        newFocusedItem = firstListItem
      } else {
        newFocusedItem = lastListItem
      }
    }

    if (newFocusedItem) {
      newFocusedItem.classList.add('focused')
      newFocusedItem.scrollIntoView()
    }
  }

  openStationMenu() {
    const container = this.stationMenuToggle.closest('.dropdown')
    container.classList.add('active')
    this.stationMenuToggle.classList.add('selected')

    const focusedItem = this.stationListEl.querySelector('.station-list-item.focused')
    if (focusedItem) {
      focusedItem.scrollIntoView()
    }
  }

  closeStationMenu() {
    const container = this.stationMenuToggle.closest('.dropdown')
    container.classList.remove('active')
    this.stationMenuToggle.classList.remove('selected')
  }

  toggleStationMenu() {
    if (this.stationMenuToggle.classList.contains('selected')) {
      this.closeStationMenu()
    } else {
      this.openStationMenu()
    }
  }

  listenForPlayback() {
    this.playButton.addEventListener('click', () => this.play())
    this.pauseButton.addEventListener('click', () => this.pause())
  }

  findElements() {
    this.stationMenuToggle = document.getElementById('station-menu-toggle')
    this.stationListEl = document.getElementById('station-list')
    this.stationListItemTpl = document.getElementById('station-list-item-template')
    this.playButton = document.getElementById('play')
    this.pauseButton = document.getElementById('pause')
    this.currentInfoEl = document.getElementById('currently-playing')
    this.trackListEl = document.getElementById('track-list')
    this.trackListItemTpl = document.getElementById('track-list-item-template')
    this.stationImg = document.getElementById('station-image')
  }

  insertStationOptions(stations) {
    const activeStationID = SomaLocalStorage.getCurrentStation()

    for (const data of stations) {
      const listItem = this.createStationListItem(data, data.id === activeStationID)
      this.stationListEl.appendChild(listItem)
    }

    this.stationMenuToggle.classList.remove('disabled')
    this.loadCurrentInfo()
  }

  loadDefaultStations() {
    console.debug('loading default station list')
    const url = chrome.extension.getURL('defaultStations.json')
    SomaPlayerUtil.getJSON(url).
      then(defaultStations => this.insertStationOptions(defaultStations))
  }

  fetchSomaStations() {
    chrome.runtime.sendMessage({ action: 'get_stations' }, stations => {
      if (!stations || stations.length < 1) {
        const msg = { action: 'refresh_stations' }

        chrome.runtime.sendMessage(msg, (stations, error) => {
          if (error) {
            console.error('failed to fetch stations, using defaults')
            this.loadDefaultStations()
          } else {
            console.debug('fetched stations', stations)
            this.insertStationOptions(stations)
          }
        })

      } else {
        console.debug('stations already stored', stations)
        this.insertStationOptions(stations)
      }
    })
  }

  emptyTrackList() {
    while (this.trackListEl.hasChildNodes()) {
      this.trackListEl.removeChild(this.trackListEl.lastChild);
    }
  }

  listTracks(tracks) {
    setTimeout(() => {
      this.emptyTrackList()

      for (const track of tracks) {
        const listItem = this.createTrackListItem(track)
        this.trackListEl.appendChild(listItem)
      }
    }, 70)
  }

  onStationButtonClick(event) {
    const button = event.currentTarget
    this.playStationFromButton(button)
  }

  playStationFromButton(button) {
    const stationID = button.value
    const stationName = button.querySelector('.station-name').textContent
    const focusedItem = this.stationListEl.querySelector('.station-list-item.focused')
    const listItem = button.closest('.station-list-item')

    if (focusedItem) {
      focusedItem.classList.remove('focused')
    }
    button.blur()
    this.toggleStationMenu()
    this.stationChanged(stationID, stationName)
    listItem.classList.add('focused')
  }

  createStationListItem(data, isActive) {
    const el = this.stationListItemTpl.content.cloneNode(true)
    const button = el.querySelector('.station-button')
    const nameEl = el.querySelector('.station-name')
    const imageEl = el.querySelector('.station-image')

    if (isActive) {
      const listItem = el.querySelector('.station-list-item')
      listItem.classList.add('focused')
    }

    imageEl.src = `station-images/${data.id}.png`
    button.value = data.id
    nameEl.textContent = data.title
    button.addEventListener('click', e => this.onStationButtonClick(e))

    return el
  }

  createTrackListItem(track) {
    const el = this.trackListItemTpl.content.cloneNode(true)
    const nameEl = el.querySelector('.track-name')
    const artistEl = el.querySelector('.artist')
    const timeEl = el.querySelector('.track-date')
    const date = new Date(track.date)

    nameEl.textContent = track.title
    artistEl.textContent = track.artist
    timeEl.textContent = this.prettyDate(date)

    return el
  }

  prettyDate(date) {
    const fullHours = date.getHours()
    const hours = fullHours - 12
    let minutes = date.getMinutes()
    if (minutes < 10) {
      minutes = `0${minutes}`
    }
    let amPM = 'am'
    if (fullHours >= 12) {
      amPM = 'pm'
    }
    return `${hours}:${minutes} ${amPM}`
  }

  hideTrackInfo() {
    this.emptyTrackList()
    this.trackListEl.classList.add('d-none')
  }

  loadCurrentInfo() {
    this.stationMenuToggle.classList.add('disabled')

    chrome.runtime.sendMessage({ action: 'info' }, info => {
      console.debug('finished info request, info', info);
      const currentStationButton = document.querySelector(`.station-button[value="${info.station}"]`)
      if (currentStationButton) {
        this.stationMenuToggle.value = info.station
        this.stationMenuToggle.textContent = currentStationButton.textContent
      }

      if (info.paused) {
        this.stationIsPaused()
      } else {
        this.play()
      }

      this.updateStationImage(info.station)
      this.stationMenuToggle.classList.remove('disabled')
      this.listTracks(info.tracks)
    })
  }

  stationIsPlaying() {
    this.pauseButton.classList.remove('d-none')
    this.playButton.classList.add('d-none')
    this.stationMenuToggle.focus()
  }

  stationIsPaused() {
    this.pauseButton.classList.add('d-none')
    this.playButton.classList.remove('d-none')
    this.playButton.disabled = false
    this.stationMenuToggle.focus()
  }

  play() {
    const station = this.stationMenuToggle.value
    if (!station || station.length < 1) {
      return
    }
    console.debug('play', station)
    this.updateStationImage(station)

    chrome.runtime.sendMessage({ action: 'play', station }, () => {
      this.stationIsPlaying()

      chrome.runtime.sendMessage({ action: 'info' }, info => {
        if (info.tracks && info.tracks.length > 0) {
          this.listTracks(info.tracks)
        } else {
          const api = new SomaAPI()
          api.getStationTracks(station).
            then(tracks => this.listTracks(tracks)).
            catch(error => console.error('failed to get tracks', error))
        }
      })
    })
  }

  pause() {
    const station = this.stationMenuToggle.value
    console.debug('pausing station', station)

    return new Promise(resolve => {
      chrome.runtime.sendMessage({ action: 'pause', station }, () => {
        this.stationIsPaused()
        this.stationMenuToggle.focus()
        resolve()
      })
    })
  }

  updateStationImage(station) {
    if (station && station.length > 0) {
      this.stationImg.src = `station-images/${station}.png`
      this.stationImg.classList.remove('d-none')
    } else {
      this.stationImg.classList.add('d-none')
    }
  }

  stationChanged(stationID, stationName) {
    if (stationID === '') {
      chrome.runtime.sendMessage({ action: 'clear' }, () => {
        this.playButton.disabled = true
        this.hideTrackInfo()
        this.pause()
      })
      return
    }

    this.stationMenuToggle.value = stationID
    this.stationMenuToggle.textContent = stationName
    this.closeStationMenu()

    chrome.runtime.sendMessage({ action: 'info' }, info => {
      const currentStation = info.station

      if (stationID !== currentStation) {
        console.debug(`station changed to ${stationID}`)
        this.playButton.disabled = false
        this.pause().then(() => this.play())
      }
    })
  }

  handleLinks() {
    const links = Array.from(document.querySelectorAll('a'));
    for (let i = 0; i < links.length; i++) {
      this.handleLink(links[i]);
    }
  }

  handleLink(link) {
    link.addEventListener('click', event => {
      event.preventDefault();
      let url;
      const href = link.href;
      const optionsSuffix = '#options';
      if (href.indexOf(optionsSuffix) === href.length - optionsSuffix.length) {
        url = chrome.extension.getURL('options.html');
      } else {
        url = href;
      }
      chrome.tabs.create({ url });
      return false;
    });
  }

  applyTheme() {
    SomaPlayerUtil.getOptions().then(opts => {
      const theme = opts.theme || 'light'
      document.body.classList.add(`theme-${theme}`)
    })
  }
}

document.addEventListener('DOMContentLoaded', () => {
  new SomaPlayerPopup();
});
<|endoftext|>


<commit_before>function setOpts() {
    browser.storage.local.get().then((res) => {
        res.options = res.hasOwnProperty("options") ? res.options : {};
        res.options.theme = res.options.hasOwnProperty("theme") ? res.options.theme : "light";
        res.options.background_color = res.options.hasOwnProperty("background_color") ? res.options.background_color : "ffffff";
        res.options.font_color = res.options.hasOwnProperty("font_color") ? res.options.font_color : "000000";
        res.options.background_color_dark = res.options.hasOwnProperty("background_color_dark") ? res.options.background_color_dark : "000000";
        res.options.font_color_dark = res.options.hasOwnProperty("font_color_dark") ? res.options.font_color_dark : "ffffff";
        res.options.width = res.options.hasOwnProperty("width") ? res.options.width : 400;
        res.options.height = res.options.hasOwnProperty("height") ? res.options.height : 300;
        res.options.font_family = res.options.hasOwnProperty("font_family") ? res.options.font_family : "default";
        res.options.font_size = res.options.hasOwnProperty("font_size") ? res.options.font_size : 16;
        res.options.font_css = res.options.hasOwnProperty("font_css") ? res.options.font_css : "";
        res.options.default_display = res.options.hasOwnProperty("default_display") ? res.options.default_display : "general_notes";
        res.options.per_site = res.options.hasOwnProperty("per_site") ? res.options.per_site : "domain";
        res.options.private_browsing = res.options.hasOwnProperty("private_browsing") ? res.options.private_browsing : false;
        res.options.subdomains_mode = res.options.hasOwnProperty("subdomains_mode") ? res.options.subdomains_mode : "blacklist";
        res.options.subdomains = res.options.hasOwnProperty("subdomains") ? res.options.subdomains : [];
        res.options.notification_badge = res.options.hasOwnProperty("notification_badge") ? res.options.notification_badge : "disabled";
        res.options.notification_badge_color = res.options.hasOwnProperty("notification_badge_color") ? res.options.notification_badge_color : "d90000";
        res.options.bullet_types = res.options.hasOwnProperty("bullet_types") ? res.options.bullet_types : ["*", "-", "+"];
        res.site_notes = res.hasOwnProperty("site_notes") ? res.site_notes : {};
        browser.storage.local.set(res);
    });
}
function escapeRegex(regex) {
    return regex.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
function bulletCounter(bulletTypes, note) {
    var bulletCount = 0;
    var escaped = bulletTypes.map(escapeRegex);
    var regex = new RegExp("^\\s*(" + escaped.join("|") + ")");
    for (var line of note.split("\n")) {
        if (line.search(regex) > -1) {
            ++bulletCount;
        }
    }
    return bulletCount;
}
function setBadge(bullet_types, notification_badge, notes, tabId) {
    if (notes !== "") {
        if (notification_badge.indexOf("enabled") > -1) {
            browser.browserAction.setBadgeText({text: "!", tabId: tabId});
        }
        if (notification_badge.indexOf("bullets") > -1) {
            var bulletCount = bulletCounter(bullet_types, notes);
            if (bulletCount > 0) {
                browser.browserAction.setBadgeText({text: bulletCount.toString(), tabId: tabId});
            } else if (notification_badge.indexOf("enabled") === -1) {
                browser.browserAction.setBadgeText({text: "", tabId: tabId});
            }
        }
    } else {
        browser.browserAction.setBadgeText({text: "", tabId: tabId});
    }
}
async function setBadgeSite(tab) {
    var res = await browser.storage.local.get(["site_notes", "options"]);
    if (!tab.incognito || (res.options.private_browsing && tab.incognito)) {
        var site = await siteParser(tab.url);
        if (res.site_notes.hasOwnProperty(site)) {
            setBadge(res.options.bullet_types, res.options.notification_badge, res.site_notes[site], tab.id)
        } else {
            browser.browserAction.setBadgeText({text: "", tabId: tab.id});
        }
    } else {
        browser.browserAction.setBadgeText({text: "", tabId: tab.id});
    }
}
async function setBadgeGeneral(tab) {
    var res = await browser.storage.local.get(["general_notes", "options"]);
    setBadge(res.options.bullet_types, res.options.notification_badge, res.general_notes, tab.id);
}
async function updateBadge() {
    var res = await browser.storage.local.get("options");
    var tabs = await browser.tabs.query({active: true});
    if (res.options.notification_badge !== "disabled") {
        switch (res.options.default_display) {
            case "site_notes":
                tabs.forEach(setBadgeSite);
                break;
            case "general_notes":
                tabs.forEach(setBadgeGeneral);
                break;
        }
    } else {
        var allTabs = await browser.tabs.query({});
        for (tab of allTabs) {
            browser.browserAction.setBadgeText({text: "", tabId: tab.id});
        }
    }
}
browser.storage.local.get("options").then((res) => {
    browser.browserAction.setBadgeBackgroundColor({color: "#" + res.options.notification_badge_color});
})
browser.tabs.onActivated.addListener(updateBadge);
browser.tabs.onUpdated.addListener(updateBadge);
browser.storage.onChanged.addListener(updateBadge);
browser.runtime.onInstalled.addListener(setOpts);
<commit_msg>Badge falls back to gen_notes on sites w/o notes<commit_after>function setOpts() {
    browser.storage.local.get().then((res) => {
        res.options = res.hasOwnProperty("options") ? res.options : {};
        res.options.theme = res.options.hasOwnProperty("theme") ? res.options.theme : "light";
        res.options.background_color = res.options.hasOwnProperty("background_color") ? res.options.background_color : "ffffff";
        res.options.font_color = res.options.hasOwnProperty("font_color") ? res.options.font_color : "000000";
        res.options.background_color_dark = res.options.hasOwnProperty("background_color_dark") ? res.options.background_color_dark : "000000";
        res.options.font_color_dark = res.options.hasOwnProperty("font_color_dark") ? res.options.font_color_dark : "ffffff";
        res.options.width = res.options.hasOwnProperty("width") ? res.options.width : 400;
        res.options.height = res.options.hasOwnProperty("height") ? res.options.height : 300;
        res.options.font_family = res.options.hasOwnProperty("font_family") ? res.options.font_family : "default";
        res.options.font_size = res.options.hasOwnProperty("font_size") ? res.options.font_size : 16;
        res.options.font_css = res.options.hasOwnProperty("font_css") ? res.options.font_css : "";
        res.options.default_display = res.options.hasOwnProperty("default_display") ? res.options.default_display : "general_notes";
        res.options.per_site = res.options.hasOwnProperty("per_site") ? res.options.per_site : "domain";
        res.options.private_browsing = res.options.hasOwnProperty("private_browsing") ? res.options.private_browsing : false;
        res.options.subdomains_mode = res.options.hasOwnProperty("subdomains_mode") ? res.options.subdomains_mode : "blacklist";
        res.options.subdomains = res.options.hasOwnProperty("subdomains") ? res.options.subdomains : [];
        res.options.notification_badge = res.options.hasOwnProperty("notification_badge") ? res.options.notification_badge : "disabled";
        res.options.notification_badge_color = res.options.hasOwnProperty("notification_badge_color") ? res.options.notification_badge_color : "d90000";
        res.options.bullet_types = res.options.hasOwnProperty("bullet_types") ? res.options.bullet_types : ["*", "-", "+"];
        res.site_notes = res.hasOwnProperty("site_notes") ? res.site_notes : {};
        browser.storage.local.set(res);
    });
}
function escapeRegex(regex) {
    return regex.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
function bulletCounter(bulletTypes, note) {
    var bulletCount = 0;
    var escaped = bulletTypes.map(escapeRegex);
    var regex = new RegExp("^\\s*(" + escaped.join("|") + ")");
    for (var line of note.split("\n")) {
        if (line.search(regex) > -1) {
            ++bulletCount;
        }
    }
    return bulletCount;
}
function setBadge(bullet_types, notification_badge, notes, tabId) {
    if (notes !== "") {
        if (notification_badge.indexOf("enabled") > -1) {
            browser.browserAction.setBadgeText({text: "!", tabId: tabId});
        }
        if (notification_badge.indexOf("bullets") > -1) {
            var bulletCount = bulletCounter(bullet_types, notes);
            if (bulletCount > 0) {
                browser.browserAction.setBadgeText({text: bulletCount.toString(), tabId: tabId});
            } else if (notification_badge.indexOf("enabled") === -1) {
                browser.browserAction.setBadgeText({text: "", tabId: tabId});
            }
        }
    } else {
        browser.browserAction.setBadgeText({text: "", tabId: tabId});
    }
}
async function setBadgeSite(tab) {
    var res = await browser.storage.local.get(["site_notes", "options"]);
    var site = await siteParser(tab.url);
    if (site === "general_notes") {
        setBadgeGeneral(tab);
    } else if (!tab.incognito || (res.options.private_browsing && tab.incognito)) {
        if (res.site_notes.hasOwnProperty(site)) {
            setBadge(res.options.bullet_types, res.options.notification_badge, res.site_notes[site], tab.id)
        } else {
            browser.browserAction.setBadgeText({text: "", tabId: tab.id});
        }
    } else {
        setBadgeGeneral(tab);
    }
}
async function setBadgeGeneral(tab) {
    var res = await browser.storage.local.get(["general_notes", "options"]);
    setBadge(res.options.bullet_types, res.options.notification_badge, res.general_notes, tab.id);
}
async function updateBadge() {
    var res = await browser.storage.local.get("options");
    var tabs = await browser.tabs.query({active: true});
    if (res.options.notification_badge !== "disabled") {
        switch (res.options.default_display) {
            case "site_notes":
                tabs.forEach(setBadgeSite);
                break;
            case "general_notes":
                tabs.forEach(setBadgeGeneral);
                break;
        }
    } else {
        var allTabs = await browser.tabs.query({});
        for (tab of allTabs) {
            browser.browserAction.setBadgeText({text: "", tabId: tab.id});
        }
    }
}
browser.storage.local.get("options").then((res) => {
    browser.browserAction.setBadgeBackgroundColor({color: "#" + res.options.notification_badge_color});
})
browser.tabs.onActivated.addListener(updateBadge);
browser.tabs.onUpdated.addListener(updateBadge);
browser.storage.onChanged.addListener(updateBadge);
browser.runtime.onInstalled.addListener(setOpts);
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react'
import {
  AppRegistry,
} from 'react-native'

import { StackNavigator } from 'react-navigation'

import DrawerMenu from './DrawerMenu'
import {
  SimpleView,
  NestedScrollView,
  AnchorSheetView,
  GoogleMapsView
} from './views'

const initialRouteName = 'GoogleMaps'

class BSBExample extends Component {
  static childContextTypes = {
    openDrawer: PropTypes.func,
    closeDrawer: PropTypes.func
  };

  state = {
    currentRoute: initialRouteName,
  };

  handlePush = (routeName) => {
    this.navigation.dispatch({
      type: 'Navigation/NAVIGATE',
      routeName
    })
  }

  handleCurrentSceneState = (prev, current) => {
    this.setState({ currentRoute: current.routes[current.index].routeName })
  }

  render() {
    return (
      <DrawerMenu
        push={this.handlePush}
        currentRoute={this.state.currentRoute}>
        <NavigatorStack
          ref={ref => {this.navigation = ref}}
          onNavigationStateChange={this.handleCurrentSceneState}
        />
      </DrawerMenu>
    )
  }
}

const NavigatorStack = StackNavigator({
  Simple: { screen: SimpleView },
  NestedScroll: { screen: NestedScrollView },
  AnchorSheet: { screen: AnchorSheetView },
  GoogleMaps: { screen: GoogleMapsView },
}, { initialRouteName })

AppRegistry.registerComponent('BSBExample', () => BSBExample)
<commit_msg>Fixed context warning<commit_after>import React, { Component } from 'react'
import {
  AppRegistry,
} from 'react-native'

import { StackNavigator } from 'react-navigation'

import DrawerMenu from './DrawerMenu'
import {
  SimpleView,
  NestedScrollView,
  AnchorSheetView,
  GoogleMapsView
} from './views'

const initialRouteName = 'GoogleMaps'

class BSBExample extends Component {
  state = {
    currentRoute: initialRouteName,
  };

  handlePush = (routeName) => {
    this.navigation.dispatch({
      type: 'Navigation/NAVIGATE',
      routeName
    })
  }

  handleCurrentSceneState = (prev, current) => {
    this.setState({ currentRoute: current.routes[current.index].routeName })
  }

  render() {
    return (
      <DrawerMenu
        push={this.handlePush}
        currentRoute={this.state.currentRoute}>
        <NavigatorStack
          ref={ref => {this.navigation = ref}}
          onNavigationStateChange={this.handleCurrentSceneState}
        />
      </DrawerMenu>
    )
  }
}

const NavigatorStack = StackNavigator({
  Simple: { screen: SimpleView },
  NestedScroll: { screen: NestedScrollView },
  AnchorSheet: { screen: AnchorSheetView },
  GoogleMaps: { screen: GoogleMapsView },
}, { initialRouteName })

AppRegistry.registerComponent('BSBExample', () => BSBExample)
<|endoftext|>


<commit_before>const nest = require('depnest')
const extend = require('xtend')
const pull = require('pull-stream')

exports.needs = nest({
  'sbot.pull.log': 'first',
  'message.sync.unbox': 'first',
  'sbot.sync.cache': 'first'
})

exports.gives = nest('feed.pull.mentions')

exports.create = function (api) {
  return nest('feed.pull.mentions', function (id) {
    var cache = api.sbot.sync.cache()

    return function getStream (opts) {
      opts = extend(opts)
      var take = opts.limit
      opts.limit = 5000

      var stream = pull(
        api.sbot.pull.log(opts),
        unboxIfNeeded(),
        pull.filter((msg) => {
          if (!msg) return false
          if (msg.sync) return true
          return msg.value.author !== id && (
            linksToUs(msg.value.content.mentions) ||
            belongsToUs(msg.value.content.root) ||
            belongsToUs(msg.value.content.branch) ||
            belongsToUs(msg.value.content.repo) ||
            belongsToUs(msg.value.content.vote) ||
            belongsToUs(msg.value.content.about) ||
            belongsToUs(msg.value.content.contact)
          )
        }),
        pull.through(x => console.log())
      )

      if (take) {
        return pull(
          stream,
          pull.take(take)
        )
      } else {
        return stream
      }
    }

    function linksToUs (ids) {
      if (ids) {
        if (Array.isArray(ids)) {
          return ids.some((item) => {
            if (item) {
              return item === id || item.link === id
            }
          })
        } else {
          return ids === id
        }
      }
    }

    function belongsToUs (link) {
      if (link) {
        link = typeof link === 'object' ? link.link : link
        if (link === id) return true
        var item = cache[link]
        if (item) {
          return (item.author === id)
        }
      }
    }
  })

  function unboxIfNeeded () {
    return pull.map(function (msg) {
      if (msg.sync || (msg.value && typeof msg.value.content === 'object')) {
        return msg
      } else {
        return api.message.sync.unbox(msg)
      }
    })
  }
}
<commit_msg>mentions feed: include backlink mentions<commit_after>const nest = require('depnest')
const extend = require('xtend')
const pull = require('pull-stream')

exports.needs = nest({
  'sbot.pull.log': 'first',
  'message.sync.unbox': 'first',
  'sbot.sync.cache': 'first'
})

exports.gives = nest('feed.pull.mentions')

exports.create = function (api) {
  return nest('feed.pull.mentions', function (id) {
    var cache = api.sbot.sync.cache()

    return function getStream (opts) {
      opts = extend(opts)
      var take = opts.limit
      opts.limit = 5000

      var stream = pull(
        api.sbot.pull.log(opts),
        unboxIfNeeded(),
        pull.filter((msg) => {
          if (!msg) return false
          if (msg.sync) return true
          return msg.value.author !== id && (
            belongsToUs(msg.value.content.mentions) ||
            belongsToUs(msg.value.content.root) ||
            belongsToUs(msg.value.content.branch) ||
            belongsToUs(msg.value.content.repo) ||
            belongsToUs(msg.value.content.vote) ||
            belongsToUs(msg.value.content.about) ||
            belongsToUs(msg.value.content.contact)
          )
        }),
        pull.through(x => console.log())
      )

      if (take) {
        return pull(
          stream,
          pull.take(take)
        )
      } else {
        return stream
      }
    }

    function linksToUs (ids) {
      if (ids) {
        if (Array.isArray(ids)) {
          return ids.some((item) => {
            if (item) {
              return item === id || item.link === id
            }
          })
        } else {
          return ids === id
        }
      }
    }

    function belongsToUs (link) {
      if (Array.isArray(link)) {
        return link.some(belongsToUs)
      }
      if (link) {
        link = typeof link === 'object' ? link.link : link
        if (link === id) return true
        var item = cache[link]
        if (item) {
          return (item.author === id)
        }
      }
    }
  })

  function unboxIfNeeded () {
    return pull.map(function (msg) {
      if (msg.sync || (msg.value && typeof msg.value.content === 'object')) {
        return msg
      } else {
        return api.message.sync.unbox(msg)
      }
    })
  }
}
<|endoftext|>


<commit_before>(function (root, factory) {
    if (typeof define === 'function' && define.amd) {
        // AMD. Register as an anonymous module.
        define([], factory);
    } else if (typeof module === 'object' && module.exports) {
        // Node. Does not work with strict CommonJS, but
        // only CommonJS-like environments that support module.exports,
        // like Node.
        module.exports = factory();
    } else {
        // Browser globals (root is window)
        root.try = root._try = factory();
  }
}(this, function tryCatchFinallyFactory() {

  function add__toObject__() {
    String.prototype.__toObject__ =
      Number.prototype.__toObject__ =
      Boolean.prototype.__toObject__ =
      function __toObject__() { return this; };
  }

  function remove_toObject__() {
    delete String.prototype.__toObject__;
    delete Number.prototype.__toObject__;
    delete Boolean.prototype.__toObject__;
  }

  function isConstructablePrimitive(obj) {
    return obj !== undefined
      && obj !== null
      && typeof obj.__toObject__ === 'function';
  }

  function ObjectChecker(subject) {

    this.valueEquals = function valueEquals(value) {
      return subject === value;
    };

    this.nameEquals = function nameEquals(name) {
      if (typeof name !== 'string') { return false; }

      var classNamePattern = new RegExp('^\\[object ' + name + '\\]$', 'i');

      var className = Object.prototype.toString.call(subject);

      return classNamePattern.test(className);
    };

    this.instanceOf = function instanceOf(constructor) {
      add__toObject__();

      var subjectAsObject = isConstructablePrimitive(subject)
        ? subject.__toObject__()
        : subject;

      remove_toObject__();

      return (typeof constructor === 'function')
        && (subjectAsObject instanceof constructor);
    };
  }

  return function _try(tryBlock) {
    // Store error in an object so we can use 'in', which will always
    // return true if a property has been set on the object, even if it
    // was set to 'undefined'.
    var state = {};

    function isUnhandledError() { return 'error' in state; }

    function setErrorHandled() { delete state.error; }

    function caughtErrorIs(expectedErr) {
      var actualErr = new ObjectChecker(state.error);

      return actualErr.valueEquals(expectedErr)
        || actualErr.nameEquals(expectedErr)
        || actualErr.instanceOf(expectedErr);
    }

    try {
      if (tryBlock) { tryBlock(); }
    }
    catch (e) {
      state.error = e;
    }

    return {

      catch: function (expectedErr, catchBlock) {
        /*
          there is a catchBlock
          and an error was thrown
          and either it's an indiscriminate catch
          or the error matches the expected error
        */
        var callHandler = arguments.length > 0
          && isUnhandledError()
          && (catchBlock === undefined
          || caughtErrorIs(expectedErr));

        if (callHandler) {
          catchBlock = catchBlock || expectedErr;
          catchBlock(state.error);
          setErrorHandled();
        }

        return this;
      },

      finally: function (finallyBlock) {
        if (finallyBlock) {
          finallyBlock();
        }
        if (isUnhandledError()) {
          throw state.error;
        }
      }

    };
  };

}));
<commit_msg>Refactor implementation<commit_after>(function umd(root, factory) {
  'use strict';
    if (typeof define === 'function' && define.amd) {
        // AMD. Register as an anonymous module.
        define([], factory);
    } else if (typeof module === 'object' && module.exports) {
        // Node. Does not work with strict CommonJS, but
        // only CommonJS-like environments that support module.exports,
        // like Node.
        module.exports = factory();
    } else {
        // Browser globals (root is window)
        root.try = root._try = factory();
  }
}(this, (function tryCatchFinallyFactory(getBoxed) {
  'use strict';

  function hasName(obj, name) {
    if (typeof name !== 'string') { return false; }
    var className = Object.prototype.toString.call(obj).toLowerCase();
    return className === '[object ' + name.toLowerCase() + ']';
  }

  function instanceOf(obj, constructor) {
    var defined = obj !== undefined && obj !== null;
    if (defined && typeof constructor === 'function') {
      // Get the 'boxed' value so instanceof works for primitive literals
      return getBoxed(obj) instanceof constructor;
    }
  }

  return function _try(tryBlock) {
    // Store error in an object so we can use 'in', which will always
    // return true if a property has been set on the object, even if it
    // was set to 'undefined'.
    var state = {};

    function isUnhandledError() { return 'error' in state; }

    function setErrorHandled() { delete state.error; }

    function caughtErrorIs(expectedErr) {
      return state.error === expectedErr
        || hasName(state.error, expectedErr)
        || instanceOf(state.error, expectedErr);
    }

    try {
      if (tryBlock) { tryBlock(); }
    }
    catch (e) {
      state.error = e;
    }

    var chain = {
      catch: function _catch(expectedErr, catchBlock) {
        /*
          there is a catchBlock
          and an error was thrown
          and either it's an indiscriminate catch
          or the error matches the expected error
        */
        var callHandler = arguments.length > 0
          && isUnhandledError()
          && (catchBlock === undefined
          || caughtErrorIs(expectedErr));

        if (callHandler) {
          catchBlock = catchBlock || expectedErr;
          catchBlock(state.error);
          setErrorHandled();
        }

        return chain;
      },
      finally: function _finally(finallyBlock) {
        if (finallyBlock) {
          finallyBlock();
        }
        if (isUnhandledError()) {
          throw state.error;
        }
      }
    };

    return chain;
  };
}).bind(null, function getBoxed(obj) {
  // Injected because boxing doesn't work in strict mode </nasty>
  // https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Strict_mode#Securing_JavaScript
  // jshint strict: false
  return (function () { return this; }).call(obj);
})));
<|endoftext|>


<commit_before>#!/Users/damo/.nvm/versions/node/v12.18.1/bin/node
const [
  nodePath,
  filePath,
  s1 = '/Users/damo/Downloads/export.json',
  s2 = '/Users/damo/Downloads/a.json'] = process.argv;

function echoHelp(exitCode, msg) {
  console.log(msg || 'uniqueappend.js source target')
  exitCode !== undefined && process.exit(exitCode)
}

try {
  const c1 = require(s1)
  const c2 = require(s2)
  console.log('s1==============', s1);
  console.log('c1==============', c1.configs.length);
  console.log('s2==============', s2);
  console.log('c2==============', c2.configs.length);
  c1.configs = c1.configs.concat(c2.configs)
  if (!s1 || !c1.configs || c1.configs.length) echoHelp(1);
  if (!s1 || !c1.configs || c1.configs.length) echoHelp(1);
} catch (error) {
  echoHelp(1, error);
}

let s = new Set();
c1.configs = c1.configs.filter(u => {
  const { password, server, server_port } = u;
  let key = `${server}#${server_port}` // #${password}
  if (s.has(key)) { return false; }
  else { s.add(key) }
  return true;
})

console.log('re path==============', s1);
console.log('re length==============', c1.configs.length);
const fs = require('fs');
fs.writeFileSync(s1, JSON.stringify(c1));<commit_msg>fix:!c1.configs.length<commit_after>#!/Users/damo/.nvm/versions/node/v12.18.1/bin/node
const [
  nodePath,
  filePath,
  s1 = '/Users/damo/Downloads/export.json',
  s2 = '/Users/damo/Downloads/a.json'] = process.argv;

function echoHelp(exitCode, msg) {
  console.log(msg || 'uniqueappend.js source target')
  exitCode !== undefined && process.exit(exitCode)
}

try {
  const c1 = require(s1)
  const c2 = require(s2)
  console.log('s1==============', s1);
  console.log('c1==============', c1.configs.length);
  console.log('s2==============', s2);
  console.log('c2==============', c2.configs.length);
  c1.configs = c1.configs.concat(c2.configs)
  if (!s1 || !c1.configs || !c1.configs.length) echoHelp(1);
} catch (error) {
  echoHelp(1, error);
}

let s = new Set();
c1.configs = c1.configs.filter(u => {
  const { password, server, server_port } = u;
  let key = `${server}#${server_port}` // #${password}
  if (s.has(key)) { return false; }
  else { s.add(key) }
  return true;
})

console.log('re path==============', s1);
console.log('re length==============', c1.configs.length);
const fs = require('fs');
fs.writeFileSync(s1, JSON.stringify(c1));
console.log(`write to ${s1} done`, c1.configs.length);<|endoftext|>