<commit_before>
/* Dasca, an idle game by @CylonicRaider (github.com/CylonicRaider/dasca)
 * Abstract engine for JS-based idle games */

'use strict';

/* *** Clock ***
 * An object allowing to sample "time" (as defined by an external source)
 * with a linear transform in place. */

/* Construct a new clock
 * source is a function that, when called, returns the current time; scale
 * and offset modify source's output in a linear fashion: the reading of
 * source is multiplied by scale and offset is added to that to obtain the
 * current time. If scale is undefined or null, it defaults to 1; if offset
 * is null, it is computed such that the clock starts counting from zero. */
function Clock(source, scale, offset) {
  if (scale == null) scale = 1.0;
  if (offset == null) offset = -source() * scale;
  this.source = source;
  this.scale = scale;
  this.offset = offset;
}

Clock.prototype = {
  /* Sample the current time */
  now: function() {
    return this.source() * this.scale + this.offset;
  },

  /* Construct a new clock from this one, applying the given transformation
   * The new clock derives its time from the same (!) source as this one,
   * but its scale and offset are modified to simulate a clock constructed
   * like
   *     new Clock(this, scale, offset);
   */
  derive: function(scale, offset) {
    if (scale == null) scale = 1.0;
    if (offset != null) offset = this.offset * scale + offset;
    var ret = new Clock(this.source, this.scale * scale, offset);
    if (this._realTime) ret._realTime = true;
    return ret;
  },

  /* Change the scale of the clock, ensuring that readings continue to be
   * continuous */
  setScale: function(scale) {
    var samp = this.source();
    this.offset = (this.scale - scale) * samp + this.offset;
    this.scale = scale;
  },

  /* Change the scale of the clock relatively to the current one */
  changeScale: function(factor) {
    this.setScale(this.scale * factor);
  },

  /* Jump to the given time */
  setTime: function(time) {
    this.offset = time - this.source() * this.scale;
  },

  /* Change the time reported by the clock by the given increment */
  changeTime: function(delta) {
    this.offset -= delta;
  },

  /* Serialization boilerplate */
  constructor: Clock
};

/* Construct a clock that returns (optionally scaled) real time
 * The clock counts from zero */
Clock.realTime = function(scale, start) {
  if (scale == null) scale = 1.0;
  var offset = (start == null) ? null : start - performance.now() * scale;
  var ret = new Clock(function() {
    return performance.now() / 1000.0;
  }, scale, offset);
  ret._realTime = true;
  return ret;
};

/* Seralize a Clock */
Clock.__save__ = function(clock) {
  if (! clock._realTime) throw new Error("Clock not serializable!");
  return {scale: clock.scale, time: clock.now()};
};

/* Deserialize a Clock */
Clock.__restore__ = function(data) {
  return Clock.realTime(data.scale, data.time);
};

/* *** Scheduler ***
 * Schedules callbacks to happen at certain times as defined by a Clock
 * instance. */

/* Construct a new scheduler
 * requeue is a funtion that somehow ("magically") ensures the function
 * to it is asynchronously called again some short time later; tasks is an
 * ordered array of objects whose "time" property is used to determine when
 * they are to run; contTasks is an array of tasks to be run continuously,
 * i.e. at every call of run() (the same semantics as for tasks apply); clock
 * is a Clock instance determining the time this instance works with. The
 * "running" property is set to true; it can be used to stop the Scheduler.
 * To start it, ensure the "running" property is true and call the run()
 * method. */
function Scheduler(requeue, tasks, contTasks, clock) {
  if (tasks == null) tasks = [];
  if (contTasks == null) contTasks = [];
  if (clock == null) clock = Clock.realTime();
  this.requeue = requeue;
  this.tasks = tasks;
  this.contTasks = contTasks;
  this.clock = clock;
  this.running = true;
  this._idle = true;
}

Scheduler.prototype = {
  /* Queue the next run of the scheduler, and perform any tasks whose time
   * has come
   * After obtaining a timestamp from the clock, all tasks whose time
   * property is not less than the timestamp are removed from the queue and
   * run using runTask(); after that, if the "running" property is true,
   * requeue is called with a bound version of this.run as only argument to
   * trigger another execution. */
  run: function() {
    /* Abort if not running */
    if (! this.running) return;
    /* Obtain current timestamp */
    var now = this.clock.now();
    /* For each task that is (over)due */
    while (this.tasks[0] && this.tasks[0].time <= now) {
      /* Run it */
      this.runTask(this.tasks.shift(), now);
    }
    /* Run the continuous tasks */
    for (var i = 0; i < this.contTasks.length; i++) {
      this.runTask(this.contTasks[i], now);
    }
    /* Schedule next iteration */
    if (this.tasks.length || this.contTasks.length) {
      this.requeue(this.run.bind(this));
    } else {
      this._idle = true;
    }
  },

  /* Run a singular task, providing the given timestamp
   * The default implementation calls the task's "cb" (which is assumed to be
   * a function) with the value of now and the Scheduler instance as
   * arguments, and consumes exceptions by logging them to the console. */
  runTask: function(task, now) {
    try {
      task.cb(now, this);
    } catch (e) {
      console.error(e);
    }
  },

  /* Schedule a task to be run
   * If time is not null, it is inserted into task as the "time" property;
   * after that, the task is inserted at an appropriate position. */
  addTask: function(task, time) {
    if (time != null) task.time = time;
    time = task.time;
    var i;
    for (i = 0; i < this.tasks.length; i++) {
      if (this.tasks[i].time > time) break;
    }
    this.tasks.splice(i, 0, task);
    if (this.running && this._idle) {
      this._idle = false;
      this.requeue(this.run.bind(this));
    }
  },

  /* Cancel all tasks */
  clear: function() {
    this.tasks.splice(0, this.tasks.length);
  },

  /* OOP boilerplate */
  constructor: Scheduler
};

/* Create a Scheduler for animations */
Scheduler.makeAnimated = function(clock) {
  var ret = new Scheduler(requestAnimationFrame.bind(window), null, null,
                          clock);
  ret._type = "animated";
  return ret;
};

/* Create a Scheduler that polls tasks at regular intervals */
Scheduler.makeStrobe = function(fps, clock) {
  var delay = 1000.0 / fps;
  var ret = new Scheduler(function(cb) {
    setTimeout(cb, delay);
  }, null, null, clock);
  ret._type = "strobe";
  ret._fps = fps;
  return ret;
};

/* Prepare for serializing a Scheduler */
Scheduler.__save__ = function(sched) {
  var ret = {type: sched._type, tasks: sched.tasks,
             contTasks: sched.contTasks, clock: sched.clock,
             running: sched.running};
  if (sched._type == "strobe") {
    ret.fps = sched._fps;
  } else if (sched._type != "animated") {
    throw new Error("Scheduler not serializable!");
  }
  return ret;
};

/* Deserialize a Scheduler */
Scheduler.__restore__ = function(data) {
  var ret;
  if (data.type == "animated") {
    ret = Scheduler.makeAnimated(data.clock);
  } else if (data.type == "strobe") {
    ret = Scheduler.makeStrobe(data.fps, data.clock);
  }
  ret.tasks = data.tasks;
  ret.contTasks = data.contTasks;
  ret.running = data.running;
  return ret;
};

/* *** Serialization ***
 * Serializes object trees (!) into JSON strings, allowing reified objects to
 * be of the correct type, to hook their (de)serialization process.
 * Input containing enumerable function properties is rejected (since those
 * are silently swallowed by JSON); use hooks to meaningfully handle them. */

/* Serialize an object tree to JSON, storing type information */
function serialize(obj) {
  return JSON.stringify(obj, function(name, value) {
    /* Fail verbosely */
    if (typeof value == "function")
      throw new Error("Cannot serialize function");
    /* Only transform object values */
    if (typeof value != "object" || Array.isArray(value)) return value;
    /* Get a meaningful constructor name */
    var cons = value.constructor.__sername__;
    if (! cons) cons = value.constructor.name;
    if (! cons) cons = Object.prototype.toString(value).slice(8, -1);
    if (cons == "Object") cons = undefined;
    /* Copy properties into new object, or let object serialize itself */
    var ret;
    if (value.constructor.__save__) {
      ret = value.constructor.__save__(value);
    } else {
      ret = {};
      for (var prop in value) ret[prop] = value[prop];
    }
    /* Add __type__ */
    ret.__type__ = cons;
    /* Done */
    return ret;
  });
}

/* Deserialize a JSON string into an object structure */
function deserialize(obj, env) {
  if (env == null) env = window;
  return JSON.parse(obj, function(name, value) {
    /* Ignore non-objects */
    if (typeof value != "object" || Array.isArray(value)) return value;
    /* Check for a __type__ */
    if (value.__type__) {
      /* Obtain type object */
      var type = env[value.__type__];
      if (type && type.__restore__) {
        /* Use restorer function */
        value = type.__restore__(value, env);
      } else if (type) {
        /* Assume an object is deserializable as-is */
        var newVal = Object.create(type.prototype);
        for (var k in value) if (! /^__.+__$/.test(k)) newVal[k] = value[k];
        value = newVal;
      } else {
        /* Nope */
        throw new Error("Object not deserializable (cannot find type): " +
                        JSON.stringify(value));
      }
    }
    return value;
  });
}
<commit_msg>Fix unhooked object serialization bug<commit_after>
/* Dasca, an idle game by @CylonicRaider (github.com/CylonicRaider/dasca)
 * Abstract engine for JS-based idle games */

'use strict';

/* *** Clock ***
 * An object allowing to sample "time" (as defined by an external source)
 * with a linear transform in place. */

/* Construct a new clock
 * source is a function that, when called, returns the current time; scale
 * and offset modify source's output in a linear fashion: the reading of
 * source is multiplied by scale and offset is added to that to obtain the
 * current time. If scale is undefined or null, it defaults to 1; if offset
 * is null, it is computed such that the clock starts counting from zero. */
function Clock(source, scale, offset) {
  if (scale == null) scale = 1.0;
  if (offset == null) offset = -source() * scale;
  this.source = source;
  this.scale = scale;
  this.offset = offset;
}

