<commit_before>function APS( server, events, options ){
	this.option = {
		'poll': 25000,
		debug: false,
		session: true,
		connectionArgs: {},
		server: server,
		transport: ["wb", "lp"],
		//transport: "lp",
		secure: false,
		eventPush: false
	}
	this.identifier = "APS";
	this.version = '1.0b5';
	this.state = 0;
	this._events = {};
	this.chl = 0;
	this.user = {};
	this.pipes = {};
	this.channels = {};
	this.eQueue = {};

	//Add Events
	if(!!events)
		this.on(events);

	//Update options
	if(!!options){
		for(var opt in options){
			this.option[opt] = options[opt];
		}
	}

	//IE9 crap - log function fix
	if(navigator.appName == "Microsoft Internet Explorer"){
		if(typeof window.console == "undefined"){
			this.log = function(){};
		}else{
			this.log = function(){
				if(this.option.debug == false) return;

				var args =  Array.prototype.slice.call(arguments);
				args.unshift("["+this.identifier+"]");

				window.console.log(args.join().replace(",",""));
			}

		}
	}

	function TransportError(e){
		this.trigger("dead", [e]);
		this.transport.close();
	}

	var cb = {
		'onmessage': this.onMessage.bind(this),
		'onerror': TransportError.bind(this)
	}

	this.connect = function(args){
		if(this.state == 1)
			return this.log("Already Connected!");

		var cmd = "CONNECT";
		args = this.option.connectionArgs = args || this.option.connectionArgs;

		//Handle sessions
		if(this.option.session == true){
			var restore = this.session.restore();
			if(typeof restore == "object"){
				args = restore;
				//Change initial command CONNECT by RESTORE
				cmd = "RESTORE";
				//Apply frequency to the server
				server = this.session.freq.value + "." + server;
			}else{
				//Fresh Connect
				if(this.trigger("connect") == false)
					return false;
			}

			//increase frequency
			this.session.freq.change(parseInt(this.session.freq.value) + 1);

		}else{
			//Fresh Connect
			if(this.trigger("connect") == false)
				return false;
		}

		//Handle transport
		if(!!this.transport){
			if(this.transport.state == 0){
				this.transport = new APS.transport(server, cb, this);
			}else{
				//Use current active transport

			}
		}else{
			this.transport = new APS.transport(server, cb, this);
		}

		//Send seleced command arguments
		this.sendCmd(cmd, args);

		return this;
	}

	function getTransport() {
		if('XMLHttpRequest' in window) return XMLHttpRequest;
		if('ActiveXObject' in window) {
			var names = [
				"Msxml2.XMLHTTP.6.0",
				"Msxml2.XMLHTTP.3.0",
				"Msxml2.XMLHTTP",
				"Microsoft.XMLHTTP"
			];
			for(var i in names){
				try{ return ActiveXObject(names[i]); }
				catch(e){}
			}
		}
		return false;
	}

	var transport = getTransport();

	this.request = function(addr, data, callback){
		request = new transport();
		request.onreadystatechange = function(){
			if(this.readyState == 4)
				callback(this.responseText);
		};
		request.open('POST', addr, true);
		request.setRequestHeader("Content-type", "application/x-www-form-urlencoded")
		request.send(data);
	}

	this.session._client = this;
	return this;
}

APS.prototype.reconnect = function(){
	if(this.state > 0) return this.log("Client already connected!");
	//Clear channels stack
	this.channels = {};
	this.connect();
}

APS.prototype.trigger = function(ev, args){
	ev = ev.toLowerCase();

	if(!(args instanceof Array)) args = [args];

	//GLobal
	if("_client" in this){
		for(var i in this._client._events[ev]){
			if(this._client._events[ev].hasOwnProperty(i)){
				this.log("{{{ " + ev + " }}} on client ", this._client);
				if(this._client._events[ev][i].apply(this, args) === false)
					return false;
			}
		}
	}

	//Local
	for(var i in this._events[ev]){
		if(this._events[ev].hasOwnProperty(i)){
			if(!this._client){
				this.log("{{{ " + ev + " }}} on client ", this);
			}else{
				this.log("{{{ " + ev + " }}} ", this);
			}
			if(this._events[ev][i].apply(this, args) === false)
				return false;
		}
	}

	return true;
}

APS.prototype.on = function(ev, fn){
	var Events = [];

	if(typeof ev == 'string' && typeof fn == 'function'){
		Events[ev] = fn;
	}else if(typeof ev == "object"){
		Events = ev;
	}else{
		return this;
	}

	for(var e in Events){
		var fn = Events[e];
		e = e.toLowerCase();
		if(!this._events[e])
			this._events[e] = [];
		this._events[e].push(fn);
	}

	return this;
}

APS.prototype.getPipe = function(user){
	if(typeof user == 'string'){
		return this.pipes[user];
	} else {
		return this.pipes[user.pubid];
	}
}

APS.prototype.send = function(pipe, $event, data, sync, callback){
	this.sendCmd("Event", {
		event: $event,
		data: data,
		sync: sync
	}, pipe, callback);
}

APS.prototype.sendCmd = function(cmd, args, pipe, callback){
	var specialCmd = {CONNECT: 0, RESTORE:0};
	if(this.state == 1 || cmd in specialCmd){

		var tmp = {
			'cmd': cmd,
			'chl': this.chl
		}

		if(args) tmp.params = args;
		if(pipe) tmp.params.pipe = typeof pipe == 'string' ? pipe : pipe.pubid;
		if(this.session.id) tmp.sessid = this.session.id;

		this.log('<<<< ', cmd.toUpperCase() , " >>>> ", tmp);

		if(typeof callback != "function")	callback = function(){};

		var data = [];
		try {
			data = JSON.stringify([tmp]);
		}catch(e){
			this.log(e);
			this.log(data);
		}

		//Send command
		if(this.transport.send(data, callback, tmp) != "pushed"){
			this.chl++;
			this.session.saveChl();
		}

	} else {
		this.on('ready', this.sendCmd.bind(this, cmd, args));
	}

	return this;
}

APS.prototype.poll = function(){
	if(this.transport.id == 0){
		clearTimeout(this.poller);
		this.poller = setTimeout(this.check.bind(this), this.option.poll);
	}
}

APS.prototype.check = function(force){
	if(this.transport.id == 0 || !!force){
		this.sendCmd('CHECK');
		this.poll();
	}
}

APS.prototype.quit = function(){
	this.sendCmd('QUIT');
	this.transport.close();
	this.trigger("dead");
	//Clear session on 'quit'
	this.session.destroy();
	this.state = 0;
}

APS.prototype.sub = function(channel, Events, callback){
	//Handle the events
	if(typeof Events == "object"){
		if(typeof channel == "object"){
			for(var chan in channel){
				this.onChannel(channel[chan], Events);
			}
		}else{
			this.onChannel(channel, Events);
		}
	}

	//Handle callback
	if(typeof callback == "function"){
		if(typeof channel == "object"){
			for(var chan in channel){
				this.onChannel(channel[chan], "joined", callback);
			}
		}else{
			this.onChannel(channel, "joined", callback);
		}
	}

	//Join Channel
	if(this.state == 0){
		this.on("ready", this.sub.bind(this, channel));
		this.connect({user: this.user});

	}else{
		//Logic to only send the JOIN request to only non-existing channels in the client object
		if(typeof channel == "string"){
			//Single Channel
			if(typeof this.channels[channel] != "object"){
				this.sendCmd('JOIN', {'channels': channel});
			}
		}else{
			//Multi Channel
			var toJoin = [];
			for(var chan in channel){
				if(typeof this.channels[chan] != "object")
					toJoin.push(chan);
			}

			if(toJoin.length > 0)
				this.sendCmd('JOIN', {'channels': toJoin});

		}
	}

	return this;
}

APS.prototype.pub = function(channel, data, sync, callback){
	var pipe = this.getChannel(channel);
	if(!pipe && channel.length == 32) pipe = this.getPipe(channel);

	if(pipe){
		var $event = typeof data == "string" ? "message" : "data";
		pipe.send($event, data, sync, callback);
	}else{
		this.log("NO Channel " + channel);
	}
};

APS.prototype.getChannel = function(channel){
	channel = channel.toLowerCase();
	if(channel in this.channels){
		return this.channels[channel];
	}

	return false;
}

APS.prototype.onChannel = function(channel, Events, fn){
	channel = channel.toLowerCase();

	if(channel in this.channels){
		this.channels[channel].on(Events, fn);
		return true;
	}

	if(typeof Events == "object"){
		//add events to queue
		if(typeof this.eQueue[channel] != "object")
			this.eQueue[channel] = [];

		//this.eQueue[channel].push(Events);
		for(var $event in Events){
			var fn = Events[$event];

			this.eQueue[channel].push([$event, fn]);

			this.log("Adding ["+channel+"] event '"+$event+"' to queue");
		}
	}else{
		var xnew = Object();
		xnew[Events] = fn;
		this.onChannel(channel,xnew);
	}
}

APS.prototype.unSub = function(channel){
	if(channel == "") return;
	this.getChannel(channel).leave();
}

//Debug Function for Browsers console
if(navigator.appName != "Microsoft Internet Explorer"){
	APS.prototype.log = function(){
		if(!this.option.debug) return;

		var args =  Array.prototype.slice.call(arguments);
		args.unshift("["+this.identifier+"]");

		window.console.log.apply(console, args);
	};

}
<commit_msg>Finished pathcing #4<commit_after>function APS( server, events, options ){
	this.option = {
		'poll': 25000,
		debug: false,
		session: true,
		connectionArgs: {},
		server: server,
		transport: ["wb", "lp"],
		//transport: "lp",
		secure: false,
		eventPush: false
	}
	this.identifier = "APS";
	this.version = '1.0b5';
	this.state = 0;
	this._events = {};
	this.chl = 0;
	this.user = {};
	this.pipes = {};
	this.channels = {};
	this.eQueue = {};

	//Add Events
	if(!!events)
		this.on(events);

	//Update options
	if(!!options){
		for(var opt in options){
			this.option[opt] = options[opt];
		}
	}

	//IE9 crap - log function fix
	if(navigator.appName == "Microsoft Internet Explorer"){
		if(typeof window.console == "undefined"){
			this.log = function(){};
		}else{
			this.log = function(){
				if(this.option.debug == false) return;

				var args =  Array.prototype.slice.call(arguments);
				args.unshift("["+this.identifier+"]");

				window.console.log(args.join().replace(",",""));
			}

		}
	}

	function TransportError(e){
		this.trigger("dead", [e]);
		this.transport.close();
	}

	var cb = {
		'onmessage': this.onMessage.bind(this),
		'onerror': TransportError.bind(this)
	}

	this.connect = function(args){
		if(this.state == 1)
			return this.log("Already Connected!");

		var cmd = "CONNECT";
		args = this.option.connectionArgs = args || this.option.connectionArgs;

		//Handle sessions
		if(this.option.session == true){
			var restore = this.session.restore();
			if(typeof restore == "object"){
				args = restore;
				//Change initial command CONNECT by RESTORE
				cmd = "RESTORE";
				//Apply frequency to the server
				server = this.session.freq.value + "." + server;
			}else{
				//Fresh Connect
				if(this.trigger("connect") == false)
					return false;
			}

			//increase frequency
			this.session.freq.change(parseInt(this.session.freq.value) + 1);

		}else{
			//Fresh Connect
			if(this.trigger("connect") == false)
				return false;
		}

		//Handle transport
		if(!!this.transport){
			if(this.transport.state == 0){
				this.transport = new APS.transport(server, cb, this);
			}else{
				//Use current active transport

			}
		}else{
			this.transport = new APS.transport(server, cb, this);
		}

		//Send seleced command arguments
		this.sendCmd(cmd, args);

		return this;
	}

	function getTransport() {
		if('XMLHttpRequest' in window) return XMLHttpRequest;
		if('ActiveXObject' in window) {
			var names = [
				"Msxml2.XMLHTTP.6.0",
				"Msxml2.XMLHTTP.3.0",
				"Msxml2.XMLHTTP",
				"Microsoft.XMLHTTP"
			];
			for(var i in names){
				try{ return ActiveXObject(names[i]); }
				catch(e){}
			}
		}
		return false;
	}

	var transport = getTransport();

	this.request = function(addr, data, callback){
		request = new transport();
		request.onreadystatechange = function(){
			if(this.readyState == 4)
				callback(this.responseText);
		};
		request.open('POST', addr, true);
		request.setRequestHeader("Content-type", "application/x-www-form-urlencoded")
		request.send(data);
	}

	this.session._client = this;
	return this;
}

