<commit_before>const path = require('path');

module.exports = function(config) {
    config.set({
        files: ['src/**/*.ts', 'tests/**/*.js'],

        preprocessors: {
            'tests/**/*.js': ['rollup'],
            'src/**/*.ts': ['rollup', 'typescript', 'coverage']
        },
        plugins: [
            'karma-mocha',
            'karma-chrome-launcher',
            'karma-rollup-preprocessor',
            'karma-typescript-preprocessor',
            'karma-coverage'
        ],
        rollupPreprocessor: {
            output: {
                format: 'umd',
                sourcemap: 'inline',
                name: 'scroll'
            },
            plugins: [
                require('rollup-plugin-node-resolve')(),
                require('rollup-plugin-typescript')(),
                require('rollup-plugin-commonjs')()
            ]
        },
        typescriptPreprocessor: {
            options: {
                sourceMap: false,
                target: 'es6',
                module: 'esnext'
            }
        },
        coverageReporter: {
            includeAllSources: true,
            dir: '.coverage',
            reporters: [{ type: 'lcov', subdir: '.' }, { type: 'text-summary' }]
        },
        reporters: ['progress', 'coverage'],
        frameworks: ['mocha'],
        port: 9876,
        colors: true,
        logLevel: config.LOG_INFO,
        browsers: ['ChromeHeadless'],
        autoWatch: true,
        singleRun: true,
        concurrency: Infinity
    });
};
<commit_msg>Add comment<commit_after>const path = require('path');

module.exports = function(config) {
    config.set({
        files: ['src/**/*.ts', 'tests/**/*.js'],

        preprocessors: {
            'tests/**/*.js': ['rollup'],
            'src/**/*.ts': ['rollup', 'typescript', 'coverage']
        },
        plugins: [
            'karma-mocha',
            'karma-chrome-launcher',
            'karma-rollup-preprocessor',
            'karma-typescript-preprocessor',
            'karma-coverage'
        ],
        rollupPreprocessor: {
            output: {
                format: 'umd', // we must output to UMD until karma-chrome-launcher supports 'esm'
                sourcemap: 'inline',
                name: 'scroll'
            },
            plugins: [
                require('rollup-plugin-node-resolve')(),
                require('rollup-plugin-typescript')(),
                require('rollup-plugin-commonjs')()
            ]
        },
        typescriptPreprocessor: {
            options: {
                sourceMap: false,
                target: 'es6',
                module: 'esnext'
            }
        },
        coverageReporter: {
            includeAllSources: true,
            dir: '.coverage',
            reporters: [{ type: 'lcov', subdir: '.' }, { type: 'text-summary' }]
        },
        reporters: ['progress', 'coverage'],
        frameworks: ['mocha'],
        port: 9876,
        colors: true,
        logLevel: config.LOG_INFO,
        browsers: ['ChromeHeadless'],
        autoWatch: true,
        singleRun: true,
        concurrency: Infinity
    });
};
<|endoftext|>


<commit_before>var Tokenizer = require("./Tokenizer.js"),
    WritableStream = require("stream").Writable;

var defaultOpts = {
	xmlMode: false, //Special behavior for script/style tags by default
	lowerCaseAttributeNames: false, //call .toLowerCase for each attribute name
	lowerCaseTags: false //call .toLowerCase for each tag name
};

var defaultCbs = {
	/*
		This is just a plain object
		so that the parser doesn't
		throw if no arguments were
		provided.
	*/
	/*
		oncdataend,
		oncdatastart,
		onclosetag,
		oncomment,
		oncommentend,
		onerror,
		onopentag,
		onprocessinginstruction,
		onreset,
		ontext
	*/
};

var formTags = {
	input: true,
	option: true,
	optgroup: true,
	select: true,
	button: true,
	datalist: true,
	textarea: true
};
var openImpliesClose = {
	tr      : { tr:true, th:true, td:true },
	th      : { th:true },
	td      : { thead:true, td:true },
	body    : { head:true, link:true, script:true },
	li      : { li:true },
	p       : { p:true },
	select  : formTags,
	input   : formTags,
	output  : formTags,
	button  : formTags,
	datalist: formTags,
	textarea: formTags,
	option  : { option:true },
	optgroup: { optgroup:true }
};

var emptyTags = {
	__proto__: null,
	area: true,
	base: true,
	basefont: true,
	br: true,
	col: true,
	frame: true,
	hr: true,
	img: true,
	input: true,
	isindex: true,
	link: true,
	meta: true,
	param: true,
	embed: true
};