Clock.prototype = {
  /* Sample the current time */
  now: function() {
    return this.source() * this.scale + this.offset;
  },

  /* Construct a new clock from this one, applying the given transformation
   * The new clock derives its time from the same (!) source as this one,
   * but its scale and offset are modified to simulate a clock constructed
   * like
   *     new Clock(this, scale, offset);
   */
  derive: function(scale, offset) {
    if (scale == null) scale = 1.0;
    if (offset != null) offset = this.offset * scale + offset;
    var ret = new Clock(this.source, this.scale * scale, offset);
    if (this._realTime) ret._realTime = true;
    return ret;
  },

  /* Change the scale of the clock, ensuring that readings continue to be
   * continuous */
  setScale: function(scale) {
    var samp = this.source();
    this.offset = (this.scale - scale) * samp + this.offset;
    this.scale = scale;
  },

  /* Change the scale of the clock relatively to the current one */
  changeScale: function(factor) {
    this.setScale(this.scale * factor);
  },

  /* Jump to the given time */
  setTime: function(time) {
    this.offset = time - this.source() * this.scale;
  },

  /* Change the time reported by the clock by the given increment */
  changeTime: function(delta) {
    this.offset -= delta;
  },

  /* Serialization boilerplate */
  constructor: Clock
};

/* Construct a clock that returns (optionally scaled) real time
 * The clock counts from zero */
Clock.realTime = function(scale, start) {
  if (scale == null) scale = 1.0;
  var offset = (start == null) ? null : start - performance.now() * scale;
  var ret = new Clock(function() {
    return performance.now() / 1000.0;
  }, scale, offset);
  ret._realTime = true;
  return ret;
};

/* Seralize a Clock */
Clock.__save__ = function(clock) {
  if (! clock._realTime) throw new Error("Clock not serializable!");
  return {scale: clock.scale, time: clock.now()};
};

/* Deserialize a Clock */
Clock.__restore__ = function(data) {
  return Clock.realTime(data.scale, data.time);
};

/* *** Scheduler ***
 * Schedules callbacks to happen at certain times as defined by a Clock
 * instance. */

/* Construct a new scheduler
 * requeue is a funtion that somehow ("magically") ensures the function
 * to it is asynchronously called again some short time later; tasks is an
 * ordered array of objects whose "time" property is used to determine when
 * they are to run; contTasks is an array of tasks to be run continuously,
 * i.e. at every call of run() (the same semantics as for tasks apply); clock
 * is a Clock instance determining the time this instance works with. The
 * "running" property is set to true; it can be used to stop the Scheduler.
 * To start it, ensure the "running" property is true and call the run()
 * method. */
function Scheduler(requeue, tasks, contTasks, clock) {
  if (tasks == null) tasks = [];
  if (contTasks == null) contTasks = [];
  if (clock == null) clock = Clock.realTime();
  this.requeue = requeue;
  this.tasks = tasks;
  this.contTasks = contTasks;
  this.clock = clock;
  this.running = true;
  this._idle = true;
}

Scheduler.prototype = {
  /* Queue the next run of the scheduler, and perform any tasks whose time
   * has come
   * After obtaining a timestamp from the clock, all tasks whose time
   * property is not less than the timestamp are removed from the queue and
   * run using runTask(); after that, if the "running" property is true,
   * requeue is called with a bound version of this.run as only argument to
   * trigger another execution. */
  run: function() {
    /* Abort if not running */
    if (! this.running) return;
    /* Obtain current timestamp */
    var now = this.clock.now();
    /* For each task that is (over)due */
    while (this.tasks[0] && this.tasks[0].time <= now) {
      /* Run it */
      this.runTask(this.tasks.shift(), now);
    }
    /* Run the continuous tasks */
    for (var i = 0; i < this.contTasks.length; i++) {
      this.runTask(this.contTasks[i], now);
    }
    /* Schedule next iteration */
    if (this.tasks.length || this.contTasks.length) {
      this.requeue(this.run.bind(this));
    } else {
      this._idle = true;
    }
  },

  /* Run a singular task, providing the given timestamp
   * The default implementation calls the task's "cb" (which is assumed to be
   * a function) with the value of now and the Scheduler instance as
   * arguments, and consumes exceptions by logging them to the console. */
  runTask: function(task, now) {
    try {
      task.cb(now, this);
    } catch (e) {
      console.error(e);
    }
  },

  /* Schedule a task to be run
   * If time is not null, it is inserted into task as the "time" property;
   * after that, the task is inserted at an appropriate position. */
  addTask: function(task, time) {
    if (time != null) task.time = time;
    time = task.time;
    var i;
    for (i = 0; i < this.tasks.length; i++) {
      if (this.tasks[i].time > time) break;
    }
    this.tasks.splice(i, 0, task);
    if (this.running && this._idle) {
      this._idle = false;
      this.requeue(this.run.bind(this));
    }
  },

  /* Cancel all tasks */
  clear: function() {
    this.tasks.splice(0, this.tasks.length);
  },

  /* OOP boilerplate */
  constructor: Scheduler
};

/* Create a Scheduler for animations */
Scheduler.makeAnimated = function(clock) {
  var ret = new Scheduler(requestAnimationFrame.bind(window), null, null,
                          clock);
  ret._type = "animated";
  return ret;
};

/* Create a Scheduler that polls tasks at regular intervals */
Scheduler.makeStrobe = function(fps, clock) {
  var delay = 1000.0 / fps;
  var ret = new Scheduler(function(cb) {
    setTimeout(cb, delay);
  }, null, null, clock);
  ret._type = "strobe";
  ret._fps = fps;
  return ret;
};

/* Prepare for serializing a Scheduler */
Scheduler.__save__ = function(sched) {
  var ret = {type: sched._type, tasks: sched.tasks,
             contTasks: sched.contTasks, clock: sched.clock,
             running: sched.running};
  if (sched._type == "strobe") {
    ret.fps = sched._fps;
  } else if (sched._type != "animated") {
    throw new Error("Scheduler not serializable!");
  }
  return ret;
};

/* Deserialize a Scheduler */
Scheduler.__restore__ = function(data) {
  var ret;
  if (data.type == "animated") {
    ret = Scheduler.makeAnimated(data.clock);
  } else if (data.type == "strobe") {
    ret = Scheduler.makeStrobe(data.fps, data.clock);
  }
  ret.tasks = data.tasks;
  ret.contTasks = data.contTasks;
  ret.running = data.running;
  return ret;
};

/* *** Serialization ***
 * Serializes object trees (!) into JSON strings, allowing reified objects to
 * be of the correct type, to hook their (de)serialization process.
 * Input containing enumerable function properties is rejected (since those
 * are silently swallowed by JSON); use hooks to meaningfully handle them. */

/* Serialize an object tree to JSON, storing type information */
function serialize(obj) {
  return JSON.stringify(obj, function(name, value) {
    /* Fail verbosely */
    if (typeof value == "function")
      throw new Error("Cannot serialize function");
    /* Only transform object values */
    if (typeof value != "object" || Array.isArray(value)) return value;
    /* Get a meaningful constructor name */
    var cons = value.constructor.__sername__;
    if (! cons) cons = value.constructor.name;
    if (! cons) cons = Object.prototype.toString(value).slice(8, -1);
    if (cons == "Object") cons = undefined;
    /* Copy properties into new object, or let object serialize itself */
    var ret;
    if (value.constructor.__save__) {
      ret = value.constructor.__save__(value);
    } else {
      ret = {};
      for (var prop in value) {
        if (typeof value[prop] == "function" && ! value.hasOwnProperty(prop))
          continue;
        ret[prop] = value[prop];
      }
    }
    /* Add __type__ */
    ret.__type__ = cons;
    /* Done */
    return ret;
  });
}

/* Deserialize a JSON string into an object structure */
function deserialize(obj, env) {
  if (env == null) env = window;
  return JSON.parse(obj, function(name, value) {
    /* Ignore non-objects */
    if (typeof value != "object" || Array.isArray(value)) return value;
    /* Check for a __type__ */
    if (value.__type__) {
      /* Obtain type object */
      var type = env[value.__type__];
      if (type && type.__restore__) {
        /* Use restorer function */
        value = type.__restore__(value, env);
      } else if (type) {
        /* Assume an object is deserializable as-is */
        var newVal = Object.create(type.prototype);
        for (var k in value) if (! /^__.+__$/.test(k)) newVal[k] = value[k];
        value = newVal;
      } else {
        /* Nope */
        throw new Error("Object not deserializable (cannot find type): " +
                        JSON.stringify(value));
      }
    }
    return value;
  });
}
<|endoftext|>


<commit_before>
var Draggable = require('ti.draggable');

var win = Ti.UI.createWindow({
	backgroundColor:'white',
	height: '100%',
	width: '100%',
	fullscreen: true,
	exitOnClose: true
});

var rel = 0;
var newLeft = 0;
var newTop = 0;

var image1 = Ti.UI.createImageView({
		    image: '/images/colorear/alegria/alegria1.png',
		    left:'5%', top:'35%',
		    width:'20%', height:'20%'
		  });
var image2 = Ti.UI.createImageView({
		    image: '/images/colorear/miedo/miedo2.png',
		    left:'25%', top:'35%',
		    width:'20%', height:'20%'
		  });

var image3 = Ti.UI.createImageView({
		    image: '/images/colorear/miedo/miedo3.png',
		    left:'45%', top:'35%',
		    width:'20%', height:'20%'
		  });

var image4 = Ti.UI.createImageView({
		    image: '/images/colorear/miedo/miedo4.png',
		    left:'65%', top:'35%',
		    width:'20%', height:'20%'
		  });

var image5 = Ti.UI.createImageView({
		    image: '/images/colorear/rabia/enojo10.png',
		    left:'5%', top:'65%',
		    width:'20%', height:'20%'
		  });

var image6 = Ti.UI.createImageView({
		    image: '/images/colorear/tristeza/pena2.png',
		    left:'25%', top:'65%',
		    width:'20%', height:'20%'
		  });

var image7 = Ti.UI.createImageView({
		    image: '/images/colorear/tristeza/pena9.png',
		    left:'45%', top:'65%',
		    width:'20%', height:'20%'
		  });

var image8 = Ti.UI.createImageView({
		    image: '/images/colorear/tristeza/pena5.png',
		    left:'65%', top:'65%',
		    width:'20%', height:'20%'
		  });

win.add(image1);
win.add(image2);
win.add(image3);
win.add(image4);
win.add(image5);
win.add(image6);
win.add(image7);
win.add(image8);

var rojo = Draggable.createView({
			backgroundImage: '/images/colorear/rojo.png',
		    left:'85%', top: '35%',
		    width:'10%',
			height:'18%',
});

win.add(rojo);

var amarillo = Draggable.createView({
			backgroundImage: '/images/colorear/amarillo.png',
		    left:'85%', top: '10%',
		    width:'10%',
			height:'18%',
});