APS.prototype.reconnect = function(){
	if(this.state > 0) return this.log("Client already connected!");
	//Clear channels stack
	this.channels = {};
	this.connect();
}

APS.prototype.trigger = function(ev, args){
	ev = ev.toLowerCase();

	if(!(args instanceof Array)) args = [args];

	//GLobal
	if("_client" in this){
		for(var i in this._client._events[ev]){
			if(this._client._events[ev].hasOwnProperty(i)){
				this.log("{{{ " + ev + " }}} on client ", this._client);
				if(this._client._events[ev][i].apply(this, args) === false)
					return false;
			}
		}
	}

	//Local
	for(var i in this._events[ev]){
		if(this._events[ev].hasOwnProperty(i)){
			if(!this._client){
				this.log("{{{ " + ev + " }}} on client ", this);
			}else{
				this.log("{{{ " + ev + " }}} ", this);
			}
			if(this._events[ev][i].apply(this, args) === false)
				return false;
		}
	}

	return true;
}

APS.prototype.on = function(ev, fn){
	var Events = [];

	if(typeof ev == 'string' && typeof fn == 'function'){
		Events[ev] = fn;
	}else if(typeof ev == "object"){
		Events = ev;
	}else{
		return this;
	}

	for(var e in Events){
		var fn = Events[e];
		e = e.toLowerCase();
		if(!this._events[e])
			this._events[e] = [];
		this._events[e].push(fn);
	}

	return this;
}

APS.prototype.getPipe = function(user){
	if(typeof user == 'string'){
		return this.pipes[user];
	} else {
		return this.pipes[user.pubid];
	}
}

APS.prototype.send = function(pipe, $event, data, sync, callback){
	this.sendCmd("Event", {
		event: $event,
		data: data,
		sync: sync
	}, pipe, callback);
}

APS.prototype.sendCmd = function(cmd, args, pipe, callback){
	var specialCmd = {CONNECT: 0, RESTORE:0};
	if(this.state == 1 || cmd in specialCmd){

		var tmp = {
			'cmd': cmd,
			'chl': this.chl
		}

		if(args) tmp.params = args;
		if(pipe) tmp.params.pipe = typeof pipe == 'string' ? pipe : pipe.pubid;
		if(this.session.id) tmp.sessid = this.session.id;

		this.log('<<<< ', cmd.toUpperCase() , " >>>> ", tmp);

		if(typeof callback != "function")	callback = function(){};

		var data = [];
		try {
			data = JSON.stringify([tmp]);
		}catch(e){
			this.log(e);
			this.log(data);
		}

		//Send command
		if(this.transport.send(data, callback, tmp) != "pushed"){
			this.chl++;
			this.session.saveChl();
		}

	} else {
		this.on('ready', this.sendCmd.bind(this, cmd, args));
	}

	return this;
}

APS.prototype.poll = function(){
	if(this.transport.id == 0){
		clearTimeout(this.poller);
		this.poller = setTimeout(this.check.bind(this), this.option.poll);
	}
}

APS.prototype.check = function(force){
	if(this.transport.id == 0 || !!force){
		this.sendCmd('CHECK');
		this.poll();
	}
}

APS.prototype.quit = function(){
	this.sendCmd('QUIT');
	this.transport.close();
	this.trigger("dead");
	//Clear session on 'quit'
	this.session.destroy();
	this.state = 0;
}

APS.prototype.sub = function(channel, Events, callback){
	//Handle the events
	if(typeof Events == "object"){
		if(typeof channel == "object"){
			for(var chan in channel){
				this.onChannel(channel[chan], Events);
			}
		}else{
			this.onChannel(channel, Events);
		}
	}

	//Handle callback
	if(typeof callback == "function"){
		if(typeof channel == "object"){
			for(var chan in channel){
				this.onChannel(channel[chan], "joined", callback);
			}
		}else{
			this.onChannel(channel, "joined", callback);
		}
	}

	//Join Channel
	if(this.state == 0){
		this.on("ready", this.sub.bind(this, channel));
		this.connect({user: this.user});

	}else{
		//Logic to only send the JOIN request to only non-existing channels in the client object
		if(typeof channel == "string"){
			//Single Channel
			if(typeof this.channels[channel] != "object"){
				this.sendCmd('JOIN', {'channels': channel});
			}
		}else{
			//Multi Channel
			var toJoin = [];
			for(var x in channel){
				if(typeof this.channels[channel[x]] != "object")
					toJoin.push(channel[x]);
			}

			if(toJoin.length > 0)
				this.sendCmd('JOIN', {'channels': toJoin});

		}
	}

	return this;
}

APS.prototype.pub = function(channel, data, sync, callback){
	var pipe = this.getChannel(channel);
	if(!pipe && channel.length == 32) pipe = this.getPipe(channel);

	if(pipe){
		var $event = typeof data == "string" ? "message" : "data";
		pipe.send($event, data, sync, callback);
	}else{
		this.log("NO Channel " + channel);
	}
};

APS.prototype.getChannel = function(channel){
	channel = channel.toLowerCase();
	if(channel in this.channels){
		return this.channels[channel];
	}

	return false;
}

APS.prototype.onChannel = function(channel, Events, fn){
	channel = channel.toLowerCase();

	if(channel in this.channels){
		this.channels[channel].on(Events, fn);
		return true;
	}

	if(typeof Events == "object"){
		//add events to queue
		if(typeof this.eQueue[channel] != "object")
			this.eQueue[channel] = [];

		//this.eQueue[channel].push(Events);
		for(var $event in Events){
			var fn = Events[$event];

			this.eQueue[channel].push([$event, fn]);

			this.log("Adding ["+channel+"] event '"+$event+"' to queue");
		}
	}else{
		var xnew = Object();
		xnew[Events] = fn;
		this.onChannel(channel,xnew);
	}
}

APS.prototype.unSub = function(channel){
	if(channel == "") return;
	this.getChannel(channel).leave();
}

//Debug Function for Browsers console
if(navigator.appName != "Microsoft Internet Explorer"){
	APS.prototype.log = function(){
		if(!this.option.debug) return;

		var args =  Array.prototype.slice.call(arguments);
		args.unshift("["+this.identifier+"]");

		window.console.log.apply(console, args);
	};

}
<|endoftext|>


<commit_before>// Copyright 2002-2012, University of Colorado

/**
 * Represents a trail (path in the graph) from a "root" node down to a descendant node.
 * In a DAG, or with different views, there can be more than one trail up from a node,
 * even to the same root node!
 *
 * This trail also mimics an Array, so trail[0] will be the root, and trail[trail.length-1]
 * will be the end node of the trail.
 *
 * @author Jonathan Olson <olsonsjc@gmail.com>
 */

