<commit_before>/*!
 * EventEmitter v4.2.9 - git.io/ee
 * Oliver Caldwell
 * MIT license
 * @preserve
 */
(function(){"use strict";function EventEmitter(){}function indexOfListener(listeners,listener){for(var i=listeners.length;i--;)if(listeners[i].listener===listener)return i;return-1}function alias(name){return function(){return this[name].apply(this,arguments)}}var proto=EventEmitter.prototype,exports=this,originalGlobalValue=exports.EventEmitter;proto.getListeners=function(evt){var response,key,events=this._getEvents();if(evt instanceof RegExp){response={};for(key in events)events.hasOwnProperty(key)&&evt.test(key)&&(response[key]=events[key])}else response=events[evt]||(events[evt]=[]);return response},proto.flattenListeners=function(listeners){var i,flatListeners=[];for(i=0;i<listeners.length;i+=1)flatListeners.push(listeners[i].listener);return flatListeners},proto.getListenersAsObject=function(evt){var response,listeners=this.getListeners(evt);return listeners instanceof Array&&(response={},response[evt]=listeners),response||listeners},proto.addListener=function(evt,listener){var key,listeners=this.getListenersAsObject(evt),listenerIsWrapped="object"==typeof listener;for(key in listeners)listeners.hasOwnProperty(key)&&-1===indexOfListener(listeners[key],listener)&&listeners[key].push(listenerIsWrapped?listener:{listener:listener,once:!1});return this},proto.on=alias("addListener"),proto.addOnceListener=function(evt,listener){return this.addListener(evt,{listener:listener,once:!0})},proto.once=alias("addOnceListener"),proto.defineEvent=function(evt){return this.getListeners(evt),this},proto.defineEvents=function(evts){for(var i=0;i<evts.length;i+=1)this.defineEvent(evts[i]);return this},proto.removeListener=function(evt,listener){var index,key,listeners=this.getListenersAsObject(evt);for(key in listeners)listeners.hasOwnProperty(key)&&(index=indexOfListener(listeners[key],listener),-1!==index&&listeners[key].splice(index,1));return this},proto.off=alias("removeListener"),proto.addListeners=function(evt,listeners){return this.manipulateListeners(!1,evt,listeners)},proto.removeListeners=function(evt,listeners){return this.manipulateListeners(!0,evt,listeners)},proto.manipulateListeners=function(remove,evt,listeners){var i,value,single=remove?this.removeListener:this.addListener,multiple=remove?this.removeListeners:this.addListeners;if("object"!=typeof evt||evt instanceof RegExp)for(i=listeners.length;i--;)single.call(this,evt,listeners[i]);else for(i in evt)evt.hasOwnProperty(i)&&(value=evt[i])&&("function"==typeof value?single.call(this,i,value):multiple.call(this,i,value));return this},proto.removeEvent=function(evt){var key,type=typeof evt,events=this._getEvents();if("string"===type)delete events[evt];else if(evt instanceof RegExp)for(key in events)events.hasOwnProperty(key)&&evt.test(key)&&delete events[key];else delete this._events;return this},proto.removeAllListeners=alias("removeEvent"),proto.emitEvent=function(evt,args){var listener,i,key,response,listeners=this.getListenersAsObject(evt);for(key in listeners)if(listeners.hasOwnProperty(key))for(i=listeners[key].length;i--;)listener=listeners[key][i],listener.once===!0&&this.removeListener(evt,listener.listener),response=listener.listener.apply(this,args||[]),response===this._getOnceReturnValue()&&this.removeListener(evt,listener.listener);return this},proto.trigger=alias("emitEvent"),proto.emit=function(evt){var args=Array.prototype.slice.call(arguments,1);return this.emitEvent(evt,args)},proto.setOnceReturnValue=function(value){return this._onceReturnValue=value,this},proto._getOnceReturnValue=function(){return this.hasOwnProperty("_onceReturnValue")?this._onceReturnValue:!0},proto._getEvents=function(){return this._events||(this._events={})},EventEmitter.noConflict=function(){return exports.EventEmitter=originalGlobalValue,EventEmitter},"function"==typeof define&&define.amd?define(function(){return EventEmitter}):"object"==typeof module&&module.exports?module.exports=EventEmitter:exports.EventEmitter=EventEmitter}).call(this);/**
 * @file
 * File picker and finder for device storages on Firefox OS devices
 *
 * This library provides an easy-to-use asynchronous interface for other Firefox OS apps to search for files
 * on Firefox OS devices. The library is based on an event-based architecture, letting developers build
 * beautiful asynchronous API for their apps.
 *
 * The `Finder` library is best used by developers looking to pick a file from the `sdcard` for their apps.
 *
 * This library depends on [EventEmitter](https://github.com/Wolfy87/EventEmitter) by Wolfy87, included with the
 * package.
 *
 * @version 1.1.1
 * @license The MIT License (MIT)
 * @author Applait Technologies LLP
 * @copyright Copyright (c) 2014 Applait Technologies LLP
 */
var Applait=Applait||{};Applait.Finder=function(options){this.options=options||{},this.type=options.type||"sdcard",this.hidden=options.hidden||!1,this.casesensitive=options.caseSensitive||!1,this.minsearchlength=options.minSearchLength&&"number"==typeof options.minSearchLength?options.minSearchLength:3,this.debugmode=options.debugMode?!0:!1,this.storages=navigator.getDeviceStorages&&navigator.getDeviceStorages(this.type),this.searchcompletecount=0,this.filematchcount=0,this.searchkey=""},Applait.Finder.prototype=new EventEmitter,Applait.Finder.prototype.checkhidden=function(filename){return 0===filename.indexOf(".")&&this.hidden!==!0?!1:!0},Applait.Finder.prototype.search=function(needle){var self=this;return self.reset(),self.searchkey=self.casesensitive?needle.trim():needle.trim().toLowerCase(),self.searchkey.length<self.minsearchlength?(self.log("searchCancelled",["Search string should be at least "+self.minsearchlength+" characters"]),self.emitEvent("searchCancelled",["Search string should be at least "+self.minsearchlength+" characters"]),null):self.storagecount()<1?(self.log("empty",[self.searchkey]),self.emitEvent("empty",[self.searchkey]),null):(self.log("searchBegin",[self.searchkey]),self.emitEvent("searchBegin",[self.searchkey]),void self.storages.forEach(function(storage){var cursor=storage.enumerate();self.log("storageSearchBegin",[storage.storageName,self.searchkey]),self.emitEvent("storageSearchBegin",[storage.storageName,self.searchkey]),cursor.onsuccess=function(){if(this.result){var file=this.result,fileinfo=self.splitname(file.name);self.matchname(fileinfo.name)&&(self.filematchcount++,self.log("fileFound",[file,fileinfo,storage.storageName]),self.emitEvent("fileFound",[file,fileinfo,storage.storageName])),this.done?(self.searchcompletecount++,self.log("storageSearchComplete",[storage.storageName,self.searchkey]),self.emitEvent("storageSearchComplete",[storage.storageName,self.searchkey])):this.continue()}else self.searchcompletecount++,self.log("storageSearchComplete",[storage.storageName,self.searchkey]),self.emitEvent("storageSearchComplete",[storage.storageName,self.searchkey]);self.searchcompletecount===self.storagecount()&&(self.log("searchComplete",[self.searchkey,self.filematchcount]),self.emitEvent("searchComplete",[self.searchkey,self.filematchcount]))},cursor.onerror=function(){self.log("error",["Error accessing device storage '"+storage.storageName+"'",this.error]),self.emitEvent("error",["Error accessing device storage '"+storage.storageName+"'",this.error])}}))},Applait.Finder.prototype.splitname=function(filename){return filename=filename.split(/[\\/]/),{name:filename.pop(),path:filename.join("/")}},Applait.Finder.prototype.storagecount=function(){return this.storages&&this.storages.length?this.storages.length:0},Applait.Finder.prototype.reset=function(){this.filematchcount=0,this.searchcompletecount=0,this.searchkey=""},Applait.Finder.prototype.log=function(message,args){this.debugmode&&console.log(message,args)},Applait.Finder.prototype.matchname=function(name){return name=this.casesensitive?name.trim():name.trim().toLowerCase(),name.indexOf(this.searchkey)>-1&&this.checkhidden(name)};<commit_msg>Update minified script after fixing issues<commit_after>/*!
 * EventEmitter v4.2.9 - git.io/ee
 * Oliver Caldwell
 * MIT license
 * @preserve
 */
(function(){"use strict";function EventEmitter(){}function indexOfListener(listeners,listener){for(var i=listeners.length;i--;)if(listeners[i].listener===listener)return i;return-1}function alias(name){return function(){return this[name].apply(this,arguments)}}var proto=EventEmitter.prototype,exports=this,originalGlobalValue=exports.EventEmitter;proto.getListeners=function(evt){var response,key,events=this._getEvents();if(evt instanceof RegExp){response={};for(key in events)events.hasOwnProperty(key)&&evt.test(key)&&(response[key]=events[key])}else response=events[evt]||(events[evt]=[]);return response},proto.flattenListeners=function(listeners){var i,flatListeners=[];for(i=0;i<listeners.length;i+=1)flatListeners.push(listeners[i].listener);return flatListeners},proto.getListenersAsObject=function(evt){var response,listeners=this.getListeners(evt);return listeners instanceof Array&&(response={},response[evt]=listeners),response||listeners},proto.addListener=function(evt,listener){var key,listeners=this.getListenersAsObject(evt),listenerIsWrapped="object"==typeof listener;for(key in listeners)listeners.hasOwnProperty(key)&&-1===indexOfListener(listeners[key],listener)&&listeners[key].push(listenerIsWrapped?listener:{listener:listener,once:!1});return this},proto.on=alias("addListener"),proto.addOnceListener=function(evt,listener){return this.addListener(evt,{listener:listener,once:!0})},proto.once=alias("addOnceListener"),proto.defineEvent=function(evt){return this.getListeners(evt),this},proto.defineEvents=function(evts){for(var i=0;i<evts.length;i+=1)this.defineEvent(evts[i]);return this},proto.removeListener=function(evt,listener){var index,key,listeners=this.getListenersAsObject(evt);for(key in listeners)listeners.hasOwnProperty(key)&&(index=indexOfListener(listeners[key],listener),-1!==index&&listeners[key].splice(index,1));return this},proto.off=alias("removeListener"),proto.addListeners=function(evt,listeners){return this.manipulateListeners(!1,evt,listeners)},proto.removeListeners=function(evt,listeners){return this.manipulateListeners(!0,evt,listeners)},proto.manipulateListeners=function(remove,evt,listeners){var i,value,single=remove?this.removeListener:this.addListener,multiple=remove?this.removeListeners:this.addListeners;if("object"!=typeof evt||evt instanceof RegExp)for(i=listeners.length;i--;)single.call(this,evt,listeners[i]);else for(i in evt)evt.hasOwnProperty(i)&&(value=evt[i])&&("function"==typeof value?single.call(this,i,value):multiple.call(this,i,value));return this},proto.removeEvent=function(evt){var key,type=typeof evt,events=this._getEvents();if("string"===type)delete events[evt];else if(evt instanceof RegExp)for(key in events)events.hasOwnProperty(key)&&evt.test(key)&&delete events[key];else delete this._events;return this},proto.removeAllListeners=alias("removeEvent"),proto.emitEvent=function(evt,args){var listener,i,key,response,listeners=this.getListenersAsObject(evt);for(key in listeners)if(listeners.hasOwnProperty(key))for(i=listeners[key].length;i--;)listener=listeners[key][i],listener.once===!0&&this.removeListener(evt,listener.listener),response=listener.listener.apply(this,args||[]),response===this._getOnceReturnValue()&&this.removeListener(evt,listener.listener);return this},proto.trigger=alias("emitEvent"),proto.emit=function(evt){var args=Array.prototype.slice.call(arguments,1);return this.emitEvent(evt,args)},proto.setOnceReturnValue=function(value){return this._onceReturnValue=value,this},proto._getOnceReturnValue=function(){return this.hasOwnProperty("_onceReturnValue")?this._onceReturnValue:!0},proto._getEvents=function(){return this._events||(this._events={})},EventEmitter.noConflict=function(){return exports.EventEmitter=originalGlobalValue,EventEmitter},"function"==typeof define&&define.amd?define(function(){return EventEmitter}):"object"==typeof module&&module.exports?module.exports=EventEmitter:exports.EventEmitter=EventEmitter}).call(this);/**
 * @file
 * File picker and finder for device storages on Firefox OS devices
 *
 * This library provides an easy-to-use asynchronous interface for other Firefox OS apps to search for files
 * on Firefox OS devices. The library is based on an event-based architecture, letting developers build
 * beautiful asynchronous API for their apps.
 *
 * The `Finder` library is best used by developers looking to pick a file from the `sdcard` for their apps.
 *
 * This library depends on [EventEmitter](https://github.com/Wolfy87/EventEmitter) by Wolfy87, included with the
 * package.
 *
 * @version 1.1.1
 * @license The MIT License (MIT)
 * @author Applait Technologies LLP
 * @copyright Copyright (c) 2014 Applait Technologies LLP
 */