win.add(amarillo);

var azul = Draggable.createView({
			backgroundImage: '/images/colorear/azul.png',
		    left:'85%', top: '60%',
		    width:'10%',
			height:'18%',
});

win.add(azul);

var verde = Draggable.createView({
			backgroundImage: '/images/colorear/verde.png',
		    left:'85%', top: '85%',
		    width:'10%',
			height:'18%',
});

win.add(verde);

rojo.addEventListener('start', function(e){
	Ti.API.info('-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-');
	Ti.API.info('Event "start"');
	Ti.API.info('left: '+e.left);
	Ti.API.info('top:  '+e.top);
	Ti.API.info('center:'+JSON.stringify(e.center));
});

rojo.addEventListener('move', function(e){
	Ti.API.info('-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-');
	Ti.API.info('Event "move"');
	Ti.API.info('left: '+e.left);
	Ti.API.info('top:  '+e.top);
	Ti.API.info('center:'+JSON.stringify(e.center));
});

rojo.addEventListener('end', function(e){
	Ti.API.info('-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-');
	Ti.API.info('Event "end"');
	Ti.API.info('left: '+e.left);
	Ti.API.info('top:  '+e.top);
	Ti.API.info('directionVertical: '+ e.directionVertical);
	Ti.API.info('center:'+JSON.stringify(e.center));
	var p = {x: e.left, y: e.top};
    var tp = win.convertPointToView(p, image5);

	ancho = Titanium.Platform.displayCaps.platformWidth;
	alto = Titanium.Platform.displayCaps.platformHeight;

		if((-0.02*ancho)<=tp.x && tp.x<=(0.15*ancho) && (-0.10*alto)<=tp.y && tp.y<=(0.20*alto)){
					image5.backgroundColor="red";
					alert('Muy bien!!!!');
					rojo.setTop('35%');
					rojo.setLeft('85%');
		}
		else{
			alert('intentalo nuevamente!!!');
			rojo.setTop('35%');
			rojo.setLeft('85%');
		}

});

amarillo.addEventListener('end', function(e){
			alert('intentalo nuevamente!!!');
			amarillo.setTop(amarillo.getTop());
			amarillo.setLeft(amarillo.getLeft());

});

azul.addEventListener('end', function(e){
			alert('intentalo nuevamente!!!');
			azul.setTop(azul.getTop());
			azul.setLeft(azul.getLeft());

});

verde.addEventListener('end', function(e){
			alert('intentalo nuevamente!!!');
			verde.setTop(verde.getTop());
			verde.setLeft(verde.getLeft());

});

win.open();
<commit_msg>uñtimo commit enojo<commit_after>
var Draggable = require('ti.draggable');

var win = Ti.UI.createWindow({
	backgroundColor:'white',
	height: '100%',
	width: '100%',
	fullscreen: true,
	exitOnClose: true
});

titulo = Ti.UI.createLabel({
		  color: 'black',
		  font: { fontSize:20 },
		  shadowColor: '#000099',
		  text: 'Colorea los dibujos de rabia y enojo con rojo',
		  textAlign: Ti.UI.TEXT_ALIGNMENT_CENTER,
		  top: 0,
		  width: Ti.UI.SIZE, height: Ti.UI.SIZE
		});
win.add(titulo);

var rel = 0;
var newLeft = 0;
var newTop = 0;
var mensaje=0;
/**************random array*******************************************/
function shuffle(array) {
  var currentIndex = array.length, temporaryValue, randomIndex ;

  // While there remain elements to shuffle...
  while (0 !== currentIndex) {

    // Pick a remaining element...
    randomIndex = Math.floor(Math.random() * currentIndex);
    currentIndex -= 1;

    // And swap it with the current element.
    temporaryValue = array[currentIndex];
    array[currentIndex] = array[randomIndex];
    array[randomIndex] = temporaryValue;
  }

  return array;
}
var dir = [
'/images/colorear/alegria/alegria',
'/images/colorear/miedo/miedo',
'/images/colorear/miedo/miedo',
'/images/colorear/tristeza/pena',
'/images/colorear/rabia/enojo',
'/images/colorear/rabia/enojo',
'/images/colorear/tristeza/pena',
'/images/colorear/alegria/alegria'
];
shuffle(dir);

var indice = [1,2,3,4,5,6,7,8,9,10,11,12];
shuffle(indice);
/********************************************************************************************/

var image1 = Ti.UI.createImageView({
			id: ''+dir[0],
		    image: ''+dir[0]+''+indice[0]+'.png',
		    left:'5%', top:'35%',
		    width:'20%', height:'20%'
		  });
var image2 = Ti.UI.createImageView({
			id: ''+dir[1],
		    image: ''+dir[1]+''+indice[1]+'.png',
		    left:'25%', top:'35%',
		    width:'20%', height:'20%'
		  });

var image3 = Ti.UI.createImageView({
			id: ''+dir[2],
		    image: ''+dir[2]+''+indice[2]+'.png',
		    left:'45%', top:'35%',
		    width:'20%', height:'20%'
		  });

var image4 = Ti.UI.createImageView({
			id: ''+dir[3],
		    image: ''+dir[3]+''+indice[3]+'.png',
		    left:'65%', top:'35%',
		    width:'20%', height:'20%'
		  });

var image5 = Ti.UI.createImageView({
			id: ''+dir[4],
		    image: ''+dir[4]+''+indice[4]+'.png',
		    left:'5%', top:'65%',
		    width:'20%', height:'20%'
		  });

var image6 = Ti.UI.createImageView({
			id: ''+dir[5],
		    image: ''+dir[5]+''+indice[5]+'.png',
		    left:'25%', top:'65%',
		    width:'20%', height:'20%'
		  });

var image7 = Ti.UI.createImageView({
			id: ''+dir[6],
		    image: ''+dir[6]+''+indice[6]+'.png',
		    left:'45%', top:'65%',
		    width:'20%', height:'20%'
		  });

var image8 = Ti.UI.createImageView({
			id: ''+dir[7],
		    image: ''+dir[7]+''+indice[7]+'.png',
		    left:'65%', top:'65%',
		    width:'20%', height:'20%'
		  });

win.add(image1);
win.add(image2);
win.add(image3);
win.add(image4);
win.add(image5);
win.add(image6);
win.add(image7);
win.add(image8);

var rojo = Draggable.createView({
			backgroundImage: '/images/colorear/rojo.png',
		    left:'85%', top: '35%',
		    width:'10%',
			height:'18%',
});

win.add(rojo);

var amarillo = Draggable.createView({
			backgroundImage: '/images/colorear/amarillo.png',
		    left:'85%', top: '10%',
		    width:'10%',
			height:'18%',
});

win.add(amarillo);

var azul = Draggable.createView({
			backgroundImage: '/images/colorear/azul.png',
		    left:'85%', top: '60%',
		    width:'10%',
			height:'18%',
});

win.add(azul);

var verde = Draggable.createView({
			backgroundImage: '/images/colorear/verde.png',
		    left:'85%', top: '85%',
		    width:'10%',
			height:'18%',
});

win.add(verde);

rojo.addEventListener('start', function(e){
	Ti.API.info('-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-');
	Ti.API.info('Event "start"');
	Ti.API.info('left: '+e.left);
	Ti.API.info('top:  '+e.top);
	Ti.API.info('center:'+JSON.stringify(e.center));
});

rojo.addEventListener('move', function(e){
	Ti.API.info('-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-');
	Ti.API.info('Event "move"');
	Ti.API.info('left: '+e.left);
	Ti.API.info('top:  '+e.top);
	Ti.API.info('center:'+JSON.stringify(e.center));
});

rojo.addEventListener('end', function(e){

    mensaje=0;
	ancho = Titanium.Platform.displayCaps.platformWidth;
	alto = Titanium.Platform.displayCaps.platformHeight;

			if(image1.id == '/images/colorear/rabia/enojo'){
				var p = {x: e.left, y: e.top};
    			var tp = win.convertPointToView(p, image1);

    			if((-0.02*ancho)<=tp.x && tp.x<=(0.15*ancho) && (-0.10*alto)<=tp.y && tp.y<=(0.20*alto)){
					image1.backgroundColor="red";
					mensaje = 1;
				}
			}

			if(image2.id == '/images/colorear/rabia/enojo'){
				var p = {x: e.left, y: e.top};
    			var tp = win.convertPointToView(p, image2);

    			if((-0.02*ancho)<=tp.x && tp.x<=(0.15*ancho) && (-0.10*alto)<=tp.y && tp.y<=(0.20*alto)){
					image2.backgroundColor="red";
					mensaje = 1;
				}
			}

			if(image3.id == '/images/colorear/rabia/enojo'){
				var p = {x: e.left, y: e.top};
    			var tp = win.convertPointToView(p, image3);

    			if((-0.02*ancho)<=tp.x && tp.x<=(0.15*ancho) && (-0.10*alto)<=tp.y && tp.y<=(0.20*alto)){
					image3.backgroundColor="red";
					mensaje = 1;
				}
			}

			if(image4.id == '/images/colorear/rabia/enojo'){
				var p = {x: e.left, y: e.top};
    			var tp = win.convertPointToView(p, image4);

    			if((-0.02*ancho)<=tp.x && tp.x<=(0.15*ancho) && (-0.10*alto)<=tp.y && tp.y<=(0.20*alto)){
					image4.backgroundColor="red";
					mensaje = 1;
				}
			}

			if(image5.id == '/images/colorear/rabia/enojo'){
				var p = {x: e.left, y: e.top};
    			var tp = win.convertPointToView(p, image5);

    			if((-0.02*ancho)<=tp.x && tp.x<=(0.15*ancho) && (-0.10*alto)<=tp.y && tp.y<=(0.20*alto)){
					image5.backgroundColor="red";
					mensaje = 1;
				}
			}

			if(image6.id == '/images/colorear/rabia/enojo'){
				var p = {x: e.left, y: e.top};
    			var tp = win.convertPointToView(p, image6);

    			if((-0.02*ancho)<=tp.x && tp.x<=(0.15*ancho) && (-0.10*alto)<=tp.y && tp.y<=(0.20*alto)){
					image6.backgroundColor="red";
					mensaje = 1;
				}
			}

			if(image7.id == '/images/colorear/rabia/enojo'){
				var p = {x: e.left, y: e.top};
    			var tp = win.convertPointToView(p, image7);

    			if((-0.02*ancho)<=tp.x && tp.x<=(0.15*ancho) && (-0.10*alto)<=tp.y && tp.y<=(0.20*alto)){
					image7.backgroundColor="red";
					mensaje = 1;
				}
			}

			if(image8.id == '/images/colorear/rabia/enojo'){
				var p = {x: e.left, y: e.top};
    			var tp = win.convertPointToView(p, image8);

    			if((-0.02*ancho)<=tp.x && tp.x<=(0.15*ancho) && (-0.10*alto)<=tp.y && tp.y<=(0.20*alto)){
					image8.backgroundColor="red";
					mensaje = 1;
				}
			}

			if(mensaje == 1){
				alert('Muy bien!!!!');
				rojo.setTop('35%');
				rojo.setLeft('85%');
			}
			else{
				alert('intentalo nuevamente!!!');
				rojo.setTop('35%');
				rojo.setLeft('85%');
			}

});