define( function( require ) {
  "use strict";

  var assert = require( 'ASSERT/assert' )( 'scenery' );
  var assertExtra = require( 'ASSERT/assert' )( 'scenery.extra', false );

  var Transform3 = require( 'DOT/Transform3' );

  var scenery = require( 'SCENERY/scenery' );

  require( 'SCENERY/nodes/Node' );
  // require( 'SCENERY/util/TrailPointer' );

  scenery.Trail = function( nodes ) {
    if ( nodes instanceof Trail ) {
      // copy constructor (takes advantage of already built index information)
      var otherTrail = nodes;

      this.nodes = otherTrail.nodes.slice( 0 );
      this.length = otherTrail.length;
      this.indices = otherTrail.indices.slice( 0 );
      return;
    }

    this.nodes = [];
    this.length = 0;

    // indices[x] stores the index of nodes[x] in nodes[x-1]'s children
    this.indices = [];

    var trail = this;
    if ( nodes ) {
      if ( nodes instanceof scenery.Node ) {
        var node = nodes;

        // add just a single node in
        trail.addDescendant( node );
      } else {
        // process it as an array
        _.each( nodes, function( node ) {
          trail.addDescendant( node );
        } );
      }
    }
  };
  var Trail = scenery.Trail;

  Trail.prototype = {
    constructor: Trail,

    copy: function() {
      return new Trail( this );
    },

    // convenience function to determine whether this trail will render something
    isPainted: function() {
      return this.lastNode().isPainted();
    },

    get: function( index ) {
      if ( index >= 0 ) {
        return this.nodes[index];
      } else {
        // negative index goes from the end of the array
        return this.nodes[this.nodes.length + index];
      }
    },

    slice: function( startIndex, endIndex ) {
      return new Trail( this.nodes.slice( startIndex, endIndex ) );
    },

    subtrailTo: function( node, excludeNode ) {
      return this.slice( 0, _.indexOf( this.nodes, node ) + ( excludeNode ? 0 : 1 ) );
    },

    isEmpty: function() {
      return this.nodes.length === 0;
    },

    getTransform: function() {
      // always return a defensive copy of a transform
      var transform = new Transform3();

      // from the root up
      _.each( this.nodes, function( node ) {
        transform.appendTransform( node._transform );
      } );

      return transform;
    },

    addAncestor: function( node, index ) {
      var oldRoot = this.nodes[0];

      this.nodes.unshift( node );
      if ( oldRoot ) {
        this.indices.unshift( index === undefined ? _.indexOf( node._children, oldRoot ) : index );
      }

      // mimic an Array
      this.length++;
      return this;
    },

    removeAncestor: function() {
      this.nodes.shift();
      if ( this.indices.length ) {
        this.indices.shift();
      }

      // mimic an Array
      this.length--;
      return this;
    },

    addDescendant: function( node, index ) {
      var parent = this.lastNode();

      this.nodes.push( node );
      if ( parent ) {
        this.indices.push( index === undefined ? _.indexOf( parent._children, node ) : index );
      }

      // mimic an Array
      this.length++;
      return this;
    },

    removeDescendant: function() {
      this.nodes.pop();
      if ( this.indices.length ) {
        this.indices.pop();
      }

      // mimic an Array
      this.length--;
      return this;
    },

    // refreshes the internal index references (important if any children arrays were modified!)
    reindex: function() {
      for ( var i = 1; i < this.length; i++ ) {
        // only replace indices where they have changed (this was a performance hotspot)
        var currentIndex = this.indices[i-1];
        if ( this.nodes[i-1]._children[currentIndex] !== this.nodes[i] ) {
          this.indices[i-1] = _.indexOf( this.nodes[i-1]._children, this.nodes[i] );
        }
      }
    },

    areIndicesValid: function() {
      for ( var i = 1; i < this.length; i++ ) {
        var currentIndex = this.indices[i-1];
        if ( this.nodes[i-1]._children[currentIndex] !== this.nodes[i] ) {
          return false;
        }
      }
      return true;
    },

    equals: function( other ) {
      if ( this.length !== other.length ) {
        return false;
      }

      for ( var i = 0; i < this.nodes.length; i++ ) {
        if ( this.nodes[i] !== other.nodes[i] ) {
          return false;
        }
      }

      return true;
    },

    // whether this trail contains the complete 'other' trail, but with added descendants afterwards
    isExtensionOf: function( other, allowSameTrail ) {
      assertExtra && assertExtra( this.areIndicesValid(), 'Trail.compare this.areIndicesValid() failed' );
      assertExtra && assertExtra( other.areIndicesValid(), 'Trail.compare other.areIndicesValid() failed' );

      if ( this.length <= other.length - ( allowSameTrail ? 1 : 0 ) ) {
        return false;
      }

      for ( var i = 0; i < other.nodes.length; i++ ) {
        if ( this.nodes[i] !== other.nodes[i] ) {
          return false;
        }
      }

      return true;
    },

    // TODO: phase out in favor of get()
    nodeFromTop: function( offset ) {
      return this.nodes[this.length - 1 - offset];
    },

    lastNode: function() {
      return this.nodeFromTop( 0 );
    },

    rootNode: function() {
      return this.nodes[0];
    },

    // returns the previous graph trail in the order of self-rendering
    previous: function() {
      if ( this.nodes.length <= 1 ) {
        return null;
      }

      var top = this.nodeFromTop( 0 );
      var parent = this.nodeFromTop( 1 );

      var parentIndex = _.indexOf( parent._children, top );
      assert && assert( parentIndex !== -1 );
      var arr = this.nodes.slice( 0, this.nodes.length - 1 );
      if ( parentIndex === 0 ) {
        // we were the first child, so give it the trail to the parent
        return new Trail( arr );
      } else {
        // previous child
        arr.push( parent._children[parentIndex-1] );

        // and find its last terminal
        while( arr[arr.length-1]._children.length !== 0 ) {
          var last = arr[arr.length-1];
          arr.push( last._children[last._children.length-1] );
        }

        return new Trail( arr );
      }
    },

    // like previous(), but keeps moving back until the trail goes to a node with isPainted() === true
    previousPainted: function() {
      var result = this.previous();
      while ( result && !result.isPainted() ) {
        result = result.previous();
      }
      return result;
    },

    // in the order of self-rendering
    next: function() {
      var arr = this.nodes.slice( 0 );

      var top = this.nodeFromTop( 0 );
      if ( top._children.length > 0 ) {
        // if we have children, return the first child
        arr.push( top._children[0] );
        return new Trail( arr );
      } else {
        // walk down and attempt to find the next parent
        var depth = this.nodes.length - 1;

        while ( depth > 0 ) {
          var node = this.nodes[depth];
          var parent = this.nodes[depth-1];

          arr.pop(); // take off the node so we can add the next sibling if it exists

          var index = _.indexOf( parent._children, node );
          if ( index !== parent._children.length - 1 ) {
            // there is another (later) sibling. use that!
            arr.push( parent._children[index+1] );
            return new Trail( arr );
          } else {
            depth--;
          }
        }

        // if we didn't reach a later sibling by now, it doesn't exist
        return null;
      }
    },

    // like next(), but keeps moving back until the trail goes to a node with isPainted() === true
    nextPainted: function() {
      var result = this.next();
      while ( result && !result.isPainted() ) {
        result = result.next();
      }
      return result;
    },

    // calls callback( trail ) for this trail, and each descendant trail
    eachTrailUnder: function( callback ) {
      new scenery.TrailPointer( this, true ).eachTrailBetween( new scenery.TrailPointer( this, false ), callback );
    },

    /*
     * Standard Java-style compare. -1 means this trail is before (under) the other trail, 0 means equal, and 1 means this trail is
     * after (on top of) the other trail.
     * A shorter subtrail will compare as -1.
     *
     * Assumes that the Trails are properly indexed. If not, please reindex them!
     *
     * Comparison is for the rendering order, so an ancestor is 'before' a descendant
     */
    compare: function( other ) {
      assert && assert( !this.isEmpty(), 'cannot compare with an empty trail' );
      assert && assert( !other.isEmpty(), 'cannot compare with an empty trail' );
      assert && assert( this.nodes[0] === other.nodes[0], 'for Trail comparison, trails must have the same root node' );
      assertExtra && assertExtra( this.areIndicesValid(), 'Trail.compare this.areIndicesValid() failed' );
      assertExtra && assertExtra( other.areIndicesValid(), 'Trail.compare other.areIndicesValid() failed' );

      var minNodeIndex = Math.min( this.indices.length, other.indices.length );
      for ( var i = 0; i < minNodeIndex; i++ ) {
        if ( this.indices[i] !== other.indices[i] ) {
          if ( this.indices[i] < other.indices[i] ) {
            return -1;
          } else {
            return 1;
          }
        }
      }

      // we scanned through and no nodes were different (one is a subtrail of the other)
      if ( this.nodes.length < other.nodes.length ) {
        return -1;
      } else if ( this.nodes.length > other.nodes.length ) {
        return 1;
      } else {
        return 0;
      }
    },

    localToGlobalPoint: function( point ) {
      return this.getTransform().transformPosition2( point );
    },

    localToGlobalBounds: function( bounds ) {
      return this.getTransform().transformBounds2( bounds );
    },

    globalToLocalPoint: function( point ) {
      return this.getTransform().inversePosition2( point );
    },

    globalToLocalBounds: function( bounds ) {
      return this.getTransform().inverseBounds2( bounds );
    },

    // concatenates the unique IDs of nodes in the trail, so that we can do id-based lookups
    getUniqueId: function() {
      // TODO: consider caching this if it is ever a bottleneck. it seems like it might be called in layer-refresh inner loops
      return _.map( this.nodes, function( node ) { return node.getId(); } ).join( '-' );
    },

    toString: function() {
      this.reindex();
      if ( !this.length ) {
        return 'Empty Trail';
      }
      return '[Trail ' + this.indices.join( '.' ) + ' ' + this.getUniqueId() + ']';
    }
  };

  // like eachTrailBetween, but only fires for painted trails
  Trail.eachPaintedTrailbetween = function( a, b, callback, excludeEndTrails, scene ) {
    Trail.eachTrailBetween( a, b, function( trail ) {
      if ( trail && trail.isPainted() ) {
        callback( trail );
      }
    }, excludeEndTrails, scene );
  };

  // global way of iterating across trails
  Trail.eachTrailBetween = function( a, b, callback, excludeEndTrails, scene ) {
    var aPointer = a ? new scenery.TrailPointer( a.copy(), true ) : new scenery.TrailPointer( new scenery.Trail( scene ), true );
    var bPointer = b ? new scenery.TrailPointer( b.copy(), true ) : new scenery.TrailPointer( new scenery.Trail( scene ), false );

    // if we are excluding endpoints, just bump the pointers towards each other by one step
    if ( excludeEndTrails ) {
      aPointer.nestedForwards();
      bPointer.nestedBackwards();

      // they were adjacent, so no callbacks will be executed
      if ( aPointer.compareNested( bPointer ) === 1 ) {
        return;
      }
    }

    aPointer.depthFirstUntil( bPointer, function( pointer ) {
      if ( pointer.isBefore ) {
        callback( pointer.trail );
      }
    }, false );
  };

  return Trail;
} );

<commit_msg>Improving assertion failure messages<commit_after>// Copyright 2002-2012, University of Colorado

/**
 * Represents a trail (path in the graph) from a "root" node down to a descendant node.
 * In a DAG, or with different views, there can be more than one trail up from a node,
 * even to the same root node!
 *
 * This trail also mimics an Array, so trail[0] will be the root, and trail[trail.length-1]
 * will be the end node of the trail.
 *
 * @author Jonathan Olson <olsonsjc@gmail.com>
 */