var Applait=Applait||{};Applait.Finder=function(options){this.options=options||{},this.type=this.options.type||"sdcard",this.hidden=this.options.hidden||!1,this.casesensitive=this.options.caseSensitive||!1,this.minsearchlength=this.options.minSearchLength&&"number"==typeof this.options.minSearchLength?options.minSearchLength:3,this.debugmode=this.options.debugMode?!0:!1,this.storages=navigator.getDeviceStorages&&navigator.getDeviceStorages(this.type),this.searchcompletecount=0,this.filematchcount=0,this.searchkey=""},Applait.Finder.prototype=new EventEmitter,Applait.Finder.prototype.checkhidden=function(filename){return 0===filename.indexOf(".")&&this.hidden!==!0?!1:!0},Applait.Finder.prototype.search=function(needle){var self=this;return self.reset(),self.searchkey=self.casesensitive?needle.trim():needle.trim().toLowerCase(),self.searchkey.length<self.minsearchlength?(self.log("searchCancelled",["Search string should be at least "+self.minsearchlength+" characters"]),self.emitEvent("searchCancelled",["Search string should be at least "+self.minsearchlength+" characters"]),null):self.storagecount()<1?(self.log("empty",[self.searchkey]),self.emitEvent("empty",[self.searchkey]),null):(self.log("searchBegin",[self.searchkey]),self.emitEvent("searchBegin",[self.searchkey]),void self.storages.forEach(function(storage){var cursor=storage.enumerate();self.log("storageSearchBegin",[storage.storageName,self.searchkey]),self.emitEvent("storageSearchBegin",[storage.storageName,self.searchkey]),cursor.onsuccess=function(){if(this.result){var file=this.result,fileinfo=self.splitname(file.name);self.matchname(fileinfo.name)&&(self.filematchcount++,self.log("fileFound",[file,fileinfo,storage.storageName]),self.emitEvent("fileFound",[file,fileinfo,storage.storageName])),this.done?(self.searchcompletecount++,self.log("storageSearchComplete",[storage.storageName,self.searchkey]),self.emitEvent("storageSearchComplete",[storage.storageName,self.searchkey])):this.continue()}else self.searchcompletecount++,self.log("storageSearchComplete",[storage.storageName,self.searchkey]),self.emitEvent("storageSearchComplete",[storage.storageName,self.searchkey]);self.searchcompletecount===self.storagecount()&&(self.log("searchComplete",[self.searchkey,self.filematchcount]),self.emitEvent("searchComplete",[self.searchkey,self.filematchcount]))},cursor.onerror=function(){self.log("error",["Error accessing device storage '"+storage.storageName+"'",this.error]),self.emitEvent("error",["Error accessing device storage '"+storage.storageName+"'",this.error])}}))},Applait.Finder.prototype.splitname=function(filename){return filename=filename.split(/[\\/]/),{name:filename.pop(),path:filename.join("/")}},Applait.Finder.prototype.storagecount=function(){return this.storages&&this.storages.length?this.storages.length:0},Applait.Finder.prototype.reset=function(){this.filematchcount=0,this.searchcompletecount=0,this.searchkey=""},Applait.Finder.prototype.log=function(message,args){this.debugmode&&console.log(message,args)},Applait.Finder.prototype.matchname=function(name){return name=this.casesensitive?name.trim():name.trim().toLowerCase(),name.indexOf(this.searchkey)>-1&&this.checkhidden(name)};<|endoftext|>