amarillo.addEventListener('end', function(e){
			alert('intentalo nuevamente!!!');
			amarillo.setTop(amarillo.getTop());
			amarillo.setLeft(amarillo.getLeft());

});

azul.addEventListener('end', function(e){
			alert('intentalo nuevamente!!!');
			azul.setTop(azul.getTop());
			azul.setLeft(azul.getLeft());

});

verde.addEventListener('end', function(e){
			alert('intentalo nuevamente!!!');
			verde.setTop(verde.getTop());
			verde.setLeft(verde.getLeft());

});

win.open();
<|endoftext|>


<commit_before>import { expect } from 'chai';

describe('RetryHelper', function() {
	const RetryHelper = require('./RetryHelper');

	it('should have defaults', function(done) {
		const retryHelper = new RetryHelper();
		expect(retryHelper._maxRetries).to.equal(1);
		expect(retryHelper._retryIntervalMs).to.equal(50);
		done();
	});

	it('should load settings', function(done) {
		const retryHelper = new RetryHelper({ maxRetries: 3, retryIntervalMs: 10 });
		expect(retryHelper._maxRetries).to.equal(3);
		expect(retryHelper._retryIntervalMs).to.equal(10);
		done();
	});

	it('should have function "retry"', function(done) {
		const retryHelper = new RetryHelper({ maxRetries: 3 });
		expect(retryHelper.retry).to.exist;
		expect(typeof retryHelper.retry).to.equal('function');
		done();
	});

	it('should run once', function(done) {
		const retryHelper = new RetryHelper({ maxRetries: 3 });
		let runCount = 0;

		function task(cb) {
			if (typeof cb !== 'function') {
				throw new Error('Invalid cb value!');
			}

			runCount++;
			cb(null, 'whee');
		}

		retryHelper.retry(task, function(err, results) {
			expect(err).not.to.exist;
			expect(results).to.equal('whee');
			expect(runCount).to.equal(1);

			done();
		});
	});

	it('should run three times', function(done) {
		const retryHelper = new RetryHelper({ maxRetries: 3 });
		let runCount = 0;

		function task(cb) {
			if (typeof cb !== 'function') {
				throw new Error('Invalid cb value!');
			}

			runCount++;
			if (runCount < 3) {
				cb('error');
			} else {
				cb(null, 'whee');
			}
		}

		retryHelper.retry(task, function(err, results) {
			expect(err).not.to.exist;
			expect(results).to.equal('whee');
			expect(runCount).to.equal(3);

			done();
		});
	});

	it('should run and return error', function(done) {
		const retryHelper = new RetryHelper({ maxRetries: 3 });

		function task(cb) {
			if (typeof cb !== 'function') {
				throw new Error('Invalid cb value!');
			}

			cb('error');
		}

		retryHelper.retry(task, function(err, results) {
			expect(err).to.equal('error');
			expect(results).not.to.exist;

			done();
		});
	});
});
<commit_msg>speed up a couple slow tests<commit_after>import { expect } from 'chai';

describe('RetryHelper', function() {
	const RetryHelper = require('./RetryHelper');

	it('should have defaults', function(done) {
		const retryHelper = new RetryHelper();
		expect(retryHelper._maxRetries).to.equal(1);
		expect(retryHelper._retryIntervalMs).to.equal(50);
		done();
	});

	it('should load settings', function(done) {
		const retryHelper = new RetryHelper({ maxRetries: 3, retryIntervalMs: 10 });
		expect(retryHelper._maxRetries).to.equal(3);
		expect(retryHelper._retryIntervalMs).to.equal(10);
		done();
	});

	it('should have function "retry"', function(done) {
		const retryHelper = new RetryHelper({ maxRetries: 3 });
		expect(retryHelper.retry).to.exist;
		expect(typeof retryHelper.retry).to.equal('function');
		done();
	});

	it('should run once', function(done) {
		const retryHelper = new RetryHelper({ maxRetries: 3 });
		let runCount = 0;

		function task(cb) {
			if (typeof cb !== 'function') {
				throw new Error('Invalid cb value!');
			}

			runCount++;
			cb(null, 'whee');
		}

		retryHelper.retry(task, function(err, results) {
			expect(err).not.to.exist;
			expect(results).to.equal('whee');
			expect(runCount).to.equal(1);

			done();
		});
	});

	it('should run three times', function(done) {
		const retryHelper = new RetryHelper({ maxRetries: 3, retryIntervalMs: 25 });
		let runCount = 0;

		function task(cb) {
			if (typeof cb !== 'function') {
				throw new Error('Invalid cb value!');
			}

			runCount++;
			if (runCount < 3) {
				cb('error');
			} else {
				cb(null, 'whee');
			}
		}

		retryHelper.retry(task, function(err, results) {
			expect(err).not.to.exist;
			expect(results).to.equal('whee');
			expect(runCount).to.equal(3);

			done();
		});
	});

	it('should run and return error', function(done) {
		const retryHelper = new RetryHelper({ maxRetries: 3, retryIntervalMs: 25 });

		function task(cb) {
			if (typeof cb !== 'function') {
				throw new Error('Invalid cb value!');
			}

			cb('error');
		}

		retryHelper.retry(task, function(err, results) {
			expect(err).to.equal('error');
			expect(results).not.to.exist;

			done();
		});
	});
});
<|endoftext|>


<commit_before><commit_msg>AG-xxx small changes to docs, got example working on both main page and website<commit_after><|endoftext|>