define( function( require ) {
  "use strict";

  var assert = require( 'ASSERT/assert' )( 'scenery' );
  var assertExtra = require( 'ASSERT/assert' )( 'scenery.extra', false );

  var Transform3 = require( 'DOT/Transform3' );

  var scenery = require( 'SCENERY/scenery' );

  require( 'SCENERY/nodes/Node' );
  // require( 'SCENERY/util/TrailPointer' );

  scenery.Trail = function( nodes ) {
    if ( nodes instanceof Trail ) {
      // copy constructor (takes advantage of already built index information)
      var otherTrail = nodes;

      this.nodes = otherTrail.nodes.slice( 0 );
      this.length = otherTrail.length;
      this.indices = otherTrail.indices.slice( 0 );
      return;
    }

    this.nodes = [];
    this.length = 0;

    // indices[x] stores the index of nodes[x] in nodes[x-1]'s children
    this.indices = [];

    var trail = this;
    if ( nodes ) {
      if ( nodes instanceof scenery.Node ) {
        var node = nodes;

        // add just a single node in
        trail.addDescendant( node );
      } else {
        // process it as an array
        _.each( nodes, function( node ) {
          trail.addDescendant( node );
        } );
      }
    }
  };
  var Trail = scenery.Trail;

  Trail.prototype = {
    constructor: Trail,

    copy: function() {
      return new Trail( this );
    },

    // convenience function to determine whether this trail will render something
    isPainted: function() {
      return this.lastNode().isPainted();
    },

    get: function( index ) {
      if ( index >= 0 ) {
        return this.nodes[index];
      } else {
        // negative index goes from the end of the array
        return this.nodes[this.nodes.length + index];
      }
    },

    slice: function( startIndex, endIndex ) {
      return new Trail( this.nodes.slice( startIndex, endIndex ) );
    },

    subtrailTo: function( node, excludeNode ) {
      return this.slice( 0, _.indexOf( this.nodes, node ) + ( excludeNode ? 0 : 1 ) );
    },

    isEmpty: function() {
      return this.nodes.length === 0;
    },

    getTransform: function() {
      // always return a defensive copy of a transform
      var transform = new Transform3();

      // from the root up
      _.each( this.nodes, function( node ) {
        transform.appendTransform( node._transform );
      } );

      return transform;
    },

    addAncestor: function( node, index ) {
      var oldRoot = this.nodes[0];

      this.nodes.unshift( node );
      if ( oldRoot ) {
        this.indices.unshift( index === undefined ? _.indexOf( node._children, oldRoot ) : index );
      }

      // mimic an Array
      this.length++;
      return this;
    },

    removeAncestor: function() {
      this.nodes.shift();
      if ( this.indices.length ) {
        this.indices.shift();
      }

      // mimic an Array
      this.length--;
      return this;
    },

    addDescendant: function( node, index ) {
      var parent = this.lastNode();

      this.nodes.push( node );
      if ( parent ) {
        this.indices.push( index === undefined ? _.indexOf( parent._children, node ) : index );
      }

      // mimic an Array
      this.length++;
      return this;
    },

    removeDescendant: function() {
      this.nodes.pop();
      if ( this.indices.length ) {
        this.indices.pop();
      }

      // mimic an Array
      this.length--;
      return this;
    },

    // refreshes the internal index references (important if any children arrays were modified!)
    reindex: function() {
      for ( var i = 1; i < this.length; i++ ) {
        // only replace indices where they have changed (this was a performance hotspot)
        var currentIndex = this.indices[i-1];
        if ( this.nodes[i-1]._children[currentIndex] !== this.nodes[i] ) {
          this.indices[i-1] = _.indexOf( this.nodes[i-1]._children, this.nodes[i] );
        }
      }
    },

    areIndicesValid: function() {
      for ( var i = 1; i < this.length; i++ ) {
        var currentIndex = this.indices[i-1];
        if ( this.nodes[i-1]._children[currentIndex] !== this.nodes[i] ) {
          return false;
        }
      }
      return true;
    },

    equals: function( other ) {
      if ( this.length !== other.length ) {
        return false;
      }

      for ( var i = 0; i < this.nodes.length; i++ ) {
        if ( this.nodes[i] !== other.nodes[i] ) {
          return false;
        }
      }

      return true;
    },

    // whether this trail contains the complete 'other' trail, but with added descendants afterwards
    isExtensionOf: function( other, allowSameTrail ) {
      assertExtra && assertExtra( this.areIndicesValid(), 'Trail.compare this.areIndicesValid() failed' );
      assertExtra && assertExtra( other.areIndicesValid(), 'Trail.compare other.areIndicesValid() failed' );

      if ( this.length <= other.length - ( allowSameTrail ? 1 : 0 ) ) {
        return false;
      }

      for ( var i = 0; i < other.nodes.length; i++ ) {
        if ( this.nodes[i] !== other.nodes[i] ) {
          return false;
        }
      }

      return true;
    },

    // TODO: phase out in favor of get()
    nodeFromTop: function( offset ) {
      return this.nodes[this.length - 1 - offset];
    },

    lastNode: function() {
      return this.nodeFromTop( 0 );
    },

    rootNode: function() {
      return this.nodes[0];
    },

    // returns the previous graph trail in the order of self-rendering
    previous: function() {
      if ( this.nodes.length <= 1 ) {
        return null;
      }

      var top = this.nodeFromTop( 0 );
      var parent = this.nodeFromTop( 1 );

      var parentIndex = _.indexOf( parent._children, top );
      assert && assert( parentIndex !== -1 );
      var arr = this.nodes.slice( 0, this.nodes.length - 1 );
      if ( parentIndex === 0 ) {
        // we were the first child, so give it the trail to the parent
        return new Trail( arr );
      } else {
        // previous child
        arr.push( parent._children[parentIndex-1] );

        // and find its last terminal
        while( arr[arr.length-1]._children.length !== 0 ) {
          var last = arr[arr.length-1];
          arr.push( last._children[last._children.length-1] );
        }

        return new Trail( arr );
      }
    },

    // like previous(), but keeps moving back until the trail goes to a node with isPainted() === true
    previousPainted: function() {
      var result = this.previous();
      while ( result && !result.isPainted() ) {
        result = result.previous();
      }
      return result;
    },

    // in the order of self-rendering
    next: function() {
      var arr = this.nodes.slice( 0 );

      var top = this.nodeFromTop( 0 );
      if ( top._children.length > 0 ) {
        // if we have children, return the first child
        arr.push( top._children[0] );
        return new Trail( arr );
      } else {
        // walk down and attempt to find the next parent
        var depth = this.nodes.length - 1;

        while ( depth > 0 ) {
          var node = this.nodes[depth];
          var parent = this.nodes[depth-1];

          arr.pop(); // take off the node so we can add the next sibling if it exists

          var index = _.indexOf( parent._children, node );
          if ( index !== parent._children.length - 1 ) {
            // there is another (later) sibling. use that!
            arr.push( parent._children[index+1] );
            return new Trail( arr );
          } else {
            depth--;
          }
        }

        // if we didn't reach a later sibling by now, it doesn't exist
        return null;
      }
    },

    // like next(), but keeps moving back until the trail goes to a node with isPainted() === true
    nextPainted: function() {
      var result = this.next();
      while ( result && !result.isPainted() ) {
        result = result.next();
      }
      return result;
    },

    // calls callback( trail ) for this trail, and each descendant trail
    eachTrailUnder: function( callback ) {
      new scenery.TrailPointer( this, true ).eachTrailBetween( new scenery.TrailPointer( this, false ), callback );
    },

    /*
     * Standard Java-style compare. -1 means this trail is before (under) the other trail, 0 means equal, and 1 means this trail is
     * after (on top of) the other trail.
     * A shorter subtrail will compare as -1.
     *
     * Assumes that the Trails are properly indexed. If not, please reindex them!
     *
     * Comparison is for the rendering order, so an ancestor is 'before' a descendant
     */
    compare: function( other ) {
      assert && assert( !this.isEmpty(), 'cannot compare with an empty trail' );
      assert && assert( !other.isEmpty(), 'cannot compare with an empty trail' );
      assert && assert( this.nodes[0] === other.nodes[0], 'for Trail comparison, trails must have the same root node' );
      assertExtra && assertExtra( this.areIndicesValid(), 'Trail.compare this.areIndicesValid() failed on ' + this.toString() );
      assertExtra && assertExtra( other.areIndicesValid(), 'Trail.compare other.areIndicesValid() failed on ' + other.toString() );

      var minNodeIndex = Math.min( this.indices.length, other.indices.length );
      for ( var i = 0; i < minNodeIndex; i++ ) {
        if ( this.indices[i] !== other.indices[i] ) {
          if ( this.indices[i] < other.indices[i] ) {
            return -1;
          } else {
            return 1;
          }
        }
      }

      // we scanned through and no nodes were different (one is a subtrail of the other)
      if ( this.nodes.length < other.nodes.length ) {
        return -1;
      } else if ( this.nodes.length > other.nodes.length ) {
        return 1;
      } else {
        return 0;
      }
    },

    localToGlobalPoint: function( point ) {
      return this.getTransform().transformPosition2( point );
    },

    localToGlobalBounds: function( bounds ) {
      return this.getTransform().transformBounds2( bounds );
    },

    globalToLocalPoint: function( point ) {
      return this.getTransform().inversePosition2( point );
    },

    globalToLocalBounds: function( bounds ) {
      return this.getTransform().inverseBounds2( bounds );
    },

    // concatenates the unique IDs of nodes in the trail, so that we can do id-based lookups
    getUniqueId: function() {
      // TODO: consider caching this if it is ever a bottleneck. it seems like it might be called in layer-refresh inner loops
      return _.map( this.nodes, function( node ) { return node.getId(); } ).join( '-' );
    },

    toString: function() {
      this.reindex();
      if ( !this.length ) {
        return 'Empty Trail';
      }
      return '[Trail ' + this.indices.join( '.' ) + ' ' + this.getUniqueId() + ']';
    }
  };

  // like eachTrailBetween, but only fires for painted trails
  Trail.eachPaintedTrailbetween = function( a, b, callback, excludeEndTrails, scene ) {
    Trail.eachTrailBetween( a, b, function( trail ) {
      if ( trail && trail.isPainted() ) {
        callback( trail );
      }
    }, excludeEndTrails, scene );
  };

  // global way of iterating across trails
  Trail.eachTrailBetween = function( a, b, callback, excludeEndTrails, scene ) {
    var aPointer = a ? new scenery.TrailPointer( a.copy(), true ) : new scenery.TrailPointer( new scenery.Trail( scene ), true );
    var bPointer = b ? new scenery.TrailPointer( b.copy(), true ) : new scenery.TrailPointer( new scenery.Trail( scene ), false );

    // if we are excluding endpoints, just bump the pointers towards each other by one step
    if ( excludeEndTrails ) {
      aPointer.nestedForwards();
      bPointer.nestedBackwards();

      // they were adjacent, so no callbacks will be executed
      if ( aPointer.compareNested( bPointer ) === 1 ) {
        return;
      }
    }

    aPointer.depthFirstUntil( bPointer, function( pointer ) {
      if ( pointer.isBefore ) {
        callback( pointer.trail );
      }
    }, false );
  };

  return Trail;
} );

<|endoftext|>


<commit_before>(function(W) {
	'use strict';

	var events = [],
		bound,
		current,

		/**
		 * Bind individual rule
		 *
		 * @private
		 * @param {object} conf - breakpoint rules
		 * @param {Array} [conf.args] - callback arguments
		 * @param {function} conf.callback
		 * @param {boolean} [conf.each=false] - execute for each matching breakpoint
		 * @param {boolean} [conf.init=true] - check event on load
		 * @param {int} [conf.max] - maximum breakpoint value
		 * @param {int} [conf.min] - minimum breakpoint value
		 * @param {boolean} [conf.once=false] - only execute the callback once
		 * @param {object} [conf.scope] - callback scope
		 * @param {int} [conf.size] - specific breakpoint value
		 * @param {boolean} [conf.watch=true] - check event on screen resize
		 * @param {string} [conf.namespace] - namespace the event
		 */
		_addRule = function(conf) {
			if (conf.callback) {
				// Only setup watching when enabled
				if (conf.watch !== false) {
					events.push(conf);

					// Only attach event once
					if (! bound) {
						var run = _run.bind(this, false, 0);
						bound = 1;
						events = [conf];

						// Attach resize event
						W._win.addEventListener('resize', run);
					}
				}

				// Evaluate rule immediately if not disabled
				if (conf.init !== false) {
					_run(true, [conf]);
				}
			}
		},

		/**
		 * Check mapped rules for matching conditions
		 *
		 * @private
		 * @param {boolean} [init=false] - initial page load
		 * @param {Array} [rules] - breakpoint rules
		 */
		_run = function(init, rules) {
			var size = W.screen.size();

			// If breakpoint has been hit or resize logic initialized
			if (size && (init || size !== current)) {
				var evts = rules || events,
					i = 0;

				for (; i < evts.length; i++) {
					var evt = evts[i];

					if (_eq(evt, size, init)) {
						var f = init && ! current;

						_exec(evt, {
							dir: f ? 0 : (size > current ? 1 : -1),
							init: f,
							prev: current,
							size: size
						});
					}
				}

				// Cache current value
				current = size;
			}
		},

		/**
		 * Compare event rules against current size
		 *
		 * @private
		 * @param {object} evt
		 * @param {number} size
		 * @param {boolean} init
		 * @returns {boolean}
		 */
		_eq = function(evt, size, init) {
			var sz = evt.size,
				mn = evt.min,
				mx = evt.max,
				ex = evt.each || init;

			// Check match against rules
			return (! sz && ! mn && ! mx) ||
				(sz && sz === size) ||
				(mn && size >= mn && (ex || current < mn) && (! mx || size <= mx)) ||
				(mx && size <= mx && (ex || current > mx) && (! mn || size >= mn));
		},

		/**
		 * Execute matching breakpoint callback
		 *
		 * @private
		 * @param {object} evt
		 * @param {object} data
		 */
		_exec = function(evt, data) {
			W.$exec(evt.callback, {
				args: evt.args ? [data].concat(evt.args) : [data],
				scope: evt.scope
			});

			// Disable future execution if once
			if (evt.once) {
				events = events.filter(function(el) {
					return el !== evt;
				});
			}
		};

	W.screen = {
		/**
		 * Get current breakpoint value
		 *
		 * @returns {number} size
		 */
		size: function() {
			return parseFloat(
				getComputedStyle(W._html, null)
					.getPropertyValue('font-family')
					.replace(/[^0-9\.]+/g, '')
			);
		},

		/**
		 * Map conditional events to breakpoint values
		 *
		 * @param {(Array|object)} rules - breakpoint rules
		 * @param {Array} [rules.args] - callback arguments
		 * @param {function} rules.callback
		 * @param {boolean} [rules.each=false] - execute for each matching breakpoint
		 * @param {boolean} [rules.init=true] - check event on load
		 * @param {int} [rules.max] - maximum breakpoint value
		 * @param {int} [rules.min] - minimum breakpoint value
		 * @param {boolean} [rules.once=false] - only execute the callback once
		 * @param {object} [rules.scope] - callback scope
		 * @param {int} [rules.size] - specific breakpoint value
		 * @param {boolean} [rules.watch=true] - check event on screen resize
		 * @param {string} [rules.namespace] - namespace the event
		 */
		map: function(rules) {
			var sets = W.$toArray(rules),
				i = 0;

			// Delay check to prevent incorrect IE value
			for (; i < sets.length; i++) {
				_addRule(sets[i]);
			}
		},

		/**
		 * Evaluate the current breakpoint
		 */
		run: function() {
			_run(true);
		},

		/**
		 * Reset all bound events
		 */
		reset: function(namespace) {
			if (namespace) {
				events = events.filter(function(el) {
					return el['namespace'] !== namespace;
				});
			} else {
				events = [];
			}
		}
	};
})(Wee);<commit_msg>Update code format<commit_after>(function(W) {
	'use strict';

	var events = [],
		bound,
		current,

		/**
		 * Bind individual rule
		 *
		 * @private
		 * @param {object} conf - breakpoint rules
		 * @param {Array} [conf.args] - callback arguments
		 * @param {function} conf.callback
		 * @param {boolean} [conf.each=false] - execute for each matching breakpoint
		 * @param {boolean} [conf.init=true] - check event on load
		 * @param {int} [conf.max] - maximum breakpoint value
		 * @param {int} [conf.min] - minimum breakpoint value
		 * @param {boolean} [conf.once=false] - only execute the callback once
		 * @param {object} [conf.scope] - callback scope
		 * @param {int} [conf.size] - specific breakpoint value
		 * @param {boolean} [conf.watch=true] - check event on screen resize
		 * @param {string} [conf.namespace] - namespace the event
		 */
		_addRule = function(conf) {
			if (conf.callback) {
				// Only setup watching when enabled
				if (conf.watch !== false) {
					events.push(conf);

					// Only attach event once
					if (! bound) {
						var run = _run.bind(this, false, 0);
						bound = 1;
						events = [conf];

						// Attach resize event
						W._win.addEventListener('resize', run);
					}
				}

				// Evaluate rule immediately if not disabled
				if (conf.init !== false) {
					_run(true, [conf]);
				}
			}
		},

		/**
		 * Check mapped rules for matching conditions
		 *
		 * @private
		 * @param {boolean} [init=false] - initial page load
		 * @param {Array} [rules] - breakpoint rules
		 */
		_run = function(init, rules) {
			var size = W.screen.size();

			// If breakpoint has been hit or resize logic initialized
			if (size && (init || size !== current)) {
				var evts = rules || events,
					i = 0;

				for (; i < evts.length; i++) {
					var evt = evts[i];

					if (_eq(evt, size, init)) {
						var f = init && ! current;

						_exec(evt, {
							dir: f ? 0 : (size > current ? 1 : -1),
							init: f,
							prev: current,
							size: size
						});
					}
				}

				// Cache current value
				current = size;
			}
		},

		/**
		 * Compare event rules against current size
		 *
		 * @private
		 * @param {object} evt
		 * @param {number} size
		 * @param {boolean} init
		 * @returns {boolean}
		 */
		_eq = function(evt, size, init) {
			var sz = evt.size,
				mn = evt.min,
				mx = evt.max,
				ex = evt.each || init;

			// Check match against rules
			return (! sz && ! mn && ! mx) ||
				(sz && sz === size) ||
				(mn && size >= mn && (ex || current < mn) && (! mx || size <= mx)) ||
				(mx && size <= mx && (ex || current > mx) && (! mn || size >= mn));
		},

		/**
		 * Execute matching breakpoint callback
		 *
		 * @private
		 * @param {object} evt
		 * @param {object} data
		 */
		_exec = function(evt, data) {
			W.$exec(evt.callback, {
				args: evt.args ? [data].concat(evt.args) : [data],
				scope: evt.scope
			});

			// Disable future execution if once
			if (evt.once) {
				events = events.filter(function(el) {
					return el !== evt;
				});
			}
		};

	W.screen = {
		/**
		 * Get current breakpoint value
		 *
		 * @returns {number} size
		 */
		size: function() {
			return parseFloat(
				getComputedStyle(W._html, null)
					.getPropertyValue('font-family')
					.replace(/[^0-9\.]+/g, '')
			);
		},

		/**
		 * Map conditional events to breakpoint values
		 *
		 * @param {(Array|object)} rules - breakpoint rules
		 * @param {Array} [rules.args] - callback arguments
		 * @param {function} rules.callback
		 * @param {boolean} [rules.each=false] - execute for each matching breakpoint
		 * @param {boolean} [rules.init=true] - check event on load
		 * @param {int} [rules.max] - maximum breakpoint value
		 * @param {int} [rules.min] - minimum breakpoint value
		 * @param {boolean} [rules.once=false] - only execute the callback once
		 * @param {object} [rules.scope] - callback scope
		 * @param {int} [rules.size] - specific breakpoint value
		 * @param {boolean} [rules.watch=true] - check event on screen resize
		 * @param {string} [rules.namespace] - namespace the event
		 */
		map: function(rules) {
			var sets = W.$toArray(rules),
				i = 0;

			// Delay check to prevent incorrect IE value
			for (; i < sets.length; i++) {
				_addRule(sets[i]);
			}
		},

		/**
		 * Evaluate the current breakpoint
		 */
		run: function() {
			_run(true);
		},

		/**
		 * Reset all bound events
		 */
		reset: function(namespace) {
			events = namespace ?
				events.filter(function(el) {
					return el['namespace'] !== namespace;
				}) :
				[];
		}
	};
})(Wee);<|endoftext|>