<commit_before>describe("CFI INSTRUCTION OBJECT", function () {

	it("finds the target element on an index step", function () {

		var contentDocXHTML = jasmine.getFixtures().read('moby_dick_content_doc.xhtml');
		var domParser = new window.DOMParser();
		var contentDoc = domParser.parseFromString(contentDocXHTML, "text/xml");

		var $nextNode = EPUBcfi.CFIInstructions.getNextNode(4, $(contentDoc.firstChild), ["cfiMarker"]);
		var nodeType = $nextNode.is("body");

		expect(nodeType).toEqual(true);
	});

	// Finds the target element on an iframe indirection step
	it("finds the target element on an iframe indirection step", function () {

		var iframeContentXHTML = jasmine.getFixtures().read("iframe_content.xhtml");

		// Rationale: Append iframe to the body of the current window. This is a bit hacky, but a document containing an iframe
		//   appears to have to be loaded in a window to create an iframe, with content, as an element of that document. The original
		//   intention was to create a document object and then append an iframe; I was unable to find a way to do this, hence appending
		//   the iframe to the current document (jasmine).
		var iframe = $('')[0];
		document.body.appendChild(iframe);

		iframe.contentDocument.open("text/xml", "replace");
		iframe.contentDocument.write(iframeContentXHTML);
		iframe.contentDocument.close();

		var $nextNode = EPUBcfi.CFIInstructions.followIndirectionStep(4, $("iframe", document), [], []);

		expect($nextNode.attr("id")).toBe("body1");

		// Remove the injected iframe
		$(iframe).remove();
	});

	it("injects text at the specified offset", function () {

		var contentDocXHTML = jasmine.getFixtures().read('moby_dick_content_doc.xhtml');
		var domParser = new window.DOMParser();
		var contentDoc = domParser.parseFromString(contentDocXHTML, "text/xml");

		var $injectedElement = EPUBcfi.CFIInstructions.textTermination($($("#c01p0002", $(contentDoc))[0].firstChild), 4, '');

		expect($injectedElement.attr("id")).toBe("injected");
		expect($injectedElement.parent().attr("id")).toBe("c01p0002");
	});

	it("injects text at the specified offset in the FIRST sub-node, when a target text node is specified as a list", function () {

		// Get a list of text nodes
		var $currNode = $('
 asdfasd

 aslasjd

 alsjflkds
');
		var $targetTextNodeList = EPUBcfi.CFIInstructions.getNextNode(1, $currNode, ["cfiMarker"], []);

		var $injectedElement = EPUBcfi.CFIInstructions.textTermination($targetTextNodeList, 4, '');
		var $currNodeChildren = $injectedElement.parent().contents();

		expect($currNodeChildren[0].nodeValue).toBe(" asd");
		expect($injectedElement.hasClass('epub_cfi')).toBe(true);
		expect($currNodeChildren[2].nodeValue).toBe("fasd ");
	});

    it("injects text at the specified offset in the SECOND sub-node, when a target text node is specified as a list", function () {

        // Get a list of text nodes
        var $currNode = $('
 asdfasd

 aslasjd

 alsjflkds
');
        var $targetTextNodeList = EPUBcfi.CFIInstructions.getNextNode(1, $currNode, ["cfiMarker"], []);

        var $injectedElement = EPUBcfi.CFIInstructions.textTermination($targetTextNodeList, 12, '');
        var $currNodeChildren = $injectedElement.parent().contents();

        expect($currNodeChildren[2].nodeValue).toBe(" as");
        expect($injectedElement.hasClass('epub_cfi')).toBe(true);
        expect($currNodeChildren[4].nodeValue).toBe("lasjd ");
    });

	it('excludes elements that have a class that indicates they are "cfi markers" and returns a list of text nodes', function () {

		var domParser = new window.DOMParser();
		var xhtml = '
asdfsd

 ddfd

';
		var doc = domParser.parseFromString(xhtml, 'text/xml');
		var $currentNode = $(doc.firstChild.firstChild);

		var $result = EPUBcfi.CFIInstructions.getNextNode(1, $currentNode, ["cfiMarker"], []);
		expect($result.length).toEqual(2);
		expect($result[0].nodeValue).toEqual("asdfsd ");
		expect($result[1].nodeValue).toEqual(" ddfd");
	});

	it('returns the correct text node if the node is in the first position of a set of child nodes', function () {

		var domParser = new window.DOMParser();
		var xhtml = '
text1

text2

text3
';
		var doc = domParser.parseFromString(xhtml, 'text/xml');
		var $currentNode = $(doc.firstChild);

		var $result = EPUBcfi.CFIInstructions.getNextNode(1, $currentNode, ["cfiMarker"], []);
		expect($result.length).toEqual(1);
		expect($result[0].nodeValue).toEqual("text1");
	});

	it('returns the correct text node if the node is between elements in a set of child nodes', function () {

		var domParser = new window.DOMParser();
		var xhtml = '
text1

text2

text3
';
		var doc = domParser.parseFromString(xhtml, 'text/xml');
		var $currentNode = $(doc.firstChild);

		var $result = EPUBcfi.CFIInstructions.getNextNode(3, $currentNode, ["cfiMarker"], []);
		expect($result.length).toEqual(1);
		expect($result[0].nodeValue).toEqual("text2");
	});

	it('returns the correct text node if the node is the last in a set of child nodes', function () {

		var domParser = new window.DOMParser();
		var xhtml = '
text1

text2

text3
';
		var doc = domParser.parseFromString(xhtml, 'text/xml');
		var $currentNode = $(doc.firstChild);

		var $result = EPUBcfi.CFIInstructions.getNextNode(5, $currentNode, ["cfiMarker"], []);
		expect($result.length).toEqual(1);
		expect($result[0].nodeValue).toEqual("text3");
	});

	it("filters blacklist classes", function () {

		var $elements = $(
			"

"
			+ "

"
			+ "

"
			+ "

"
			+ "

"
			);

		$result = EPUBcfi.CFIInstructions.applyBlacklist($elements, ["blacklistClass1", "blacklistClass2"], []);

		expect($result[0].id).toEqual("survivor-1");
		expect($result[1].id).toEqual("survivor-2");
		expect($result[2].id).toEqual("survivor-3");
	});

	it("filters multiple blacklist classes", function () {

		var $elements = $(
			"

"
			+ "

"
			+ "

"
			+ "

"
			+ "

"
			+ "

"
			+ "

"
			);

		$result = EPUBcfi.CFIInstructions.applyBlacklist($elements, ["blacklistClass1", "blacklistClass2"], []);

		expect($result[0].id).toEqual("survivor-1");
		expect($result[1].id).toEqual("survivor-2");
		expect($result[2].id).toEqual("survivor-3");
	});

	it("filters blacklist elements", function () {

		var $elements = $(
			"

"
			+ "<mathjax></mathjax>"
			+ "

"
			+ "<blacklistElement></blacklistElement>"
			+ "

"
			);

		$result = EPUBcfi.CFIInstructions.applyBlacklist($elements, [], ["mathjax", "blacklistElement"]);

		expect($result[0].id).toEqual("survivor-1");
		expect($result[1].id).toEqual("survivor-2");
		expect($result[2].id).toEqual("survivor-3");
	});

	it("filters blacklist classes with text nodes", function () {

		var $elements = $(
			"

"
			+ "textNode-1"
			+ "

"
			+ "

"
			+ "

"
			+ "textNode-2"
			+ "

"
			);

		$result = EPUBcfi.CFIInstructions.applyBlacklist($elements, ["blacklistClass1", "blacklistClass2"], []);

		expect($result[0].nodeType).toEqual(3);
		expect($result[1].id).toEqual("survivor-1");
		expect($result[2].id).toEqual("survivor-2");
		expect($result[3].id).toEqual("survivor-3");
		expect($result[4].nodeType).toEqual(3);
	});

	it("filters blacklist elements with text nodes", function () {

		var $elements = $(
			"

"
			+ "<mathjax></mathjax>"
			+ "textNode-1"
			+ "

"
			+ "<blacklistElement></blacklistElement>"
			+ "textNode-2"
			+ "

"
			);

		$result = EPUBcfi.CFIInstructions.applyBlacklist($elements, [], ["mathjax", "blacklistElement"]);

		expect($result[0].id).toEqual("survivor-1");
		expect($result[1].nodeType).toEqual(3);
		expect($result[2].id).toEqual("survivor-2");
		expect($result[3].nodeType).toEqual(3);
		expect($result[4].id).toEqual("survivor-3");
	});

	it("filters blacklist ids with text nodes", function () {

		var $elements = $(
			"

"
			+ "

"
			+ "textNode-1"
			+ "

"
			+ "<blacklistElement id='blacklist-1'></blacklistElement>"
			+ "textNode-2"
			+ "

"
			);

		$result = EPUBcfi.CFIInstructions.applyBlacklist($elements, [], [], ["mathjax", "blacklist-1"]);

		expect($result[0].id).toEqual("survivor-1");
		expect($result[1].nodeType).toEqual(3);
		expect($result[2].id).toEqual("survivor-2");
		expect($result[3].nodeType).toEqual(3);
		expect($result[4].id).toEqual("survivor-3");
	});
});

describe('CFI INSTRUCTION ERROR HANDLING', function () {

	// Throws out of range errors for any "following" instructions
	it('throws an out of range error for an index step', function () {

		var contentDocXHTML = jasmine.getFixtures().read('moby_dick_content_doc.xhtml');
		var domParser = new window.DOMParser();
		var contentDoc = domParser.parseFromString(contentDocXHTML, "text/xml");

		// A step of 16 is greater than the number of child elements of the content document
		expect(function () {
			EPUBcfi.CFIInstructions.getNextNode(16, $(contentDoc.firstChild))}, ["cfiMarker"], [])
		.toThrow(
			EPUBcfi.OutOfRangeError(7, 1, ""));
	});

	it('throws an out of range error for an indirection step', function () {

		var iframeContentXHTML = jasmine.getFixtures().read("iframe_content.xhtml");

		// Rationale: Append iframe to the body of the current window. This is a bit hacky, but a document containing an iframe
		//   appears to have to be loaded in a window to create an iframe, with content, as an element of that document. The original
		//   intention was to create a document object and then append an iframe; I was unable to find a way to do this, hence appending
		//   the iframe to the current document (jasmine).
		var iframe = $('')[0];
		document.body.appendChild(iframe);

		iframe.contentDocument.open("text/xml", "replace");
		iframe.contentDocument.write(iframeContentXHTML);
		iframe.contentDocument.close();

		// A step of 16 is greater than the number of child elements of the content document
		expect(function () {
			EPUBcfi.CFIInstructions.followIndirectionStep(6, $('iframe', document))}, [], [])
		.toThrow(
			EPUBcfi.OutOfRangeError(2, 1, ""));

		// Remove the injected iframe
		$(iframe).remove();
	});

	// Throws a node type error for itemref
	it('throws a node type error if an iframe indirection step is called on a different element', function () {

		var packageDocXML = jasmine.getFixtures().read('moby_dick_package.opf');
		var contentDocXHTML = jasmine.getFixtures().read('moby_dick_content_doc.xhtml');
		var domParser = new window.DOMParser();
		var packageDoc = domParser.parseFromString(packageDocXML, "text/xml");
		var contentDoc = domParser.parseFromString(contentDocXHTML, "text/xml");
		var spineElement = $($(packageDoc.firstChild).children()[2]).children()[6];

		var nextNode;
		var calledHref;

		spyOn($, "ajax").and.callFake(function (params) {

			params.success(contentDoc);
		});

		expect(function () {
			EPUBcfi.CFIInstructions.followIndirectionStep(16, undefined, $(packageDoc))}, [], [])
		.toThrow(
			EPUBcfi.NodeTypeError(undefined, "expected an iframe element"));
	});

	// Throws terminus errors for invalid text offsets
	it('throws a terminus error if an invalid text offset is provided', function () {

		var $currentNode = $('

');

		expect(function () {
			EPUBcfi.CFIInstructions.textTermination($($currentNode.contents().firstChild), 84, '')})
		.toThrow(
			EPUBcfi.TerminusError("Text", "Text offset:84", "no nodes found for termination condition"));
	});
});
<commit_msg>Add test cases for empty text nodes and comments<commit_after>describe("CFI INSTRUCTION OBJECT", function () {

	it("finds the target element on an index step", function () {

		var contentDocXHTML = jasmine.getFixtures().read('moby_dick_content_doc.xhtml');
		var domParser = new window.DOMParser();
		var contentDoc = domParser.parseFromString(contentDocXHTML, "text/xml");

		var $nextNode = EPUBcfi.CFIInstructions.getNextNode(4, $(contentDoc.firstChild), ["cfiMarker"]);
		var nodeType = $nextNode.is("body");

		expect(nodeType).toEqual(true);
	});

	// Finds the target element on an iframe indirection step
	it("finds the target element on an iframe indirection step", function () {

		var iframeContentXHTML = jasmine.getFixtures().read("iframe_content.xhtml");

		// Rationale: Append iframe to the body of the current window. This is a bit hacky, but a document containing an iframe
		//   appears to have to be loaded in a window to create an iframe, with content, as an element of that document. The original
		//   intention was to create a document object and then append an iframe; I was unable to find a way to do this, hence appending
		//   the iframe to the current document (jasmine).
		var iframe = $('')[0];
		document.body.appendChild(iframe);

		iframe.contentDocument.open("text/xml", "replace");
		iframe.contentDocument.write(iframeContentXHTML);
		iframe.contentDocument.close();

		var $nextNode = EPUBcfi.CFIInstructions.followIndirectionStep(4, $("iframe", document), [], []);

		expect($nextNode.attr("id")).toBe("body1");

		// Remove the injected iframe
		$(iframe).remove();
	});

	it("injects text at the specified offset", function () {

		var contentDocXHTML = jasmine.getFixtures().read('moby_dick_content_doc.xhtml');
		var domParser = new window.DOMParser();
		var contentDoc = domParser.parseFromString(contentDocXHTML, "text/xml");

		var $injectedElement = EPUBcfi.CFIInstructions.textTermination($($("#c01p0002", $(contentDoc))[0].firstChild), 4, '');

		expect($injectedElement.attr("id")).toBe("injected");
		expect($injectedElement.parent().attr("id")).toBe("c01p0002");
	});

	it("injects text at the specified offset in the FIRST sub-node, when a target text node is specified as a list", function () {

		// Get a list of text nodes
		var $currNode = $('
 asdfasd

 aslasjd

 alsjflkds
');
		var $targetTextNodeList = EPUBcfi.CFIInstructions.getNextNode(1, $currNode, ["cfiMarker"], []);

		var $injectedElement = EPUBcfi.CFIInstructions.textTermination($targetTextNodeList, 4, '');
		var $currNodeChildren = $injectedElement.parent().contents();

		expect($currNodeChildren[0].nodeValue).toBe(" asd");
		expect($injectedElement.hasClass('epub_cfi')).toBe(true);
		expect($currNodeChildren[2].nodeValue).toBe("fasd ");
	});

    it("injects text at the specified offset in the SECOND sub-node, when a target text node is specified as a list", function () {

        // Get a list of text nodes
        var $currNode = $('
 asdfasd

 aslasjd

 alsjflkds
');
        var $targetTextNodeList = EPUBcfi.CFIInstructions.getNextNode(1, $currNode, ["cfiMarker"], []);

        var $injectedElement = EPUBcfi.CFIInstructions.textTermination($targetTextNodeList, 12, '');
        var $currNodeChildren = $injectedElement.parent().contents();

        expect($currNodeChildren[2].nodeValue).toBe(" as");
        expect($injectedElement.hasClass('epub_cfi')).toBe(true);
        expect($currNodeChildren[4].nodeValue).toBe("lasjd ");
    });

	it('excludes elements that have a class that indicates they are "cfi markers" and returns a list of text nodes', function () {

		var domParser = new window.DOMParser();
		var xhtml = '
asdfsd

 ddfd

';
		var doc = domParser.parseFromString(xhtml, 'text/xml');
		var $currentNode = $(doc.firstChild.firstChild);

		var $result = EPUBcfi.CFIInstructions.getNextNode(1, $currentNode, ["cfiMarker"], []);
		expect($result.length).toEqual(2);
		expect($result[0].nodeValue).toEqual("asdfsd ");
		expect($result[1].nodeValue).toEqual(" ddfd");
	});

	it('returns the correct text node if the first text node is empty', function () {

		var domParser = new window.DOMParser();
		var xhtml = '

text1
text2
';
		var doc = domParser.parseFromString(xhtml, 'text/xml');
		var $currentNode = $(doc.firstChild);

		var $result = EPUBcfi.CFIInstructions.getNextNode(3, $currentNode, ["cfiMarker"], []);
		expect($result.length).toEqual(1);
		expect($result[0].nodeValue).toEqual("text2");
	});

	it('returns the correct text node if a previous one contains a comment node', function () {

		var domParser = new window.DOMParser();
		var xhtml = '
text1text2
text
text3
';
		var doc = domParser.parseFromString(xhtml, 'text/xml');
		var $currentNode = $(doc.firstChild);

		var $result = EPUBcfi.CFIInstructions.getNextNode(3, $currentNode, ["cfiMarker"], []);
		expect($result.length).toEqual(1);
		expect($result[0].nodeValue).toEqual("text3");
	});

	it('returns the correct text node if it contains a comment node', function () {

		var domParser = new window.DOMParser();
		var xhtml = '
text1
text
text2text3
';
		var doc = domParser.parseFromString(xhtml, 'text/xml');
		var $currentNode = $(doc.firstChild);

		var $result = EPUBcfi.CFIInstructions.getNextNode(3, $currentNode, ["cfiMarker"], []);
		expect($result.length).toEqual(3);
		expect($result[0].nodeValue).toEqual("text2");
		expect($result[1].nodeValue).toEqual("comment");
		expect($result[2].nodeValue).toEqual("text3");
	});

	it('returns the correct text node if the node is in the first position of a set of child nodes', function () {

		var domParser = new window.DOMParser();
		var xhtml = '
text1

text2

text3
';
		var doc = domParser.parseFromString(xhtml, 'text/xml');
		var $currentNode = $(doc.firstChild);

		var $result = EPUBcfi.CFIInstructions.getNextNode(1, $currentNode, ["cfiMarker"], []);
		expect($result.length).toEqual(1);
		expect($result[0].nodeValue).toEqual("text1");
	});

	it('returns the correct text node if the node is between elements in a set of child nodes', function () {

		var domParser = new window.DOMParser();
		var xhtml = '
text1

text2

text3
';
		var doc = domParser.parseFromString(xhtml, 'text/xml');
		var $currentNode = $(doc.firstChild);

		var $result = EPUBcfi.CFIInstructions.getNextNode(3, $currentNode, ["cfiMarker"], []);
		expect($result.length).toEqual(1);
		expect($result[0].nodeValue).toEqual("text2");
	});

	it('returns the correct text node if the node is the last in a set of child nodes', function () {

		var domParser = new window.DOMParser();
		var xhtml = '
text1

text2

text3
';
		var doc = domParser.parseFromString(xhtml, 'text/xml');
		var $currentNode = $(doc.firstChild);

		var $result = EPUBcfi.CFIInstructions.getNextNode(5, $currentNode, ["cfiMarker"], []);
		expect($result.length).toEqual(1);
		expect($result[0].nodeValue).toEqual("text3");
	});

	it("filters blacklist classes", function () {

		var $elements = $(
			"

"
			+ "

"
			+ "

"
			+ "

"
			+ "

"
			);

		$result = EPUBcfi.CFIInstructions.applyBlacklist($elements, ["blacklistClass1", "blacklistClass2"], []);

		expect($result[0].id).toEqual("survivor-1");
		expect($result[1].id).toEqual("survivor-2");
		expect($result[2].id).toEqual("survivor-3");
	});

	it("filters multiple blacklist classes", function () {

		var $elements = $(
			"

"
			+ "

"
			+ "

"
			+ "

"
			+ "

"
			+ "

"
			+ "

"
			);

		$result = EPUBcfi.CFIInstructions.applyBlacklist($elements, ["blacklistClass1", "blacklistClass2"], []);

		expect($result[0].id).toEqual("survivor-1");
		expect($result[1].id).toEqual("survivor-2");
		expect($result[2].id).toEqual("survivor-3");
	});

	it("filters blacklist elements", function () {

		var $elements = $(
			"

"
			+ "<mathjax></mathjax>"
			+ "

"
			+ "<blacklistElement></blacklistElement>"
			+ "

"
			);

		$result = EPUBcfi.CFIInstructions.applyBlacklist($elements, [], ["mathjax", "blacklistElement"]);

		expect($result[0].id).toEqual("survivor-1");
		expect($result[1].id).toEqual("survivor-2");
		expect($result[2].id).toEqual("survivor-3");
	});

	it("filters blacklist classes with text nodes", function () {

		var $elements = $(
			"

"
			+ "textNode-1"
			+ "

"
			+ "

"
			+ "

"
			+ "textNode-2"
			+ "

"
			);

		$result = EPUBcfi.CFIInstructions.applyBlacklist($elements, ["blacklistClass1", "blacklistClass2"], []);

		expect($result[0].nodeType).toEqual(3);
		expect($result[1].id).toEqual("survivor-1");
		expect($result[2].id).toEqual("survivor-2");
		expect($result[3].id).toEqual("survivor-3");
		expect($result[4].nodeType).toEqual(3);
	});

	it("filters blacklist elements with text nodes", function () {

		var $elements = $(
			"

"
			+ "<mathjax></mathjax>"
			+ "textNode-1"
			+ "

"
			+ "<blacklistElement></blacklistElement>"
			+ "textNode-2"
			+ "

"
			);

		$result = EPUBcfi.CFIInstructions.applyBlacklist($elements, [], ["mathjax", "blacklistElement"]);

		expect($result[0].id).toEqual("survivor-1");
		expect($result[1].nodeType).toEqual(3);
		expect($result[2].id).toEqual("survivor-2");
		expect($result[3].nodeType).toEqual(3);
		expect($result[4].id).toEqual("survivor-3");
	});

	it("filters blacklist ids with text nodes", function () {

		var $elements = $(
			"

"
			+ "

"
			+ "textNode-1"
			+ "

"
			+ "<blacklistElement id='blacklist-1'></blacklistElement>"
			+ "textNode-2"
			+ "

"
			);

		$result = EPUBcfi.CFIInstructions.applyBlacklist($elements, [], [], ["mathjax", "blacklist-1"]);

		expect($result[0].id).toEqual("survivor-1");
		expect($result[1].nodeType).toEqual(3);
		expect($result[2].id).toEqual("survivor-2");
		expect($result[3].nodeType).toEqual(3);
		expect($result[4].id).toEqual("survivor-3");
	});
});

describe('CFI INSTRUCTION ERROR HANDLING', function () {

	// Throws out of range errors for any "following" instructions
	it('throws an out of range error for an index step', function () {

		var contentDocXHTML = jasmine.getFixtures().read('moby_dick_content_doc.xhtml');
		var domParser = new window.DOMParser();
		var contentDoc = domParser.parseFromString(contentDocXHTML, "text/xml");

		// A step of 16 is greater than the number of child elements of the content document
		expect(function () {
			EPUBcfi.CFIInstructions.getNextNode(16, $(contentDoc.firstChild))}, ["cfiMarker"], [])
		.toThrow(
			EPUBcfi.OutOfRangeError(7, 1, ""));
	});

	it('throws an out of range error for an indirection step', function () {

		var iframeContentXHTML = jasmine.getFixtures().read("iframe_content.xhtml");

		// Rationale: Append iframe to the body of the current window. This is a bit hacky, but a document containing an iframe
		//   appears to have to be loaded in a window to create an iframe, with content, as an element of that document. The original
		//   intention was to create a document object and then append an iframe; I was unable to find a way to do this, hence appending
		//   the iframe to the current document (jasmine).
		var iframe = $('')[0];
		document.body.appendChild(iframe);

		iframe.contentDocument.open("text/xml", "replace");
		iframe.contentDocument.write(iframeContentXHTML);
		iframe.contentDocument.close();

		// A step of 16 is greater than the number of child elements of the content document
		expect(function () {
			EPUBcfi.CFIInstructions.followIndirectionStep(6, $('iframe', document))}, [], [])
		.toThrow(
			EPUBcfi.OutOfRangeError(2, 1, ""));

		// Remove the injected iframe
		$(iframe).remove();
	});

	// Throws a node type error for itemref
	it('throws a node type error if an iframe indirection step is called on a different element', function () {

		var packageDocXML = jasmine.getFixtures().read('moby_dick_package.opf');
		var contentDocXHTML = jasmine.getFixtures().read('moby_dick_content_doc.xhtml');
		var domParser = new window.DOMParser();
		var packageDoc = domParser.parseFromString(packageDocXML, "text/xml");
		var contentDoc = domParser.parseFromString(contentDocXHTML, "text/xml");
		var spineElement = $($(packageDoc.firstChild).children()[2]).children()[6];

		var nextNode;
		var calledHref;

		spyOn($, "ajax").and.callFake(function (params) {

			params.success(contentDoc);
		});

		expect(function () {
			EPUBcfi.CFIInstructions.followIndirectionStep(16, undefined, $(packageDoc))}, [], [])
		.toThrow(
			EPUBcfi.NodeTypeError(undefined, "expected an iframe element"));
	});

	// Throws terminus errors for invalid text offsets
	it('throws a terminus error if an invalid text offset is provided', function () {

		var $currentNode = $('

');

		expect(function () {
			EPUBcfi.CFIInstructions.textTermination($($currentNode.contents().firstChild), 84, '')})
		.toThrow(
			EPUBcfi.TerminusError("Text", "Text offset:84", "no nodes found for termination condition"));
	});
});
<|endoftext|>


<commit_before>describe('CharacterCounter', function() {
  'use strict';

  var sandbox;

  beforeEach(function(done) {
    var self = this;

    requirejs([
      'phantom-shims',
      'jquery',
      'CharacterCounter'
    ],
    function(
      phantomShims,
      $,
      CharacterCounter
    ) {
      self.$html = $(window.__html__['spec/javascripts/fixtures/CharacterCounter.html']).appendTo('body');
      self.$fixture = self.$html.find('[data-dough-component="CharacterCounter"]');
      self.CharacterCounter = CharacterCounter;
      sandbox = sinon.sandbox.create();
      done();
    }, done);
  });

  afterEach(function() {
    this.$html.remove();
    sandbox.restore();
  });

  describe('On initialisation', function() {
    beforeEach(function(done) {
      this.component = new this.CharacterCounter(this.$fixture);
      done();
    });

    it('should find and cache at least one indicator element', function() {
      this.component.init();
      expect(this.component.$indicators.length).to.be.at.least(1);
    });

    it('should update the character indicator with the number of inputted characters', function() {
      var str = 'foo';
      this.component.$el.val(str);
      this.component.init();
      expect(parseInt(this.component.$indicators.eq(0).text(),10)).to.eq(this.component.maxChars - str.length);
    });
  });

  describe('#getCharacterLength', function () {
    beforeEach(function(done) {
      this.component = new this.CharacterCounter(this.$fixture);
      this.component.init();
      done();
    });

    it('should get the string length of the input', function() {
      var str = 'foo';
      this.component.$el.val(str);
      expect(this.component.getInputCharacterLength()).to.equal(str.length);
    });
  });

  describe('#calculateCharacterCount', function () {
    beforeEach(function(done) {
      this.component = new this.CharacterCounter(this.$fixture);
      this.component.init();
      done();
    });

    it('should calculate the difference between maxChars and the count', function() {
      var count = 100,
          maxChars = 50;

      expect(this.component.calculateCharacterCount(count, maxChars)).to.equal(maxChars - count);
    });
  });

  describe('#updateUI', function () {
    beforeEach(function(done) {
      this.component = new this.CharacterCounter(this.$fixture);
      this.component.init();
      done();
    });

    it('should add the aboveMaxClass if the count is above max number of characters', function() {
      this.component.updateUI(120, 100);
      expect(this.component.$indicators.add(this.component.$warnings)).to.have.class(this.component.config.selectors.aboveMaxClass);
    });

    it('should add the belowMaxClass if the count is below max number of characters', function() {
      this.component.updateUI(100, 120);
      expect(this.component.$indicators.add(this.component.$warnings)).to.have.class(this.component.config.selectors.belowMaxClass);
    });

    it('should add the zeroClass if the count is equal to 0', function() {
      this.component.updateUI(100, 100);
      expect(this.component.$indicators.add(this.component.$warnings)).to.have.class(this.component.config.selectors.zeroClass);
    });

    it('should update the indicator text with the character count', function() {
      this.component.updateUI(100, 140);
      expect(this.component.$indicators.eq(0).text()).to.equal('40');
    });
  });

  describe('#_handleInput', function () {
    beforeEach(function(done) {
      this.component = new this.CharacterCounter(this.$fixture);
      done();
    });

    it('should call the #updateUI() function when input is given', function() {
      var spy = sandbox.spy(this.CharacterCounter.prototype, 'updateUI'),
          val = 'foo';

      this.component.init();
      this.component.$el.val(val).trigger('input');
      expect(spy.calledWith(val.length, this.component.maxChars)).to.be.true;
    });

    it('should set the count property', function() {
      this.component.count = 0;
      this.component.init();
      this.component.$el.val('foo').trigger('input');
      expect(this.component.count).to.not.equal(0);
    });
  });
});
<commit_msg>Improve test assertion wording<commit_after>describe('CharacterCounter', function() {
  'use strict';

  var sandbox;

  beforeEach(function(done) {
    var self = this;

    requirejs([
      'phantom-shims',
      'jquery',
      'CharacterCounter'
    ],
    function(
      phantomShims,
      $,
      CharacterCounter
    ) {
      self.$html = $(window.__html__['spec/javascripts/fixtures/CharacterCounter.html']).appendTo('body');
      self.$fixture = self.$html.find('[data-dough-component="CharacterCounter"]');
      self.CharacterCounter = CharacterCounter;
      sandbox = sinon.sandbox.create();
      done();
    }, done);
  });

  afterEach(function() {
    this.$html.remove();
    sandbox.restore();
  });

  describe('On initialisation', function() {
    beforeEach(function(done) {
      this.component = new this.CharacterCounter(this.$fixture);
      done();
    });

    it('should find and cache at least one indicator element', function() {
      this.component.init();
      expect(this.component.$indicators.length).to.be.at.least(1);
    });

    it('should update the character indicator with the number of inputted characters', function() {
      var str = 'foo',
          inputValue,
          remainingCharactersLength;

      this.component.$el.val(str);
      this.component.init();

      inputValue = parseInt(this.component.$indicators.eq(0).text(),10),
      remainingCharactersLength = this.component.maxChars - str.length;

      expect(inputValue).to.eq(remainingCharactersLength);
    });
  });

  describe('#getCharacterLength', function () {
    beforeEach(function(done) {
      this.component = new this.CharacterCounter(this.$fixture);
      this.component.init();
      done();
    });

    it('should get the string length of the input', function() {
      var str = 'foo';
      this.component.$el.val(str);
      expect(this.component.getInputCharacterLength()).to.equal(str.length);
    });
  });

  describe('#calculateCharacterCount', function () {
    beforeEach(function(done) {
      this.component = new this.CharacterCounter(this.$fixture);
      this.component.init();
      done();
    });

    it('should calculate the difference between maxChars and the count', function() {
      var count = 100,
          maxChars = 50,
          remainingCharactersLength = maxChars - count,
          currentCharacterLength = this.component.calculateCharacterCount(count, maxChars);

      expect(currentCharacterLength).to.equal(remainingCharactersLength);
    });
  });

  describe('#updateUI', function () {
    beforeEach(function(done) {
      this.component = new this.CharacterCounter(this.$fixture);
      this.component.init();
      done();
    });

    it('should add the aboveMaxClass if the count is above max number of characters', function() {
      this.component.updateUI(120, 100);
      expect(this.component.$indicators.add(this.component.$warnings)).to.have.class(this.component.config.selectors.aboveMaxClass);
    });

    it('should add the belowMaxClass if the count is below max number of characters', function() {
      this.component.updateUI(100, 120);
      expect(this.component.$indicators.add(this.component.$warnings)).to.have.class(this.component.config.selectors.belowMaxClass);
    });

    it('should add the zeroClass if the count is equal to 0', function() {
      this.component.updateUI(100, 100);
      expect(this.component.$indicators.add(this.component.$warnings)).to.have.class(this.component.config.selectors.zeroClass);
    });

    it('should update the indicator text with the character count', function() {
      this.component.updateUI(100, 140);
      expect(this.component.$indicators.eq(0).text()).to.equal('40');
    });
  });

  describe('#_handleInput', function () {
    beforeEach(function(done) {
      this.component = new this.CharacterCounter(this.$fixture);
      done();
    });

    it('should call the #updateUI() function when input is given', function() {
      var spy = sandbox.spy(this.CharacterCounter.prototype, 'updateUI'),
          val = 'foo';

      this.component.init();
      this.component.$el.val(val).trigger('input');
      expect(spy.calledWith(val.length, this.component.maxChars)).to.be.true;
    });

    it('should set the count property', function() {
      this.component.count = 0;
      this.component.init();
      this.component.$el.val('foo').trigger('input');
      expect(this.component.count).to.not.equal(0);
    });
  });
});
<|endoftext|>


<commit_before>describe("jquery.passwordStrengthIndicator", function() {
	var fixtureHtml = '
<input type="password" id="password" />
';

	beforeEach(function() {
		jasmine.getFixtures().set(fixtureHtml);
	});

	describe("PasswordStrengthCalculator", function() {
		var input;
		var result;

		beforeEach(function() {
			input = $("#password");
		});

		it("should give 0 points for empty string", function() {
			result = input.psi("calculate", "");
			expect(result).toEqual(0);
		});

		it("should give 3 point for 1 lowercase letter", function() {
			result = input.psi("calculate", "a");
			expect(result).toEqual(3);
		});

		it("should give 4 points for 2 lowercase letters", function() {
			result = input.psi("calculate", "ab");
			expect(result).toEqual(4);
		});

		it("should give 3 points for 1 uppercase letter", function() {
			result = input.psi("calculate", "A");
			expect(result).toEqual(3);
		});

		it("should give 4 points for 2 uppercase letter", function() {
			result = input.psi("calculate", "AB");
			expect(result).toEqual(4);
		});

		it("should give 4 points extra when it contains a number", function() {
			result = input.psi("calculate", "1");
			expect(result).toEqual(5);
		});

		it("should give 5 points extra when it contains a symbol", function() {
			result = input.psi("calculate", "!");
			expect(result).toEqual(6);
		});

		it("should give 6 points for 1 lowercase, 1 uppercase characters", function() {
			result = input.psi("calculate", "aB");
			expect(result).toEqual(6);
		});

		it("should give 8 points for 2 lowercase, 2 uppercase characters", function() {
			result = input.psi("calculate", "aBcD");
			expect(result).toEqual(8);
		});

		it("should give 17 points for 1 lowercase, 1 uppercase characters, 1 number and 1 symbol", function() {
			result = input.psi("calculate", "aB1?");
			expect(result).toEqual(17);
		});

	});

	describe("Indicator", function() {
		var html;
		var input;
		var defaults;

		beforeEach(function() {
			html = $("#root");
			input = $("#password").psi();
			defaults = input.psi("defaults");
		});

		function getIndicator() {
			return html.find("." + defaults.indicatorClassName);
		};

		it("should have default indicator class name", function() {
			expect(getIndicator().attr("class")).toEqual("password-strength-indicator");
		});

		it("should be hidden before typing", function() {
			expect(getIndicator().css("display")).toEqual("none");
		});

		it("should be displayed when it contains 1 ore more characters", function() {
			input.val("a").trigger("keyup");
			expect(getIndicator().css("display")).toEqual("block");
		});

		it("should be hidden when it becomes emtpy", function() {
			input.val("a").trigger("keyup");
			expect(getIndicator().css("display")).toEqual("block");
			input.val("").trigger("keyup");
			expect(getIndicator().css("display")).toEqual("none");
		});

		describe("Strength class names", function() {
			var html;
			var input;
			var options;

			beforeEach(function() {
				options = { indicatorClassName: "indicator" };

				html = $("#root");
				input = $("#password").psi(options);
			});

			function setInputValueTo(value) {
				input.val(value).trigger("keyup");
			};

			function getIndicator() {
				return html.find("." + options.indicatorClassName);
			};

			it("should have very-week class for password with 0 points", function() {
				setInputValueTo("");
				expect(getIndicator().hasClass("very-weak")).toEqual(true);
			});

			it("should have very-strong class for password with 25 points", function() {
				setInputValueTo("abcdefghijklmnopqrstuvwxz");
				expect(getIndicator().hasClass("very-strong")).toEqual(true);
			});

		});

	});

});<commit_msg>- Fixed spelling mistake in spec.<commit_after>describe("jquery.passwordStrengthIndicator", function() {
	var fixtureHtml = '
<input type="password" id="password" />
';

	beforeEach(function() {
		jasmine.getFixtures().set(fixtureHtml);
	});

	describe("PasswordStrengthCalculator", function() {
		var input;
		var result;

		beforeEach(function() {
			input = $("#password");
		});

		it("should give 0 points for empty string", function() {
			result = input.psi("calculate", "");
			expect(result).toEqual(0);
		});

		it("should give 3 points for 1 lowercase letter", function() {
			result = input.psi("calculate", "a");
			expect(result).toEqual(3);
		});

		it("should give 4 points for 2 lowercase letters", function() {
			result = input.psi("calculate", "ab");
			expect(result).toEqual(4);
		});

		it("should give 3 points for 1 uppercase letter", function() {
			result = input.psi("calculate", "A");
			expect(result).toEqual(3);
		});

		it("should give 4 points for 2 uppercase letter", function() {
			result = input.psi("calculate", "AB");
			expect(result).toEqual(4);
		});

		it("should give 4 points extra when it contains a number", function() {
			result = input.psi("calculate", "1");
			expect(result).toEqual(5);
		});

		it("should give 5 points extra when it contains a symbol", function() {
			result = input.psi("calculate", "!");
			expect(result).toEqual(6);
		});

		it("should give 6 points for 1 lowercase, 1 uppercase characters", function() {
			result = input.psi("calculate", "aB");
			expect(result).toEqual(6);
		});

		it("should give 8 points for 2 lowercase, 2 uppercase characters", function() {
			result = input.psi("calculate", "aBcD");
			expect(result).toEqual(8);
		});

		it("should give 17 points for 1 lowercase, 1 uppercase characters, 1 number and 1 symbol", function() {
			result = input.psi("calculate", "aB1?");
			expect(result).toEqual(17);
		});

	});

	describe("Indicator", function() {
		var html;
		var input;
		var defaults;

		beforeEach(function() {
			html = $("#root");
			input = $("#password").psi();
			defaults = input.psi("defaults");
		});

		function getIndicator() {
			return html.find("." + defaults.indicatorClassName);
		};

		it("should have default indicator class name", function() {
			expect(getIndicator().attr("class")).toEqual("password-strength-indicator");
		});

		it("should be hidden before typing", function() {
			expect(getIndicator().css("display")).toEqual("none");
		});

		it("should be displayed when it contains 1 ore more characters", function() {
			input.val("a").trigger("keyup");
			expect(getIndicator().css("display")).toEqual("block");
		});

		it("should be hidden when it becomes emtpy", function() {
			input.val("a").trigger("keyup");
			expect(getIndicator().css("display")).toEqual("block");
			input.val("").trigger("keyup");
			expect(getIndicator().css("display")).toEqual("none");
		});

		describe("Strength class names", function() {
			var html;
			var input;
			var options;

			beforeEach(function() {
				options = { indicatorClassName: "indicator" };

				html = $("#root");
				input = $("#password").psi(options);
			});

			function setInputValueTo(value) {
				input.val(value).trigger("keyup");
			};

			function getIndicator() {
				return html.find("." + options.indicatorClassName);
			};

			it("should have very-week class for password with 0 points", function() {
				setInputValueTo("");
				expect(getIndicator().hasClass("very-weak")).toEqual(true);
			});

			it("should have very-strong class for password with 25 points", function() {
				setInputValueTo("abcdefghijklmnopqrstuvwxz");
				expect(getIndicator().hasClass("very-strong")).toEqual(true);
			});

		});

	});

});<|endoftext|>


<commit_before>describe('jquery.ui.autocomplete.localcache', function() {

  describe('#amendResponse', function() {
    describe('when the autocomplete menu is visible', function() {
      it('adds new elements to the existing menu', function() {
        // expect(true).toEqual(true);
      });
      it('adds the new results to the local cache');
    });
    describe('when the autocomplete menu is not visible', function() {
      it('adds creates and shows the menu');
      it('adds the new results to the local cache');
    });
    describe('when results already exist in cache', function() {
      it('does not add new entries to the cache');
    });
    describe('when element.val() has changed since search was requested', function() {
      it('does not show the autocomplete results');
      it('adds the new results to the local cache');
    });
  });
});
<commit_msg>add stub tests<commit_after>describe('jquery.ui.autocomplete.localcache', function() {

  describe('#amendResponse', function() {
    describe('when the autocomplete menu is visible', function() {
      it('adds new elements to the existing menu', function() {
        // expect(true).toEqual(true);
      });
      it('adds the new results to the local cache');
    });
    describe('when the autocomplete menu is not visible', function() {
      it('adds creates and shows the menu');
      it('adds the new results to the local cache');
    });
    describe('when results already exist in cache', function() {
      it('does not add new entries to the cache');
    });
    describe('when element.val() has changed since search was requested', function() {
      it('does not show the autocomplete results');
      it('adds the new results to the local cache');
    });
  });

  describe('autocompletesearch event', function() {
    it('calls #abort');
  });
  describe('autocompleteclose event', function() {
    it('calls #abort');
  });

  describe('#abort', function() {
    describe('when currentXhr exists', function() {
      it('calls currentXhr.abort');
      it('sets currentXhr to null');
    });
  });

  describe('#localAndRemoteSource', function() {
    describe('when options.remoteDelay has passed', function() {
      it('adds ui-autocomplete-loading class');
      it('fires xhr request');
    });
    describe('when search event is cancelled', function() {
      it('does not fire xhr request');
    });
    describe('when xhr completes', function() {
      it('sets pending = 0');
      it('removes ui-autocomplete-loading class');
    });
  });
});
<|endoftext|>


<commit_before>/*!
  GPII INI Settings Handler

  Copyright 2012 Raising the Floor - International

  Licensed under the New BSD license. You may not use this file except in
  compliance with this License.

  You may obtain a copy of the License at
  https://github.com/gpii/universal/LICENSE.txt
*/

/*global require*/

(function () {

    "use strict";

    var fluid = require("infusion"),
        fs = require("fs"),
        gpii = fluid.registerNamespace("gpii"),
        iniparser = require("ini");

    fluid.registerNamespace("gpii.settingsHandlers.INISettingsHandler");

    var transformPayload = function (payload, modifier) {
        var returnObj = fluid.copy(payload);
        return fluid.transform(returnObj, function (solution) {
            return fluid.transform(solution, function(solutionEntry) {
                var options = solutionEntry.options;

                if (options && options.path) {
                    var currentSettings = iniparser.parse(fs.readFileSync(options.path, 'utf-8'), options);
                    return modifier(solutionEntry, currentSettings);
                }
                return solutionEntry;
            });
        });
    };

    gpii.settingsHandlers.INISettingsHandler.get = function (payload) {
        return transformPayload(payload, function (solutionEntry, currentSettings) {
            var newSettingsResponse = {};
            var userRequestedSettings = solutionEntry.settings;

            fluid.each(userRequestedSettings, function (settingVal, settingKey) {
                var value = fluid.get(currentSettings, settingKey, fluid.model.escapedGetConfig);
                newSettingsResponse[settingKey] = value;
            });

            return { options: solutionEntry.options, settings: newSettingsResponse };
        });
    };

    gpii.settingsHandlers.INISettingsHandler.set = function (payload) {
        var oldValue;

        return transformPayload(payload, function (solutionEntry, currentSettings) {
            var newSettingsResponse = {};
            var userRequestedSettings = solutionEntry.settings;
            var options = solutionEntry.options;

            // record differences between required and default settings
            // so that they default settings can be restored
            fluid.each(userRequestedSettings, function (settingVal, settingKey) {
                oldValue = fluid.get(currentSettings, settingKey, fluid.model.escapedGetConfig);
                fluid.set(currentSettings, settingKey, settingVal, fluid.model.escapedSetConfig);
                newSettingsResponse[settingKey] = {
                    "oldValue": oldValue,
                    "newValue": settingVal
                };
            });
            solutionEntry.settings = currentSettings;

            // Write the new settings to a file for the application to read
            var iniOutput = iniparser.stringify(currentSettings, null, options);
            fs.writeFileSync(options.path, iniOutput);
            fluid.log("Settings explicitly configured in GPII profile:", iniOutput);
            return { options: options, settings: newSettingsResponse };
        });
    };
}());<commit_msg>GPII-83: Added a few comments<commit_after>/*!
  GPII INI Settings Handler

  Copyright 2012 Raising the Floor - International

  Licensed under the New BSD license. You may not use this file except in
  compliance with this License.

  You may obtain a copy of the License at
  https://github.com/gpii/universal/LICENSE.txt
*/

/*global require*/

(function () {

    "use strict";

    var fluid = require("infusion"),
        fs = require("fs"),
        gpii = fluid.registerNamespace("gpii"),
        iniparser = require("ini");

    fluid.registerNamespace("gpii.settingsHandlers.INISettingsHandler");

    //Function for looping through the payload sent to get/set calls. Looping is done
    //by fluid.transform calls, and at the level of each solution entry, the 'modifier' function
    //passed as parameter is called to allow editing of the solutionEntry based on the current settings
    //(both passed as parameters to the modifier function)
    var transformPayload = function (payload, modifier) {
        var returnObj = fluid.copy(payload);
        return fluid.transform(returnObj, function (solution) {
            return fluid.transform(solution, function(solutionEntry) {
                var options = solutionEntry.options;

                if (options && options.path) {
                    //read ini file
                    var currentSettings = iniparser.parse(fs.readFileSync(options.path, 'utf-8'), options);
                    //modification of the entry
                    return modifier(solutionEntry, currentSettings);
                }
                return solutionEntry;
            });
        });
    };

    /* PUBLIC API FUNCTIONS */
    gpii.settingsHandlers.INISettingsHandler.get = function (payload) {
        return transformPayload(payload, function (solutionEntry, currentSettings) {
            var newSettingsResponse = {};
            var userRequestedSettings = solutionEntry.settings;

            fluid.each(userRequestedSettings, function (settingVal, settingKey) {
                var value = fluid.get(currentSettings, settingKey, fluid.model.escapedGetConfig);
                newSettingsResponse[settingKey] = value;
            });

            return { options: solutionEntry.options, settings: newSettingsResponse };
        });
    };

    gpii.settingsHandlers.INISettingsHandler.set = function (payload) {
        var oldValue;

        return transformPayload(payload, function (solutionEntry, currentSettings) {
            var newSettingsResponse = {};
            var userRequestedSettings = solutionEntry.settings;
            var options = solutionEntry.options;

            // record differences between required and default settings
            // so that they default settings can be restored
            fluid.each(userRequestedSettings, function (settingVal, settingKey) {
                oldValue = fluid.get(currentSettings, settingKey, fluid.model.escapedGetConfig);
                fluid.set(currentSettings, settingKey, settingVal, fluid.model.escapedSetConfig);
                newSettingsResponse[settingKey] = {
                    "oldValue": oldValue,
                    "newValue": settingVal
                };
            });
            solutionEntry.settings = currentSettings;

            // Write the new settings to a file for the application to read
            var iniOutput = iniparser.stringify(currentSettings, null, options);
            fs.writeFileSync(options.path, iniOutput);
            fluid.log("Settings explicitly configured in GPII profile:", iniOutput);
            return { options: options, settings: newSettingsResponse };
        });
    };
}());<|endoftext|>


<commit_before>'use strict';

import _defaults from 'lodash/object/defaults';
import _throttle from 'lodash/function/throttle';

import h from 'virtual-dom/h';
import diff from 'virtual-dom/diff';
import patch from 'virtual-dom/patch';

import {pixelsToSeconds} from './utils/conversions'
import extractPeaks from './utils/peaks';
import LoaderFactory from './track/loader/LoaderFactory';

import ScrollHook from './render/ScrollHook';

import Track from './Track';
import Playout from './Playout';

export default class {

    constructor() {

        this.tracks = [];
        this.soloedTracks = [];
        this.mutedTracks = [];
        this.playoutPromises = [];

        this.cursor = 0;
        this.playbackSeconds = 0;
        this.duration = 0;
        this.scrollLeft = 0;

        this.fadeType = "logarithmic";
    }

    setSampleRate(sampleRate) {
        this.sampleRate = sampleRate;
    }

    setSamplesPerPixel(samplesPerPixel) {
        this.samplesPerPixel = samplesPerPixel;
    }

    setAudioContext(ac) {
        this.ac = ac;
    }

    setControlOptions(controlOptions) {
        this.controls = controlOptions;
    }

    setWaveHeight(height) {
        this.waveHeight = height;
    }

    setColors(colors) {
        this.colors = colors;
    }

    setEventEmitter(ee) {
        this.ee = ee;
    }

    getEventEmitter() {
        return this.ee;
    }

    setUpEventEmitter() {
        let ee = this.ee;

        ee.on('select', (start, end, track) => {

            if (this.isPlaying()) {
                this.lastSeeked = start;
                this.pausedAt = undefined;
                this.restartPlayFrom(start);
            }
            else {
                //reset if it was paused.
                this.playbackSeconds = 0;
                this.setTimeSelection(start, end);
                this.setActiveTrack(track);
                this.draw(this.render());
            }
        });

        ee.on('statechange', (state) => {
            this.setState(state);
            this.draw(this.render());
        });

        ee.on('shift', (deltaTime, track) => {
            track.setStartTime(track.getStartTime() + deltaTime);
            this.adjustDuration();
            this.draw(this.render());
        });

        ee.on('play', () => {
            this.play();
        });

        ee.on('pause', () => {
            this.pause();
        });

        ee.on('stop', () => {
            this.stop();
        });

        ee.on('rewind', () => {
            this.rewind();
        });

        ee.on('fastforward', () => {
            this.fastForward();
        });

        ee.on('solo', (track) => {
            this.soloTrack(track);
            this.adjustTrackPlayout();
            this.draw(this.render());
        });

        ee.on('mute', (track) => {
            this.muteTrack(track);
            this.adjustTrackPlayout();
            this.draw(this.render());
        });

        ee.on('volumechange', (volume, track) => {
            track.setGainLevel(volume/100);
        });

        ee.on('fadein', (end, track) => {
            track.setFadeIn({
                "shape": this.fadeType,
                "end": end
            });
        });

        ee.on('fadeout', (start, track) => {
            track.setFadeOut({
                "shape": this.fadeType,
                "start": start
            });
        });

        ee.on('fadetype', (type) => {
            this.fadeType = type;
        });

        ee.on('newtrack', (file) => {
            this.load([{
                src: file,
                name: file.name
            }]);
        });
    }

    load(trackList, options={}) {
        let loadPromises = trackList.map((trackInfo) => {
            let loader = LoaderFactory.createLoader(trackInfo.src, this.ac);
            return loader.load();
        });

        return Promise.all(loadPromises).then((audioBuffers) => {
            let tracks = audioBuffers.map((audioBuffer, index) => {
                let name = trackList[index].name || "Untitled";
                let start = trackList[index].start || 0;
                let states = trackList[index].states || {};
                let fadeIn = trackList[index].fadeIn;
                let fadeOut = trackList[index].fadeOut;

                //extract peaks with AudioContext for now.
                let peaks = extractPeaks(audioBuffer, this.samplesPerPixel, true);
                //webaudio specific playout for now.
                let playout = new Playout(this.ac, audioBuffer);

                let track = new Track();
                track.setName(name);
                track.setEventEmitter(this.ee);
                track.setEnabledStates(states);
                track.setPeaks(peaks);
                track.setCues(0, audioBuffer.duration);

                if (fadeIn !== undefined) {
                    track.setFadeIn(fadeIn);
                }

                if (fadeOut !== undefined) {
                    track.setFadeOut(fadeOut);
                }

                track.setState(this.getState());
                track.setStartTime(start);
                track.setPlayout(playout);

                return track;
            });

            this.tracks = this.tracks.concat(tracks);
            this.adjustDuration();
            this.draw(this.render());
        });
    }

    /*
        track instance of Track.
    */
    setActiveTrack(track) {
        this.activeTrack = track;
    }

    getActiveTrack() {
        return this.activeTrack;
    }

    /*
        start, end in seconds.
    */
    setTimeSelection(start, end) {
        this.timeSelection = {
            start,
            end,
        };

        this.cursor = start;
    }

    getTimeSelection() {
        return this.timeSelection;
    }

    setState(state) {
        this.state = state;

        this.tracks.forEach((track) => {
            track.setState(state);
        });
    }

    getState() {
        return this.state;
    }

    muteTrack(track) {
        let mutedList = this.mutedTracks;
        let index = mutedList.indexOf(track);

        if (index > -1) {
            mutedList.splice(index, 1);
        }
        else {
            mutedList.push(track);
        }
    }

    soloTrack(track) {
        let soloedList = this.soloedTracks;
        let index = soloedList.indexOf(track);

        if (index > -1) {
            soloedList.splice(index, 1);
        }
        else {
            soloedList.push(track);
        }
    }

    adjustTrackPlayout() {
        var masterGain;

        this.tracks.forEach((track) => {
            masterGain = this.shouldTrackPlay(track) ? 1 : 0;
            track.setMasterGainLevel(masterGain);
        });
    }

    adjustDuration() {
        var duration = 0;

        this.tracks.forEach((track) => {
            duration = Math.max(duration, track.getEndTime());
        });

        this.duration = duration;
    }

    shouldTrackPlay(track) {
        var shouldPlay;
        //if there are solo tracks, only they should play.
        if (this.soloedTracks.length > 0) {
            shouldPlay = false;
            if (this.soloedTracks.indexOf(track) > -1) {
                shouldPlay = true;
            }
        }
        //play all tracks except any muted tracks.
        else {
            shouldPlay = true;
            if (this.mutedTracks.indexOf(track) > -1) {
                shouldPlay = false;
            }
        }

        return shouldPlay;
    }

    isPlaying() {
        return this.tracks.reduce((isPlaying, track) => {
            return isPlaying || track.isPlaying();
        }, false);
    }

    /*
    *   returns the current point of time in the playlist in seconds.
    */
    getCurrentTime() {
        let cursorPos = this.lastSeeked || this.pausedAt || this.cursor;

        return cursorPos + this.getElapsedTime();
    }

    getElapsedTime() {
        let currentTime = this.ac.currentTime;

        return currentTime - this.lastPlay;
    }

    restartPlayFrom(cursorPos) {
        this.stopAnimation();

        this.tracks.forEach((editor) => {
            editor.scheduleStop();
        });

        Promise.all(this.playoutPromises).then(this.play.bind(this, cursorPos));
    }

    play(startTime) {
        var currentTime = this.ac.currentTime,
            endTime,
            selected = this.getTimeSelection(),
            playoutPromises = [];

        startTime = startTime || this.pausedAt || this.cursor;

        if (selected.end > startTime) {
            endTime = selected.end;
        }

        this.tracks.forEach((track) => {
            track.setState('cursor');
            playoutPromises.push(track.schedulePlay(currentTime, startTime, endTime, {
                masterGain: this.shouldTrackPlay(track) ? 1 : 0
            }));
        });

        this.lastPlay = currentTime;
        //use these to track when the playlist has fully stopped.
        this.playoutPromises = playoutPromises;
        this.startAnimation(startTime);
    }

    pause() {
        if (!this.isPlaying()) {
            return;
        }

        this.pausedAt = this.getCurrentTime();
        this.playbackReset();
    }

    stop() {
        this.pausedAt = undefined;
        this.playbackSeconds = 0;
        this.playbackReset();
    }

    playbackReset() {
        this.lastSeeked = undefined;
        this.stopAnimation();

        this.tracks.forEach((track) => {
            track.scheduleStop();
            track.setState(this.getState());
        });

        this.draw(this.render());
    }

    rewind() {
        this.stop();

        Promise.all(this.playoutPromises).then(() => {
            this.scrollLeft = 0;
            this.ee.emit('select', 0, 0);
        });
    }

    fastForward() {
        this.stop();

        Promise.all(this.playoutPromises).then(() => {
            this.scrollLeft = this.duration;
            this.ee.emit('select', this.duration, this.duration);
        });
    }

    startAnimation(startTime) {
        this.lastDraw = this.ac.currentTime;
        this.animationRequest = window.requestAnimationFrame(this.updateEditor.bind(this, startTime));
    }

    stopAnimation() {
        window.cancelAnimationFrame(this.animationRequest);
        this.lastDraw = undefined;
    }

    /*
    * Animation function for the playlist.
    */
    updateEditor(cursorPos) {
        let currentTime = this.ac.currentTime;
        let playbackSeconds = 0;
        let elapsed;

        cursorPos = cursorPos || this.cursor;
        elapsed = currentTime - this.lastDraw;

        if (this.isPlaying()) {
            playbackSeconds = cursorPos + elapsed;

            this.animationRequest = window.requestAnimationFrame(this.updateEditor.bind(this, playbackSeconds));
        }
        else {
            this.stopAnimation();
            this.pausedAt = undefined;
            this.lastSeeked = undefined;
            this.setState(this.getState());
        }

        this.playbackSeconds = playbackSeconds;
        this.ee.emit('timeupdate', playbackSeconds);

        this.draw(this.render());
        this.lastDraw = currentTime;
    }

    draw(newTree) {
        let patches = diff(this.tree, newTree);

        window.requestAnimationFrame(() => {
            this.rootNode = patch(this.rootNode, patches);
            this.tree = newTree;
        });
    }

    getTrackRenderData(data={}) {
        let defaults = {
            "height": this.waveHeight,
            "resolution": this.samplesPerPixel,
            "sampleRate": this.sampleRate,
            "controls": this.controls,
            "isActive": false,
            "timeSelection": this.getTimeSelection(),
            "playlistLength": this.duration,
            "playbackSeconds": this.playbackSeconds,
            "colors": this.colors
        };

        return _defaults(data, defaults);
    }

    render() {
        var activeTrack = this.getActiveTrack();

        let trackElements = this.tracks.map((track) => {
            return track.render(this.getTrackRenderData({
                "isActive": (activeTrack === track) ? true : false,
                "masterGain": this.shouldTrackPlay(track) ? 1 : 0,
                "soloed": this.soloedTracks.indexOf(track) > -1,
                "muted": this.mutedTracks.indexOf(track) > -1
            }));
        });

        return h("div.playlist", {
            "attributes": {
                "style": "overflow: hidden; position: relative;"
            }}, [
            h("div.playlist-tracks", {
                "attributes": {
                    "style": "overflow: auto;"
                },
                "ev-scroll": _throttle((e) => {
                    this.scrollLeft = pixelsToSeconds(e.target.scrollLeft, this.samplesPerPixel, this.sampleRate);
                }, 200),
                "hook": new ScrollHook(this, this.samplesPerPixel, this.sampleRate)
            }, trackElements)
        ]);
    }
}<commit_msg>Move towards promise based api.<commit_after>'use strict';

import _defaults from 'lodash/object/defaults';
import _throttle from 'lodash/function/throttle';

import h from 'virtual-dom/h';
import diff from 'virtual-dom/diff';
import patch from 'virtual-dom/patch';

import {pixelsToSeconds} from './utils/conversions'
import extractPeaks from './utils/peaks';
import LoaderFactory from './track/loader/LoaderFactory';

import ScrollHook from './render/ScrollHook';

import Track from './Track';
import Playout from './Playout';

export default class {

    constructor() {

        this.tracks = [];
        this.soloedTracks = [];
        this.mutedTracks = [];
        this.playoutPromises = [];

        this.cursor = 0;
        this.playbackSeconds = 0;
        this.duration = 0;
        this.scrollLeft = 0;

        this.fadeType = "logarithmic";
    }

    setSampleRate(sampleRate) {
        this.sampleRate = sampleRate;
    }

    setSamplesPerPixel(samplesPerPixel) {
        this.samplesPerPixel = samplesPerPixel;
    }

    setAudioContext(ac) {
        this.ac = ac;
    }

    setControlOptions(controlOptions) {
        this.controls = controlOptions;
    }

    setWaveHeight(height) {
        this.waveHeight = height;
    }

    setColors(colors) {
        this.colors = colors;
    }

    setEventEmitter(ee) {
        this.ee = ee;
    }

    getEventEmitter() {
        return this.ee;
    }

    setUpEventEmitter() {
        let ee = this.ee;

        ee.on('select', (start, end, track) => {

            if (this.isPlaying()) {
                this.lastSeeked = start;
                this.pausedAt = undefined;
                this.restartPlayFrom(start);
            }
            else {
                //reset if it was paused.
                this.playbackSeconds = 0;
                this.setTimeSelection(start, end);
                this.setActiveTrack(track);
                this.draw(this.render());
            }
        });

        ee.on('statechange', (state) => {
            this.setState(state);
            this.draw(this.render());
        });

        ee.on('shift', (deltaTime, track) => {
            track.setStartTime(track.getStartTime() + deltaTime);
            this.adjustDuration();
            this.draw(this.render());
        });

        ee.on('play', () => {
            this.play();
        });

        ee.on('pause', () => {
            this.pause();
        });

        ee.on('stop', () => {
            this.stop();
        });

        ee.on('rewind', () => {
            this.rewind();
        });

        ee.on('fastforward', () => {
            this.fastForward();
        });

        ee.on('solo', (track) => {
            this.soloTrack(track);
            this.adjustTrackPlayout();
            this.draw(this.render());
        });

        ee.on('mute', (track) => {
            this.muteTrack(track);
            this.adjustTrackPlayout();
            this.draw(this.render());
        });

        ee.on('volumechange', (volume, track) => {
            track.setGainLevel(volume/100);
        });

        ee.on('fadein', (end, track) => {
            track.setFadeIn({
                "shape": this.fadeType,
                "end": end
            });
        });

        ee.on('fadeout', (start, track) => {
            track.setFadeOut({
                "shape": this.fadeType,
                "start": start
            });
        });

        ee.on('fadetype', (type) => {
            this.fadeType = type;
        });

        ee.on('newtrack', (file) => {
            this.load([{
                src: file,
                name: file.name
            }]);
        });

        ee.on('trim', () => {

        });
    }

    load(trackList, options={}) {
        let loadPromises = trackList.map((trackInfo) => {
            let loader = LoaderFactory.createLoader(trackInfo.src, this.ac);
            return loader.load();
        });

        return Promise.all(loadPromises).then((audioBuffers) => {
            let tracks = audioBuffers.map((audioBuffer, index) => {
                let name = trackList[index].name || "Untitled";
                let start = trackList[index].start || 0;
                let states = trackList[index].states || {};
                let fadeIn = trackList[index].fadeIn;
                let fadeOut = trackList[index].fadeOut;

                //extract peaks with AudioContext for now.
                let peaks = extractPeaks(audioBuffer, this.samplesPerPixel, true);
                //webaudio specific playout for now.
                let playout = new Playout(this.ac, audioBuffer);

                let track = new Track();
                track.setName(name);
                track.setEventEmitter(this.ee);
                track.setEnabledStates(states);
                track.setPeaks(peaks);
                track.setCues(0, audioBuffer.duration);

                if (fadeIn !== undefined) {
                    track.setFadeIn(fadeIn);
                }

                if (fadeOut !== undefined) {
                    track.setFadeOut(fadeOut);
                }

                track.setState(this.getState());
                track.setStartTime(start);
                track.setPlayout(playout);

                return track;
            });

            this.tracks = this.tracks.concat(tracks);
            this.adjustDuration();
            this.draw(this.render());
        });
    }

    /*
        track instance of Track.
    */
    setActiveTrack(track) {
        this.activeTrack = track;
    }

    getActiveTrack() {
        return this.activeTrack;
    }

    /*
        start, end in seconds.
    */
    setTimeSelection(start, end) {
        this.timeSelection = {
            start,
            end,
        };

        this.cursor = start;
    }

    getTimeSelection() {
        return this.timeSelection;
    }

    setState(state) {
        this.state = state;

        this.tracks.forEach((track) => {
            track.setState(state);
        });
    }

    getState() {
        return this.state;
    }

    muteTrack(track) {
        let mutedList = this.mutedTracks;
        let index = mutedList.indexOf(track);

        if (index > -1) {
            mutedList.splice(index, 1);
        }
        else {
            mutedList.push(track);
        }
    }

    soloTrack(track) {
        let soloedList = this.soloedTracks;
        let index = soloedList.indexOf(track);

        if (index > -1) {
            soloedList.splice(index, 1);
        }
        else {
            soloedList.push(track);
        }
    }

    adjustTrackPlayout() {
        var masterGain;

        this.tracks.forEach((track) => {
            masterGain = this.shouldTrackPlay(track) ? 1 : 0;
            track.setMasterGainLevel(masterGain);
        });
    }

    adjustDuration() {
        this.duration = this.tracks.reduce((duration, track) => {
            return Math.max(duration, track.getEndTime());
        }, 0);
    }

    shouldTrackPlay(track) {
        var shouldPlay;
        //if there are solo tracks, only they should play.
        if (this.soloedTracks.length > 0) {
            shouldPlay = false;
            if (this.soloedTracks.indexOf(track) > -1) {
                shouldPlay = true;
            }
        }
        //play all tracks except any muted tracks.
        else {
            shouldPlay = true;
            if (this.mutedTracks.indexOf(track) > -1) {
                shouldPlay = false;
            }
        }

        return shouldPlay;
    }

    isPlaying() {
        return this.tracks.reduce((isPlaying, track) => {
            return isPlaying || track.isPlaying();
        }, false);
    }

    /*
    *   returns the current point of time in the playlist in seconds.
    */
    getCurrentTime() {
        let cursorPos = this.lastSeeked || this.pausedAt || this.cursor;

        return cursorPos + this.getElapsedTime();
    }

    getElapsedTime() {
        let currentTime = this.ac.currentTime;

        return currentTime - this.lastPlay;
    }

    restartPlayFrom(cursorPos) {
        this.stopAnimation();

        this.tracks.forEach((editor) => {
            editor.scheduleStop();
        });

        return Promise.all(this.playoutPromises).then(this.play.bind(this, cursorPos));
    }

    play(startTime) {
        var currentTime = this.ac.currentTime,
            endTime,
            selected = this.getTimeSelection(),
            playoutPromises = [];

        startTime = startTime || this.pausedAt || this.cursor;

        if (selected.end > startTime) {
            endTime = selected.end;
        }

        this.tracks.forEach((track) => {
            track.setState('cursor');
            playoutPromises.push(track.schedulePlay(currentTime, startTime, endTime, {
                masterGain: this.shouldTrackPlay(track) ? 1 : 0
            }));
        });

        this.lastPlay = currentTime;
        //use these to track when the playlist has fully stopped.
        this.playoutPromises = playoutPromises;
        this.startAnimation(startTime);

        return this.playoutPromises;
    }

    pause() {
        if (!this.isPlaying()) {
            return;
        }

        this.pausedAt = this.getCurrentTime();
        return this.playbackReset();
    }

    stop() {
        this.pausedAt = undefined;
        this.playbackSeconds = 0;
        return this.playbackReset();
    }

    playbackReset() {
        this.lastSeeked = undefined;
        this.stopAnimation();

        this.tracks.forEach((track) => {
            track.scheduleStop();
            track.setState(this.getState());
        });

        this.draw(this.render());

        return this.playoutPromises;
    }

    rewind() {
        return Promise.all(this.stop()).then(() => {
            this.scrollLeft = 0;
            this.ee.emit('select', 0, 0);
        });
    }

    fastForward() {
        return Promise.all(this.stop()).then(() => {
            this.scrollLeft = this.duration;
            this.ee.emit('select', this.duration, this.duration);
        });
    }

    startAnimation(startTime) {
        this.lastDraw = this.ac.currentTime;
        this.animationRequest = window.requestAnimationFrame(this.updateEditor.bind(this, startTime));
    }

    stopAnimation() {
        window.cancelAnimationFrame(this.animationRequest);
        this.lastDraw = undefined;
    }

    /*
    * Animation function for the playlist.
    */
    updateEditor(cursorPos) {
        let currentTime = this.ac.currentTime;
        let playbackSeconds = 0;
        let elapsed;

        cursorPos = cursorPos || this.cursor;
        elapsed = currentTime - this.lastDraw;

        if (this.isPlaying()) {
            playbackSeconds = cursorPos + elapsed;
            this.ee.emit('timeupdate', playbackSeconds);
            this.animationRequest = window.requestAnimationFrame(this.updateEditor.bind(this, playbackSeconds));
        }
        else {
            this.stopAnimation();
            this.pausedAt = undefined;
            this.lastSeeked = undefined;
            this.setState(this.getState());
        }

        this.playbackSeconds = playbackSeconds;

        this.draw(this.render());
        this.lastDraw = currentTime;
    }

    draw(newTree) {
        let patches = diff(this.tree, newTree);

        window.requestAnimationFrame(() => {
            this.rootNode = patch(this.rootNode, patches);
            this.tree = newTree;
        });
    }

    getTrackRenderData(data={}) {
        let defaults = {
            "height": this.waveHeight,
            "resolution": this.samplesPerPixel,
            "sampleRate": this.sampleRate,
            "controls": this.controls,
            "isActive": false,
            "timeSelection": this.getTimeSelection(),
            "playlistLength": this.duration,
            "playbackSeconds": this.playbackSeconds,
            "colors": this.colors
        };

        return _defaults(data, defaults);
    }

    render() {
        var activeTrack = this.getActiveTrack();

        let trackElements = this.tracks.map((track) => {
            return track.render(this.getTrackRenderData({
                "isActive": (activeTrack === track) ? true : false,
                "masterGain": this.shouldTrackPlay(track) ? 1 : 0,
                "soloed": this.soloedTracks.indexOf(track) > -1,
                "muted": this.mutedTracks.indexOf(track) > -1
            }));
        });

        return h("div.playlist", {
            "attributes": {
                "style": "overflow: hidden; position: relative;"
            }}, [
            h("div.playlist-tracks", {
                "attributes": {
                    "style": "overflow: auto;"
                },
                "ev-scroll": _throttle((e) => {
                    this.scrollLeft = pixelsToSeconds(e.target.scrollLeft, this.samplesPerPixel, this.sampleRate);
                }, 200),
                "hook": new ScrollHook(this, this.samplesPerPixel, this.sampleRate)
            }, trackElements)
        ]);
    }
}<|endoftext|>


<commit_before>import { buildRequestBody, parseJSON, resetProps } from '../utils/helpers'

class Credentials {
  constructor(client_id, access_token, expires) {
    this.client_id = client_id
    this.access_token = access_token
    this.expires = expires
  }

  toObject() {
    return {
      client_id: this.client_id,
      access_token: this.access_token,
      expires: this.expires
    }
  }
}

const createAuthRequest = config => {
  if (!config.client_id) {
    throw new Error('You must have a client_id set')
  }

  if (!config.host) {
    throw new Error('You have not specified an API host')
  }

  const body = {
    grant_type: config.client_secret ? 'client_credentials' : 'implicit',
    client_id: config.client_id
  }

  if (config.client_secret) {
    body.client_secret = config.client_secret
  }

  return new Promise((resolve, reject) => {
    config.auth.fetch
      .bind()(`${config.protocol}://${config.host}/${config.auth.uri}`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/x-www-form-urlencoded',
          'X-MOLTIN-SDK-LANGUAGE': config.sdk.language,
          'X-MOLTIN-SDK-VERSION': config.sdk.version
        },
        body: Object.keys(body)
          .map(k => `${encodeURIComponent(k)}=${encodeURIComponent(body[k])}`)
          .join('&')
      })
      .then(parseJSON)
      .then(response => {
        if (response.ok) {
          resolve(response.json)
        }

        reject(response.json)
      })
      .catch(error => reject(error))
  })
}

class RequestFactory {
  constructor(config) {
    this.config = config
    this.storage = config.storage
  }

  authenticate() {
    const { config, storage } = this

    const promise = config.custom_authenticator
      ? config.custom_authenticator()
      : createAuthRequest(config)

    promise
      .then(response => {
        const credentials = new Credentials(
          config.client_id,
          response.access_token,
          response.expires
        )
        storage.set('moltinCredentials', JSON.stringify(credentials))
      })
      .catch(() => {})

    return promise
  }

  send(uri, method, body = undefined, token = undefined, instance) {
    const { config, storage } = this

    const promise = new Promise((resolve, reject) => {
      const credentials = JSON.parse(storage.get('moltinCredentials'))
      const req = ({ access_token }) => {
        const headers = {
          Authorization: `Bearer: ${access_token}`,
          'Content-Type': 'application/json',
          'X-MOLTIN-SDK-LANGUAGE': config.sdk.language,
          'X-MOLTIN-SDK-VERSION': config.sdk.version
        }

        headers['X-MOLTIN-APPLICATION'] = config.application
          ? config.application
          : 'epcc sdk'

        if (config.currency) {
          headers['X-MOLTIN-CURRENCY'] = config.currency
        }

        if (config.language) {
          headers['X-MOLTIN-LANGUAGE'] = config.language
        }

        if (token) {
          headers['X-MOLTIN-CUSTOMER-TOKEN'] = token
        }

        const version = (instance && instance.version) || config.version

        fetch(`${config.protocol}://${config.host}/${version}/${uri}`, {
          method: method.toUpperCase(),
          headers,
          body: buildRequestBody(body)
        })
          .then(parseJSON)
          .then(response => {
            if (response.ok) {
              resolve(response.json)
            }

            reject(response.json)
          })
          .catch(error => reject(error))
      }

      if (
        !credentials ||
        !credentials.access_token ||
        credentials.client_id !== config.client_id ||
        Math.floor(Date.now() / 1000) >= credentials.expires
      ) {
        return this.authenticate()
          .then(() => req(JSON.parse(storage.get('moltinCredentials'))))
          .catch(error => reject(error))
      }
      return req(credentials)
    })

    if (instance) resetProps(instance)

    return promise
  }
}

export default RequestFactory
<commit_msg>refactor: support caching refresh_token in localStorage (#363)<commit_after>import { buildRequestBody, parseJSON, resetProps } from '../utils/helpers'

const createAuthRequest = config => {
  if (!config.client_id) {
    throw new Error('You must have a client_id set')
  }

  if (!config.host) {
    throw new Error('You have not specified an API host')
  }

  const body = {
    grant_type: config.client_secret ? 'client_credentials' : 'implicit',
    client_id: config.client_id
  }

  if (config.client_secret) {
    body.client_secret = config.client_secret
  }

  return new Promise((resolve, reject) => {
    config.auth.fetch
      .bind()(`${config.protocol}://${config.host}/${config.auth.uri}`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/x-www-form-urlencoded',
          'X-MOLTIN-SDK-LANGUAGE': config.sdk.language,
          'X-MOLTIN-SDK-VERSION': config.sdk.version
        },
        body: Object.keys(body)
          .map(k => `${encodeURIComponent(k)}=${encodeURIComponent(body[k])}`)
          .join('&')
      })
      .then(parseJSON)
      .then(response => {
        if (response.ok) {
          resolve(response.json)
        }

        reject(response.json)
      })
      .catch(error => reject(error))
  })
}

class RequestFactory {
  constructor(config) {
    this.config = config
    this.storage = config.storage
  }

  authenticate() {
    const { config, storage } = this

    const promise = config.custom_authenticator
      ? config.custom_authenticator()
      : createAuthRequest(config)

    promise
      .then(({ access_token, refresh_token, expires }) => {
        const credentials = {
          client_id: config.client_id,
          access_token,
          expires,
          ...(refresh_token && { refresh_token })
        }

        storage.set('moltinCredentials', JSON.stringify(credentials))
      })
      .catch(() => {})

    return promise
  }

  send(uri, method, body = undefined, token = undefined, instance) {
    const { config, storage } = this

    const promise = new Promise((resolve, reject) => {
      const credentials = JSON.parse(storage.get('moltinCredentials'))
      const req = ({ access_token }) => {
        const headers = {
          Authorization: `Bearer: ${access_token}`,
          'Content-Type': 'application/json',
          'X-MOLTIN-SDK-LANGUAGE': config.sdk.language,
          'X-MOLTIN-SDK-VERSION': config.sdk.version
        }

        headers['X-MOLTIN-APPLICATION'] = config.application
          ? config.application
          : 'epcc sdk'

        if (config.currency) {
          headers['X-MOLTIN-CURRENCY'] = config.currency
        }

        if (config.language) {
          headers['X-MOLTIN-LANGUAGE'] = config.language
        }

        if (token) {
          headers['X-MOLTIN-CUSTOMER-TOKEN'] = token
        }

        const version = (instance && instance.version) || config.version

        fetch(`${config.protocol}://${config.host}/${version}/${uri}`, {
          method: method.toUpperCase(),
          headers,
          body: buildRequestBody(body)
        })
          .then(parseJSON)
          .then(response => {
            if (response.ok) {
              resolve(response.json)
            }

            reject(response.json)
          })
          .catch(error => reject(error))
      }

      if (
        !credentials ||
        !credentials.access_token ||
        credentials.client_id !== config.client_id ||
        Math.floor(Date.now() / 1000) >= credentials.expires
      ) {
        return this.authenticate()
          .then(() => req(JSON.parse(storage.get('moltinCredentials'))))
          .catch(error => reject(error))
      }
      return req(credentials)
    })

    if (instance) resetProps(instance)

    return promise
  }
}

export default RequestFactory
<|endoftext|>