<commit_before>describe("", function() {});
describe("Function Expressions", function() {
	describe("Function Declarations", function() {
		it("The function name property can return the identifier of the function", function() {
			function functionName() {};
			expect(functionName.name).toBe('functionName');
		});
		it("Function declarations are hoisted to the top to be read before code executes", function() {
			// helloWorld is called
			helloWorld();
			expect(helloWorld()).toBe("hello world");
			// even though helloWorld() is called before it's declared, it works because of hoisting
			function helloWorld() {
				return "hello world";
			};
		});
		describe("Function expressions", function() {
			describe("looks like a normal variable assignment", function() {});
			describe("The function created is considered an anonymous function because it has no identifier after the function keyword, so the name keyword is an empty string", function() {});
			it("They act like other expressions, and must be assigned before usage", function() {
				expect(function() {

					helloWorld() // invoking helloWorld() and we expect it to throw an error

				}).toThrow(new Error("undefined is not a function"));

				var helloWorld = function() {
					return "Hello world";
				};

			});
			it("This code, written with function declarations, will behave unexpectedly", function() {

					var isTrue = true;
					if (isTrue == true) {
						function helloWorld() {
							return "hello";
						};
					} else {
						function helloWorld() {
							return "world";
						};
					};
					expect(helloWorld()).toBe("world");
					// Most browsers will return the second case no matter what.
					// when the javascript is loaded, remember that it looks through the script to hoist functions
					// the second declaration overwrites the first declaration
			});
			it("Using function expressions, this performs as we might expect", function() {
				var isTrue = true;
				if (isTrue == true) {
					var helloWorld = function() {
						return "hello";
					};
				} else {
					var helloWorld = function() {
						return "world";
					};
				};
				expect(helloWorld()).toBe("hello");
				// this one assigns the correct function expression to the variable helloWorld based on condition
			});
		});
		describe("Any time a function is being used as avalue, it is a function expression", function() {});
	}); // Function Declarations end
	describe("Recursion", function() {
		it("A classic factorial recursive function", function() {
			function factorial(num) {
				if (num <= 1) {
					return 1;
				} else {
					return num * factorial(num - 1);
				};
			};
			expect(factorial(5)).toEqual(120);
		});
		it("You can prevent a recursive function from functioning by changing the reference to the original function", function() {
			function factorial(num) {
				if (num <= 1) {
					return 1;
				} else {
					return num * factorial(num - 1);
				};
			};

			var anotherFactorial = factorial;
			factorial = null;

			expect(function(){

				anotherFactorial(5)

			}).toThrow(new TypeError("object is not a function"));

			//this doesn't work because when factorial is set to null, only one reference to the original function remains
			// therefore when anotherFactorial() is called, it will try to invoke factorial() which is null

		});
		it("You can use arguments.callee, a pointer to the functon being executed, to call a function recursively", function() {
			function factorial(num) {
				if (num <= 1) {
					return 1;
				} else {
					return num * arguments.callee(num - 1); // using arguments.callee will ensure that the function will work regardless of how its accessed
				};
			};
			var anotherFactorial = factorial;
			factorial = null;
			expect(anotherFactorial(5)).toEqual(120);
		});

		it("arguments.callee will not work in strict mode.  Instead, use named function expressions", function() {
			var factorial = (function f(num) {
				if (num <= 1) {
					return 1;
				} else {
					return num * f(num -1);
				};
			});

			expect(factorial(5)).toBe(120);
			// a named function express, f() is created and assigned to the variable factorial
			// the name f remains the same if the funtion is assigned to another variable, so the recursive call will always execute correctly

		});

	}); // Recursion end
	describe("Closures", function() {
		function compare(num1, num2) {
			if (num1 < num2) {
				return -1;
			} else if (num1 > num2) {
				return 1;
			} else {
				return 0;
			};
		};
		describe("are functions that have access to  variables from another function's scope", function() {});
		describe("When a function is called, an execution context is created, and its scope chain created", function() {});
		describe("The activation object for the function is initialized with values for arguments and any named arguments", function() {});
		describe("Then the outer function's activation object is the second object in the scope chain, and this continues until the scope chain reaches the global execution context", function() {});
		it("For example", function() {
			var result = compare(5,10);
			// this defines a function named compare() that is called in the global execution context
			// when compare is called, a new activation object is created that contains arguments, num1, and num2
			// the global execution context's variable object is next in the compare() execution context's scope chain, and it contains this, result, and compare
		});
		describe("An object represents the variables in each execution context.", function() {});
		describe("A global context's variable object always exists, while a local context variable object exist while the function is executed", function() {});
		describe("when defining a function, its scope chain is created, loaded with the global variable object, and saved to the [[scope]] property", function() {});
		describe("When the function is called, an execution context is created and its scope chain is built up by coping the objects in the function's [[scope]] property", function() {});
		describe("Then, the activation object, which also acts as a variable object, is created and pushed to the front of the context's scope chain", function() {});
		describe("when the function is done executing, the local activation object is destroyed.  Closures, on the other hand...", function() {});
		function createComparisonFunction(propertyName) {
			return function(object1, object2) {
				var value1 = object1[propertyName];
				var value2 = object2[propertyName];

				if (value1 < value2) {
					return -1;
				} else if (value1 > value2) {
					return 1;
				} else {
					return 0;
				};
			};
		};
		it("An anonymous function's scope chain contains a reference to the activation object for the outer function", function() {
			var compare = createComparisonFunction("name");
			var result = compare({name:"Quincy"}, {name: "Ruth"});
			expect(result).toEqual(-1);
		});
		describe("when the anonymous function is returned from createComparisonFunction(), its scope chain has been initialized to contain the activation object from createComparisonFunction() and the global variable object", function() {});
		describe("and the activation object from createComparisonFunction() cannot be destroyed until the function finishes executing, because the reference still exists in the anonymous function's scope chain", function() {});
		it("after createComparisonFunction() completes, the scope chain for its execution context is destroyed, but its activation object will remain in memory until the anonymous function is destroyed", function() {
			// the comparison function is created and stored in the variable compareNames
			var compareNames = createComparisonFunction("name");
			// call function
			var result = compareNames({name:"Quincy"}, {name: "Ruth"});
			expect(result).toEqual(-1);

			// deference function so memory can be reclaimed
			compareNames = null;
			expect(compareNames).toBe(null);
		});
		describe("Closures and variables", function() {
// TO DO -- REDO THIS ONE, pg 225
			it("The closure always gest te last value from the containing function, as they store a reference for the entire variable object, not just a particular variable", function() {
				function createFunctions() {
					var result = new Array();
					for (var i = 0; i < 10; i++) {
						result[i] = function() {
							return i;
						};
					};
					return result;
				};

				function createFunctionsCorrect() {
					var result = new Array();

					for (var i = 0; i < 10; i++) {
						result[i] = function(num) {
							return function() {
								return num;
							};
						}(i);
					};
					return result;
				};
			});
		}); // closures and variables end

		describe("The this object", function() {
			it("is bound at runtime based on the context which a function is executed, so within an anonymous function, is not bound to the object, but window/undefined", function() {
				// no var here to make contextName global
				contextName = "The window";
				var obj = {
					contextName: "My object",
					getName: function() {
						return function() {
							return this.contextName;
						};
					}
				};
				// getName() returns an anonymous function, which returns this.contextName;
				// because getName returns a function, calling getName()() immediately calls the function that is returned, which returns a string
				// each function automatically gets two variables as soon as the function is called: this, and arguments
				// an inner function can't access these variables directly from an outer function
				// but we can allow a closure to access a different this object by storing it in another variable that the closure can have acccess to
				expect(obj.getName()()).toBe("The window");
			});
			it(" we can allow a closure to access a different this object by storing it (saving the reference) in another variable that the closure can have acccess to", function() {
				contextName = "The window";
				var obj = {
					contextName: "My object",
					getName: function() {
						var that = this; 					// that
						return function() {
							return that.contextName;   // that
						};
					}
				};
				expect(obj.getName()()).toBe("My object");
				// before defining the anonymous function, a variable named that is assigned to the this object
				// therefore when the closure is defined, the slocure has access to that, because it is in the containing function
				// even after the function has returned, that is still bound to obj, therefore when we call obj.getName()() it requires "my object"
			});
			it("A few other ways to call a method and their effect on a this object", function() {
				contextName = "The window";
				var obj = {
					contextName: "My object",
					getName: function() {
						return this.contextName;
					}
				};

				expect(   obj.getName()                   ).toBe("My object"); // calls the method the normal way, and returns "My object" because this.name is the same as object.name
				expect(   (obj.getName())                 ).toBe("My object");  // places () around objecet.getName, which is actually equivalent to without the ()
				expect(   (obj.getName = obj.getName)()   ).toBe("The window"); // it performs an assignment.  then calls the result.  the value of this assignment expression is the expression itself, (a function expression), so the this value is not maintained, and "The window" is returned

			});

		}); // this object end

	}); // Closures end

}); // Function Expressions end

<commit_msg>add - mimicking block scope<commit_after>describe("", function() {});
describe("Function Expressions", function() {
	describe("Function Declarations", function() {
		it("The function name property can return the identifier of the function", function() {
			function functionName() {};
			expect(functionName.name).toBe('functionName');
		});
		it("Function declarations are hoisted to the top to be read before code executes", function() {
			// helloWorld is called
			helloWorld();
			expect(helloWorld()).toBe("hello world");
			// even though helloWorld() is called before it's declared, it works because of hoisting
			function helloWorld() {
				return "hello world";
			};
		});
		describe("Function expressions", function() {
			describe("looks like a normal variable assignment", function() {});
			describe("The function created is considered an anonymous function because it has no identifier after the function keyword, so the name keyword is an empty string", function() {});
			it("They act like other expressions, and must be assigned before usage", function() {
				expect(function() {

					helloWorld() // invoking helloWorld() and we expect it to throw an error

				}).toThrow(new Error("undefined is not a function"));

				var helloWorld = function() {
					return "Hello world";
				};

			});
			it("This code, written with function declarations, will behave unexpectedly", function() {

					var isTrue = true;
					if (isTrue == true) {
						function helloWorld() {
							return "hello";
						};
					} else {
						function helloWorld() {
							return "world";
						};
					};
					expect(helloWorld()).toBe("world");
					// Most browsers will return the second case no matter what.
					// when the javascript is loaded, remember that it looks through the script to hoist functions
					// the second declaration overwrites the first declaration
			});
			it("Using function expressions, this performs as we might expect", function() {
				var isTrue = true;
				if (isTrue == true) {
					var helloWorld = function() {
						return "hello";
					};
				} else {
					var helloWorld = function() {
						return "world";
					};
				};
				expect(helloWorld()).toBe("hello");
				// this one assigns the correct function expression to the variable helloWorld based on condition
			});
		});
		describe("Any time a function is being used as avalue, it is a function expression", function() {});
	}); // Function Declarations end
	describe("Recursion", function() {
		it("A classic factorial recursive function", function() {
			function factorial(num) {
				if (num <= 1) {
					return 1;
				} else {
					return num * factorial(num - 1);
				};
			};
			expect(factorial(5)).toEqual(120);
		});
		it("You can prevent a recursive function from functioning by changing the reference to the original function", function() {
			function factorial(num) {
				if (num <= 1) {
					return 1;
				} else {
					return num * factorial(num - 1);
				};
			};

			var anotherFactorial = factorial;
			factorial = null;

			expect(function(){

				anotherFactorial(5)

			}).toThrow(new TypeError("object is not a function"));

			//this doesn't work because when factorial is set to null, only one reference to the original function remains
			// therefore when anotherFactorial() is called, it will try to invoke factorial() which is null

		});
		it("You can use arguments.callee, a pointer to the functon being executed, to call a function recursively", function() {
			function factorial(num) {
				if (num <= 1) {
					return 1;
				} else {
					return num * arguments.callee(num - 1); // using arguments.callee will ensure that the function will work regardless of how its accessed
				};
			};
			var anotherFactorial = factorial;
			factorial = null;
			expect(anotherFactorial(5)).toEqual(120);
		});

		it("arguments.callee will not work in strict mode.  Instead, use named function expressions", function() {
			var factorial = (function f(num) {
				if (num <= 1) {
					return 1;
				} else {
					return num * f(num -1);
				};
			});

			expect(factorial(5)).toBe(120);
			// a named function express, f() is created and assigned to the variable factorial
			// the name f remains the same if the funtion is assigned to another variable, so the recursive call will always execute correctly

		});

	}); // Recursion end
	describe("Closures", function() {
		function compare(num1, num2) {
			if (num1 < num2) {
				return -1;
			} else if (num1 > num2) {
				return 1;
			} else {
				return 0;
			};
		};
		describe("are functions that have access to  variables from another function's scope", function() {});
		describe("When a function is called, an execution context is created, and its scope chain created", function() {});
		describe("The activation object for the function is initialized with values for arguments and any named arguments", function() {});
		describe("Then the outer function's activation object is the second object in the scope chain, and this continues until the scope chain reaches the global execution context", function() {});
		it("For example", function() {
			var result = compare(5,10);
			// this defines a function named compare() that is called in the global execution context
			// when compare is called, a new activation object is created that contains arguments, num1, and num2
			// the global execution context's variable object is next in the compare() execution context's scope chain, and it contains this, result, and compare
		});
		describe("An object represents the variables in each execution context.", function() {});
		describe("A global context's variable object always exists, while a local context variable object exist while the function is executed", function() {});
		describe("when defining a function, its scope chain is created, loaded with the global variable object, and saved to the [[scope]] property", function() {});
		describe("When the function is called, an execution context is created and its scope chain is built up by coping the objects in the function's [[scope]] property", function() {});
		describe("Then, the activation object, which also acts as a variable object, is created and pushed to the front of the context's scope chain", function() {});
		describe("when the function is done executing, the local activation object is destroyed.  Closures, on the other hand...", function() {});
		function createComparisonFunction(propertyName) {
			return function(object1, object2) {
				var value1 = object1[propertyName];
				var value2 = object2[propertyName];

				if (value1 < value2) {
					return -1;
				} else if (value1 > value2) {
					return 1;
				} else {
					return 0;
				};
			};
		};
		it("An anonymous function's scope chain contains a reference to the activation object for the outer function", function() {
			var compare = createComparisonFunction("name");
			var result = compare({name:"Quincy"}, {name: "Ruth"});
			expect(result).toEqual(-1);
		});
		describe("when the anonymous function is returned from createComparisonFunction(), its scope chain has been initialized to contain the activation object from createComparisonFunction() and the global variable object", function() {});
		describe("and the activation object from createComparisonFunction() cannot be destroyed until the function finishes executing, because the reference still exists in the anonymous function's scope chain", function() {});
		it("after createComparisonFunction() completes, the scope chain for its execution context is destroyed, but its activation object will remain in memory until the anonymous function is destroyed", function() {
			// the comparison function is created and stored in the variable compareNames
			var compareNames = createComparisonFunction("name");
			// call function
			var result = compareNames({name:"Quincy"}, {name: "Ruth"});
			expect(result).toEqual(-1);

			// deference function so memory can be reclaimed
			compareNames = null;
			expect(compareNames).toBe(null);
		});
		describe("Closures and variables", function() {
// TO DO -- REDO THIS ONE, pg 225
			it("The closure always gest te last value from the containing function, as they store a reference for the entire variable object, not just a particular variable", function() {
				function createFunctions() {
					var result = new Array();
					for (var i = 0; i < 10; i++) {
						result[i] = function() {
							return i;
						};
					};
					return result;
				};

				function createFunctionsCorrect() {
					var result = new Array();

					for (var i = 0; i < 10; i++) {
						result[i] = function(num) {
							return function() {
								return num;
							};
						}(i);
					};
					return result;
				};
			});
		}); // closures and variables end

		describe("The this object", function() {
			it("is bound at runtime based on the context which a function is executed, so within an anonymous function, is not bound to the object, but window/undefined", function() {
				// no var here to make contextName global
				contextName = "The window";
				var obj = {
					contextName: "My object",
					getName: function() {
						return function() {
							return this.contextName;
						};
					}
				};
				// getName() returns an anonymous function, which returns this.contextName;
				// because getName returns a function, calling getName()() immediately calls the function that is returned, which returns a string
				// each function automatically gets two variables as soon as the function is called: this, and arguments
				// an inner function can't access these variables directly from an outer function
				// but we can allow a closure to access a different this object by storing it in another variable that the closure can have acccess to
				expect(obj.getName()()).toBe("The window");
			});
			it(" we can allow a closure to access a different this object by storing it (saving the reference) in another variable that the closure can have acccess to", function() {
				contextName = "The window";
				var obj = {
					contextName: "My object",
					getName: function() {
						var that = this; 					// that
						return function() {
							return that.contextName;   // that
						};
					}
				};
				expect(obj.getName()()).toBe("My object");
				// before defining the anonymous function, a variable named that is assigned to the this object
				// therefore when the closure is defined, the slocure has access to that, because it is in the containing function
				// even after the function has returned, that is still bound to obj, therefore when we call obj.getName()() it requires "my object"
			});
			it("A few other ways to call a method and their effect on a this object", function() {
				contextName = "The window";
				var obj = {
					contextName: "My object",
					getName: function() {
						return this.contextName;
					}
				};

				expect(   obj.getName()                   ).toBe("My object"); // calls the method the normal way, and returns "My object" because this.name is the same as object.name
				expect(   (obj.getName())                 ).toBe("My object");  // places () around objecet.getName, which is actually equivalent to without the ()
				expect(   (obj.getName = obj.getName)()   ).toBe("The window"); // it performs an assignment.  then calls the result.  the value of this assignment expression is the expression itself, (a function expression), so the this value is not maintained, and "The window" is returned
			});

		}); // this object end

	}); // Closures end
	describe("Memory leaks", function() {
//TO DO, pg 227
	});
	describe("Mimicking block scope", function() {
		it("The syntax for an anonymous function used as a block/private scope/immediately invoked function is", function() {
			(function(){
				// block code
			})();
			// this is a function declaration inside () to indicate that it's actually a function expression, and then invoked with the second set of () at the end
		});
		it("A less confusing view of the same syntax", function() {
			// A less confusing view of the same syntax:
			var someFunction = function() {  // 1
				// block code
			};
			someFunction();	// 2

			// 1 a function expression. an anonymous function is created and assigned to the var someFunction
			// 2 invoking it.
		});
		it("Refactoring it, a function declaration can't have () at the end of it, throwing an error", function() {
			// refactor

			// if we do this, it will throw an error because javascript sees the fuction keyword as the beginning of a function declaration, and they cannot be followed by ()
			// function() {
			// 	// block code
			// }()
		});
		it("but function expressions can be surrounded by (), therefore to turn a function declaration into an expression, we just surround it with ()", function() {
			(function() {
				// block code
			})();
		});
		it("A block/private scope/immediately invoked function can be used where variables are needed temporarily", function() {
			function outputNumbers(count) {
				(function() {
					for (var i =0; i < count; i++) {
						return i;
					};
				})();
				expect(function() {

					i;

				}).toThrow(new ReferenceError("i is not defined"));

				//outside of the function, the i var is no longer accessible
			};
			outputNumbers(5);
		});

	});
	describe("Private variables", function() {

	});
	describe("Static Private variables", function() {

	});
	describe("The module pattern", function() {

	});
	describe("The Module-Augmentation pattern", function() {

	});

}); // Function Expressions end