<commit_before>function Canvas(id) {
  try {
    this._canvas = document.getElementById(id);
    if(this._canvas.nodeName != 'CANVAS'){
      console.error("Invalide Canvas element Id pass :"+ id);
    }else{
      console.log("setup canvas :"+ id);
    }
  }catch(error){

    console.log(error);

  }

}

function Layer2D(canvas){
Canvas.prototype.addLayer = function (layer) {
  try {
    this._2Dcontext = canvas._canvas.getContext("2d");

    layer._2Dcontext = this._canvas.getContext("2d");

  } catch (error) {
    console.log(error);
  }
};

function Layer(){
   this._2Dcontext = null;
}

Layer2D.prototype.add = function (element) {

Layer.prototype.add = function (element) {
  try {
    if (element.tag == "line") {
      this._2Dcontext.moveTo(element._startPoint.x,element._startPoint.y);
      this._2Dcontext.lineTo(element._endPoint.x,element._endPoint.y);
      this._2Dcontext.stroke();
    }else if (element.tag == "circle") {
      this._2Dcontext.beginPath();
      this._2Dcontext.arc(element._centerPint.x,element._centerPint.y,element._radius, 0, 2 * Math.PI);
      this._2Dcontext.stroke();
    }
    element._2Dcontext = this._2Dcontext;
  } catch (error) {
    console.log(error);
  }

};

function Line(point_start,point_end) {
  this.tag ="line"
  this._2Dcontext = null;
  this._startPoint = point_start
  this._endPoint = point_end

}

function Circle(point_center,radius) {
  this.tag ="circle"
  this._centerPint = point_center
  this._radius = radius
}

function Point(_x,_y) {
  return {x:_x,y:_y}
}
<commit_msg>change<commit_after>function Canvas(id) {
  try {
    this._canvas = document.getElementById(id);
    if(this._canvas.nodeName != 'CANVAS'){
      console.error("Invalide Canvas element Id pass :"+ id);
    }else{
      console.log("setup canvas :"+ id);
    }
  }catch(error){

    console.log(error);

  }

}

Canvas.prototype.addLayer = function (layer) {
  try {

    layer._2Dcontext = this._canvas.getContext("2d");

  } catch (error) {
    console.log(error);
  }
};

function Layer(){
   this._2Dcontext = null;
}

Layer.prototype.add = function (element) {
  try {
    if (element.tag == "line") {
      this._2Dcontext.moveTo(element._startPoint.x,element._startPoint.y);
      this._2Dcontext.lineTo(element._endPoint.x,element._endPoint.y);
      this._2Dcontext.stroke();
    }else if (element.tag == "circle") {
      this._2Dcontext.beginPath();
      this._2Dcontext.arc(element._centerPint.x,element._centerPint.y,element._radius, 0, 2 * Math.PI);
      this._2Dcontext.stroke();
    }
    element._2Dcontext = this._2Dcontext;
  } catch (error) {
    console.log(error);
  }

};

function Line(point_start,point_end) {
  this.tag ="line"
  this._2Dcontext = null;
  this._startPoint = point_start
  this._endPoint = point_end

}

function Circle(point_center,radius) {
  this.tag ="circle"
  this._centerPint = point_center
  this._radius = radius
}

function Point(_x,_y) {
  return {x:_x,y:_y}
}
<|endoftext|>


<commit_before>/**
 * The React application used in `jsapp/js/main.es6` bundle file.
 *
 * TODO: move routes configuration to separate file for clarity.
 */

import $ from 'jquery';
window.jQuery = $;
window.$ = $;
require('jquery-ui/ui/widgets/sortable');

import React from 'react';
import PropTypes from 'prop-types';
import DocumentTitle from 'react-document-title';
import reactMixin from 'react-mixin';
import autoBind from 'react-autobind';
import Reflux from 'reflux';
import {
  IndexRoute,
  IndexRedirect,
  Route,
  hashHistory,
  Router
} from 'react-router';
import moment from 'moment';
import actions from './actions';
import stores from './stores';
import {dataInterface} from './dataInterface';
import bem from './bem';
import ui from './ui';
import mixins from './mixins';
import MainHeader from './components/header';
import Drawer from './components/drawer';
import {
  AddToLibrary,
  FormPage,
  LibraryPage
} from './components/formEditors';
import Reports from './components/reports';
import FormLanding from './components/formLanding';
import FormSummary from './components/formSummary';
import FormSubScreens from './components/formSubScreens';
import FormViewTabs from './components/formViewTabs';
import IntercomHandler from './components/intercomHandler';
import Modal from './components/modal';
import {ChangePassword, AccountSettings} from './components/accountSettings';
import {
  t,
  assign,
  currentLang
} from './utils';
import keymap from './keymap';
import { ShortcutManager, Shortcuts } from 'react-shortcuts';

const shortcutManager = new ShortcutManager(keymap);

class App extends React.Component {
  constructor(props) {
    super(props);
    moment.locale(currentLang());
    this.state = assign({
      pageState: stores.pageState.state
    });
  }
  componentWillReceiveProps() {
    // slide out drawer overlay on every page change (better mobile experience)
    if (this.state.pageState.showFixedDrawer)
      stores.pageState.setState({showFixedDrawer: false});
    // hide modal on every page change
    if (this.state.pageState.modal)
      stores.pageState.hideModal();
  }
  componentDidMount () {
    actions.misc.getServerEnvironment();

    // TODO: this operation should be removed after March 1, 2019
    // To avoid issues with localStorage limits, delete user.history from browser's localStorage
    // user.history was an unusued store, it was removed in https://github.com/kobotoolbox/kpi/pull/1878
    if (localStorage && localStorage['user.history']) {
      localStorage.removeItem('user.history');
    }
  }
  _handleShortcuts(action) {
    switch (action) {
      case 'EDGE':
        document.body.classList.toggle('hide-edge');
        break;
    }
  }
  getChildContext() {
    return { shortcuts: shortcutManager };
  }
  render() {
    var assetid = this.props.params.assetid || null;
    return (
      <DocumentTitle title='KoBoToolbox'>
        <Shortcuts
          name='APP_SHORTCUTS'
          handler={this._handleShortcuts}
          className='mdl-wrapper'
          global
          isolate>

        <IntercomHandler/>

          { !this.isFormBuilder() &&

          }
          <bem.PageWrapper m={{
              'fixed-drawer': this.state.pageState.showFixedDrawer,
              'in-formbuilder': this.isFormBuilder()
                }} className='mdl-layout mdl-layout--fixed-header'>
              { this.state.pageState.modal &&
                <Modal params={this.state.pageState.modal} />
              }

              { !this.isFormBuilder() &&
                <MainHeader assetid={assetid}/>
              }
              { !this.isFormBuilder() &&
                <Drawer/>
              }
              <bem.PageWrapper__content className='mdl-layout__content' m={this.isFormSingle() ? 'form-landing' : ''}>
                { !this.isFormBuilder() &&
                  <FormViewTabs type={'top'} show={this.isFormSingle()} />
                }
                { !this.isFormBuilder() &&
                  <FormViewTabs type={'side'} show={this.isFormSingle()} />
                }
                {this.props.children}

              </bem.PageWrapper__content>
          </bem.PageWrapper>
        </Shortcuts>
      </DocumentTitle>
    );
  }
};

App.contextTypes = {
  router: PropTypes.object
};

App.childContextTypes = {
  shortcuts: PropTypes.object.isRequired
};

reactMixin(App.prototype, Reflux.connect(stores.pageState, 'pageState'));
reactMixin(App.prototype, mixins.contextRouter);

class FormJson extends React.Component {
  constructor (props) {
    super(props);
    this.state = {
      assetcontent: false
    };
    autoBind(this);
  }
  componentDidMount () {
    this.listenTo(stores.asset, this.assetStoreTriggered);
    actions.resources.loadAsset({id: this.props.params.assetid});

  }
  assetStoreTriggered (data, uid) {
    this.setState({
      assetcontent: data[uid].content
    });
  }
  render () {
    return (
        <ui.Panel>
          <bem.FormView>

              { this.state.assetcontent ?
                JSON.stringify(this.state.assetcontent, null, 4)
                : null }

          </bem.FormView>
        </ui.Panel>
      );
  }
}

reactMixin(FormJson.prototype, Reflux.ListenerMixin);

class FormXform extends React.Component {
  constructor (props) {
    super(props);
    this.state = {
      xformLoaded: false
    };
  }
  componentDidMount () {
    dataInterface.getAssetXformView(this.props.params.assetid).done((content) => {
      this.setState({
        xformLoaded: true,
        xformHtml: {
          __html: $('
').html(content).find('.pygment').html()
        },
      });
    });
  }
  render () {
    if (!this.state.xformLoaded) {
      return (
        <ui.Panel>
          <bem.Loading>
            <bem.Loading__inner>

XForm is loading

            </bem.Loading__inner>
          </bem.Loading>
        </ui.Panel>

        );
    } else {
      return (
        <ui.Panel>
          <bem.FormView>

          </bem.FormView>
        </ui.Panel>
        );
    }
  }
}

var LibrarySearchableList = require('./lists/library');
var FormsSearchableList = require('./lists/forms');

class FormNotFound extends React.Component {
  render () {
    return (
        <ui.Panel>
          <bem.Loading>
            <bem.Loading__inner>
              {t('path not found / recognized')}
            </bem.Loading__inner>
          </bem.Loading>
        </ui.Panel>
      );
  }
}

class SectionNotFound extends React.Component {
  render () {
    return (
        <ui.Panel className='k404'>

          section not found
        </ui.Panel>
      );
  }
}

export var routes = (
  <Route name='home' path='/' component={App}>
    <Route path='account-settings' component={AccountSettings} />
    <Route path='change-password' component={ChangePassword} />

    <Route path='library' >
      <Route path='new' component={AddToLibrary} />
      <Route path='new/template' component={AddToLibrary} />
      <Route path='/library/:assetid'>
        {/*<Route name="library-form-download" path="download" handler={FormDownload} />,*/}
        <Route path='json' component={FormJson} />,
        <Route path='xform' component={FormXform} />,
        <Route path='edit' component={LibraryPage} />
      </Route>
      <IndexRoute component={LibrarySearchableList} />
    </Route>

    <IndexRedirect to='forms' />
    <Route path='forms' >
      <IndexRoute component={FormsSearchableList} />

      <Route path='/forms/:assetid'>
        {/*<Route name="form-download" path="download" component={FormDownload} />*/}
        <Route path='json' component={FormJson} />
        <Route path='xform' component={FormXform} />
        <Route path='edit' component={FormPage} />

        <Route path='summary'>
          <IndexRoute component={FormSummary} />
        </Route>

        <Route path='landing'>
          <IndexRoute component={FormLanding} />
        </Route>

        <Route path='data'>
          <Route path='report' component={Reports} />
          <Route path='report-legacy' component={FormSubScreens} />
          <Route path='table' component={FormSubScreens} />
          <Route path='downloads' component={FormSubScreens} />
          <Route path='gallery' component={FormSubScreens} />
          <Route path='map' component={FormSubScreens} />
          <Route path='map/:viewby' component={FormSubScreens} />
          <IndexRedirect to='report' />
        </Route>

        <Route path='settings'>
          <IndexRoute component={FormSubScreens} />
          <Route path='media' component={FormSubScreens} />
          <Route path='sharing' component={FormSubScreens} />
          <Route path='rest' component={FormSubScreens} />
          <Route path='rest/:hookUid' component={FormSubScreens} />
          <Route path='kobocat' component={FormSubScreens} />
        </Route>

        {/* used to force refresh form screens */}
        <Route path='reset' component={FormSubScreens} />

        <IndexRedirect to='landing' />
      </Route>

      <Route path='*' component={FormNotFound} />
    </Route>

    <Route path='*' component={SectionNotFound} />
  </Route>
);

/* Send a pageview to Google Analytics for every change in routes */
hashHistory.listen(function() {
  if (typeof ga === 'function') {
    ga('send', 'pageview', window.location.hash);
  }
});

class RunRoutes extends React.Component {
  componentDidMount(){
    // when hot reloading, componentWillReceiveProps whines about changing the routes prop so this shuts that up
    this.router.componentWillReceiveProps = function(){};
  }

  render() {
    return (
      <Router history={hashHistory} ref={ref=>this.router = ref} routes={this.props.routes} />
    );
  }
}

export default RunRoutes;
<commit_msg>Remove clean-up code whose purpose has been served<commit_after>/**
 * The React application used in `jsapp/js/main.es6` bundle file.
 *
 * TODO: move routes configuration to separate file for clarity.
 */

import $ from 'jquery';
window.jQuery = $;
window.$ = $;
require('jquery-ui/ui/widgets/sortable');

import React from 'react';
import PropTypes from 'prop-types';
import DocumentTitle from 'react-document-title';
import reactMixin from 'react-mixin';
import autoBind from 'react-autobind';
import Reflux from 'reflux';
import {
  IndexRoute,
  IndexRedirect,
  Route,
  hashHistory,
  Router
} from 'react-router';
import moment from 'moment';
import actions from './actions';
import stores from './stores';
import {dataInterface} from './dataInterface';
import bem from './bem';
import ui from './ui';
import mixins from './mixins';
import MainHeader from './components/header';
import Drawer from './components/drawer';
import {
  AddToLibrary,
  FormPage,
  LibraryPage
} from './components/formEditors';
import Reports from './components/reports';
import FormLanding from './components/formLanding';
import FormSummary from './components/formSummary';
import FormSubScreens from './components/formSubScreens';
import FormViewTabs from './components/formViewTabs';
import IntercomHandler from './components/intercomHandler';
import Modal from './components/modal';
import {ChangePassword, AccountSettings} from './components/accountSettings';
import {
  t,
  assign,
  currentLang
} from './utils';
import keymap from './keymap';
import { ShortcutManager, Shortcuts } from 'react-shortcuts';

const shortcutManager = new ShortcutManager(keymap);

class App extends React.Component {
  constructor(props) {
    super(props);
    moment.locale(currentLang());
    this.state = assign({
      pageState: stores.pageState.state
    });
  }
  componentWillReceiveProps() {
    // slide out drawer overlay on every page change (better mobile experience)
    if (this.state.pageState.showFixedDrawer)
      stores.pageState.setState({showFixedDrawer: false});
    // hide modal on every page change
    if (this.state.pageState.modal)
      stores.pageState.hideModal();
  }
  componentDidMount () {
    actions.misc.getServerEnvironment();
  }
  _handleShortcuts(action) {
    switch (action) {
      case 'EDGE':
        document.body.classList.toggle('hide-edge');
        break;
    }
  }
  getChildContext() {
    return { shortcuts: shortcutManager };
  }
  render() {
    var assetid = this.props.params.assetid || null;
    return (
      <DocumentTitle title='KoBoToolbox'>
        <Shortcuts
          name='APP_SHORTCUTS'
          handler={this._handleShortcuts}
          className='mdl-wrapper'
          global
          isolate>

        <IntercomHandler/>

          { !this.isFormBuilder() &&

          }
          <bem.PageWrapper m={{
              'fixed-drawer': this.state.pageState.showFixedDrawer,
              'in-formbuilder': this.isFormBuilder()
                }} className='mdl-layout mdl-layout--fixed-header'>
              { this.state.pageState.modal &&
                <Modal params={this.state.pageState.modal} />
              }

              { !this.isFormBuilder() &&
                <MainHeader assetid={assetid}/>
              }
              { !this.isFormBuilder() &&
                <Drawer/>
              }
              <bem.PageWrapper__content className='mdl-layout__content' m={this.isFormSingle() ? 'form-landing' : ''}>
                { !this.isFormBuilder() &&
                  <FormViewTabs type={'top'} show={this.isFormSingle()} />
                }
                { !this.isFormBuilder() &&
                  <FormViewTabs type={'side'} show={this.isFormSingle()} />
                }
                {this.props.children}

              </bem.PageWrapper__content>
          </bem.PageWrapper>
        </Shortcuts>
      </DocumentTitle>
    );
  }
};

App.contextTypes = {
  router: PropTypes.object
};

App.childContextTypes = {
  shortcuts: PropTypes.object.isRequired
};

reactMixin(App.prototype, Reflux.connect(stores.pageState, 'pageState'));
reactMixin(App.prototype, mixins.contextRouter);

class FormJson extends React.Component {
  constructor (props) {
    super(props);
    this.state = {
      assetcontent: false
    };
    autoBind(this);
  }
  componentDidMount () {
    this.listenTo(stores.asset, this.assetStoreTriggered);
    actions.resources.loadAsset({id: this.props.params.assetid});

  }
  assetStoreTriggered (data, uid) {
    this.setState({
      assetcontent: data[uid].content
    });
  }
  render () {
    return (
        <ui.Panel>
          <bem.FormView>

              { this.state.assetcontent ?
                JSON.stringify(this.state.assetcontent, null, 4)
                : null }

          </bem.FormView>
        </ui.Panel>
      );
  }
}

reactMixin(FormJson.prototype, Reflux.ListenerMixin);

class FormXform extends React.Component {
  constructor (props) {
    super(props);
    this.state = {
      xformLoaded: false
    };
  }
  componentDidMount () {
    dataInterface.getAssetXformView(this.props.params.assetid).done((content) => {
      this.setState({
        xformLoaded: true,
        xformHtml: {
          __html: $('
').html(content).find('.pygment').html()
        },
      });
    });
  }
  render () {
    if (!this.state.xformLoaded) {
      return (
        <ui.Panel>
          <bem.Loading>
            <bem.Loading__inner>

XForm is loading

            </bem.Loading__inner>
          </bem.Loading>
        </ui.Panel>

        );
    } else {
      return (
        <ui.Panel>
          <bem.FormView>

          </bem.FormView>
        </ui.Panel>
        );
    }
  }
}

var LibrarySearchableList = require('./lists/library');
var FormsSearchableList = require('./lists/forms');

class FormNotFound extends React.Component {
  render () {
    return (
        <ui.Panel>
          <bem.Loading>
            <bem.Loading__inner>
              {t('path not found / recognized')}
            </bem.Loading__inner>
          </bem.Loading>
        </ui.Panel>
      );
  }
}

class SectionNotFound extends React.Component {
  render () {
    return (
        <ui.Panel className='k404'>

          section not found
        </ui.Panel>
      );
  }
}

export var routes = (
  <Route name='home' path='/' component={App}>
    <Route path='account-settings' component={AccountSettings} />
    <Route path='change-password' component={ChangePassword} />

    <Route path='library' >
      <Route path='new' component={AddToLibrary} />
      <Route path='new/template' component={AddToLibrary} />
      <Route path='/library/:assetid'>
        {/*<Route name="library-form-download" path="download" handler={FormDownload} />,*/}
        <Route path='json' component={FormJson} />,
        <Route path='xform' component={FormXform} />,
        <Route path='edit' component={LibraryPage} />
      </Route>
      <IndexRoute component={LibrarySearchableList} />
    </Route>

    <IndexRedirect to='forms' />
    <Route path='forms' >
      <IndexRoute component={FormsSearchableList} />

      <Route path='/forms/:assetid'>
        {/*<Route name="form-download" path="download" component={FormDownload} />*/}
        <Route path='json' component={FormJson} />
        <Route path='xform' component={FormXform} />
        <Route path='edit' component={FormPage} />

        <Route path='summary'>
          <IndexRoute component={FormSummary} />
        </Route>

        <Route path='landing'>
          <IndexRoute component={FormLanding} />
        </Route>

        <Route path='data'>
          <Route path='report' component={Reports} />
          <Route path='report-legacy' component={FormSubScreens} />
          <Route path='table' component={FormSubScreens} />
          <Route path='downloads' component={FormSubScreens} />
          <Route path='gallery' component={FormSubScreens} />
          <Route path='map' component={FormSubScreens} />
          <Route path='map/:viewby' component={FormSubScreens} />
          <IndexRedirect to='report' />
        </Route>

        <Route path='settings'>
          <IndexRoute component={FormSubScreens} />
          <Route path='media' component={FormSubScreens} />
          <Route path='sharing' component={FormSubScreens} />
          <Route path='rest' component={FormSubScreens} />
          <Route path='rest/:hookUid' component={FormSubScreens} />
          <Route path='kobocat' component={FormSubScreens} />
        </Route>

        {/* used to force refresh form screens */}
        <Route path='reset' component={FormSubScreens} />

        <IndexRedirect to='landing' />
      </Route>

      <Route path='*' component={FormNotFound} />
    </Route>

    <Route path='*' component={SectionNotFound} />
  </Route>
);

/* Send a pageview to Google Analytics for every change in routes */
hashHistory.listen(function() {
  if (typeof ga === 'function') {
    ga('send', 'pageview', window.location.hash);
  }
});

class RunRoutes extends React.Component {
  componentDidMount(){
    // when hot reloading, componentWillReceiveProps whines about changing the routes prop so this shuts that up
    this.router.componentWillReceiveProps = function(){};
  }

  render() {
    return (
      <Router history={hashHistory} ref={ref=>this.router = ref} routes={this.props.routes} />
    );
  }
}

export default RunRoutes;
<|endoftext|>


<commit_before>
/* global describe, it, expect, t10, sinon */

describe('t10', function () {
    'use strict';

    it('exists', function () {

        expect(t10).to.not.equal(null);

    });

    describe('setResource', function () {

        it('sets the resource', function () {

            var resource = {abc: 'abc string'};

            t10.setResource(resource);

            expect(t10.getResource()).to.equal(resource);
        });

        it('resets entire resource', function () {

            var resource1 = {abc: 'abc string'};
            var resource2 = {def: 'def string'};

            t10.setResource(resource1);
            t10.setResource(resource2);

            expect(t10.getResource()).to.equal(resource2);
        });
    });

    describe('t', function () {

        it('can translate the existing keys', function () {
            var resource = {abc: 'abc string'};

            t10.setResource(resource);

            expect(t10.t('abc')).to.equal('abc string');
        });

        it('returns the key itself if the corresponding resource doesn\'t exist', function () {
            var resource = {abc: 'abc string'};

            t10.setResource(resource);

            expect(t10.t('nonexistent.key')).to.equal('nonexistent.key');
        });

    });

    describe('scan', function () {
    });

    describe('scanTTag', function () {
    });

    describe('scanTText', function () {
    });

    describe('scanTAttr', function () {
    });

    describe('setAvailableLanguages', function () {
    });

    describe('setLanguage', function () {
    });

    describe('pickUsableLanguage', function () {
    });

    describe('loadScript', function () {
    });

    describe('loadJSON', function () {
    });

});
<commit_msg>Remove sinon for now<commit_after>
/* global describe, it, expect, t10 */

describe('t10', function () {
    'use strict';

    it('exists', function () {

        expect(t10).to.not.equal(null);

    });

    describe('setResource', function () {

        it('sets the resource', function () {

            var resource = {abc: 'abc string'};

            t10.setResource(resource);

            expect(t10.getResource()).to.equal(resource);
        });

        it('resets entire resource', function () {

            var resource1 = {abc: 'abc string'};
            var resource2 = {def: 'def string'};

            t10.setResource(resource1);
            t10.setResource(resource2);

            expect(t10.getResource()).to.equal(resource2);
        });
    });

    describe('t', function () {

        it('can translate the existing keys', function () {
            var resource = {abc: 'abc string'};

            t10.setResource(resource);

            expect(t10.t('abc')).to.equal('abc string');
        });

        it('returns the key itself if the corresponding resource doesn\'t exist', function () {
            var resource = {abc: 'abc string'};

            t10.setResource(resource);

            expect(t10.t('nonexistent.key')).to.equal('nonexistent.key');
        });

    });

    describe('scan', function () {
    });

    describe('scanTTag', function () {
    });

    describe('scanTText', function () {
    });

    describe('scanTAttr', function () {
    });

    describe('setAvailableLanguages', function () {
    });

    describe('setLanguage', function () {
    });

    describe('pickUsableLanguage', function () {
    });

    describe('loadScript', function () {
    });

    describe('loadJSON', function () {
    });

});
<|endoftext|>


<commit_before>//noinspection ThisExpressionReferencesGlobalObjectJS
/**
 * Turns `element` into interactive tab component and gives it the `enhancedClass`.
 * ...
 * The component has support for keyboard and assistive technologies using ARIA properties.
 */
(function (root, factory) {
	'use strict';
	if (typeof define === 'function' && define.amd) {
		define([
			'airhooks/forEach',
			'airhooks/containsClass',
			'airhooks/addClass',
			'airhooks/removeClass',
			'airhooks/addEventListener',
			'airhooks/removeEventListener'
		], factory);
	} else {
		root.expendible = factory(
			airhooks.forEach,
			airhooks.containsClass,
			airhooks.addClass,
			airhooks.removeClass,
			airhooks.addEventListener,
			airhooks.removeEventListener
		);
	}
}(this, function (forEach, containsClass, addClass, removeClass, addEventListener, removeEventListener) {
	'use strict';
	var doc = document;
	var KEY_CODES = {
		END:   35,
		HOME:  36,
		LEFT:  37,
		UP:    38,
		RIGHT: 39,
		DOWN:  40
	};

	/**
	 * @constructor
	 */
	function TabThat (element, options) {
		// only enhance if modern element selectors are supported
		if(!('querySelectorAll' in doc)){ return element; }
		if(element) {
			if(!element.isTabInterface){
				this.init(element, options);
			}
		} else {
			var elements = doc.querySelectorAll('[data-tab-that]');
			forEach(elements, function(element){
				new TabThat(element, options);
			});
			return TabThat.instances;
		}
	}

	TabThat.instances = [];

	/**
	 * @param {HTMLElement} [element] - (See constructor)
	 * @param {Object} [options] - (See constructor)
	 */
	TabThat.prototype.init = function(element, options) {
		// define component properties
		this.element = element;
		this.settings = extend(this.getDefaults(element), options);
		this.tabs = [];
		this.panels = [];
		this.list = element.querySelector(this.settings.listSelector);
		this.listItems = this.list.querySelectorAll('li');
		this.handles = this.list.querySelectorAll('a');
		this.prevButtons = element.querySelectorAll(this.settings.prevButtonSelector);
		this.nextButtons = element.querySelectorAll(this.settings.nextButtonSelector);
		this.selectedIndex = -1;

		var selectedHandle = this.list.querySelector('a.' + this.settings.selectedClass);
		var selectedIndex = Math.max(0, indexOf(this.handles, selectedHandle));

		// register instance,link elements & bind events
		this.register();
		this.link();
		this.select(selectedIndex);
		this.bind();

		// mark element as enhanced
		this.element.isTabInterface = true;
		this.element.tabThat = this;
		addClass(element, this.settings.enhancedClass);

		return this;
	};

	TabThat.prototype.destroy = function () {
		this.unregister();
		this.unlink();
		this.unbind();

		// remove enhanced states
		this.element.removeAttribute('isTabInterface');
		this.element.removeAttribute('tabThat');
		removeClass(this.element, this.settings.enhancedClass);

		return this.element;
	};

	TabThat.prototype.getDefaults = function(element) {
		return  {
			listSelector: '[data-tab-list]',
			prevButtonSelector: '[data-tab-previous]',
			nextButtonSelector: '[data-tab-next]',
			enhancedClass: 'is-tabbed',
			selectedClass: 'is-selected'
		};
	};

	TabThat.prototype.register = function() {
		TabThat.instances.push(this);
	};

	TabThat.prototype.unregister = function() {
		var index = indexOf(TabThat.instances, this);
		if(index >= 0) {
			TabThat.instances.splice(index, 1);
		}
	};

	TabThat.prototype.link = function() {
		var component = this;
		this.list.setAttribute('role','tablist');
		forEach(this.listItems, function(item){
			item.setAttribute('role','presentation');
		});
		forEach(this.handles, function(handle, index) {
			var panelId = handle.href.split('#')[1];
			var panel = doc.getElementById(panelId);
			handle.id = handle.id || panelId + '-handle';
			handle.setAttribute('role', 'tab');
			handle.setAttribute('aria-controls', panelId);
			handle.setAttribute('tabindex','-1');
			panel.setAttribute('role', 'tabpanel');
			panel.setAttribute('aria-labelledby', handle.id);
			handle.tabPanel = panel;
			panel.tabHandle = handle;
			component.tabs[index] = { handle: handle, panel: panel };
			component.panels[index] = panel;
		});
		return this;
	};

	TabThat.prototype.unlink = function() {
		this.list.removeAttribute('role');
		forEach(this.listItems, function(item){
			item.removeAttribute('role');
		});
		forEach(this.handles, function(handle) {
			handle.removeAttribute('role');
			handle.removeAttribute('aria-controls');
			handle.removeAttribute('aria-selected');
			handle.removeAttribute('tabindex');
		});
		forEach(this.panels, function(panel) {
			panel.removeAttribute('role');
			panel.removeAttribute('aria-labelledby');
			panel.removeAttribute('aria-hidden');
		});
		return this;
	};

	/**
	 * @returns {TabThat} - Returns this instance for chainability.
	 */
	TabThat.prototype.bind = function() {
		var component = this;
		forEach(this.handles, function(handle, index) {
			addEventListener(handle, 'click', function(event) {
				event.preventDefault();
				component.select.call(component, index);
			});
			addEventListener(handle, 'keydown', function(event) {
				component.onTabPress.call(component, event);
			});
		});
		forEach(this.prevButtons, function(button) {
			addEventListener(button, 'click', function(event) {
				component.selectPrevious();
			});
		});
		forEach(this.nextButtons, function(button) {
			addEventListener(button, 'click', function(event) {
				component.selectNext();
			});
		});
		return this;
	};

	TabThat.prototype.onTabPress = function(event) {
		switch(event.keyCode) {
			case KEY_CODES.LEFT:
			case KEY_CODES.UP:
				this.selectPrevious();
				break;
			case KEY_CODES.RIGHT:
			case KEY_CODES.DOWN:
				this.selectNext();
				break;
			case KEY_CODES.HOME:
				this.selectFirst();
				break;
			case KEY_CODES.END:
				this.selectLast();
				break;
			default:
				break;
		}
		this.selectedTab.handle.focus();
	};

	TabThat.prototype.unbind = function() {
		// @todo: remove click & keydown listeners
		return this;
	};

	TabThat.prototype.hideTab = function(index) {
		var tab = this.tabs[index];
		tab.handle.setAttribute('tabindex','-1');
		tab.handle.setAttribute('aria-selected', false);
		removeClass(tab.handle, this.settings.selectedClass);
		tab.panel.setAttribute('aria-hidden', true);
		removeClass(tab.panel, this.settings.selectedClass);
	};

	TabThat.prototype.showTab = function(index) {
		var tab = this.tabs[index];
		tab.handle.setAttribute('tabindex','0');
		tab.handle.setAttribute('aria-selected', true);
		addClass(tab.handle, this.settings.selectedClass);
		tab.panel.setAttribute('aria-hidden', false);
		addClass(tab.panel, this.settings.selectedClass);
	};

	TabThat.prototype.select = function(index) {
		index = Math.min(Math.max(0, index), this.tabs.length -1);
		if(index === this.selectedIndex) { return; }
		if(this.selectedIndex >= 0){
			this.hideTab(this.selectedIndex);
		}
		this.selectedIndex = index;
		this.selectedTab = this.tabs[index];
		this.showTab(this.selectedIndex);
	};

	TabThat.prototype.selectPrev     =
	TabThat.prototype.selectPrevious = function() {
		var index = (this.selectedIndex > 0) ? this.selectedIndex -1 : this.tabs.length -1;
		this.select(index);
	};
	TabThat.prototype.selectNext     = function() { this.select((this.selectedIndex +1) % this.tabs.length); };
	TabThat.prototype.selectFirst    = function() { this.select(0); };
	TabThat.prototype.selectLast     = function() { this.select(this.tabs.length -1); };

	/**
	 * Helper method for Array.prototype.indexOf
	 * [MDN docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf)
	 * @param {Array} array
	 * @param {*} item
	 * @param {Number} [fromIndex]
	 * @returns {Number}
	 */
	function indexOf(array, item, fromIndex) {
		fromIndex = fromIndex || 0;
		var nativeIndexOf = Array.prototype.indexOf;
		if(nativeIndexOf && array.indexOf === nativeIndexOf) {
			return array.indexOf(item, fromIndex);
		} else {
			for(var index = fromIndex, length = array.length; index < length; index++){
				if(array[index] === item){ return index; }
			}
			return -1;
		}
	}

	/**
	 * Shallow extend first object with properties of a second object.
	 * @param {Object} obj1
	 * @param {Object} obj2
	 */
	function extend(obj1, obj2) {
		for (var prop in obj2) {
			if (obj2.hasOwnProperty(prop)) {
				obj1[prop] = obj2[prop];
			}
		}
		return obj1;
	}

	/**
	 * Returns true if child is a descendant of parent.
	 * Borrowed from: http://stackoverflow.com/a/18162093
	 * @param {HTMLElement} child
	 * @param {HTMLElement} parent
	 * @return {Boolean}
	 */
	function childOf(child, parent){
		//noinspection StatementWithEmptyBodyJS
		while((child=child.parentNode) && child !== parent);
		return !!child;
	}

	return TabThat;
}));<commit_msg>fix location.hash change by removing unneeded preventDefault<commit_after>//noinspection ThisExpressionReferencesGlobalObjectJS
/**
 * Turns `element` into interactive tab component and gives it the `enhancedClass`.
 * ...
 * The component has support for keyboard and assistive technologies using ARIA properties.
 */
(function (root, factory) {
	'use strict';
	if (typeof define === 'function' && define.amd) {
		define([
			'airhooks/forEach',
			'airhooks/containsClass',
			'airhooks/addClass',
			'airhooks/removeClass',
			'airhooks/addEventListener',
			'airhooks/removeEventListener'
		], factory);
	} else {
		root.expendible = factory(
			airhooks.forEach,
			airhooks.containsClass,
			airhooks.addClass,
			airhooks.removeClass,
			airhooks.addEventListener,
			airhooks.removeEventListener
		);
	}
}(this, function (forEach, containsClass, addClass, removeClass, addEventListener, removeEventListener) {
	'use strict';
	var doc = document;
	var KEY_CODES = {
		END:   35,
		HOME:  36,
		LEFT:  37,
		UP:    38,
		RIGHT: 39,
		DOWN:  40
	};

	/**
	 * @constructor
	 */
	function TabThat (element, options) {
		// only enhance if modern element selectors are supported
		if(!('querySelectorAll' in doc)){ return element; }
		if(element) {
			if(!element.isTabInterface){
				this.init(element, options);
			}
		} else {
			var elements = doc.querySelectorAll('[data-tab-that]');
			forEach(elements, function(element){
				new TabThat(element, options);
			});
			return TabThat.instances;
		}
	}

	TabThat.instances = [];

	/**
	 * @param {HTMLElement} [element] - (See constructor)
	 * @param {Object} [options] - (See constructor)
	 */
	TabThat.prototype.init = function(element, options) {
		// define component properties
		this.element = element;
		this.settings = extend(this.getDefaults(element), options);
		this.tabs = [];
		this.panels = [];
		this.list = element.querySelector(this.settings.listSelector);
		this.listItems = this.list.querySelectorAll('li');
		this.handles = this.list.querySelectorAll('a');
		this.prevButtons = element.querySelectorAll(this.settings.prevButtonSelector);
		this.nextButtons = element.querySelectorAll(this.settings.nextButtonSelector);
		this.selectedIndex = -1;

		var selectedHandle = this.list.querySelector('a.' + this.settings.selectedClass);
		var selectedIndex = Math.max(0, indexOf(this.handles, selectedHandle));

		// register instance,link elements & bind events
		this.register();
		this.link();
		this.select(selectedIndex);
		this.bind();

		// mark element as enhanced
		this.element.isTabInterface = true;
		this.element.tabThat = this;
		addClass(element, this.settings.enhancedClass);

		return this;
	};

	TabThat.prototype.destroy = function () {
		this.unregister();
		this.unlink();
		this.unbind();

		// remove enhanced states
		this.element.removeAttribute('isTabInterface');
		this.element.removeAttribute('tabThat');
		removeClass(this.element, this.settings.enhancedClass);

		return this.element;
	};

	TabThat.prototype.getDefaults = function(element) {
		return  {
			listSelector: '[data-tab-list]',
			prevButtonSelector: '[data-tab-previous]',
			nextButtonSelector: '[data-tab-next]',
			enhancedClass: 'is-tabbed',
			selectedClass: 'is-selected'
		};
	};

	TabThat.prototype.register = function() {
		TabThat.instances.push(this);
	};

	TabThat.prototype.unregister = function() {
		var index = indexOf(TabThat.instances, this);
		if(index >= 0) {
			TabThat.instances.splice(index, 1);
		}
	};

	TabThat.prototype.link = function() {
		var component = this;
		this.list.setAttribute('role','tablist');
		forEach(this.listItems, function(item){
			item.setAttribute('role','presentation');
		});
		forEach(this.handles, function(handle, index) {
			var panelId = handle.href.split('#')[1];
			var panel = doc.getElementById(panelId);
			handle.id = handle.id || panelId + '-handle';
			handle.setAttribute('role', 'tab');
			handle.setAttribute('aria-controls', panelId);
			handle.setAttribute('tabindex','-1');
			panel.setAttribute('role', 'tabpanel');
			panel.setAttribute('aria-labelledby', handle.id);
			handle.tabPanel = panel;
			panel.tabHandle = handle;
			component.tabs[index] = { handle: handle, panel: panel };
			component.panels[index] = panel;
		});
		return this;
	};

	TabThat.prototype.unlink = function() {
		this.list.removeAttribute('role');
		forEach(this.listItems, function(item){
			item.removeAttribute('role');
		});
		forEach(this.handles, function(handle) {
			handle.removeAttribute('role');
			handle.removeAttribute('aria-controls');
			handle.removeAttribute('aria-selected');
			handle.removeAttribute('tabindex');
		});
		forEach(this.panels, function(panel) {
			panel.removeAttribute('role');
			panel.removeAttribute('aria-labelledby');
			panel.removeAttribute('aria-hidden');
		});
		return this;
	};

	/**
	 * @returns {TabThat} - Returns this instance for chainability.
	 */
	TabThat.prototype.bind = function() {
		var component = this;
		forEach(this.handles, function(handle, index) {
			addEventListener(handle, 'click', function(event) {
				//event.preventDefault();
				component.select.call(component, index);
			});
			addEventListener(handle, 'keydown', function(event) {
				component.onTabPress.call(component, event);
			});
		});
		forEach(this.prevButtons, function(button) {
			addEventListener(button, 'click', function(event) {
				component.selectPrevious();
			});
		});
		forEach(this.nextButtons, function(button) {
			addEventListener(button, 'click', function(event) {
				component.selectNext();
			});
		});
		return this;
	};

	TabThat.prototype.onTabPress = function(event) {
		switch(event.keyCode) {
			case KEY_CODES.LEFT:
			case KEY_CODES.UP:
				this.selectPrevious();
				break;
			case KEY_CODES.RIGHT:
			case KEY_CODES.DOWN:
				this.selectNext();
				break;
			case KEY_CODES.HOME:
				this.selectFirst();
				break;
			case KEY_CODES.END:
				this.selectLast();
				break;
			default:
				break;
		}
		this.selectedTab.handle.focus();
	};

	TabThat.prototype.unbind = function() {
		// @todo: remove click & keydown listeners
		return this;
	};

	TabThat.prototype.hideTab = function(index) {
		var tab = this.tabs[index];
		tab.handle.setAttribute('tabindex','-1');
		tab.handle.setAttribute('aria-selected', false);
		removeClass(tab.handle, this.settings.selectedClass);
		tab.panel.setAttribute('aria-hidden', true);
		removeClass(tab.panel, this.settings.selectedClass);
	};

	TabThat.prototype.showTab = function(index) {
		var tab = this.tabs[index];
		tab.handle.setAttribute('tabindex','0');
		tab.handle.setAttribute('aria-selected', true);
		addClass(tab.handle, this.settings.selectedClass);
		tab.panel.setAttribute('aria-hidden', false);
		addClass(tab.panel, this.settings.selectedClass);
		//window.location.hash = tab.panel.id;
	};

	TabThat.prototype.select = function(index) {
		index = Math.min(Math.max(0, index), this.tabs.length -1);
		if(index === this.selectedIndex) { return; }
		if(this.selectedIndex >= 0){
			this.hideTab(this.selectedIndex);
		}
		this.selectedIndex = index;
		this.selectedTab = this.tabs[index];
		this.showTab(this.selectedIndex);
	};

	TabThat.prototype.selectPrev     =
	TabThat.prototype.selectPrevious = function() {
		var index = (this.selectedIndex > 0) ? this.selectedIndex -1 : this.tabs.length -1;
		this.select(index);
	};
	TabThat.prototype.selectNext     = function() { this.select((this.selectedIndex +1) % this.tabs.length); };
	TabThat.prototype.selectFirst    = function() { this.select(0); };
	TabThat.prototype.selectLast     = function() { this.select(this.tabs.length -1); };

	/**
	 * Helper method for Array.prototype.indexOf
	 * [MDN docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf)
	 * @param {Array} array
	 * @param {*} item
	 * @param {Number} [fromIndex]
	 * @returns {Number}
	 */
	function indexOf(array, item, fromIndex) {
		fromIndex = fromIndex || 0;
		var nativeIndexOf = Array.prototype.indexOf;
		if(nativeIndexOf && array.indexOf === nativeIndexOf) {
			return array.indexOf(item, fromIndex);
		} else {
			for(var index = fromIndex, length = array.length; index < length; index++){
				if(array[index] === item){ return index; }
			}
			return -1;
		}
	}

	/**
	 * Shallow extend first object with properties of a second object.
	 * @param {Object} obj1
	 * @param {Object} obj2
	 */
	function extend(obj1, obj2) {
		for (var prop in obj2) {
			if (obj2.hasOwnProperty(prop)) {
				obj1[prop] = obj2[prop];
			}
		}
		return obj1;
	}

	/**
	 * Returns true if child is a descendant of parent.
	 * Borrowed from: http://stackoverflow.com/a/18162093
	 * @param {HTMLElement} child
	 * @param {HTMLElement} parent
	 * @return {Boolean}
	 */
	function childOf(child, parent){
		//noinspection StatementWithEmptyBodyJS
		while((child=child.parentNode) && child !== parent);
		return !!child;
	}

	return TabThat;
}));<|endoftext|>