function Parser(cbs, options){
	if(!options) options = defaultOpts;
	if(!cbs) cbs = defaultCbs;
	this._options = options;
	this._cbs = cbs;
	this._tokenizer = new Tokenizer(options);

	WritableStream.call(this, options);

	var that = this,
	    tagname = "",
	    attribname = "",
	    attribs = null,
	    stack = [];

	function closeTag(name){
		if(options.lowerCaseTags) name = name.toLowerCase();
		if(stack && (!(name in emptyTags) || options.xmlMode)){
			var pos = stack.lastIndexOf(name);
			if(pos !== -1)
				if(cbs.onclosetag){
					pos = stack.length - pos;
					while(pos--) cbs.onclosetag(stack.pop());
				}
				else stack.splice(pos);
		}
	}

	function attribValue(value){
		if(cbs.onattribute) cbs.onattribute(attribname, value);
		if(attribs) attribs[attribname] = value;
		attribname = "";
	}

	this._tokenizer
		.on("text", function(data){
			if(tagname !== ""){
				if(attribname !== "") attribValue("");
				if(attribs){
					if(cbs.onopentag) cbs.onopentag(tagname, attribs);
					attribs = null;
				}
				attribname = "";
			}
			if(cbs.ontext) cbs.ontext(data);
		})
		.on("opentagname", function(name){
			if(options.lowerCaseTags) name = name.toLowerCase();
			tagname = name;

			if (!options.xmlMode && name in openImpliesClose) {
				for(
					var el;
					(el = stack[stack.length-1]) in openImpliesClose[name];
					closeTag(el)
				);
			}
			if(cbs.onopentagname) cbs.onopentagname(name);
			if(cbs.onopentag) attribs = {};
		})
		.on("closetag", closeTag)
		.on("selfclosingtag", function(){
			closeTag(tagname);
		})
		.on("attribname", function(name){
			if(attribname !== "") attribValue("");
			if(options.lowerCaseAttributeNames) name = name.toLowerCase;
			attribname = name;
		})
		.on("attribvalue", attribValue)
		.on("declaration", function(value){
			if(cbs.onprocessinginstruction){
				cbs.onprocessinginstruction("!" + value.split(/\s|\//, 1)[0], "!" + value);
			}
		})
		.on("processinginstruction", function(value){
			if(cbs.onprocessinginstruction){
				cbs.onprocessinginstruction("?" + value.split(/\s|\//, 1)[0], "?" + value);
			}
		})
		.on("comment", function(value){
			if(cbs.oncomment) cbs.oncomment(value);
			if(cbs.oncommentend) cbs.oncommentend();
		})
		.on("cdata", function(value){
			if(cbs.oncdatastart) cbs.oncdatastart();
			if(cbs.ontext) cbs.ontext(value);
			if(cbs.oncdataend) cbs.oncdataend();
		})
		.on("error", function(err){
			if(cbs.onerror) cbs.onerror(err);
			else that.emit("error", err);
		})
		;
}

require("util").inherits(Parser, WritableStream);

//Resets the parser to a blank state, ready to parse a new HTML document
Parser.prototype.reset = function(){
	this._tokenizer.removeAllListeners();
	Parser.call(this, this._cbs, this._options);
	if(this._cbs.onreset) this._cbs.onreset();
};

//Parses a complete HTML and pushes it to the handler
Parser.prototype.parseComplete = function(data){
	this.reset();
	this.end(data);
};

Parser.prototype._write = function(chunk, encoding, cb){
	this._tokenizer.write(chunk, cb);
};

//alias for backwards compat
Parser.prototype.parseChunk = Parser.prototype.write;
Parser.prototype.done = Parser.prototype.end;

module.exports = Parser;
<commit_msg>[parser] made Parser#reset work again<commit_after>var Tokenizer = require("./Tokenizer.js"),
    WritableStream = require("stream").Writable;

var defaultOpts = {
	xmlMode: false, //Special behavior for script/style tags by default
	lowerCaseAttributeNames: false, //call .toLowerCase for each attribute name
	lowerCaseTags: false //call .toLowerCase for each tag name
};

var defaultCbs = {
	/*
		This is just a plain object
		so that the parser doesn't
		throw if no arguments were
		provided.
	*/
	/*
		oncdataend,
		oncdatastart,
		onclosetag,
		oncomment,
		oncommentend,
		onerror,
		onopentag,
		onprocessinginstruction,
		onreset,
		ontext
	*/
};

var formTags = {
	input: true,
	option: true,
	optgroup: true,
	select: true,
	button: true,
	datalist: true,
	textarea: true
};
var openImpliesClose = {
	tr      : { tr:true, th:true, td:true },
	th      : { th:true },
	td      : { thead:true, td:true },
	body    : { head:true, link:true, script:true },
	li      : { li:true },
	p       : { p:true },
	select  : formTags,
	input   : formTags,
	output  : formTags,
	button  : formTags,
	datalist: formTags,
	textarea: formTags,
	option  : { option:true },
	optgroup: { optgroup:true }
};

var emptyTags = {
	__proto__: null,
	area: true,
	base: true,
	basefont: true,
	br: true,
	col: true,
	frame: true,
	hr: true,
	img: true,
	input: true,
	isindex: true,
	link: true,
	meta: true,
	param: true,
	embed: true
};

function Parser(cbs, options){
	if(!options) options = defaultOpts;
	if(!cbs) cbs = defaultCbs;
	this._options = options;
	this._cbs = cbs;
	this._tokenizer = new Tokenizer(options);

	WritableStream.call(this, options);

	var that = this,
	    tagname = "",
	    attribname = "",
	    attribs = null,
	    stack = [];

	function closeTag(name){
		if(options.lowerCaseTags) name = name.toLowerCase();
		if(stack && (!(name in emptyTags) || options.xmlMode)){
			var pos = stack.lastIndexOf(name);
			if(pos !== -1)
				if(cbs.onclosetag){
					pos = stack.length - pos;
					while(pos--) cbs.onclosetag(stack.pop());
				}
				else stack.splice(pos);
		}
	}

	function attribValue(value){
		if(cbs.onattribute) cbs.onattribute(attribname, value);
		if(attribs) attribs[attribname] = value;
		attribname = "";
	}

	this._tokenizer
		.on("text", function(data){
			if(tagname !== ""){
				if(attribname !== "") attribValue("");
				if(attribs){
					if(cbs.onopentag) cbs.onopentag(tagname, attribs);
					attribs = null;
				}
				attribname = "";
			}
			if(cbs.ontext) cbs.ontext(data);
		})
		.on("opentagname", function(name){
			if(options.lowerCaseTags) name = name.toLowerCase();
			tagname = name;

			if (!options.xmlMode && name in openImpliesClose) {
				for(
					var el;
					(el = stack[stack.length-1]) in openImpliesClose[name];
					closeTag(el)
				);
			}
			if(cbs.onopentagname) cbs.onopentagname(name);
			if(cbs.onopentag) attribs = {};
		})
		.on("closetag", closeTag)
		.on("selfclosingtag", function(){
			closeTag(tagname);
		})
		.on("attribname", function(name){
			if(attribname !== "") attribValue("");
			if(options.lowerCaseAttributeNames) name = name.toLowerCase;
			attribname = name;
		})
		.on("attribvalue", attribValue)
		.on("declaration", function(value){
			if(cbs.onprocessinginstruction){
				cbs.onprocessinginstruction("!" + value.split(/\s|\//, 1)[0], "!" + value);
			}
		})
		.on("processinginstruction", function(value){
			if(cbs.onprocessinginstruction){
				cbs.onprocessinginstruction("?" + value.split(/\s|\//, 1)[0], "?" + value);
			}
		})
		.on("comment", function(value){
			if(cbs.oncomment) cbs.oncomment(value);
			if(cbs.oncommentend) cbs.oncommentend();
		})
		.on("cdata", function(value){
			if(cbs.oncdatastart) cbs.oncdatastart();
			if(cbs.ontext) cbs.ontext(value);
			if(cbs.oncdataend) cbs.oncdataend();
		})
		.on("error", function(err){
			if(cbs.onerror) cbs.onerror(err);
			else that.emit("error", err);
		})
		;
}

require("util").inherits(Parser, WritableStream);

//Resets the parser to a blank state, ready to parse a new HTML document
Parser.prototype.reset = function(){
	this._tokenizer.removeAllListeners();
	if(this._cbs.onreset) this._cbs.onreset();
	//TODO there needs to be a better way
	var p = new Parser(this._cbs, this._options);
	for(var k in p){
		if(typeof p[k] === "function") this[k] = p[k].bind(p);
	}
};

//Parses a complete HTML and pushes it to the handler
Parser.prototype.parseComplete = function(data){
	this.reset();
	this.end(data);
};

Parser.prototype._write = function(chunk, encoding, cb){
	this._tokenizer.write(chunk, cb);
};

//alias for backwards compat
Parser.prototype.parseChunk = Parser.prototype.write;
Parser.prototype.done = Parser.prototype.end;

module.exports = Parser;
<|endoftext|>


<commit_before>/* @flow */
import type {
  HTTPClientAbstruct,
  LRUCacheAbstruct,
} from '../../types';
import BlogPost from '../../entities/BlogPost';

const DEFAULT_LIMIT = 10;

class BlogPostsRepository {
  httpClient: HTTPClientAbstruct;
  cache: LRUCacheAbstruct;
  individualCache: LRUCacheAbstruct;

  constructor({ httpClient, individualCache, cache }: { httpClient: HTTPClientAbstruct, cache: LRUCacheAbstruct, individualCache: LRUCacheAbstruct }) {
    this.httpClient = httpClient;
    this.cache = cache;
    this.individualCache = individualCache;
  }

  getAll({ page = 1, limit = DEFAULT_LIMIT }: { page: ?number, limit: ?number } = {}): Promise<Array<BlogPost>> {
    const cached = this.cache.get(`${limit}*${page}`);

    if (cached !== undefined) return Promise.resolve(cached);

    return this.httpClient.get('/v0.1/posts', {
      params: {
        include: 'tags',
        page,
        limit,
      },
    })
      .then(res => {
        const blogPosts = res.data.posts.map(item => BlogPost.fromGhostAPI(item));

        this.cache.set(`${limit}*${page}`, blogPosts);

        for (const blogPost of blogPosts) {
          this.individualCache.set(blogPost.slug, blogPost);
        }

        return blogPosts;
      });
  }

  getBySlug(slug: string): Promise<?BlogPost> {
    const cached = this.individualCache.get(slug);

    if (cached !== undefined) return Promise.resolve(cached);

    return this.httpClient.get(`/v0.1/posts/slug/${slug}`, {
      params: { include: 'tags' },
    })
      .then(res => {
        if (res.data.posts.length === 0) return null;

        const blogPost = BlogPost.fromGhostAPI(res.data.posts[0]);

        this.individualCache.set(slug, blogPost);

        return blogPost;
      });
  }
}

export default BlogPostsRepository;
<commit_msg>Apply Base URL to BlogPost#thumbnailImageURL through from BlogPostsRepository<commit_after>/* @flow */
import url from 'url';
import type {
  HTTPClientAbstruct,
  LRUCacheAbstruct,
} from '../../types';
import BlogPost from '../../entities/BlogPost';

const DEFAULT_LIMIT = 10;

class BlogPostsRepository {
  httpClient: HTTPClientAbstruct;
  cache: LRUCacheAbstruct;
  individualCache: LRUCacheAbstruct;

  constructor({ httpClient, cache, individualCache }: { httpClient: HTTPClientAbstruct, cache: LRUCacheAbstruct, individualCache: LRUCacheAbstruct }) {
    this.httpClient = httpClient;
    this.cache = cache;
    this.individualCache = individualCache;
  }

  getAll({ page = 1, limit = DEFAULT_LIMIT }: { page: ?number, limit: ?number } = {}): Promise<Array<BlogPost>> {
    const cached = this.cache.get(`${limit}*${page}`);

    if (cached !== undefined) return Promise.resolve(cached);

    return this.httpClient.get('/v0.1/posts', {
      params: {
        include: 'tags',
        page,
        limit,
      },
    })
      .then(res => {
        const blogPosts = res.data.posts
          .map(item => Object.assign({}, item, {
            image: url.resolve(this.httpClient.defaults.baseURL, item.image),
          }))
          .map(item => BlogPost.fromGhostAPI(item));

        this.cache.set(`${limit}*${page}`, blogPosts);

        for (const blogPost of blogPosts) {
          this.individualCache.set(blogPost.slug, blogPost);
        }

        return blogPosts;
      });
  }

  getBySlug(slug: string): Promise<?BlogPost> {
    const cached = this.individualCache.get(slug);

    if (cached !== undefined) return Promise.resolve(cached);

    return this.httpClient.get(`/v0.1/posts/slug/${slug}`, {
      params: { include: 'tags' },
    })
      .then(res => {
        if (res.data.posts.length === 0) return null;

        const item = res.data.posts[0];
        const blogPost = BlogPost.fromGhostAPI(Object.assign({}, item, {
          image: url.resolve(this.httpClient.defaults.baseURL, item.image),
        }));

        this.individualCache.set(slug, blogPost);

        return blogPost;
      });
  }
}

export default BlogPostsRepository;
<|endoftext|>


<commit_before>/**
 * Module dependencies
 */

var Emitter = require('events').EventEmitter;
var compose = require('koa-compose');
var debug = require('debug')('fax');
var qs = require('querystring');
var assert = require('assert');
var xhr = require('xhr');
var co = require('co');

var response = require('./response');
var context = require('./context');
var request = require('./request');

/**
 * Store prototype.
 */

var app = Application.prototype;

/**
 * Expose `Store`.
 */

exports = module.exports = Application;

/**
 * Initialize a new `Store`.
 *
 * @api public
 */

function Application() {
  if (!(this instanceof Application)) return new Application;
  this.env = process.env.NODE_ENV || 'development';
  this._middleware = [];
  this.context = Object.create(context);
  this.request = Object.create(request);
  this.response = Object.create(response);
}

/**
 * Inherit from `Emitter.prototype`.
 */

Application.prototype.__proto__ = Emitter.prototype;

/**
 * Use the given middleware `fn`.
 *
 * @param {GeneratorFunction} fn
 * @return {Application} self
 * @api public
 */

app.use = function(fn) {
  assert(fn && 'GeneratorFunction' == fn.constructor.name, 'app.use() requires a generator function');
  debug('use %s', fn._name || fn.name || '-');
  this._middleware.push(fn);
  return this;
};

/**
 * Start the whole shebang.
 *
 * @return {Function}
 * @api public
 */

app.send = function(req) {
  var mw = this._middleware.concat([xhrmw]);
  var gen = compose(mw);
  var fn = co(gen);

  assert(!req || 'object' == typeof req, 'Request should be an object');

  if (!this.listeners('error').length) this.on('error', this.onerror);

  var ctx = this.createContext(req);
  fn.call(ctx);
};

/**
 * Initialize a new context.
 *
 * @param {Object} req
 * @api private
 */

app.createContext = function(req) {
  req = req || {};

  assert('object' == typeof req, 'fax: req should be an object');

  var context = Object.create(this.context);
  var request = context.request = Object.create(this.request);
  var response = context.response = Object.create(this.response);

  context.app = request.app = response.app = this;
  context.req = request.req = response.req = req || {};
  context.res = request.res = response.res = {};
  request.ctx = response.ctx = context;

  request.response = response;
  response.request = request;

  context.originalUrl = request.originalUrl = req.url;
  context.onerror = context.onerror.bind(context);

  return context;
}

/**
 * Default error handler.
 *
 * @param {Error} err
 * @api private
 */

app.onerror = function(err) {
  assert(err instanceof Error, 'non-error thrown: ' + err);

  if (404 == err.status) return;
  if ('test' == this.env) return;

  var msg = err.stack || err.toString();
  console.error();
  console.error(msg.replace(/^/gm, '  '));
  console.error();
};

/**
 * XHR middleware.
 *
 * @param {Function} next
 * @api private
 */

function *xhrmw(next) {
  if (!this.url) return yield next;

  var opts = {
    url: this.url,
    body: this.body,
    method: this.method,
    headers: this.headers
  };

  var thunk = xhr.bind(null, opts);
  var response = yield thunk;

  var res = response[0];
  if (res instanceof Error) throw res;

  this.res = res;

  yield next;
}
<commit_msg>context: add default params<commit_after>/**
 * Module dependencies
 */

var Emitter = require('events').EventEmitter;
var compose = require('koa-compose');
var debug = require('debug')('fax');
var qs = require('querystring');
var assert = require('assert');
var xhr = require('xhr');
var co = require('co');

var response = require('./response');
var context = require('./context');
var request = require('./request');

/**
 * Store prototype.
 */

var app = Application.prototype;

/**
 * Expose `Store`.
 */

exports = module.exports = Application;

/**
 * Initialize a new `Store`.
 *
 * @api public
 */

function Application() {
  if (!(this instanceof Application)) return new Application;
  this.env = process.env.NODE_ENV || 'development';
  this._middleware = [];
  this.context = Object.create(context);
  this.request = Object.create(request);
  this.response = Object.create(response);
}

/**
 * Inherit from `Emitter.prototype`.
 */

Application.prototype.__proto__ = Emitter.prototype;

/**
 * Use the given middleware `fn`.
 *
 * @param {GeneratorFunction} fn
 * @return {Application} self
 * @api public
 */

app.use = function(fn) {
  assert(fn && 'GeneratorFunction' == fn.constructor.name, 'app.use() requires a generator function');
  debug('use %s', fn._name || fn.name || '-');
  this._middleware.push(fn);
  return this;
};

/**
 * Start the whole shebang.
 *
 * @return {Function}
 * @api public
 */

app.send = function(req) {
  var mw = this._middleware.concat([xhrmw]);
  var gen = compose(mw);
  var fn = co(gen);

  assert(!req || 'object' == typeof req, 'Request should be an object');

  if (!this.listeners('error').length) this.on('error', this.onerror);

  var ctx = this.createContext(req);
  fn.call(ctx);
};

/**
 * Initialize a new context.
 *
 * @param {Object} req
 * @api private
 */

app.createContext = function(req) {
  req = req || {};
  req.method = req.method || 'GET';
  req.url = req.url || '';

  assert('object' == typeof req, 'fax: req should be an object');

  var context = Object.create(this.context);
  var request = context.request = Object.create(this.request);
  var response = context.response = Object.create(this.response);

  context.app = request.app = response.app = this;
  context.req = request.req = response.req = req || {};
  context.res = request.res = response.res = {};
  request.ctx = response.ctx = context;

  request.response = response;
  response.request = request;

  context.originalUrl = request.originalUrl = req.url;
  context.onerror = context.onerror.bind(context);

  return context;
}

/**
 * Default error handler.
 *
 * @param {Error} err
 * @api private
 */

app.onerror = function(err) {
  assert(err instanceof Error, 'non-error thrown: ' + err);

  if (404 == err.status) return;
  if ('test' == this.env) return;

  var msg = err.stack || err.toString();
  console.error();
  console.error(msg.replace(/^/gm, '  '));
  console.error();
};

/**
 * XHR middleware.
 *
 * @param {Function} next
 * @api private
 */

function *xhrmw(next) {
  if (!this.url) return yield next;

  var opts = {
    url: this.url,
    body: this.body,
    method: this.method,
    headers: this.headers
  };

  var thunk = xhr.bind(null, opts);
  var response = yield thunk;

  var res = response[0];
  if (res instanceof Error) throw res;

  this.res = res;

  yield next;
}
<|endoftext|>


<commit_before>// Karma configuration
// Generated on Mon Feb 29 2016 11:42:08 GMT+0800 (SGT)

module.exports = function(config) {
  config.set({

    // 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: [
      'libs/angular/angular.js',
      'libs/angular-animate/angular-animate.js',
      'libs/angular-aria/angular-aria.js',
      'libs/angular-material/angular-material.js',
      'libs/angular-messages/angular-messages.js',
      'libs/angular-ui-router/release/angular-ui-router.js',
      'libs/angular-mocks/angular-mocks.js',
      'libs/angular-material/angular-material-mocks.js',
      'libs/clipboard/dist/clipboard.js',
      'libs/ngclipboard/dist/ngclipboard.js',

      'assets/js/public/zoomableApp.js',
      'assets/js/public/directives.js',
      'assets/js/public/servicesAPI.js',
      'assets/js/public/controllers/*.js',

      'test/frontend/*.spec.js'
    ],

    // list of files to exclude
    exclude: [
    ],

    // preprocess matching files before serving them to the browser
    // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
    preprocessors: {
    },

    // test results reporter to use
    // possible values: 'dots', 'progress'
    // available reporters: https://npmjs.org/browse/keyword/karma-reporter
    reporters: ['spec'],

    // web server port
    port: 9876,

    // enable / disable colors in the output (reporters and logs)
    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_INFO,

    // enable / disable watching file and executing tests whenever any file changes
    autoWatch: true,

    // start these browsers
    // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
    browsers: ['PhantomJS'],
    //browsers: ['PhantomJS', 'Chrome', 'Safari', 'Firefox'],

    // Continuous Integration mode
    // if true, Karma captures browsers, runs the tests and exits
    singleRun: false,

    // Concurrency level
    // how many browser should be started simultaneous
    concurrency: Infinity
  })
}
<commit_msg>Set singleRun to true to ensure karma test stops once all tests are completed<commit_after>// Karma configuration
// Generated on Mon Feb 29 2016 11:42:08 GMT+0800 (SGT)

module.exports = function(config) {
  config.set({

    // 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: [
      'libs/angular/angular.js',
      'libs/angular-animate/angular-animate.js',
      'libs/angular-aria/angular-aria.js',
      'libs/angular-material/angular-material.js',
      'libs/angular-messages/angular-messages.js',
      'libs/angular-ui-router/release/angular-ui-router.js',
      'libs/angular-mocks/angular-mocks.js',
      'libs/angular-material/angular-material-mocks.js',
      'libs/clipboard/dist/clipboard.js',
      'libs/ngclipboard/dist/ngclipboard.js',

      'assets/js/public/zoomableApp.js',
      'assets/js/public/directives.js',
      'assets/js/public/servicesAPI.js',
      'assets/js/public/controllers/*.js',

      'test/frontend/*.spec.js'
    ],

    // list of files to exclude
    exclude: [
    ],

    // preprocess matching files before serving them to the browser
    // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
    preprocessors: {
    },

    // test results reporter to use
    // possible values: 'dots', 'progress'
    // available reporters: https://npmjs.org/browse/keyword/karma-reporter
    reporters: ['spec'],

    // web server port
    port: 9876,

    // enable / disable colors in the output (reporters and logs)
    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_INFO,

    // enable / disable watching file and executing tests whenever any file changes
    autoWatch: true,

    // start these browsers
    // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
    browsers: ['PhantomJS'],
    //browsers: ['PhantomJS', 'Chrome', 'Safari', 'Firefox'],

    // Continuous Integration mode
    // if true, Karma captures browsers, runs the tests and exits
    singleRun: true,

    // Concurrency level
    // how many browser should be started simultaneous
    concurrency: Infinity
  })
}
<|endoftext|>


<commit_before>
const SPAZ_DASHBOARD_STAGENAME = 'dashboard';
const SPAZ_MAIN_STAGENAME      = 'main';
const SPAZ_STAGEASSISTANTNAME = 'StageAssistant';
const SPAZ_BGNOTIFIER_DASHBOARD_STAGENAME = 'bgnotifier';

const SPAZ_DONATION_URL = 'http://getspaz.com/donate';

var LOGGEDIN_APPMENU_ITEMS = [
	Mojo.Menu.editItem,
	{ label: $L('Update Location'),	command: 'update-location' },
	//{ label: $L('New Search Card'),	command: 'new-search-card' },
	{ label: $L('Accounts'), 	command:'accounts' },
	{ label: $L('Preferences'),	command:Mojo.Menu.prefsCmd },
	{ label: $L('About Spaz'),		command: 'appmenu-about' },
	{ label: $L('Donate'),		command:'donate' },
	{ label: $L('Help'),			command:Mojo.Menu.helpCmd }

];

function AppAssistant(appController) {

	Mojo.Log.info("Logging from AppAssistant Constructor");

	this.initialize();

}

/**
 * container for global app-available stuff. We map this to "spaz_app"
 */
AppAssistant.prototype.App = {};

AppAssistant.prototype.initialize = function() {

	var that = this;

	Mojo.Log.info('INITIALIZING EVERYTHING');

	SpazAuth.addService(SPAZCORE_ACCOUNT_TWITTER, {
        authType: SPAZCORE_AUTHTYPE_OAUTH,
        consumerKey: SPAZCORE_CONSUMERKEY_TWITTER,
        consumerSecret: SPAZCORE_CONSUMERSECRET_TWITTER,
        accessURL: 'http://twitter.com/oauth/access_token'
    });

	/*
		Remap JSON parser because JSON2.js one was causing probs with unicode
	*/
	sc.helpers.deJSON = function(str) {
		try {
			var obj = JSON.parse(str);
			return obj;
		} catch(e) {
			Mojo.Log.error('There was a problem decoding the JSON string');
			Mojo.Log.error('Here is the JSON string: '+str);
			return null;
		}

	};
	sc.helpers.enJSON = function(obj) {
		var json = JSON.stringify(obj);
		return json;
	};

	sch.info = Mojo.Log.info;
	sch.dump = Mojo.Log.info;
	sch.warn = Mojo.Log.warn;
	sch.error = Mojo.Log.error;

	this.App.search_cards = [];
	this.App.new_search_card = 0;
	this.App.search_card_prefix = "searchcard_";

	this.App.username = null;
	this.App.prefs = null;

	this.App.accounts = null;

	/*
		load our prefs
		default_preferences is from default_preferences.js, loaded in index.html
	*/
	this.App.prefs = new SpazPrefs(default_preferences, null, {
		'timeline-maxentries': {
			'onGet': function(key, value){
				if (that.App.prefs.get('timeline-friends-getcount') > value) {
					value = that.App.prefs.get('timeline-friends-getcount');
				}
				sch.debug(key + ':' + value);
				return value;
			},
			'onSet': function(key, value){
				if (that.App.prefs.get('timeline-friends-getcount') > value) {
					value = that.App.prefs.get('timeline-friends-getcount');
				}
				sch.debug(key + ':' + value);
				return value;
			}
		},
		'timeline-maxentries-dm': {
			'onGet': function(key, value){
				if (that.App.prefs.get('timeline-dm-getcount') > value) {
					value = that.App.prefs.get('timeline-dm-getcount');
				}
				sch.debug(key + ':' + value);
				return value;
			},
			'onSet': function(key, value){
				if (that.App.prefs.get('timeline-dm-getcount') > value) {
					value = that.App.prefs.get('timeline-dm-getcount');
				}
				sch.debug(key + ':' + value);
				return value;
			}
		},
		'timeline-maxentries-reply': {
			'onGet': function(key, value){
				if (that.App.prefs.get('timeline-replies-getcount') > value) {
					value = that.App.prefs.get('timeline-replies-getcount');
				}
				sch.debug(key + ':' + value);
				return value;
			},
			'onSet': function(key, value){
				if (that.App.prefs.get('timeline-replies-getcount') > value) {
					value = that.App.prefs.get('timeline-replies-getcount');
				}
				sch.debug(key + ':' + value);
				return value;
			}
		}
	});
	this.App.prefs.load(); // this is sync on webOS, b/c loading from Mojo.Model.Cookie

	/*
		load app accounts
	*/
	this.App.accounts = new SpazAccounts(this.App.prefs);

	this.App.twit = new SpazTwit(null, null, {
		'event_mode':'jquery'
	});

	/*
		model for saving Tweets to Depot. We replace on every start to make sure we don't go over-budget
	*/
	this.App.Tweets = new Tweets({
		'replace':false,
		'prefs_obj':this.App.prefs
	});

	this.App.master_timeline_model = {
	    items : []
	};

};

//
// AppAssistant.prototype.cleanup = function() {
// 	Mojo.Log.info('Logging from AppAssistant.cleanup');
// 	Mojo.Log.info('Shutting down app');
// 	// if (!this.weAreHeadless) {
// 	// 	Mojo.Log.info('We are NOT headless -- registering next notification');
// 	// 	this.App.bgnotifier.registerNextNotification();
// 	// }
// };

AppAssistant.prototype.loadAccount = function(account_id) {

	if (!account_id) {
		account_id = this.App.prefs.get('last_userid');
	}

	/*
		load users from prefs obj
	*/
	this.Users = new SpazAccounts(this.App.prefs);
	this.Users.load();

	var account = this.Users.get(account_id);
	if (account !== false) {
		dump(account);
		this.App.username = account.username;
		this.App.type     = account.type;
		this.App.userid   = account.id;
	} else {
		dump("Tried to load account, but failed.");
	}

};

/**
 * @param {object} launchParams launch parameters passed to app
 * @param {string} launchParams.action the action to take. (prepPost|user|search|status)
 * @param {string} [launchParams.account] an account hash. If not passed, the last logged-in user account is used
 * @param {string} [launchParams.msg] message to insert into the posting form for the "prepPost" action (action:prepPost)
 * @param {string} [launchParams.userid] userid (integer or string) to pass to "user" action (action:user)
 * @param {string} [launchParams.query] a valid search query for the twitter API (action:search)
 * @param {string} [launchParams.statusid] a status id for a message (action:status)
 *
 */
AppAssistant.prototype.handleLaunch = function(launchParams) {
	var appAssistant = this;

	/*
	   initialize bgnotifier
	*/
	this.App.bgnotifier = new BackgroundNotifier(true);

	/*
		load up the timelineCache before going further
	*/

	var mainStageController = Mojo.Controller.getAppController().getStageController(SPAZ_MAIN_STAGENAME);

	Mojo.Log.error("Launch Parameters: %j", launchParams);

	/*
		for compatibility with Bad Kitty and Twee APIs
	*/
	if (launchParams.tweet) {
		launchParams.action = 'prepPost';
		launchParams.msg = launchParams.tweet;
	}
	if (launchParams.user) {
		launchParams.action = 'user';
		launchParams.userid = launchParams.user;
	}

	var stageCallback = function(stageController) {
		Mojo.Log.error('RUNNING stageCallback');

		switch(launchParams.action) {

			/**
			 * {
			 *   action:"prepPost",
			 *   msg:"Some Text",
			 *   account:"ACCOUNT_HASH" // optional
			 * }
			 */
			case 'prepPost':
			case 'post':
				appAssistant.loadAccount(launchParams.account||null);
				stageController.pushScene('post', {
					'text':launchParams.msg
				});
				stageController.activate();
				break;

			/**
			 * {
			 *   action:"user",
			 *   userid:"funkatron"
			 * }
			 */
			case 'user':
				// appAssistant.loadAccount(launchParams.account||null);
				stageController.pushScene('user-detail', '@'+launchParams.userid);
				stageController.activate();
				break;

			/**
			 * {
			 *   action:"search",
			 *   query:"spaz source:spaz"
			 * }
			 */
			case 'search':
				stageController.pushScene('search-twitter', {
					'searchterm':launchParams.query
				});
				stageController.activate();
				break;

			/**
			 * {
			 *   action:"status",
			 *   statusid:24426249322
			 * }
			 */
			case 'status':
				stageController.pushScene('message-detail', launchParams.statusid);
				stageController.activate();
				break;

			case 'main_timeline':
				Mojo.Log.info('main_timeline action');

				appAssistant.App.bgnotifier.registerNextNotification();

				appAssistant.loadAccount(launchParams.account||null);

				stageController.pushScene('my-timeline', { 'mark_cache_as_read':false });
				stageController.activate();
				break;

			case 'bgcheck':
				Mojo.Log.error('BGCHECK action');
				Mojo.Log.error('sendToNotificationChain refresh');
				// Mojo.Controller.getAppController().sendToNotificationChain({"event":"refresh"});
				stageController.sendEventToCommanders({'type':Mojo.Event.command, 'command':'refresh'});
				appAssistant.App.bgnotifier.registerNextNotification();
				break;

			default:
				Mojo.Log.info('default handleLaunch action');

				appAssistant.App.bgnotifier.registerNextNotification();

				if (appAssistant.App.prefs.get('always-go-to-my-timeline')) {
					stageController.pushScene('my-timeline');
				} else {
					stageController.pushScene('start');
				}
				break;

		}
	};

	/*
		we go ahead and re-activate the existing stage, or make a new main stage
	*/
	if (mainStageController) {
		// if (mainStageController.topScene() && mainStageController.topScene().sceneName == "start") {
		if (mainStageController.topScene()) {
			stageCallback(mainStageController);
		} // else {
		 // 			if (!launchParams.action || launchParams.action !== 'bgcheck') {
		 // 				mainStageController.activate();
		 // 			}
		 // 		}
		// /*
		// 	bgcheck action -- send a refresh event
		// */
		// if (launchParams.action && launchParams.action == 'bgcheck') {
		// 	Mojo.Log.error('BGCHECK action');
		// 	appAssistant.App.bgnotifier.registerNextNotification();
		// 	Mojo.Controller.getAppController().sendToNotificationChain({"event":"refresh"});
		// }

	} else {

		/*
			bgcheck action -- if called when we don't have a stage already, just run in bg
		*/
		if (launchParams.action && launchParams.action == 'bgcheck') {
			Mojo.Log.error('BGCHECK action --- no mainStageController');

			appAssistant.App.bgnotifier.init(function() {
				appAssistant.App.bgnotifier.checkForNewData();
			});

			return;
		}

		Mojo.Controller.getAppController()
			.createStageWithCallback(
				{
					name: SPAZ_MAIN_STAGENAME,
					assistantName:SPAZ_STAGEASSISTANTNAME
				},
				stageCallback
			);
	}

};

/**
 *
 */
AppAssistant.prototype.handleCommand = function(event){

	sch.debug(event.command);

	if (event.type == Mojo.Event.command) {
		switch (event.command) {
			/*
				back
			*/
			case 'back':
				Mojo.Controller.stageController.popScene();
				break;

			case 'search-trends':
				Mojo.Controller.notYetImplemented();
				break;

			case 'donate':
				var sr = new Mojo.Service.Request("palm://com.palm.applicationManager", {
				  method: "open",
				  parameters:  {
				      id: 'com.palm.app.browser',
				      params: {
				          target: SPAZ_DONATION_URL
				      }
				  }
				});

				break;

		}
	}
};

AppAssistant.prototype.openChildWindow = function() {
	this.stageController = this.appController.getStageController('lightWeight');

	if (this.stageController){
		// app window is open, give it focus
		this.stageController.activate();
	} else{
		// otherwise create the app window
		this.appController.createStageWithCallback({name: 'lightWeight', lightweight: true}, this.pushTheScene.bind(this));
	}

};

/**
 * Because the app assistant doesn't share a window object with the stages,
 * we need to map the sc. namespace to the stageController.window
 */
AppAssistant.prototype.mapObjectsToNewStage = function(stageController) {
	stageController.window.sc = sc; // map spazcore namespace
	// stageController.window.jQuery = jQuery; // map jQuery
	/*
		re-map setInterval to the stageController's window.setInterval,
		as Mojo unsets it when we make a noWindow app.
	*/
	window.setInterval = stageController.window.setInterval;
};

AppAssistant.prototype.saveLastIDs = function(home, mention, dm) {
	var cookie = new Mojo.Model.Cookie('SPAZ_WEBOS_LASTIDS');

	var data = { 'home':home, 'mention':mention, 'dm':dm };

	cookie.put(data);
};

AppAssistant.prototype.loadLastIDs = function() {
	var cookie = new Mojo.Model.Cookie('SPAZ_WEBOS_LASTIDS');

	var data = cookie.get();

	if (!data || !data.home) {
		this.resetLastIDs();
		return { 'home':1, 'mention':1, 'dm':1 };
	}

	return data;
};

AppAssistant.prototype.resetLastIDs = function() {
	this.saveLastIDs(1,1,1);
};

AppAssistant.prototype.loadTimelineCache = function(onLoad) {

	if (!this.App.cache) {
		this.App.cache = new TempCache({
			'appObj':this.App
		});
	}

	Mojo.Log.error('LOADTIMELINECACHE');

	Mojo.Timing.resume("timing_loadTimelineCache");
	var thisA = this;

	function _onLoad(e) {
		if (onLoad) {
			onLoad(e);
		}
		Mojo.Timing.pause("timing_loadTimelineCache");

		Mojo.Log.error(Mojo.Timing.createTimingString("timing_", "Cache op times"));
	}

	if (!this.App.cache.exists()) {
		Mojo.Log.error('CACHE DOES NOT EXIST in AppAssistant');
		this.App.cache.loadFromDB(_onLoad);
	} else {
		_onLoad();
	}
};<commit_msg>load account before pushing in default action<commit_after>
const SPAZ_DASHBOARD_STAGENAME = 'dashboard';
const SPAZ_MAIN_STAGENAME      = 'main';
const SPAZ_STAGEASSISTANTNAME = 'StageAssistant';
const SPAZ_BGNOTIFIER_DASHBOARD_STAGENAME = 'bgnotifier';

const SPAZ_DONATION_URL = 'http://getspaz.com/donate';

var LOGGEDIN_APPMENU_ITEMS = [
	Mojo.Menu.editItem,
	{ label: $L('Update Location'),	command: 'update-location' },
	//{ label: $L('New Search Card'),	command: 'new-search-card' },
	{ label: $L('Accounts'), 	command:'accounts' },
	{ label: $L('Preferences'),	command:Mojo.Menu.prefsCmd },
	{ label: $L('About Spaz'),		command: 'appmenu-about' },
	{ label: $L('Donate'),		command:'donate' },
	{ label: $L('Help'),			command:Mojo.Menu.helpCmd }

];

function AppAssistant(appController) {

	Mojo.Log.info("Logging from AppAssistant Constructor");

	this.initialize();

}

/**
 * container for global app-available stuff. We map this to "spaz_app"
 */
AppAssistant.prototype.App = {};

AppAssistant.prototype.initialize = function() {

	var that = this;

	Mojo.Log.info('INITIALIZING EVERYTHING');

	SpazAuth.addService(SPAZCORE_ACCOUNT_TWITTER, {
        authType: SPAZCORE_AUTHTYPE_OAUTH,
        consumerKey: SPAZCORE_CONSUMERKEY_TWITTER,
        consumerSecret: SPAZCORE_CONSUMERSECRET_TWITTER,
        accessURL: 'http://twitter.com/oauth/access_token'
    });

	/*
		Remap JSON parser because JSON2.js one was causing probs with unicode
	*/
	sc.helpers.deJSON = function(str) {
		try {
			var obj = JSON.parse(str);
			return obj;
		} catch(e) {
			Mojo.Log.error('There was a problem decoding the JSON string');
			Mojo.Log.error('Here is the JSON string: '+str);
			return null;
		}

	};
	sc.helpers.enJSON = function(obj) {
		var json = JSON.stringify(obj);
		return json;
	};

	sch.info = Mojo.Log.info;
	sch.dump = Mojo.Log.info;
	sch.warn = Mojo.Log.warn;
	sch.error = Mojo.Log.error;

	this.App.search_cards = [];
	this.App.new_search_card = 0;
	this.App.search_card_prefix = "searchcard_";

	this.App.username = null;
	this.App.prefs = null;

	this.App.accounts = null;

	/*
		load our prefs
		default_preferences is from default_preferences.js, loaded in index.html
	*/
	this.App.prefs = new SpazPrefs(default_preferences, null, {
		'timeline-maxentries': {
			'onGet': function(key, value){
				if (that.App.prefs.get('timeline-friends-getcount') > value) {
					value = that.App.prefs.get('timeline-friends-getcount');
				}
				sch.debug(key + ':' + value);
				return value;
			},
			'onSet': function(key, value){
				if (that.App.prefs.get('timeline-friends-getcount') > value) {
					value = that.App.prefs.get('timeline-friends-getcount');
				}
				sch.debug(key + ':' + value);
				return value;
			}
		},
		'timeline-maxentries-dm': {
			'onGet': function(key, value){
				if (that.App.prefs.get('timeline-dm-getcount') > value) {
					value = that.App.prefs.get('timeline-dm-getcount');
				}
				sch.debug(key + ':' + value);
				return value;
			},
			'onSet': function(key, value){
				if (that.App.prefs.get('timeline-dm-getcount') > value) {
					value = that.App.prefs.get('timeline-dm-getcount');
				}
				sch.debug(key + ':' + value);
				return value;
			}
		},
		'timeline-maxentries-reply': {
			'onGet': function(key, value){
				if (that.App.prefs.get('timeline-replies-getcount') > value) {
					value = that.App.prefs.get('timeline-replies-getcount');
				}
				sch.debug(key + ':' + value);
				return value;
			},
			'onSet': function(key, value){
				if (that.App.prefs.get('timeline-replies-getcount') > value) {
					value = that.App.prefs.get('timeline-replies-getcount');
				}
				sch.debug(key + ':' + value);
				return value;
			}
		}
	});
	this.App.prefs.load(); // this is sync on webOS, b/c loading from Mojo.Model.Cookie

	/*
		load app accounts
	*/
	this.App.accounts = new SpazAccounts(this.App.prefs);

	this.App.twit = new SpazTwit(null, null, {
		'event_mode':'jquery'
	});

	/*
		model for saving Tweets to Depot. We replace on every start to make sure we don't go over-budget
	*/
	this.App.Tweets = new Tweets({
		'replace':false,
		'prefs_obj':this.App.prefs
	});

	this.App.master_timeline_model = {
	    items : []
	};

};

//
// AppAssistant.prototype.cleanup = function() {
// 	Mojo.Log.info('Logging from AppAssistant.cleanup');
// 	Mojo.Log.info('Shutting down app');
// 	// if (!this.weAreHeadless) {
// 	// 	Mojo.Log.info('We are NOT headless -- registering next notification');
// 	// 	this.App.bgnotifier.registerNextNotification();
// 	// }
// };

AppAssistant.prototype.loadAccount = function(account_id) {

	if (!account_id) {
		account_id = this.App.prefs.get('last_userid');
	}

	/*
		load users from prefs obj
	*/
	this.Users = new SpazAccounts(this.App.prefs);
	this.Users.load();

	var account = this.Users.get(account_id);
	if (account !== false) {
		dump(account);
		this.App.username = account.username;
		this.App.type     = account.type;
		this.App.userid   = account.id;
	} else {
		dump("Tried to load account, but failed.");
	}

};

/**
 * @param {object} launchParams launch parameters passed to app
 * @param {string} launchParams.action the action to take. (prepPost|user|search|status)
 * @param {string} [launchParams.account] an account hash. If not passed, the last logged-in user account is used
 * @param {string} [launchParams.msg] message to insert into the posting form for the "prepPost" action (action:prepPost)
 * @param {string} [launchParams.userid] userid (integer or string) to pass to "user" action (action:user)
 * @param {string} [launchParams.query] a valid search query for the twitter API (action:search)
 * @param {string} [launchParams.statusid] a status id for a message (action:status)
 *
 */
AppAssistant.prototype.handleLaunch = function(launchParams) {
	var appAssistant = this;

	/*
	   initialize bgnotifier
	*/
	this.App.bgnotifier = new BackgroundNotifier(true);

	/*
		load up the timelineCache before going further
	*/

	var mainStageController = Mojo.Controller.getAppController().getStageController(SPAZ_MAIN_STAGENAME);

	Mojo.Log.error("Launch Parameters: %j", launchParams);

	/*
		for compatibility with Bad Kitty and Twee APIs
	*/
	if (launchParams.tweet) {
		launchParams.action = 'prepPost';
		launchParams.msg = launchParams.tweet;
	}
	if (launchParams.user) {
		launchParams.action = 'user';
		launchParams.userid = launchParams.user;
	}

	var stageCallback = function(stageController) {
		Mojo.Log.error('RUNNING stageCallback');

		switch(launchParams.action) {

			/**
			 * {
			 *   action:"prepPost",
			 *   msg:"Some Text",
			 *   account:"ACCOUNT_HASH" // optional
			 * }
			 */
			case 'prepPost':
			case 'post':
				appAssistant.loadAccount(launchParams.account||null);
				stageController.pushScene('post', {
					'text':launchParams.msg
				});
				stageController.activate();
				break;

			/**
			 * {
			 *   action:"user",
			 *   userid:"funkatron"
			 * }
			 */
			case 'user':
				appAssistant.loadAccount(launchParams.account||null);
				stageController.pushScene('user-detail', '@'+launchParams.userid);
				stageController.activate();
				break;

			/**
			 * {
			 *   action:"search",
			 *   query:"spaz source:spaz"
			 * }
			 */
			case 'search':
				stageController.pushScene('search-twitter', {
					'searchterm':launchParams.query
				});
				stageController.activate();
				break;

			/**
			 * {
			 *   action:"status",
			 *   statusid:24426249322
			 * }
			 */
			case 'status':
				stageController.pushScene('message-detail', launchParams.statusid);
				stageController.activate();
				break;

			case 'main_timeline':
				Mojo.Log.info('main_timeline action');

				appAssistant.App.bgnotifier.registerNextNotification();

				appAssistant.loadAccount(launchParams.account||null);

				stageController.pushScene('my-timeline', { 'mark_cache_as_read':false });
				stageController.activate();
				break;

			case 'bgcheck':
				Mojo.Log.error('BGCHECK action');
				Mojo.Log.error('sendToNotificationChain refresh');
				// Mojo.Controller.getAppController().sendToNotificationChain({"event":"refresh"});
				stageController.sendEventToCommanders({'type':Mojo.Event.command, 'command':'refresh'});
				appAssistant.App.bgnotifier.registerNextNotification();
				break;

			default:
				Mojo.Log.info('default handleLaunch action');

				appAssistant.App.bgnotifier.registerNextNotification();

				appAssistant.loadAccount(launchParams.account||null);

				if (appAssistant.App.prefs.get('always-go-to-my-timeline')) {
					stageController.pushScene('my-timeline');
				} else {
					stageController.pushScene('start');
				}
				break;

		}
	};

	/*
		we go ahead and re-activate the existing stage, or make a new main stage
	*/
	if (mainStageController) {
		// if (mainStageController.topScene() && mainStageController.topScene().sceneName == "start") {
		if (mainStageController.topScene()) {
			stageCallback(mainStageController);
		} // else {
		 // 			if (!launchParams.action || launchParams.action !== 'bgcheck') {
		 // 				mainStageController.activate();
		 // 			}
		 // 		}
		// /*
		// 	bgcheck action -- send a refresh event
		// */
		// if (launchParams.action && launchParams.action == 'bgcheck') {
		// 	Mojo.Log.error('BGCHECK action');
		// 	appAssistant.App.bgnotifier.registerNextNotification();
		// 	Mojo.Controller.getAppController().sendToNotificationChain({"event":"refresh"});
		// }

	} else {

		/*
			bgcheck action -- if called when we don't have a stage already, just run in bg
		*/
		if (launchParams.action && launchParams.action == 'bgcheck') {
			Mojo.Log.error('BGCHECK action --- no mainStageController');

			appAssistant.App.bgnotifier.init(function() {
				appAssistant.App.bgnotifier.checkForNewData();
			});

			return;
		}

		Mojo.Controller.getAppController()
			.createStageWithCallback(
				{
					name: SPAZ_MAIN_STAGENAME,
					assistantName:SPAZ_STAGEASSISTANTNAME
				},
				stageCallback
			);
	}

};

/**
 *
 */
AppAssistant.prototype.handleCommand = function(event){

	sch.debug(event.command);

	if (event.type == Mojo.Event.command) {
		switch (event.command) {
			/*
				back
			*/
			case 'back':
				Mojo.Controller.stageController.popScene();
				break;

			case 'search-trends':
				Mojo.Controller.notYetImplemented();
				break;

			case 'donate':
				var sr = new Mojo.Service.Request("palm://com.palm.applicationManager", {
				  method: "open",
				  parameters:  {
				      id: 'com.palm.app.browser',
				      params: {
				          target: SPAZ_DONATION_URL
				      }
				  }
				});

				break;

		}
	}
};

AppAssistant.prototype.openChildWindow = function() {
	this.stageController = this.appController.getStageController('lightWeight');

	if (this.stageController){
		// app window is open, give it focus
		this.stageController.activate();
	} else{
		// otherwise create the app window
		this.appController.createStageWithCallback({name: 'lightWeight', lightweight: true}, this.pushTheScene.bind(this));
	}

};

/**
 * Because the app assistant doesn't share a window object with the stages,
 * we need to map the sc. namespace to the stageController.window
 */
AppAssistant.prototype.mapObjectsToNewStage = function(stageController) {
	stageController.window.sc = sc; // map spazcore namespace
	// stageController.window.jQuery = jQuery; // map jQuery
	/*
		re-map setInterval to the stageController's window.setInterval,
		as Mojo unsets it when we make a noWindow app.
	*/
	window.setInterval = stageController.window.setInterval;
};

AppAssistant.prototype.saveLastIDs = function(home, mention, dm) {
	var cookie = new Mojo.Model.Cookie('SPAZ_WEBOS_LASTIDS');

	var data = { 'home':home, 'mention':mention, 'dm':dm };

	cookie.put(data);
};

AppAssistant.prototype.loadLastIDs = function() {
	var cookie = new Mojo.Model.Cookie('SPAZ_WEBOS_LASTIDS');

	var data = cookie.get();

	if (!data || !data.home) {
		this.resetLastIDs();
		return { 'home':1, 'mention':1, 'dm':1 };
	}

	return data;
};

AppAssistant.prototype.resetLastIDs = function() {
	this.saveLastIDs(1,1,1);
};

AppAssistant.prototype.loadTimelineCache = function(onLoad) {

	if (!this.App.cache) {
		this.App.cache = new TempCache({
			'appObj':this.App
		});
	}

	Mojo.Log.error('LOADTIMELINECACHE');

	Mojo.Timing.resume("timing_loadTimelineCache");
	var thisA = this;

	function _onLoad(e) {
		if (onLoad) {
			onLoad(e);
		}
		Mojo.Timing.pause("timing_loadTimelineCache");

		Mojo.Log.error(Mojo.Timing.createTimingString("timing_", "Cache op times"));
	}

	if (!this.App.cache.exists()) {
		Mojo.Log.error('CACHE DOES NOT EXIST in AppAssistant');
		this.App.cache.loadFromDB(_onLoad);
	} else {
		_onLoad();
	}
};<|endoftext|>


<commit_before>const request = require('request-promise');
const fs = require("fs");
const isRadioOrLineIn = require('../helpers/is-radio-or-line-in');

const appleDef = require('../music_services/appleDef');
const spotifyDef = require('../music_services/spotifyDef');
const deezerDef = require('../music_services/deezerDef');
const eliteDef = deezerDef.init(true);
const libraryDef = require('../music_services/libraryDef');

const musicServices = ['apple','spotify','deezer','elite','library'];
const musicTypes = ['album','song','station','load','setsid'];
const sidsPath = "./lib/sids.json";

var country = '';
var accountId = '';
var accountSN = '';
var searchType = 0;

var serviceIds = {
  apple:   '204',
  spotify: '12',
  deezer:  '168',
  elite:   '168',
  library: '0'
};

function getService(service) {
  if (service == 'apple') {
    return appleDef;
  } else
  if (service == 'spotify') {
    return spotifyDef;
  } else
  if (service == 'deezer') {
    return deezerDef;
  } else
  if (service == 'elite') {
    return eliteDef;
  } else
  if (service == 'library') {
    return libraryDef;
  }
}

function handleSids(err, data) {
  if (!err) {
    var storedIds = JSON.parse(data);
    Object.keys(storedIds).map(function(service) {
      serviceIds[service] = storedIds[service];
    });
  }
}

function setSid(player, service, song) {
  console.log("Setting Service ID for " + service);

  return player.browse('FV:2', 0, 0)
    .then(result => result.items)
    .then((favorites) => {
      return favorites.find((fav) => fav.title.toLowerCase() === song.toLowerCase());
    })
    .then((favorite) => {
      if (!favorite) {
        console.log("The song " + song + " was not found in Sonos Favorites");
        return Promise.reject("The song " + song + " was not found in Sonos Favorites");
      }

      var sidLoc = favorite.uri.indexOf('sid=');

      if (sidLoc == -1) {
        console.log(song + " in Sonos Favorites needs to be a song type");
        return Promise.reject(song + " in Sonos Favorites needs to be a song type");
      }
      return favorite.uri.substring(sidLoc+4,favorite.uri.indexOf('&', sidLoc));
    })
    .then((sid) => {
      serviceIds[service] = sid;
      return new Promise((resolve, reject) => {
        fs.writeFile(sidsPath,JSON.stringify(serviceIds),(err) => {
          if (err) {
            return reject(err);
          } else {
            return resolve();
          }
        });
      });
    });
}

function getAccountId(player, service)
{
  accountId = '';

  if (service != 'library') {
    return request({url: player.baseUrl + '/status/accounts',json: false})
      .then((res) => {
        var actLoc = res.indexOf(serviceIds[service] * 256 + 7);

        if (actLoc != -1) {
          var idLoc = res.indexOf('<UN>', actLoc)+4;
          var snLoc = res.indexOf('SerialNum="', actLoc)+11;

          accountId = res.substring(idLoc,res.indexOf('</UN>',idLoc));
          accountSN = res.substring(snLoc,res.indexOf('"',snLoc));
        }

        return Promise.resolve();
      });

    return promise;
  } else {
    return Promise.resolve();
  }
}

function doSearch(service, type, term)
{
  var serviceDef = getService(service);
  var url = serviceDef.search[type];

  term = decodeURIComponent(term);

  // Check for search type specifiers
  if (term.indexOf(':') > -1) {
    var newTerm = '';
    var artistPos = term.indexOf('artist:');
    var albumPos  = term.indexOf('album:');
    var trackPos  = term.indexOf('track:');
    var nextPos = -1;
    var artist = '';
    var album = '';
    var track  = '';

    if (artistPos > -1) {
      nextPos = (albumPos < trackPos)?albumPos:trackPos;
      artist = term.substring(artistPos+7,(artistPos < nextPos)?nextPos:term.length);
    }
    if (albumPos > -1) {
      nextPos = (trackPos < artistPos)?trackPos:artistPos;
      album = term.substring(albumPos+6,(albumPos < nextPos)?nextPos:term.length);
    }
    if (trackPos > -1) {
      nextPos = (albumPos < artistPos)?albumPos:artistPos;
      track = term.substring(trackPos+6,(trackPos < nextPos)?nextPos:term.length);
    }

    newTerm = serviceDef.term(type, term, artist, album, track);

  } else {
    newTerm = (service == 'library')?term:encodeURIComponent(term);
  }

  if (type == 'song') {
    searchType = (trackPos > -1)?1:((artistPos > -1)?2:0);
  }
  url += newTerm;

  if (service == 'library') {
    return Promise.resolve(libraryDef.search(type, newTerm));
  } else
  if ((serviceDef.country != '') && (country == '')) {
    return request({url: 'http://ipinfo.io',
                   json: true})
           .then((res) => {
             country = res.country;
             url += serviceDef.country + country;
             return request({url: url, json: true});
           });
  } else {
    if (serviceDef.country != '') {
      url += serviceDef.country + country;
    }

    return request({url: url, json: true});
  }
}

function loadTracks(service, type, tracksJson)
{
  var tracks = getService(service).tracks(type, tracksJson);

  if ((service == 'library') && (type == 'album')) {
    tracks.isArtist = true;
  } else
  if (searchType == 0) {
    // Determine if the request was for a specific song or for many songs by a specific artist
    if (tracks.count > 1) {
      var artistCount = 1;
      var trackCount = 1;
      var artists = tracks.queueTracks.map(function(track) {
            return track.artistName.toLowerCase();
          }).sort();
      var songs = tracks.queueTracks.map(function(track) {
            return track.trackName.toLowerCase();
          }).sort();

      var prevArtist=artists[0];
      var prevTrack=songs[0];

      for (var i=1; i < tracks.count;i++) {
        if (artists[i] != prevArtist) {
          artistCount++;
          prevArtist = artists[i];
        }
        if (songs[i] != prevTrack) {
          trackCount++;
          prevTrack = songs[i];
        }
      }
      tracks.isArtist = (trackCount/artistCount > 2);
    }
  } else {
    tracks.isArtist = (searchType == 2);
  }

  return tracks;
}

function musicSearch(player, values) {
  const service = values[0];
  const type = values[1];
  const term = values[2];
  const queueURI = 'x-rincon-queue:' + player.uuid + '#0';
  var   serviceDef = null;

  if (musicServices.indexOf(service) == -1) {
    return Promise.reject('Invalid music service');
  }

  if (musicTypes.indexOf(type) == -1) {
    return Promise.reject('Invalid type ' + type);
  }

  if ((service == 'library') && ((type == 'load') || libraryDef.nolib())) {
    return libraryDef.load(player, (type == 'load'));
  }

  if ((service != 'library') && (type == 'setsid')) {
    return setSid(player, service, term);
  }

  return getAccountId(player, service)
    .then(() => {
      serviceDef = getService(service);
      serviceDef.service(serviceIds[service], accountId, accountSN, country);
      return doSearch(service, type, term);
    })
    .then((resList) => {
      if (serviceDef.empty(type, resList)) {
        return Promise.reject('No matches were found');
      } else {
        var UaM = null;

        if (type == 'station') {
          UaM = serviceDef.urimeta(type, resList);

          return player.coordinator.setAVTransport(UaM.uri, UaM.metadata)
            .then(() => player.coordinator.play());
        } else
        if ((type == 'album') && (service != 'library')) {
          UaM = serviceDef.urimeta(type, resList);

          return player.coordinator.clearQueue()
            .then(() => {
              if (isRadioOrLineIn(player.coordinator.avTransportUri)) {
                return player.coordinator.setAVTransport(queueURI, '');
              }
            })
            .then(() => player.coordinator.addURIToQueue(UaM.uri, UaM.metadata, true, 1))
            .then(() => player.coordinator.play());
        } else { // Play songs
          var tracks = loadTracks(service, type, resList);

          if (tracks.count == 0) {
            return Promise.reject('No matches were found');
          } else {
            if (tracks.isArtist) {  // Play numerous songs by the specified artist
              return player.coordinator.clearQueue()
                .then(() => {
                  if (isRadioOrLineIn(player.coordinator.avTransportUri)) {
                    return player.coordinator.setAVTransport(queueURI, '');
                  }
                })
                .then(() => player.coordinator.addURIToQueue(tracks.queueTracks[0].uri, tracks.queueTracks[0].metadata, true, 1))
                .then(() => player.coordinator.play())
                .then(() => {
                // Do not return promise since we want to be considered done from the calling context
                  tracks.queueTracks.slice(1).reduce((promise, track, index) => {
                    return promise.then(() => player.coordinator.addURIToQueue(track.uri, track.metadata, true, index + 2));
                  }, Promise.resolve());
                });
            } else { // Play the one specified song
              var empty = false;
              var nextTrackNo = 0;

              return player.coordinator.getQueue(0, 1)
                .then((queue) => {
                  empty = (queue.numberReturned == 0);
                  nextTrackNo = (empty) ? 1 : player.coordinator.state.trackNo + 1;
                })
                .then(() => player.coordinator.addURIToQueue(tracks.queueTracks[0].uri, tracks.queueTracks[0].metadata, true, nextTrackNo))
                .then(() => {
                  if (isRadioOrLineIn(player.coordinator.state.currentTrack.uri)) {
                    return player.coordinator.setAVTransport(queueURI, '');
                  }
                })
                .then(() => {
                  if (!empty) {
                    return player.coordinator.nextTrack();
                  }
                })
                .then(() => player.coordinator.play());
            }
          }
        }
      }
    });
}

module.exports = function (api) {
  api.registerAction('musicsearch', musicSearch);
  libraryDef.read();
  fs.readFile(sidsPath, handleSids);
};
<commit_msg>Update musicSearch.js<commit_after>const request = require('request-promise');
const fs = require("fs");
const isRadioOrLineIn = require('../helpers/is-radio-or-line-in');

const appleDef = require('../music_services/appleDef');
const spotifyDef = require('../music_services/spotifyDef');
const deezerDef = require('../music_services/deezerDef');
const eliteDef = deezerDef.init(true);
const libraryDef = require('../music_services/libraryDef');

const musicServices = ['apple','spotify','deezer','elite','library'];
const musicTypes = ['album','song','station','load','setsid'];
const sidsPath = "./lib/sids.json";

var country = '';
var accountId = '';
var accountSN = '';
var searchType = 0;

var serviceIds = {
  apple:   '204',
  spotify: '12',
  deezer:  '168',
  elite:   '168',
  library: '0'
};

function getService(service) {
  if (service == 'apple') {
    return appleDef;
  } else
  if (service == 'spotify') {
    return spotifyDef;
  } else
  if (service == 'deezer') {
    return deezerDef;
  } else
  if (service == 'elite') {
    return eliteDef;
  } else
  if (service == 'library') {
    return libraryDef;
  }
}

function handleSids(err, data) {
  if (!err) {
    var storedIds = JSON.parse(data);
    Object.keys(storedIds).map(function(service) {
      serviceIds[service] = storedIds[service];
    });
  }
}

function setSid(player, service, song) {
  console.log("Setting Service ID for " + service);

  return player.browse('FV:2', 0, 0)
    .then(result => result.items)
    .then((favorites) => {
      return favorites.find((fav) => fav.title.toLowerCase() === song.toLowerCase());
    })
    .then((favorite) => {
      if (!favorite) {
        console.log("The song " + song + " was not found in Sonos Favorites");
        return Promise.reject("The song " + song + " was not found in Sonos Favorites");
      }

      var sidLoc = favorite.uri.indexOf('sid=');

      if (sidLoc == -1) {
        console.log(song + " in Sonos Favorites needs to be a song type");
        return Promise.reject(song + " in Sonos Favorites needs to be a song type");
      }
      return favorite.uri.substring(sidLoc+4,favorite.uri.indexOf('&', sidLoc));
    })
    .then((sid) => {
      serviceIds[service] = sid;
      return new Promise((resolve, reject) => {
        fs.writeFile(sidsPath,JSON.stringify(serviceIds),(err) => {
          if (err) {
            return reject(err);
          } else {
            return resolve();
          }
        });
      });
    });
}

function getAccountId(player, service)
{
  accountId = '';

  if (service != 'library') {
    return request({url: player.baseUrl + '/status/accounts',json: false})
      .then((res) => {
        var actLoc = res.indexOf(serviceIds[service] * 256 + 7);

        if (actLoc != -1) {
          var idLoc = res.indexOf('<UN>', actLoc)+4;
          var snLoc = res.indexOf('SerialNum="', actLoc)+11;

          accountId = res.substring(idLoc,res.indexOf('</UN>',idLoc));
          accountSN = res.substring(snLoc,res.indexOf('"',snLoc));
        }

        return Promise.resolve();
      });

    return promise;
  } else {
    return Promise.resolve();
  }
}

function doSearch(service, type, term)
{
  var serviceDef = getService(service);
  var url = serviceDef.search[type];

  term = decodeURIComponent(term);

  // Check for search type specifiers
  if (term.indexOf(':') > -1) {
    var newTerm = '';
    var artistPos = term.indexOf('artist:');
    var albumPos  = term.indexOf('album:');
    var trackPos  = term.indexOf('track:');
    var nextPos = -1;
    var artist = '';
    var album = '';
    var track  = '';

    if (artistPos > -1) {
      nextPos = (albumPos < trackPos)?albumPos:trackPos;
      artist = term.substring(artistPos+7,(artistPos < nextPos)?nextPos:term.length);
    }
    if (albumPos > -1) {
      nextPos = (trackPos < artistPos)?trackPos:artistPos;
      album = term.substring(albumPos+6,(albumPos < nextPos)?nextPos:term.length);
    }
    if (trackPos > -1) {
      nextPos = (albumPos < artistPos)?albumPos:artistPos;
      track = term.substring(trackPos+6,(trackPos < nextPos)?nextPos:term.length);
    }

    newTerm = serviceDef.term(type, term, artist, album, track);

  } else {
    newTerm = (service == 'library')?term:encodeURIComponent(term);
  }

  if (type == 'song') {
    searchType = (trackPos > -1)?1:((artistPos > -1)?2:0);
  }
  url += newTerm;

  if (service == 'library') {
    return Promise.resolve(libraryDef.search(type, newTerm));
  } else
  if ((serviceDef.country != '') && (country == '')) {
    return request({url: 'http://ipinfo.io',
                   json: true})
           .then((res) => {
             country = res.country;
             url += serviceDef.country + country;
             return request({url: url, json: true});
           });
  } else {
    if (serviceDef.country != '') {
      url += serviceDef.country + country;
    }

    return request({url: url, json: true});
  }
}

Array.prototype.shuffle=function(){
  var len = this.length,temp,i
  while(len){
    i=Math.random()*len-- >>> 0;
    temp=this[len],this[len]=this[i],this[i]=temp;
  }
  return this;
}

function loadTracks(service, type, tracksJson)
{
  var tracks = getService(service).tracks(type, tracksJson);

  if ((service == 'library') && (type == 'album')) {
    tracks.isArtist = true;
  } else
  if (searchType == 0) {
    // Determine if the request was for a specific song or for many songs by a specific artist
    if (tracks.count > 1) {
      var artistCount = 1;
      var trackCount = 1;
      var artists = tracks.queueTracks.map(function(track) {
            return track.artistName.toLowerCase();
          }).sort();
      var songs = tracks.queueTracks.map(function(track) {
            return track.trackName.toLowerCase();
          }).sort();

      var prevArtist=artists[0];
      var prevTrack=songs[0];

      for (var i=1; i < tracks.count;i++) {
        if (artists[i] != prevArtist) {
          artistCount++;
          prevArtist = artists[i];
        }
        if (songs[i] != prevTrack) {
          trackCount++;
          prevTrack = songs[i];
        }
      }
      tracks.isArtist = (trackCount/artistCount > 2);
    }
  } else {
    tracks.isArtist = (searchType == 2);
  }
  if (tracks.isArtist) {
    tracks.queueTracks.shuffle();
  }

  return tracks;
}

function musicSearch(player, values) {
  const service = values[0];
  const type = values[1];
  const term = values[2];
  const queueURI = 'x-rincon-queue:' + player.uuid + '#0';
  var   serviceDef = null;

  if (musicServices.indexOf(service) == -1) {
    return Promise.reject('Invalid music service');
  }

  if (musicTypes.indexOf(type) == -1) {
    return Promise.reject('Invalid type ' + type);
  }

  if ((service == 'library') && ((type == 'load') || libraryDef.nolib())) {
    return libraryDef.load(player, (type == 'load'));
  }

  if ((service != 'library') && (type == 'setsid')) {
    return setSid(player, service, term);
  }

  return getAccountId(player, service)
    .then(() => {
      serviceDef = getService(service);
      serviceDef.service(serviceIds[service], accountId, accountSN, country);
      return doSearch(service, type, term);
    })
    .then((resList) => {
      if (serviceDef.empty(type, resList)) {
        return Promise.reject('No matches were found');
      } else {
        var UaM = null;

        if (type == 'station') {
          UaM = serviceDef.urimeta(type, resList);

          return player.coordinator.setAVTransport(UaM.uri, UaM.metadata)
            .then(() => player.coordinator.play());
        } else
        if ((type == 'album') && (service != 'library')) {
          UaM = serviceDef.urimeta(type, resList);

          return player.coordinator.clearQueue()
            .then(() => {
              if (isRadioOrLineIn(player.coordinator.avTransportUri)) {
                return player.coordinator.setAVTransport(queueURI, '');
              }
            })
            .then(() => player.coordinator.addURIToQueue(UaM.uri, UaM.metadata, true, 1))
            .then(() => player.coordinator.play());
        } else { // Play songs
          var tracks = loadTracks(service, type, resList);

          if (tracks.count == 0) {
            return Promise.reject('No matches were found');
          } else {
            if (tracks.isArtist) {  // Play numerous songs by the specified artist
              return player.coordinator.clearQueue()
                .then(() => {
                  if (isRadioOrLineIn(player.coordinator.avTransportUri)) {
                    return player.coordinator.setAVTransport(queueURI, '');
                  }
                })
                .then(() => player.coordinator.addURIToQueue(tracks.queueTracks[0].uri, tracks.queueTracks[0].metadata, true, 1))
                .then(() => player.coordinator.play())
                .then(() => {
                // Do not return promise since we want to be considered done from the calling context
                  tracks.queueTracks.slice(1).reduce((promise, track, index) => {
                    return promise.then(() => player.coordinator.addURIToQueue(track.uri, track.metadata, true, index + 2));
                  }, Promise.resolve());
                });
            } else { // Play the one specified song
              var empty = false;
              var nextTrackNo = 0;

              return player.coordinator.getQueue(0, 1)
                .then((queue) => {
                  empty = (queue.numberReturned == 0);
                  nextTrackNo = (empty) ? 1 : player.coordinator.state.trackNo + 1;
                })
                .then(() => player.coordinator.addURIToQueue(tracks.queueTracks[0].uri, tracks.queueTracks[0].metadata, true, nextTrackNo))
                .then(() => {
                  if (isRadioOrLineIn(player.coordinator.state.currentTrack.uri)) {
                    return player.coordinator.setAVTransport(queueURI, '');
                  }
                })
                .then(() => {
                  if (!empty) {
                    return player.coordinator.nextTrack();
                  }
                })
                .then(() => player.coordinator.play());
            }
          }
        }
      }
    });
}

module.exports = function (api) {
  api.registerAction('musicsearch', musicSearch);
  libraryDef.read();
  fs.readFile(sidsPath, handleSids);
};
<|endoftext|>


<commit_before>Ext.namespace('Gemma');
Ext.BLANK_IMAGE_URL = '/Gemma/images/default/s.gif';

/**
 * @author keshav
 * @version $Id$
 */
Ext.onReady(function() {

	Ext.QuickTips.init();

	// turn on validation errors beside the field globally
	Ext.form.Field.prototype.msgTarget = 'side';

	var bd = Ext.getBody();

	var signup = new Ext.FormPanel({
		labelWidth : 75, // label settings here cascade unless overridden
		url : 'signup.html',
		frame : true,
		monitorValid : true, // use with formBind in Button for client side validation
		bodyStyle : 'padding:5px 5px 0',
		width : 390,
		keys : [{
			key : Ext.EventObject.ENTER,
			formBind : true,
			handler : function() {

				signup.getForm().submit({
					url : this.url,
					method : 'POST',
					success : function() {
						var target = 'mainMenu.html';
						window.location = target;
					},
					failure : function(form, action) {
						var errMsg = '';
						errMsg = Ext.util.JSON.decode(action.response.responseText);
						var fontMsg = "" + errMsg.message + "";
						Element.update('errorMessage', fontMsg);

						signup.getForm().reset();
						Ext.getCmp('my-status').clearStatus();
					}
				});

				var sb = Ext.getCmp('my-status');
				sb.showBusy();

			}
		}],
		defaults : {
			width : 230
		},
		defaultType : 'textfield',

		items : [{
			fieldLabel : 'Username',
			name : 'username',
			allowBlank : false,
			vtype : 'alphanum'
		}, {
			fieldLabel : 'Email',
			name : 'email',
			allowBlank : false,
			vtype : 'email',
			validationDelay : 1500,
			invalidText : "A valid email address is required"
		}],

		buttons : [{
			text : 'Submit',
			formBind : true, // use with monitorValid in Ext.FormPanel for client side validation
			handler : function() {

				signup.getForm().submit({
					url : this.url,
					method : 'POST',
					success : function() {
						var target = 'mainMenu.html';
						window.location = target;
					},
					failure : function(form, action) {
						var errMsg = '';
						errMsg = Ext.util.JSON.decode(action.response.responseText);
						var fontMsg = "" + errMsg.message + "";
						Element.update('errorMessage', fontMsg);

						signup.getForm().reset();
						Ext.getCmp('my-status').clearStatus();
					}
				});

				var sb = Ext.getCmp('my-status');
				sb.showBusy();

			}
		}]
	});

	/**
	 *
	 */
	var panel = new Ext.Panel({
		width : 390,
		title : 'Create a new account',
		frame : false,
		renderTo : 'signup',
		items : [signup],

		bbar : new Ext.StatusBar({
			id : 'my-status',
			text : '',
			iconCls : 'default-icon',
			busyText : 'Validating...'
		})

	});
});
<commit_msg>bug 1488 - fill email address twice<commit_after>Ext.namespace('Gemma');
Ext.BLANK_IMAGE_URL = '/Gemma/images/default/s.gif';

/**
 * @author keshav
 * @version $Id$
 */
Ext.onReady(function() {

	Ext.QuickTips.init();

	// turn on validation errors beside the field globally
	Ext.form.Field.prototype.msgTarget = 'side';

	var bd = Ext.getBody();

	var signup = new Ext.FormPanel({
		labelWidth : 75, // label settings here cascade unless overridden
		url : 'signup.html',
		frame : true,
		monitorValid : true, // use with formBind in Button for client side validation
		bodyStyle : 'padding:5px 5px 0',
		width : 390,
		keys : [{
			key : Ext.EventObject.ENTER,
			formBind : true,
			handler : function() {

				signup.getForm().submit({
					url : this.url,
					method : 'POST',
					success : function() {
						var target = 'mainMenu.html';
						window.location = target;
					},
					failure : function(form, action) {
						var errMsg = '';
						errMsg = Ext.util.JSON.decode(action.response.responseText);
						var fontMsg = "" + errMsg.message + "";
						Element.update('errorMessage', fontMsg);

						signup.getForm().reset();
						Ext.getCmp('my-status').clearStatus();
					}
				});

				var sb = Ext.getCmp('my-status');
				sb.showBusy();

			}
		}],
		defaults : {
			width : 230
		},
		defaultType : 'textfield',

		items : [{
			fieldLabel : 'Username',
			name : 'username',
			allowBlank : false,
			vtype : 'alphanum'
		}, {
			fieldLabel : 'Email',
			id : 'email',
			name : 'email',
			allowBlank : false,
			vtype : 'email',
			validationDelay : 1500,
			invalidText : "A valid email address is required"
		}, {
			fieldLabel : 'Confirm Email',
			id : 'emailConfirm',
			name : 'emailConfirm',
			allowBlank : false,
			vtype : 'email',
			validator : function(value) {
				return (value == document.getElementById("email").value) || "Your email addresses do not match";
			}
		}],

		buttons : [{
			text : 'Submit',
			formBind : true, // use with monitorValid in Ext.FormPanel for client side validation
			handler : function() {

				signup.getForm().submit({
					url : this.url,
					method : 'POST',
					success : function() {
						var target = 'mainMenu.html';
						window.location = target;
					},
					failure : function(form, action) {
						var errMsg = '';
						errMsg = Ext.util.JSON.decode(action.response.responseText);
						var fontMsg = "" + errMsg.message + "";
						Element.update('errorMessage', fontMsg);

						signup.getForm().reset();
						Ext.getCmp('my-status').clearStatus();
					}
				});

				var sb = Ext.getCmp('my-status');
				sb.showBusy();

			}
		}]
	});

	/**
	 *
	 */
	var panel = new Ext.Panel({
		width : 390,
		title : 'Create a new account',
		frame : false,
		renderTo : 'signup',
		items : [signup],

		bbar : new Ext.StatusBar({
			id : 'my-status',
			text : '',
			iconCls : 'default-icon',
			busyText : 'Validating...'
		})

	});
});
<|endoftext|>


<commit_before>/* global process */
module.exports = function (config) {
    config.set({
        browsers: process.env.CONTINUOUS_INTEGRATION ?
            [ 'Firefox' ] :
            [
                'PhantomJS',
                'Chrome',
                'ChromeCanary',
                'Firefox',
                'Opera',
                'Safari'
            ],
        singleRun: true,
        frameworks: [ 'mocha' ],
        files: [

            // This one is needed for testing in PhantomJS
            'https://raw.githubusercontent.com/es-shims/es5-shim/master/es5-shim.js',
            './__tests__/tests.webpack.js'
        ],
        preprocessors: {
            './__tests__/tests.webpack.js': [ 'webpack', 'sourcemap' ]
        },
        reporters: [ 'progress' ],
        webpack: {
            devtool: 'inline-source-map',
            module: {
                loaders: [
                    {
                        test: /\.jsx?$/,
                        loader: "babel",
                        query: {
                            // https://github.com/babel/babel-loader#options
                            cacheDirectory: true,
                            presets: ['es2015', 'stage-0', 'react'],
                            plugins: ["transform-runtime"]
                        },
                        exclude: /node_modules/
                    }
                ]
            }
        },
        webpackServer: {
            noInfo: true
        }
    });
};
<commit_msg>Trying to fix the CI<commit_after>/* global process */
module.exports = function (config) {
    config.set({
        browsers: process.env.CONTINUOUS_INTEGRATION ?
            [ 'Firefox' ] :
            [
                'PhantomJS',
                'Chrome',
                'ChromeCanary',
                'Firefox',
                'Opera',
                'Safari'
            ],
        singleRun: true,
        browserNoActivityTimeout: 30000,
        frameworks: [ 'mocha' ],
        files: [

            // This one is needed for testing in PhantomJS
            'https://raw.githubusercontent.com/es-shims/es5-shim/master/es5-shim.js',
            './__tests__/tests.webpack.js'
        ],
        preprocessors: {
            './__tests__/tests.webpack.js': [ 'webpack', 'sourcemap' ]
        },
        reporters: [ 'progress' ],
        webpack: {
            devtool: 'inline-source-map',
            module: {
                loaders: [
                    {
                        test: /\.jsx?$/,
                        loader: "babel",
                        query: {
                            // https://github.com/babel/babel-loader#options
                            cacheDirectory: true,
                            presets: ['es2015', 'stage-0', 'react'],
                            plugins: ["transform-runtime"]
                        },
                        exclude: /node_modules/
                    }
                ]
            }
        },
        webpackServer: {
            noInfo: true
        }
    });
};
<|endoftext|>