<|endoftext|>


<commit_before>/**
 * modules/analytics data store: profiles.
 *
 * Site Kit by Google, Copyright 2020 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * External dependencies
 */
import invariant from 'invariant';
import { groupBy } from 'lodash';

/**
 * Internal dependencies
 */
import API from 'googlesitekit-api';
import Data from 'googlesitekit-data';
import { STORE_NAME } from './index';
import { isValidAccountID, isValidPropertyID } from '../util';
import { PROFILE_CREATE } from './constants';

// Actions
const FETCH_CREATE_PROFILE = 'FETCH_CREATE_PROFILE';
const FETCH_CREATE_PROFILE_STARTED = 'FETCH_CREATE_PROFILE_STARTED';
const FETCH_PROFILES = 'FETCH_PROFILES';
const FETCH_PROFILES_STARTED = 'FETCH_PROFILES_STARTED';
const RECEIVE_CREATE_PROFILE = 'RECEIVE_CREATE_PROFILE';
const RECEIVE_CREATE_PROFILE_FAILED = 'RECEIVE_CREATE_PROFILE_FAILED';
const RECEIVE_PROFILES = 'RECEIVE_PROFILES';
const RECEIVE_PROFILES_COMPLETED = 'RECEIVE_PROFILES_COMPLETED';
const RECEIVE_PROFILES_FAILED = 'RECEIVE_PROFILES_FAILED';

export const INITIAL_STATE = {
	isFetchingCreateProfile: {},
	isFetchingProfiles: {},
	profiles: {},
};

export const actions = {
	/**
	 * Creates a new Analytics profile.
	 *
	 * Creates a new Analytics profile for an existing Google Analytics
	 * account + property combination.
	 *
	 * @since n.e.x.t
	 *
	 * @param {string} accountID  Google Analytics account ID.
	 * @param {string} propertyID Google Analytics property ID.
	 * @return {Function} Generator function action.
	 */
	*createProfile( accountID, propertyID ) {
		invariant( accountID, 'accountID is required.' );
		invariant( propertyID, 'propertyID is required.' );

		try {
			const profile = yield actions.fetchCreateProfile( accountID, propertyID );

			const registry = yield Data.commonActions.getRegistry();
			registry.dispatch( STORE_NAME ).setProfileID( profile.id );

			return actions.receiveCreateProfile( { accountID, propertyID, profile } );
		} catch ( error ) {
			// TODO: Implement an error handler store or some kind of centralized
			// place for error dispatch...
			return actions.receiveCreateProfileFailed( { accountID, error, propertyID } );
		}
	},

	*fetchCreateProfile( accountID, propertyID ) {
		yield {
			payload: { accountID, propertyID },
			type: FETCH_CREATE_PROFILE_STARTED,
		};

		return {
			payload: { accountID, propertyID },
			type: FETCH_CREATE_PROFILE,
		};
	},

	*fetchProfiles( accountID, propertyID ) {
		yield {
			payload: { accountID, propertyID },
			type: FETCH_PROFILES_STARTED,
		};

		return {
			payload: { accountID, propertyID },
			type: FETCH_PROFILES,
		};
	},

	/**
	 * Adds a property to the data store.
	 *
	 * Adds the newly-created property to the existing properties in
	 * the data store.
	 *
	 * @since n.e.x.t
	 * @private
	 *
	 * @param {Object} args            Argument params.
	 * @param {string} args.accountID  Google Analytics account ID.
	 * @param {string} args.propertyID Google Analytics profile ID.
	 * @param {Object} args.profile    Google Analytics profile object.
	 * @return {Object} Redux-style action.
	 */
	receiveCreateProfile( { accountID, propertyID, profile } ) {
		invariant( accountID, 'accountID is required.' );
		invariant( propertyID, 'propertyID is required.' );
		invariant( profile, 'profile is required.' );

		return {
			payload: { accountID, propertyID, profile },
			type: RECEIVE_CREATE_PROFILE,
		};
	},

	/**
	 * Logs an error with profile creation.
	 *
	 * @since n.e.x.t
	 * @private
	 *
	 * @param {Object} args            Argument params.
	 * @param {string} args.accountID  Google Analytics account ID.
	 * @param {string} args.propertyID Google Analytics property ID.
	 * @param {Object} args.error      Error object.
	 * @return {Object} Redux-style action.
	 */
	receiveCreateProfileFailed( { accountID, propertyID, error } ) {
		invariant( accountID, 'accountID is required.' );
		invariant( propertyID, 'propertyID is required.' );
		invariant( error, 'error is required.' );

		return {
			payload: { accountID, error, propertyID },
			type: RECEIVE_CREATE_PROFILE_FAILED,
		};
	},

	receiveProfiles( profiles ) {
		invariant( Array.isArray( profiles ), 'profiles must be an array.' );

		return {
			payload: { profiles },
			type: RECEIVE_PROFILES,
		};
	},

	receiveProfilesCompleted( accountID, propertyID ) {
		invariant( accountID, 'accountID is required.' );
		invariant( propertyID, 'propertyID is required.' );

		return {
			payload: { accountID, propertyID },
			type: RECEIVE_PROFILES_COMPLETED,
		};
	},

	receiveProfilesFailed( { accountID, error, propertyID } ) {
		invariant( accountID, 'accountID is required.' );
		invariant( error, 'error is required.' );
		invariant( propertyID, 'propertyID is required.' );

		return {
			payload: { accountID, error, propertyID },
			type: RECEIVE_PROFILES_FAILED,
		};
	},

	/**
	 * Sets a profile based on given account and property IDs.
	 *
	 * @since n.e.x.t
	 * @private
	 *
	 * @param {?Object} args Optional supporting arguments.
	 * @param {?string} args.accountID Account ID.
	 * @param {?string} args.property Property ID.
	 */
	*setProfileForProperty( { accountID, propertyID } = {} ) {
		const registry = yield Data.commonActions.getRegistry();

		if ( isValidAccountID( accountID ) && isValidPropertyID( propertyID ) ) {
			const profiles = registry.select( STORE_NAME ).getProfiles( accountID, propertyID ) || [];
			const matchedProfile = profiles.find( ( { webPropertyId } ) => webPropertyId === propertyID ) || { id: PROFILE_CREATE };

			registry.dispatch( STORE_NAME ).setProfileID( matchedProfile.id );
		} else {
			registry.dispatch( STORE_NAME ).setProfileID( PROFILE_CREATE );
		}
	},
};

export const controls = {
	[ FETCH_CREATE_PROFILE ]: ( { payload: { accountID, propertyID } } ) => {
		return API.set( 'modules', 'analytics', 'create-profile', {
			accountID,
			propertyID,
		} );
	},
	[ FETCH_PROFILES ]: ( { payload: { accountID, propertyID } } ) => {
		return API.get( 'modules', 'analytics', 'profiles', {
			accountID,
			propertyID,
		}, {
			useCache: false,
		} );
	},
};

export const reducer = ( state, { type, payload } ) => {
	switch ( type ) {
		case FETCH_CREATE_PROFILE_STARTED: {
			const { accountID, propertyID } = payload;

			return {
				...state,
				isFetchingCreateProfile: {
					...state.isFetchingCreateProfile,
					[ `${ accountID }::${ propertyID }` ]: true,
				},
			};
		}

		case FETCH_PROFILES_STARTED: {
			const { accountID, propertyID } = payload;

			return {
				...state,
				isFetchingProfiles: {
					...state.isFetchingProfiles,
					[ `${ accountID }::${ propertyID }` ]: true,
				},
			};
		}

		case RECEIVE_CREATE_PROFILE: {
			const { accountID, propertyID, profile } = payload;

			return {
				...state,
				isFetchingCreateProfile: {
					...state.isFetchingCreateProfile,
					[ `${ accountID }::${ propertyID }` ]: false,
				},
				profiles: {
					...state.profiles,
					[ `${ accountID }::${ propertyID }` ]: [
						...( state.profiles[ `${ accountID }::${ propertyID }` ] || [] ),
						profile,
					],
				},
			};
		}

		case RECEIVE_CREATE_PROFILE_FAILED: {
			const { accountID, error, propertyID } = payload;

			return {
				...state,
				error,
				isFetchingCreateProfile: {
					...state.isFetchingCreateProfile,
					[ `${ accountID }::${ propertyID }` ]: false,
				},
			};
		}

		case RECEIVE_PROFILES: {
			const { profiles } = payload;

			return {
				...state,
				profiles: {
					...state.profiles,
					...groupBy( profiles, ( { accountId, webPropertyId } ) => `${ accountId }::${ webPropertyId }` ), // Capitalization rule exception: `accountId` and `webPropertyId` are properties of an API returned value.
				},
			};
		}

		case RECEIVE_PROFILES_COMPLETED: {
			const { accountID, propertyID } = payload;

			return {
				...state,
				isFetchingProfiles: {
					...state.isFetchingProfiles,
					[ `${ accountID }::${ propertyID }` ]: false,
				},
			};
		}

		case RECEIVE_PROFILES_FAILED: {
			const { accountID, error, propertyID } = payload;

			return {
				...state,
				error,
				isFetchingProfiles: {
					...state.isFetchingProfiles,
					[ `${ accountID }::${ propertyID }` ]: false,
				},
			};
		}

		default: {
			return { ...state };
		}
	}
};

export const resolvers = {
	*getProfiles( accountID, propertyID ) {
		if ( ! isValidAccountID( accountID ) || ! isValidPropertyID( propertyID ) ) {
			return;
		}

		try {
			const registry = yield Data.commonActions.getRegistry();

			const existingProfiles = registry.select( STORE_NAME ).getProfiles( accountID, propertyID );

			// If there are already profiles loaded in state for this request; consider it fulfilled
			// and don't make an API request.
			if ( existingProfiles ) {
				return;
			}

			const profiles = yield actions.fetchProfiles( accountID, propertyID );

			yield actions.receiveProfiles( profiles );

			return actions.receiveProfilesCompleted( accountID, propertyID );
		} catch ( error ) {
			// TODO: Implement an error handler store or some kind of centralized
			// place for error dispatch...
			return actions.receiveProfilesFailed( { accountID, propertyID, error } );
		}
	},
};

export const selectors = {
	/**
	 * Get all Google Analytics profiles this user account+property has available.
	 *
	 * Returns an array of all profiles.
	 *
	 * Returns `undefined` if accounts have not yet loaded.
	 *
	 * @since n.e.x.t
	 *
	 * @param {Object} state      Data store's state.
	 * @param {string} accountID  The Analytics Account ID to fetch profiles for.
	 * @param {string} propertyID The Analytics Property ID to fetch profiles for.
	 * @return {?Array.<Object>} An array of Analytics profiles; `undefined` if not loaded.
	 */
	getProfiles( state, accountID, propertyID ) {
		const { profiles } = state;

		return profiles[ `${ accountID }::${ propertyID }` ];
	},

	/**
	 * Check if a profile is being created for an account and property.
	 *
	 * @since n.e.x.t
	 *
	 * @param {Object} state      Data store's state.
	 * @param {string} accountID  The Analytics Account ID to check for profile creation.
	 * @param {string} propertyID The Analytics Property ID to check for profile creation.
	 * @return {boolean} `true` if creating a profile, `false` if not.
	 */
	isDoingCreateProfile( state, accountID, propertyID ) {
		const { isFetchingCreateProfile } = state;

		return !! isFetchingCreateProfile[ `${ accountID }::${ propertyID }` ];
	},
};

export default {
	INITIAL_STATE,
	actions,
	controls,
	reducer,
	resolvers,
	selectors,
};
<commit_msg>Add isDoingGetProfiles selector.<commit_after>/**
 * modules/analytics data store: profiles.
 *
 * Site Kit by Google, Copyright 2020 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * External dependencies
 */
import invariant from 'invariant';
import { groupBy } from 'lodash';

/**
 * Internal dependencies
 */
import API from 'googlesitekit-api';
import Data from 'googlesitekit-data';
import { STORE_NAME } from './index';
import { isValidAccountID, isValidPropertyID } from '../util';
import { PROFILE_CREATE } from './constants';

// Actions
const FETCH_CREATE_PROFILE = 'FETCH_CREATE_PROFILE';
const FETCH_CREATE_PROFILE_STARTED = 'FETCH_CREATE_PROFILE_STARTED';
const FETCH_PROFILES = 'FETCH_PROFILES';
const FETCH_PROFILES_STARTED = 'FETCH_PROFILES_STARTED';
const RECEIVE_CREATE_PROFILE = 'RECEIVE_CREATE_PROFILE';
const RECEIVE_CREATE_PROFILE_FAILED = 'RECEIVE_CREATE_PROFILE_FAILED';
const RECEIVE_PROFILES = 'RECEIVE_PROFILES';
const RECEIVE_PROFILES_COMPLETED = 'RECEIVE_PROFILES_COMPLETED';
const RECEIVE_PROFILES_FAILED = 'RECEIVE_PROFILES_FAILED';

export const INITIAL_STATE = {
	isFetchingCreateProfile: {},
	isFetchingProfiles: {},
	profiles: {},
};

export const actions = {
	/**
	 * Creates a new Analytics profile.
	 *
	 * Creates a new Analytics profile for an existing Google Analytics
	 * account + property combination.
	 *
	 * @since n.e.x.t
	 *
	 * @param {string} accountID  Google Analytics account ID.
	 * @param {string} propertyID Google Analytics property ID.
	 * @return {Function} Generator function action.
	 */
	*createProfile( accountID, propertyID ) {
		invariant( accountID, 'accountID is required.' );
		invariant( propertyID, 'propertyID is required.' );

		try {
			const profile = yield actions.fetchCreateProfile( accountID, propertyID );

			const registry = yield Data.commonActions.getRegistry();
			registry.dispatch( STORE_NAME ).setProfileID( profile.id );

			return actions.receiveCreateProfile( { accountID, propertyID, profile } );
		} catch ( error ) {
			// TODO: Implement an error handler store or some kind of centralized
			// place for error dispatch...
			return actions.receiveCreateProfileFailed( { accountID, error, propertyID } );
		}
	},

	*fetchCreateProfile( accountID, propertyID ) {
		yield {
			payload: { accountID, propertyID },
			type: FETCH_CREATE_PROFILE_STARTED,
		};

		return {
			payload: { accountID, propertyID },
			type: FETCH_CREATE_PROFILE,
		};
	},

	*fetchProfiles( accountID, propertyID ) {
		yield {
			payload: { accountID, propertyID },
			type: FETCH_PROFILES_STARTED,
		};

		return {
			payload: { accountID, propertyID },
			type: FETCH_PROFILES,
		};
	},

	/**
	 * Adds a property to the data store.
	 *
	 * Adds the newly-created property to the existing properties in
	 * the data store.
	 *
	 * @since n.e.x.t
	 * @private
	 *
	 * @param {Object} args            Argument params.
	 * @param {string} args.accountID  Google Analytics account ID.
	 * @param {string} args.propertyID Google Analytics profile ID.
	 * @param {Object} args.profile    Google Analytics profile object.
	 * @return {Object} Redux-style action.
	 */
	receiveCreateProfile( { accountID, propertyID, profile } ) {
		invariant( accountID, 'accountID is required.' );
		invariant( propertyID, 'propertyID is required.' );
		invariant( profile, 'profile is required.' );

		return {
			payload: { accountID, propertyID, profile },
			type: RECEIVE_CREATE_PROFILE,
		};
	},

	/**
	 * Logs an error with profile creation.
	 *
	 * @since n.e.x.t
	 * @private
	 *
	 * @param {Object} args            Argument params.
	 * @param {string} args.accountID  Google Analytics account ID.
	 * @param {string} args.propertyID Google Analytics property ID.
	 * @param {Object} args.error      Error object.
	 * @return {Object} Redux-style action.
	 */
	receiveCreateProfileFailed( { accountID, propertyID, error } ) {
		invariant( accountID, 'accountID is required.' );
		invariant( propertyID, 'propertyID is required.' );
		invariant( error, 'error is required.' );

		return {
			payload: { accountID, error, propertyID },
			type: RECEIVE_CREATE_PROFILE_FAILED,
		};
	},

	receiveProfiles( profiles ) {
		invariant( Array.isArray( profiles ), 'profiles must be an array.' );

		return {
			payload: { profiles },
			type: RECEIVE_PROFILES,
		};
	},

	receiveProfilesCompleted( accountID, propertyID ) {
		invariant( accountID, 'accountID is required.' );
		invariant( propertyID, 'propertyID is required.' );

		return {
			payload: { accountID, propertyID },
			type: RECEIVE_PROFILES_COMPLETED,
		};
	},

	receiveProfilesFailed( { accountID, error, propertyID } ) {
		invariant( accountID, 'accountID is required.' );
		invariant( error, 'error is required.' );
		invariant( propertyID, 'propertyID is required.' );

		return {
			payload: { accountID, error, propertyID },
			type: RECEIVE_PROFILES_FAILED,
		};
	},

	/**
	 * Sets a profile based on given account and property IDs.
	 *
	 * @since n.e.x.t
	 * @private
	 *
	 * @param {?Object} args Optional supporting arguments.
	 * @param {?string} args.accountID Account ID.
	 * @param {?string} args.property Property ID.
	 */
	*setProfileForProperty( { accountID, propertyID } = {} ) {
		const registry = yield Data.commonActions.getRegistry();

		if ( isValidAccountID( accountID ) && isValidPropertyID( propertyID ) ) {
			const profiles = registry.select( STORE_NAME ).getProfiles( accountID, propertyID ) || [];
			const matchedProfile = profiles.find( ( { webPropertyId } ) => webPropertyId === propertyID ) || { id: PROFILE_CREATE };

			registry.dispatch( STORE_NAME ).setProfileID( matchedProfile.id );
		} else {
			registry.dispatch( STORE_NAME ).setProfileID( PROFILE_CREATE );
		}
	},
};

export const controls = {
	[ FETCH_CREATE_PROFILE ]: ( { payload: { accountID, propertyID } } ) => {
		return API.set( 'modules', 'analytics', 'create-profile', {
			accountID,
			propertyID,
		} );
	},
	[ FETCH_PROFILES ]: ( { payload: { accountID, propertyID } } ) => {
		return API.get( 'modules', 'analytics', 'profiles', {
			accountID,
			propertyID,
		}, {
			useCache: false,
		} );
	},
};

export const reducer = ( state, { type, payload } ) => {
	switch ( type ) {
		case FETCH_CREATE_PROFILE_STARTED: {
			const { accountID, propertyID } = payload;

			return {
				...state,
				isFetchingCreateProfile: {
					...state.isFetchingCreateProfile,
					[ `${ accountID }::${ propertyID }` ]: true,
				},
			};
		}

		case FETCH_PROFILES_STARTED: {
			const { accountID, propertyID } = payload;

			return {
				...state,
				isFetchingProfiles: {
					...state.isFetchingProfiles,
					[ `${ accountID }::${ propertyID }` ]: true,
				},
			};
		}

		case RECEIVE_CREATE_PROFILE: {
			const { accountID, propertyID, profile } = payload;

			return {
				...state,
				isFetchingCreateProfile: {
					...state.isFetchingCreateProfile,
					[ `${ accountID }::${ propertyID }` ]: false,
				},
				profiles: {
					...state.profiles,
					[ `${ accountID }::${ propertyID }` ]: [
						...( state.profiles[ `${ accountID }::${ propertyID }` ] || [] ),
						profile,
					],
				},
			};
		}

		case RECEIVE_CREATE_PROFILE_FAILED: {
			const { accountID, error, propertyID } = payload;

			return {
				...state,
				error,
				isFetchingCreateProfile: {
					...state.isFetchingCreateProfile,
					[ `${ accountID }::${ propertyID }` ]: false,
				},
			};
		}

		case RECEIVE_PROFILES: {
			const { profiles } = payload;

			return {
				...state,
				profiles: {
					...state.profiles,
					...groupBy( profiles, ( { accountId, webPropertyId } ) => `${ accountId }::${ webPropertyId }` ), // Capitalization rule exception: `accountId` and `webPropertyId` are properties of an API returned value.
				},
			};
		}

		case RECEIVE_PROFILES_COMPLETED: {
			const { accountID, propertyID } = payload;

			return {
				...state,
				isFetchingProfiles: {
					...state.isFetchingProfiles,
					[ `${ accountID }::${ propertyID }` ]: false,
				},
			};
		}

		case RECEIVE_PROFILES_FAILED: {
			const { accountID, error, propertyID } = payload;

			return {
				...state,
				error,
				isFetchingProfiles: {
					...state.isFetchingProfiles,
					[ `${ accountID }::${ propertyID }` ]: false,
				},
			};
		}

		default: {
			return { ...state };
		}
	}
};

export const resolvers = {
	*getProfiles( accountID, propertyID ) {
		if ( ! isValidAccountID( accountID ) || ! isValidPropertyID( propertyID ) ) {
			return;
		}

		try {
			const registry = yield Data.commonActions.getRegistry();

			const existingProfiles = registry.select( STORE_NAME ).getProfiles( accountID, propertyID );

			// If there are already profiles loaded in state for this request; consider it fulfilled
			// and don't make an API request.
			if ( existingProfiles ) {
				return;
			}

			const profiles = yield actions.fetchProfiles( accountID, propertyID );

			yield actions.receiveProfiles( profiles );

			return actions.receiveProfilesCompleted( accountID, propertyID );
		} catch ( error ) {
			// TODO: Implement an error handler store or some kind of centralized
			// place for error dispatch...
			return actions.receiveProfilesFailed( { accountID, propertyID, error } );
		}
	},
};

export const selectors = {
	/**
	 * Get all Google Analytics profiles this user account+property has available.
	 *
	 * Returns an array of all profiles.
	 *
	 * Returns `undefined` if accounts have not yet loaded.
	 *
	 * @since n.e.x.t
	 *
	 * @param {Object} state      Data store's state.
	 * @param {string} accountID  The Analytics Account ID to fetch profiles for.
	 * @param {string} propertyID The Analytics Property ID to fetch profiles for.
	 * @return {?Array.<Object>} An array of Analytics profiles; `undefined` if not loaded.
	 */
	getProfiles( state, accountID, propertyID ) {
		const { profiles } = state;

		return profiles[ `${ accountID }::${ propertyID }` ];
	},

	/**
	 * Check if a profile is being created for an account and property.
	 *
	 * @since n.e.x.t
	 *
	 * @param {Object} state      Data store's state.
	 * @param {string} accountID  The Analytics Account ID to check for profile creation.
	 * @param {string} propertyID The Analytics Property ID to check for profile creation.
	 * @return {boolean} `true` if creating a profile, `false` if not.
	 */
	isDoingCreateProfile( state, accountID, propertyID ) {
		const { isFetchingCreateProfile } = state;

		return !! isFetchingCreateProfile[ `${ accountID }::${ propertyID }` ];
	},

	/**
	 * Checks if profiles are being fetched for the given account and property.
	 *
	 * @since n.e.x.t
	 *
	 * @param {Object} state     Data store's state.
	 * @param {string} accountID  The Analytics Account ID to check for profile fetching.
	 * @param {string} propertyID The Analytics Property ID to check for profile fetching.
	 * @return {boolean} `true` if fetching a profiles, `false` if not.
	 */
	isDoingGetProfiles( state, accountID, propertyID ) {
		const { isFetchingProfiles } = state;

		return !! isFetchingProfiles[ `${ accountID }::${ propertyID }` ];
	},
};

export default {
	INITIAL_STATE,
	actions,
	controls,
	reducer,
	resolvers,
	selectors,
};
<|endoftext|>


<commit_before>
'use strict';

const path = require('path');

const angularUtils = require('../utils/angular-utils');

'use strict';

module.exports = {
    meta: {
        docs: {
            description: 'enforce naming modules after folder path to file',
            category: 'Stylistic Issues',
            recommended: false,
        },
        schema: [
            {
                type: 'object',
                properties: {
                    basePath: {
                        type: 'string',
                    }
                },
                additionalProperties: false,
            }
        ]
    },
    create,
};

function create(context) {
    const options = context.options[0] || { basePath: '' };

    return {
        CallExpression: gatherAngularModuleInfos,
    };

    function gatherAngularModuleInfos(node) {
        if (!angularUtils.isAngularModuleDeclaration(node) || !context.getFilename().includes(options.basePath)) {
            return;
        }

        const moduleName = node.arguments[0].value;

        const filePath = path.relative(options.basePath, context.getFilename());
        const filePathInfo = path.parse(filePath)
        const directoryPortion = filePathInfo.dir.split('/').join('.');
        const expectedModuleName = `${directoryPortion}${directoryPortion.length ? '.' : '' }${filePathInfo.base.split('.')[0]}`;
        if (moduleName !== expectedModuleName) {
            context.report(node, `Module name must be "${expectedModuleName}"`);
        }

    }
};<commit_msg>Improve angular-module-name error message<commit_after>
'use strict';

const path = require('path');

const angularUtils = require('../utils/angular-utils');

'use strict';

module.exports = {
    meta: {
        docs: {
            description: 'enforce naming modules after folder path to file',
            category: 'Stylistic Issues',
            recommended: false,
        },
        schema: [
            {
                type: 'object',
                properties: {
                    basePath: {
                        type: 'string',
                    }
                },
                additionalProperties: false,
            }
        ]
    },
    create,
};

function create(context) {
    const options = context.options[0] || { basePath: '' };

    return {
        CallExpression: gatherAngularModuleInfos,
    };

    function gatherAngularModuleInfos(node) {
        if (!angularUtils.isAngularModuleDeclaration(node) || !context.getFilename().includes(options.basePath)) {
            return;
        }

        const moduleName = node.arguments[0].value;

        const filePath = path.relative(options.basePath, context.getFilename());
        const filePathInfo = path.parse(filePath)
        const directoryPortion = filePathInfo.dir.split('/').join('.');
        const expectedModuleName = `${directoryPortion}${directoryPortion.length ? '.' : '' }${filePathInfo.base.split('.')[0]}`;
        if (moduleName !== expectedModuleName) {
            context.report(node, `Module name must be "${expectedModuleName}" to match folder hierarchy`);
        }

    }
};<|endoftext|>