<commit_before>dojo.provide("dijit.TitlePane");

dojo.require("dojo.fx");

dojo.require("dijit._Templated");
dojo.require("dijit.layout.ContentPane");

dojo.declare(
	"dijit.TitlePane",
	[dijit.layout.ContentPane, dijit._Templated],
{
	// summary: A pane with a title on top, that can be opened or collapsed.
	//
	// description: An accessible container with a Title Heading, and a content
	//	section that slides open and closed. TitlePane is an extension to
	//	ContentPane, providing all the usesful content-control aspects from.
	//
	// example:
	// | 	// load a TitlePane from remote file:
	// |	var foo = new dijit.TitlePane({ href: "foobar.html", title:"Title" });
	// |	foo.startup();
	//
	// example:
	// |
	// |

	//
	// example:
	// |
	// |

	// |
I am content

	// |

	// title: String
	//		Title of the pane
	title: "",

	// open: Boolean
	//		Whether pane is opened or closed.
	open: true,

	// duration: Integer
	//		Time in milliseconds to fade in/fade out
	duration: dijit.defaultDuration,

	// baseClass: String
	//	The root className to use for the various states of this widget
	baseClass: "dijitTitlePane",

	templatePath: dojo.moduleUrl("dijit", "templates/TitlePane.html"),

	attributeMap: dojo.mixin(dojo.clone(dijit.layout.ContentPane.prototype.attributeMap), {
		title: {node: "titleNode", type: "innerHTML" }
	}),

	postCreate: function(){
		if(!this.open){
			this.hideNode.style.display = this.wipeNode.style.display = "none";
		}
		this._setCss();
		dojo.setSelectable(this.titleNode, false);
		dijit.setWaiState(this.containerNode, "labelledby", this.titleNode.id);
		dijit.setWaiState(this.focusNode, "haspopup", "true");

		// setup open/close animations
		var hideNode = this.hideNode, wipeNode = this.wipeNode;
		this._wipeIn = dojo.fx.wipeIn({
			node: this.wipeNode,
			duration: this.duration,
			beforeBegin: function(){
				hideNode.style.display="";
			}
		});
		this._wipeOut = dojo.fx.wipeOut({
			node: this.wipeNode,
			duration: this.duration,
			onEnd: function(){
				hideNode.style.display="none";
			}
		});
		this.inherited(arguments);
	},

	_setOpenAttr: function(/* Boolean */ open){
		// summary:
		//		Hook to make attr("open", boolean) control the open/closed state of the pane.
		// open: Boolean
		//		True if you want to open the pane, false if you want to close it.
		if(this.open !== open){ this.toggle(); }
	},

	_setContentAttr: function(content){
		// summary:
		//		Hook to make attr("content", ...) work.
		// 		Typically called when an href is loaded.  Our job is to make the animation smooth

		if(!this.open || this._wipeOut.status() == "playing"){
			// we are currently *closing* the pane (or the pane is closed), so just let that continue
			this.inherited(arguments);
		}else{
			if(this._wipeIn.status() == "playing"){
				this._wipeIn.stop();
			}

			// freeze container at current height so that adding new content doesn't make it jump
			dojo.marginBox(this.wipeNode, { h: dojo.marginBox(this.wipeNode).h });

			// add the new content (erasing the old content, if any)
			this.inherited(arguments);

			// call _wipeIn.play() to animate from current height to new height
			this._wipeIn.play();
		}
	},

	toggle: function(){
		// summary: switches between opened and closed state
		dojo.forEach([this._wipeIn, this._wipeOut], function(animation){
			if(animation.status() == "playing"){
				animation.stop();
			}
		});

		this[this.open ? "_wipeOut" : "_wipeIn"].play();
		this.open =! this.open;

		// load content (if this is the first time we are opening the TitlePane
		// and content is specified as an href, or href was set when hidden)
		this._loadCheck();

		this._setCss();
	},

	_setCss: function(){
		// summary: set the open/close css state for the TitlePane
		var classes = ["dijitClosed", "dijitOpen"];
		var boolIndex = this.open;
		var node = this.titleBarNode || this.focusNode;
		dojo.removeClass(node, classes[!boolIndex+0]);
		node.className += " " + classes[boolIndex+0];

		// provide a character based indicator for images-off mode
		this.arrowNodeInner.innerHTML = this.open ? "-" : "+";
	},

	_onTitleKey: function(/*Event*/ e){
		// summary: callback when user hits a key
		if(e.charOrCode == dojo.keys.ENTER || e.charOrCode == ' '){
			this.toggle();
		}else if(e.charOrCode == dojo.keys.DOWN_ARROW && this.open){
			this.containerNode.focus();
			e.preventDefault();
	 	}
	},

	_handleFocus: function(/*Event*/ e){
		// summary: handle blur and focus for this widget

		// add/removeClass is safe to call without hasClass in this case
		dojo[(e.type == "focus" ? "addClass" : "removeClass")](this.focusNode, this.baseClass + "Focused");
	},

	setTitle: function(/*String*/ title){
		// summary: sets the text of the title
		dojo.deprecated("dijit.TitlePane.setTitle() is deprecated.  Use attr('title', ...) instead.", "", "2.0");
		this.titleNode.innerHTML = title;
	}
});
<commit_msg>Refs #7244 - do not execute animations if they have not been set up yet<commit_after>dojo.provide("dijit.TitlePane");

dojo.require("dojo.fx");

dojo.require("dijit._Templated");
dojo.require("dijit.layout.ContentPane");

dojo.declare(
	"dijit.TitlePane",
	[dijit.layout.ContentPane, dijit._Templated],
{
	// summary: A pane with a title on top, that can be opened or collapsed.
	//
	// description: An accessible container with a Title Heading, and a content
	//	section that slides open and closed. TitlePane is an extension to
	//	ContentPane, providing all the usesful content-control aspects from.
	//
	// example:
	// | 	// load a TitlePane from remote file:
	// |	var foo = new dijit.TitlePane({ href: "foobar.html", title:"Title" });
	// |	foo.startup();
	//
	// example:
	// |
	// |

	//
	// example:
	// |
	// |

	// |
I am content

	// |

	// title: String
	//		Title of the pane
	title: "",

	// open: Boolean
	//		Whether pane is opened or closed.
	open: true,

	// duration: Integer
	//		Time in milliseconds to fade in/fade out
	duration: dijit.defaultDuration,

	// baseClass: String
	//	The root className to use for the various states of this widget
	baseClass: "dijitTitlePane",

	templatePath: dojo.moduleUrl("dijit", "templates/TitlePane.html"),

	attributeMap: dojo.mixin(dojo.clone(dijit.layout.ContentPane.prototype.attributeMap), {
		title: {node: "titleNode", type: "innerHTML" }
	}),

	postCreate: function(){
		if(!this.open){
			this.hideNode.style.display = this.wipeNode.style.display = "none";
		}
		this._setCss();
		dojo.setSelectable(this.titleNode, false);
		dijit.setWaiState(this.containerNode, "labelledby", this.titleNode.id);
		dijit.setWaiState(this.focusNode, "haspopup", "true");

		// setup open/close animations
		var hideNode = this.hideNode, wipeNode = this.wipeNode;
		this._wipeIn = dojo.fx.wipeIn({
			node: this.wipeNode,
			duration: this.duration,
			beforeBegin: function(){
				hideNode.style.display="";
			}
		});
		this._wipeOut = dojo.fx.wipeOut({
			node: this.wipeNode,
			duration: this.duration,
			onEnd: function(){
				hideNode.style.display="none";
			}
		});
		this.inherited(arguments);
	},

	_setOpenAttr: function(/* Boolean */ open){
		// summary:
		//		Hook to make attr("open", boolean) control the open/closed state of the pane.
		// open: Boolean
		//		True if you want to open the pane, false if you want to close it.
		if(this.open !== open){ this.toggle(); }
	},

	_setContentAttr: function(content){
		// summary:
		//		Hook to make attr("content", ...) work.
		// 		Typically called when an href is loaded.  Our job is to make the animation smooth

		if(!this.open || !this._wipeOut || this._wipeOut.status() == "playing"){
			// we are currently *closing* the pane (or the pane is closed), so just let that continue
			this.inherited(arguments);
		}else{
			if(this._wipeIn && this._wipeIn.status() == "playing"){
				this._wipeIn.stop();
			}

			// freeze container at current height so that adding new content doesn't make it jump
			dojo.marginBox(this.wipeNode, { h: dojo.marginBox(this.wipeNode).h });

			// add the new content (erasing the old content, if any)
			this.inherited(arguments);

			// call _wipeIn.play() to animate from current height to new height
			if(this._wipeIn){
				this._wipeIn.play();
			}else{
				this.hideNode.style.display = "";
			}
		}
	},

	toggle: function(){
		// summary: switches between opened and closed state
		dojo.forEach([this._wipeIn, this._wipeOut], function(animation){
			if(animation && animation.status() == "playing"){
				animation.stop();
			}
		});

		var anim = this[this.open ? "_wipeOut" : "_wipeIn"]
		if(anim){
			anim.play();
		}else{
			this._hideNode.style.display = this.open ? "" : "none";
		}
		this.open =! this.open;

		// load content (if this is the first time we are opening the TitlePane
		// and content is specified as an href, or href was set when hidden)
		this._loadCheck();

		this._setCss();
	},

	_setCss: function(){
		// summary: set the open/close css state for the TitlePane
		var classes = ["dijitClosed", "dijitOpen"];
		var boolIndex = this.open;
		var node = this.titleBarNode || this.focusNode;
		dojo.removeClass(node, classes[!boolIndex+0]);
		node.className += " " + classes[boolIndex+0];

		// provide a character based indicator for images-off mode
		this.arrowNodeInner.innerHTML = this.open ? "-" : "+";
	},

	_onTitleKey: function(/*Event*/ e){
		// summary: callback when user hits a key
		if(e.charOrCode == dojo.keys.ENTER || e.charOrCode == ' '){
			this.toggle();
		}else if(e.charOrCode == dojo.keys.DOWN_ARROW && this.open){
			this.containerNode.focus();
			e.preventDefault();
	 	}
	},

	_handleFocus: function(/*Event*/ e){
		// summary: handle blur and focus for this widget

		// add/removeClass is safe to call without hasClass in this case
		dojo[(e.type == "focus" ? "addClass" : "removeClass")](this.focusNode, this.baseClass + "Focused");
	},

	setTitle: function(/*String*/ title){
		// summary: sets the text of the title
		dojo.deprecated("dijit.TitlePane.setTitle() is deprecated.  Use attr('title', ...) instead.", "", "2.0");
		this.titleNode.innerHTML = title;
	}
});
<|endoftext|>


<commit_before>/**
 * Created by Salvador on 4/9/2015.
 */
var util = {
  b: null,
  p: null,

  status: function() {
    if (arguments.length === 0) {
      return util.b[util.p[0]][util.p[1]];
    } else {
      var arg = arguments[0];
      return util.b[arg[0]][arg[1]];
    }

  },

  is: {
    Free: function() {
      if (arguments.length === 0) {
        return util.status() === "null";
      } else {
        return util.status(arguments[0]) === "null";
      }
    },

    Bot: function() {
      if (arguments.length === 0) {
        return util.status() === "P";
      } else {
        return util.status(arguments[0]) === "P";
      }
    },

    Other: function() {
      if (arguments.length === 0) {
        return util.status() === "C";
      } else {
        return util.status(arguments[0]) === "C";
      }
    },

    Visited: function() {
      if (arguments.length === 0) {
        return util.status() === "XP";
      } else {
        return util.status(arguments[0]) === "XP";
      }
    },

    oVisited: function() {
      if (arguments.length === 0) {
        return util.status() === "XC";
      } else {
        return util.status(arguments[0]) === "XC";
      }
    }
  },

  area: {
    raw: {
      aRawL: function() {
        var p;
        if (arguments.length === 0) {
          p = util.p;
        } else {
          p = arguments[0];
        }

        return [p[0], p[1] - 1];
      },

      aRawT: function() {
        var p;
        if (arguments.length === 0) {
          p = util.p;
        } else {
          p = arguments[0];
        }

        return [p[0] - 1, p[1]];
      },

      aRawR: function() {
        var p;
        if (arguments.length === 0) {
          p = util.p;
        } else {
          p = arguments[0];
        }

        return [p[0], p[1] + 1];
      },

      aRawB: function() {
        var p;
        if (arguments.length === 0) {
          p = util.p;
        } else {
          p = arguments[0];
        }

        return [p[0] + 1, p[1]];
      }
    },

    refined: {
      arL: function() {
        var rawCoord;
        if (arguments.length === 0) {
          rawCoord = util.area.raw.aRawL();
        } else {
          rawCoord = util.area.raw.aRawL(arguments[0]);
        }

        if (rawCoord[1] < 0) {
          rawCoord = null;
        }

        return rawCoord;
      },

      arT: function() {
        var rawCoord;
        if (arguments.length === 0) {
          rawCoord = util.area.raw.aRawT();
        } else {
          rawCoord = util.area.raw.aRawT(arguments[0]);
        }

        if (rawCoord[0] < 0) {
          rawCoord = null;
        }

        return rawCoord;
      },

      arR: function() {
        var rawCoord;
        if (arguments.length === 0) {
          rawCoord = util.area.raw.aRawR();
        } else {
          rawCoord = util.area.raw.aRawR(arguments[0]);
        }

        if (rawCoord[1] < 0) {
          rawCoord = null;
        }

        return rawCoord;
      },

      arB: function() {
        var rawCoord;
        if (arguments.length === 0) {
          rawCoord = util.area.raw.aRawB();
        } else {
          rawCoord = util.area.raw.aRawB(arguments[0]);
        }

        if (rawCoord[0] < 0) {
          rawCoord = null;
        }

        return rawCoord;
      },

      all: function() {
        if (arguments.length === 0) {
          return [util.area.refined.arL(), util.area.refined.arT(), util.area.refined.arR(), util.area.refined.arB()];
        } else {
          var arg = arguments[0];
          return [util.area.refined.arL(arg), util.area.refined.arT(arg), util.area.refined.arR(arg), util.area.refined.arB(arg)];
        }
      }
    },

    bleed: function() {
      var around = [];
      var refinedAreas;
      if (arguments.length === 0) {
        refinedAreas = util.area.refined.all();
      } else {
        refinedAreas = util.area.refined.all(arguments[0]);
      }

      for (var dir in refinedAreas) {
        if (dir !== null) {
          around.push(dir);
        }
      }
      return around;
    }
  },

  dir: {
    canLeft: function() {
      var left;
      if (arguments.length === 0) {
        left = util.area.raw.aRawL();
      } else {
        left = util.area.raw.aRawL(arguments[0]);
      }

      return left[1] !== -1 && util.is.Free(left);
    },

    canTop: function() {
      var top;
      if (arguments.length === 0) {
        top = util.area.raw.aRawT();
      } else {
        top = util.area.raw.aRawT(arguments[0]);
      }

      return top[0] !== -1 && util.is.Free(top);
    },

    canBot: function() {
      var bot;
      if (arguments.length === 0) {
        bot = util.area.raw.aRawT();
      } else {
        bot = util.area.raw.aRawT(arguments[0]);
      }

      return bot[0] !== 16 && util.is.Free(bot);
    },

    canRight: function() {
      var right;
      if (arguments.length === 0) {
        right = util.area.raw.aRawT();
      } else {
        right = util.area.raw.aRawT(arguments[0]);
      }

      return right[1] !== 16 && util.is.Free(right);
    }
  }
};

// INPUT:  board   -> [RxC] multidimensional array with each element being either
//                    "null" (empty space),
//                    "P"    (your bot)
//                    "C"    (opponent bot),
//                    "XP"   (visited space by your bot),
//                    "XC"   (visited space by opponent bot)
//         bot_loc -> [r,c] where r=row and c=col of your bot on board
//         opp_loc -> [r,c] where r=row and c=col of opponent on board
// OUTPUT: N, E, S, W which signifies which direction to move

(function bot(board, bot_loc, opp_loc) {
  util.b = board;
  util.p = bot_loc;

  if (util.dir.canTop()) {
    return "N";
  } else if (util.dir.canLeft()) {
    return "W";
  } else if (util.dir.canBot()) {
    return "S";
  } else {
    return "E";
  }

})(readline());<commit_msg>added argument funcs to handle param existence<commit_after>/**
 * Created by Salvador on 4/9/2015.
 */
var util = {
  b: null,
  p: null,

  status: function() {
    if (arguments.length === 0) {
      return util.b[util.p[0]][util.p[1]];
    } else {
      var arg = arguments[0];
      return util.b[arg[0]][arg[1]];
    }

  },

  argue: {
    defArg: function(args, defVal) {
      if (args.length === 0) {
        return defVal;
      } else {
        return args[0];
      }
    },

    defArgP: function(args) {
      if (args.length === 0) {
        return util.p;
      } else {
        return args[0];
      }
    },

    retVal: function(func, args) {
      if (args.length === 0) {
        return func();
      } else {
        return func(args[0]);
      }
    }
  },

  is: {
    Free: function() {
      if (arguments.length === 0) {
        return util.status() === "null";
      } else {
        return util.status(arguments[0]) === "null";
      }
    },

    Bot: function() {
      if (arguments.length === 0) {
        return util.status() === "P";
      } else {
        return util.status(arguments[0]) === "P";
      }
    },

    Other: function() {
      if (arguments.length === 0) {
        return util.status() === "C";
      } else {
        return util.status(arguments[0]) === "C";
      }
    },

    Visited: function() {
      if (arguments.length === 0) {
        return util.status() === "XP";
      } else {
        return util.status(arguments[0]) === "XP";
      }
    },

    oVisited: function() {
      if (arguments.length === 0) {
        return util.status() === "XC";
      } else {
        return util.status(arguments[0]) === "XC";
      }
    }
  },

  area: {
    raw: {
      aRawL: function() {
        var p;
        if (arguments.length === 0) {
          p = util.p;
        } else {
          p = arguments[0];
        }

        return [p[0], p[1] - 1];
      },

      aRawT: function() {
        var p;
        if (arguments.length === 0) {
          p = util.p;
        } else {
          p = arguments[0];
        }

        return [p[0] - 1, p[1]];
      },

      aRawR: function() {
        var p;
        if (arguments.length === 0) {
          p = util.p;
        } else {
          p = arguments[0];
        }

        return [p[0], p[1] + 1];
      },

      aRawB: function() {
        var p;
        if (arguments.length === 0) {
          p = util.p;
        } else {
          p = arguments[0];
        }

        return [p[0] + 1, p[1]];
      }
    },

    refined: {
      arL: function() {
        var rawCoord;
        if (arguments.length === 0) {
          rawCoord = util.area.raw.aRawL();
        } else {
          rawCoord = util.area.raw.aRawL(arguments[0]);
        }

        if (rawCoord[1] < 0) {
          rawCoord = null;
        }

        return rawCoord;
      },

      arT: function() {
        var rawCoord;
        if (arguments.length === 0) {
          rawCoord = util.area.raw.aRawT();
        } else {
          rawCoord = util.area.raw.aRawT(arguments[0]);
        }

        if (rawCoord[0] < 0) {
          rawCoord = null;
        }

        return rawCoord;
      },

      arR: function() {
        var rawCoord;
        if (arguments.length === 0) {
          rawCoord = util.area.raw.aRawR();
        } else {
          rawCoord = util.area.raw.aRawR(arguments[0]);
        }

        if (rawCoord[1] < 0) {
          rawCoord = null;
        }

        return rawCoord;
      },

      arB: function() {
        var rawCoord;
        if (arguments.length === 0) {
          rawCoord = util.area.raw.aRawB();
        } else {
          rawCoord = util.area.raw.aRawB(arguments[0]);
        }

        if (rawCoord[0] < 0) {
          rawCoord = null;
        }

        return rawCoord;
      },

      all: function() {
        if (arguments.length === 0) {
          return [util.area.refined.arL(), util.area.refined.arT(), util.area.refined.arR(), util.area.refined.arB()];
        } else {
          var arg = arguments[0];
          return [util.area.refined.arL(arg), util.area.refined.arT(arg), util.area.refined.arR(arg), util.area.refined.arB(arg)];
        }
      }
    },

    bleed: function() {
      var around = [];
      var refinedAreas;
      if (arguments.length === 0) {
        refinedAreas = util.area.refined.all();
      } else {
        refinedAreas = util.area.refined.all(arguments[0]);
      }

      for (var dir in refinedAreas) {
        if (dir !== null) {
          around.push(dir);
        }
      }
      return around;
    }
  },

  dir: {
    canLeft: function() {
      var left;
      if (arguments.length === 0) {
        left = util.area.raw.aRawL();
      } else {
        left = util.area.raw.aRawL(arguments[0]);
      }

      return left[1] !== -1 && util.is.Free(left);
    },

    canTop: function() {
      var top;
      if (arguments.length === 0) {
        top = util.area.raw.aRawT();
      } else {
        top = util.area.raw.aRawT(arguments[0]);
      }

      return top[0] !== -1 && util.is.Free(top);
    },

    canBot: function() {
      var bot;
      if (arguments.length === 0) {
        bot = util.area.raw.aRawT();
      } else {
        bot = util.area.raw.aRawT(arguments[0]);
      }

      return bot[0] !== 16 && util.is.Free(bot);
    },

    canRight: function() {
      var right;
      if (arguments.length === 0) {
        right = util.area.raw.aRawT();
      } else {
        right = util.area.raw.aRawT(arguments[0]);
      }

      return right[1] !== 16 && util.is.Free(right);
    }
  }
};

// INPUT:  board   -> [RxC] multidimensional array with each element being either
//                    "null" (empty space),
//                    "P"    (your bot)
//                    "C"    (opponent bot),
//                    "XP"   (visited space by your bot),
//                    "XC"   (visited space by opponent bot)
//         bot_loc -> [r,c] where r=row and c=col of your bot on board
//         opp_loc -> [r,c] where r=row and c=col of opponent on board
// OUTPUT: N, E, S, W which signifies which direction to move

(function bot(board, bot_loc, opp_loc) {
  util.b = board;
  util.p = bot_loc;

  if (util.dir.canTop()) {
    return "N";
  } else if (util.dir.canLeft()) {
    return "W";
  } else if (util.dir.canBot()) {
    return "S";
  } else {
    return "E";
  }

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


<commit_before>/** @module deliteful/ViewStack */
define(["dcl/dcl",
	"decor/sniff",
	"lie/dist/lie",
	"requirejs-dplugins/jquery!attributes/classes",
	"delite/register",
	"delite/DisplayContainer",
	"delite/theme!./ViewStack/themes/{{theme}}/ViewStack.css",
	"requirejs-dplugins/css!./ViewStack/transitions/slide.css",
	"requirejs-dplugins/css!./ViewStack/transitions/reveal.css"],
	function (dcl, has, Promise, $, register, DisplayContainer) {
		function setVisibility(node, val) {
			if (node) {
				if (val) {
					node.style.visibility = "visible";
					node.style.display = "";
				} else {
					node.style.visibility = "hidden";
					node.style.display = "none";
				}
			}
		}
		function setReverse(node) {
			if (node) {
				$(node).addClass("-d-view-stack-reverse");
			}
		}
		function cleanCSS(node) {
			if (node) {
				node.className = node.className.split(/ +/).filter(function (x) {
					return !/^-d-view-stack/.test(x);
				}).join(" ");
			}
		}
		function transitionClass(s) {
			return "-d-view-stack-" + s;
		}

		/**
		 * ViewStack container widget. Display one child at a time.
		 *
		 * The first child is displayed by default.
		 * The methods 'show' is used to change the visible child.
		 *
		 * Styling
		 * The following CSS attributes must not be changed.
		 *  ViewStack node:  position, box-sizing, overflow-x
		 *  ViewStack children:  position, box-sizing, width, height
		 *
		 * @example:
		 * <d-view-stack id="vs">
		 *
...

		 *
...

		 *
...

		 * </d-view-stack>
		 * <button onclick="vs.show(childB, {transition: 'reveal', reverse: true})">...</button>
		 * @class module:deliteful/ViewStack
		 * @augments module:delite/DisplayContainer
		 */
		return register("d-view-stack", [HTMLElement, DisplayContainer],
			/** @lends module:deliteful/ViewStack# */{
			/**
			 * The name of the CSS class of this widget.
			 * @member {string}
			 * @default "d-view-stack"
			 */
			baseClass: "d-view-stack",

			/**
			 * The transition type used if not specified in the second argument of the show method.
			 * Transitions type are: "none", "slide", "reveal", "flip", "fade".
			 * @member {string}
			 * @default "slide"
			 */
			transition: "slide",

			/**
			 * If true, the transition animation is reversed.
			 * This attribute is supported by "slide" and "reveal" transition types.
			 * @member {boolean}
			 * @default false
			 */
			reverse: false,

			/**
			 * The selected child id, can be set explicitly or through the show() method.
			 * The effect of setting this property (i.e. getting the value through the getter) might be
			 * asynchronous when an animated transition occurs.
			 * @member {string}
			 * @default ""
			 */
			selectedChildId: "",

			_pendingChild: null,

			_setSelectedChildIdAttr: function (child) {
				if (this.ownerDocument.getElementById(child)) {
					if (this.attached) {
						this.show(child);
					} else {
						this._pendingChild = child;
					}
				}
			},

			_getSelectedChildIdAttr: function () {
				return this._visibleChild ? this._visibleChild.id : "";
			},

			attachedCallback: function () {
				var noTransition = {transition: "none"};
				if (this._pendingChild) {
					this.show(this._pendingChild, noTransition);
					this._pendingChild = null;
				} else if (this.children.length > 0) {
					this.show(this.children[0], noTransition);
				}
			},

			_timing: 0,

			_setChildrenVisibility: function () {
				var cdn = this.children;
				if (!this._visibleChild && cdn.length > 0) {
					this._visibleChild = cdn[0];
				}
				for (var i = 0; i < cdn.length; i++) {
					setVisibility(cdn[i], cdn[i] === this._visibleChild);
				}
			},

			preRender: function () {
				this._transitionTiming = {default: 0, chrome: 20, ios: 20, android: 100, mozilla: 100};
				for (var o in this._transitionTiming) {
					if (has(o) && this._timing < this._transitionTiming[o]) {
						this._timing = this._transitionTiming[o];
					}
				}
			},

			/*
			 * @private
			 */
			onAddChild: dcl.superCall(function (sup) {
				return function (node) {
					var res = sup.call(this, node);
					this._setChildrenVisibility();
					return res;
				};
			}),

			postRender: function () {
				this._setChildrenVisibility();
			},

			showNext: function (props) {
				//		Shows the next child in the container.
				this._showPreviousNext("nextElementSibling", props);
			},

			showPrevious: function (props) {
				//		Shows the previous child in the container.
				this._showPreviousNext("previousElementSibling", props);
			},

			_showPreviousNext: function (direction, props) {
				if (!this._visibleChild && this.children.length > 0) {
					this._visibleChild = this.children[0];
				}
				if (this._visibleChild) {
					var target = this._visibleChild[direction];
					if (target) {
						this.show(target, props);
					}
				}
			},

			_doTransition: function (origin, target, event, transition, reverse) {
				var promise;
				if (transition !== "none") {
					if (origin) {
						promise = this._setAfterTransitionHandlers(origin);
						$(origin).addClass(transitionClass(transition));
					}
					if (target) {
						promise = this._setAfterTransitionHandlers(target);
						$(target).addClass(transitionClass(transition) + " -d-view-stack-in");
					}
					if (reverse) {
						setReverse(origin);
						setReverse(target);
					}
					this.defer(function () {
						if (target) {
							$(target).addClass("-d-view-stack-transition");
						}
						if (origin) {
							$(origin).addClass("-d-view-stack-transition -d-view-stack-out");
						}
						if (reverse) {
							setReverse(origin);
							setReverse(target);
						}
						if (target) {
							$(target).addClass("-d-view-stack-in");
						}
					}, this._timing);
				} else {
					if (origin !== target) {
						setVisibility(origin, false);
					}
				}
				return Promise.resolve(promise);
			},

			changeDisplay: function (widget, event) {
				// Resolved when display is completed.
				if (!widget || widget.parentNode !== this) {
					return Promise.resolve();
				}

				var origin = this._visibleChild;

				// Needed because the CSS state of a node can be incorrect
				// if a previous transitionend has been dropped
				cleanCSS(origin);
				cleanCSS(widget);

				setVisibility(widget, true);
				this._visibleChild = widget;

				var transition  = (origin === widget) ? "none" : (event.transition || "slide");
				var reverse = this.isLeftToRight() ? event.reverse : !event.reverse;
				return this._doTransition(origin, widget, event, transition, reverse);
			},
			/**
			 * Shows a children of the ViewStack. The parameter 'params' is optional. If not specified,
			 * this.transition, and this.reverse are used.
			 * This method must be called to display a particular destination child on this container.
			 * @param {dest} Widget or HTMLElement or id that points to the child this container must display.
			 * @param {params} Optional params. A hash like {transition: "reveal", reverse: true}. The transition value
			 * can be "slide", "overlay", "fade" or "flip". Reverse transition applies to "slide" and
			 * "reveal". Transition is internally set to "none" if the ViewStack is not visible.
			 * @returns {Promise} A promise that will be resolved when the display and transition effect will have
			 * been performed.
			 */
			show: dcl.superCall(function (sup) {
				return function (dest, params) {
					// Check visibility of the ViewStack, forces transition:"none" if not visible.
					//  - Transitions events are broken if the ViewStack is not visible

					var parent = this;
					while (parent && parent.style.display  !== "none" && parent !== this.ownerDocument.body) {
						parent = parent.parentNode;
					}
					if ((has("ie") === 9) || parent !== this.ownerDocument.body) {
						if (! params) {
							params = {};
						}
						params.transition = "none";
					}

					if (this._visibleChild && this._visibleChild.parentNode !== this) {
						// The visible child has been removed.
						this._visibleChild = null;
					}
					if (!this._visibleChild && this.children.length > 0) {
						// The default visible child is the first one.
						this._visibleChild = this.children[0];
					}
					return sup.apply(this, [dest, params]);
				};
			}),

			_setAfterTransitionHandlers: function (node) {
				var self = this, holder = { node: node};
				holder.promise = new Promise(function (resolve) {
					holder.handle =  function () { self._afterTransitionHandle(holder, resolve); };
				});
				$(this).addClass("-d-view-stack-transition");
				node.addEventListener("webkitTransitionEnd", holder.handle);
				node.addEventListener("transitionend", holder.handle); // IE10 + FF
				return holder.promise;
			},

			_afterTransitionHandle: function (holder, resolve) {
				// Workaround for FF transitionend randomly dropped.
				// This method should be called once, when the target view transition is done.
				// But when 2 transitions (ex: slide) start at the same time, the transitionend event of the
				// target view can be dropped.
				if (!holder.promise.resolved) {
					// First call
					holder.promise.resolved = true;
					var vb;
					for (var i = 0; i < this.children.length; i++) {
						vb = this._visibleChild === this.children[i];
						setVisibility(this.children[i], vb);
						if (!vb) {
							cleanCSS(this.children[i]);
						}
					}

					holder.node.removeEventListener("webkitTransitionEnd", holder.handle);
					holder.node.removeEventListener("transitionend", holder.handle);
					resolve();
				} else {
					// Second call (occurs randomly on FF). The following code is not critical but try
					// to keep a clean DOM tree as much as possible.
					cleanCSS(holder.node);
					if (holder.node !== this._visibleChild) {
						cleanCSS(this._visibleChild);
					}
					$(this).removeClass("-d-view-stack-transition");
				}
			}
		});
	});

<commit_msg>ViewStack: add missing doc of showNext and showPrevious methods. showNext/Previous now return what return the underlying show method.<commit_after>/** @module deliteful/ViewStack */
define(["dcl/dcl",
	"decor/sniff",
	"lie/dist/lie",
	"requirejs-dplugins/jquery!attributes/classes",
	"delite/register",
	"delite/DisplayContainer",
	"delite/theme!./ViewStack/themes/{{theme}}/ViewStack.css",
	"requirejs-dplugins/css!./ViewStack/transitions/slide.css",
	"requirejs-dplugins/css!./ViewStack/transitions/reveal.css"],
	function (dcl, has, Promise, $, register, DisplayContainer) {
		function setVisibility(node, val) {
			if (node) {
				if (val) {
					node.style.visibility = "visible";
					node.style.display = "";
				} else {
					node.style.visibility = "hidden";
					node.style.display = "none";
				}
			}
		}
		function setReverse(node) {
			if (node) {
				$(node).addClass("-d-view-stack-reverse");
			}
		}
		function cleanCSS(node) {
			if (node) {
				node.className = node.className.split(/ +/).filter(function (x) {
					return !/^-d-view-stack/.test(x);
				}).join(" ");
			}
		}
		function transitionClass(s) {
			return "-d-view-stack-" + s;
		}

		/**
		 * ViewStack container widget. Display one child at a time.
		 *
		 * The first child is displayed by default.
		 * The methods 'show' is used to change the visible child.
		 *
		 * Styling
		 * The following CSS attributes must not be changed.
		 *  ViewStack node:  position, box-sizing, overflow-x
		 *  ViewStack children:  position, box-sizing, width, height
		 *
		 * @example:
		 * <d-view-stack id="vs">
		 *
...

		 *
...

		 *
...

		 * </d-view-stack>
		 * <button onclick="vs.show(childB, {transition: 'reveal', reverse: true})">...</button>
		 * @class module:deliteful/ViewStack
		 * @augments module:delite/DisplayContainer
		 */
		return register("d-view-stack", [HTMLElement, DisplayContainer],
			/** @lends module:deliteful/ViewStack# */{
			/**
			 * The name of the CSS class of this widget.
			 * @member {string}
			 * @default "d-view-stack"
			 */
			baseClass: "d-view-stack",

			/**
			 * The transition type used if not specified in the second argument of the show method.
			 * Transitions type are: "none", "slide", "reveal", "flip", "fade".
			 * @member {string}
			 * @default "slide"
			 */
			transition: "slide",

			/**
			 * If true, the transition animation is reversed.
			 * This attribute is supported by "slide" and "reveal" transition types.
			 * @member {boolean}
			 * @default false
			 */
			reverse: false,

			/**
			 * The selected child id, can be set explicitly or through the show() method.
			 * The effect of setting this property (i.e. getting the value through the getter) might be
			 * asynchronous when an animated transition occurs.
			 * @member {string}
			 * @default ""
			 */
			selectedChildId: "",

			_pendingChild: null,

			_setSelectedChildIdAttr: function (child) {
				if (this.ownerDocument.getElementById(child)) {
					if (this.attached) {
						this.show(child);
					} else {
						this._pendingChild = child;
					}
				}
			},

			_getSelectedChildIdAttr: function () {
				return this._visibleChild ? this._visibleChild.id : "";
			},

			attachedCallback: function () {
				var noTransition = {transition: "none"};
				if (this._pendingChild) {
					this.show(this._pendingChild, noTransition);
					this._pendingChild = null;
				} else if (this.children.length > 0) {
					this.show(this.children[0], noTransition);
				}
			},

			_timing: 0,

			_setChildrenVisibility: function () {
				var cdn = this.children;
				if (!this._visibleChild && cdn.length > 0) {
					this._visibleChild = cdn[0];
				}
				for (var i = 0; i < cdn.length; i++) {
					setVisibility(cdn[i], cdn[i] === this._visibleChild);
				}
			},

			preRender: function () {
				this._transitionTiming = {default: 0, chrome: 20, ios: 20, android: 100, mozilla: 100};
				for (var o in this._transitionTiming) {
					if (has(o) && this._timing < this._transitionTiming[o]) {
						this._timing = this._transitionTiming[o];
					}
				}
			},

			/*
			 * @private
			 */
			onAddChild: dcl.superCall(function (sup) {
				return function (node) {
					var res = sup.call(this, node);
					this._setChildrenVisibility();
					return res;
				};
			}),

			postRender: function () {
				this._setChildrenVisibility();
			},

			/**
			 * Shows the immediately following sibling of the ViewStack visible element.
			 * The parameter 'params' is optional. If not specified, this.transition, and this.reverse are used.
			 * @param {params} Optional params. A hash like {transition: "reveal", reverse: true}. The transition value
			 * can be "slide", "overlay", "fade" or "flip". Reverse transition applies to "slide" and
			 * "reveal". Transition is internally set to "none" if the ViewStack is not visible.
			 * @returns {Promise} A promise that will be resolved when the display and transition effect will have
			 * been performed.
			 */
			showNext: function (params) {
				//		Shows the next child in the container.
				return this._showPreviousNext("nextElementSibling", params);
			},

			/**
			 * Shows the immediately preceding sibling of the ViewStack visible element.
			 * The parameter 'params' is optional. If not specified, this.transition, and this.reverse are used.
			 * @param {params} Optional params. A hash like {transition: "reveal", reverse: true}. The transition value
			 * can be "slide", "overlay", "fade" or "flip". Reverse transition applies to "slide" and
			 * "reveal". Transition is internally set to "none" if the ViewStack is not visible.
			 * @returns {Promise} A promise that will be resolved when the display and transition effect will have
			 * been performed.
			 */
			showPrevious: function (params) {
				//		Shows the previous child in the container.
				return this._showPreviousNext("previousElementSibling", params);
			},

			_showPreviousNext: function (direction, props) {
				var ret = null;
				if (!this._visibleChild && this.children.length > 0) {
					this._visibleChild = this.children[0];
				}
				if (this._visibleChild) {
					var target = this._visibleChild[direction];
					if (target) {
						ret = this.show(target, props);
					}
				}
				return ret;
			},

			_doTransition: function (origin, target, event, transition, reverse) {
				var promise;
				if (transition !== "none") {
					if (origin) {
						promise = this._setAfterTransitionHandlers(origin);
						$(origin).addClass(transitionClass(transition));
					}
					if (target) {
						promise = this._setAfterTransitionHandlers(target);
						$(target).addClass(transitionClass(transition) + " -d-view-stack-in");
					}
					if (reverse) {
						setReverse(origin);
						setReverse(target);
					}
					this.defer(function () {
						if (target) {
							$(target).addClass("-d-view-stack-transition");
						}
						if (origin) {
							$(origin).addClass("-d-view-stack-transition -d-view-stack-out");
						}
						if (reverse) {
							setReverse(origin);
							setReverse(target);
						}
						if (target) {
							$(target).addClass("-d-view-stack-in");
						}
					}, this._timing);
				} else {
					if (origin !== target) {
						setVisibility(origin, false);
					}
				}
				return Promise.resolve(promise);
			},

			changeDisplay: function (widget, event) {
				// Resolved when display is completed.
				if (!widget || widget.parentNode !== this) {
					return Promise.resolve();
				}

				var origin = this._visibleChild;

				// Needed because the CSS state of a node can be incorrect
				// if a previous transitionend has been dropped
				cleanCSS(origin);
				cleanCSS(widget);

				setVisibility(widget, true);
				this._visibleChild = widget;

				var transition  = (origin === widget) ? "none" : (event.transition || "slide");
				var reverse = this.isLeftToRight() ? event.reverse : !event.reverse;
				return this._doTransition(origin, widget, event, transition, reverse);
			},
			/**
			 * Shows a children of the ViewStack. The parameter 'params' is optional. If not specified,
			 * this.transition, and this.reverse are used.
			 * This method must be called to display a particular destination child on this container.
			 * @param {dest} Widget or HTMLElement or id that points to the child this container must display.
			 * @param {params} Optional params. A hash like {transition: "reveal", reverse: true}. The transition value
			 * can be "slide", "overlay", "fade" or "flip". Reverse transition applies to "slide" and
			 * "reveal". Transition is internally set to "none" if the ViewStack is not visible.
			 * @returns {Promise} A promise that will be resolved when the display and transition effect will have
			 * been performed.
			 */
			show: dcl.superCall(function (sup) {
				return function (dest, params) {
					// Check visibility of the ViewStack, forces transition:"none" if not visible.
					//  - Transitions events are broken if the ViewStack is not visible

					var parent = this;
					while (parent && parent.style.display  !== "none" && parent !== this.ownerDocument.body) {
						parent = parent.parentNode;
					}
					if ((has("ie") === 9) || parent !== this.ownerDocument.body) {
						if (! params) {
							params = {};
						}
						params.transition = "none";
					}

					if (this._visibleChild && this._visibleChild.parentNode !== this) {
						// The visible child has been removed.
						this._visibleChild = null;
					}
					if (!this._visibleChild && this.children.length > 0) {
						// The default visible child is the first one.
						this._visibleChild = this.children[0];
					}
					return sup.apply(this, [dest, params]);
				};
			}),

			_setAfterTransitionHandlers: function (node) {
				var self = this, holder = { node: node};
				holder.promise = new Promise(function (resolve) {
					holder.handle =  function () { self._afterTransitionHandle(holder, resolve); };
				});
				$(this).addClass("-d-view-stack-transition");
				node.addEventListener("webkitTransitionEnd", holder.handle);
				node.addEventListener("transitionend", holder.handle); // IE10 + FF
				return holder.promise;
			},

			_afterTransitionHandle: function (holder, resolve) {
				// Workaround for FF transitionend randomly dropped.
				// This method should be called once, when the target view transition is done.
				// But when 2 transitions (ex: slide) start at the same time, the transitionend event of the
				// target view can be dropped.
				if (!holder.promise.resolved) {
					// First call
					holder.promise.resolved = true;
					var vb;
					for (var i = 0; i < this.children.length; i++) {
						vb = this._visibleChild === this.children[i];
						setVisibility(this.children[i], vb);
						if (!vb) {
							cleanCSS(this.children[i]);
						}
					}

					holder.node.removeEventListener("webkitTransitionEnd", holder.handle);
					holder.node.removeEventListener("transitionend", holder.handle);
					resolve();
				} else {
					// Second call (occurs randomly on FF). The following code is not critical but try
					// to keep a clean DOM tree as much as possible.
					cleanCSS(holder.node);
					if (holder.node !== this._visibleChild) {
						cleanCSS(this._visibleChild);
					}
					$(this).removeClass("-d-view-stack-transition");
				}
			}
		});
	});

<|endoftext|>


<commit_before>/*
 * i5ting-jquery-tab
 * https://github.com/i5ting/i5ting-jquery-tab
 *
 * Copyright (c) 2013 i5ting
 * Licensed under the MIT license.
 */

(function($) {

	function i5ting_tab_list_li_hover(){
	    //tab 头hover时间处理
	  	$(".i5ting_tab_list li").hover(function(){
	  		$(this).addClass("over");
	  	},function(){
	  		$(this).removeClass("over");
	  	});

	}

	function tab_content_btn_show_or_hide(container){
		$(container).find('.i5ting_tab_list #up_sen').click(function(){
			if($(this).hasClass('up')){
				$(this).removeClass('up').addClass('down');
				$(container).find('.i5ting_tab_content').hide();
			}else{
				$(this).removeClass('down').addClass('up');
				$(container).find('.i5ting_tab_content').show();
			}
		});
	}

	function add_tab_content_btn_if_need(container,opts){
		if(opts.is_tab_content_btn_show){
			$('',
					{
					 	'href'  : "javascript:;",
						'class' : 'up',
						'id'    : 'up_sen'
					}
			).prependTo( $(container).find('.i5ting_tab_list') );
		}
	}

	function add_fix_height_if_need(container,opts){
		if(opts.fix_height){
			$(container).find('.i5ting_tab_content_item').css('height',opts.fix_height);
		}
	}

  $.fn.i5ting_jquery_tab = function(options) {
	  	var opts = $.extend({}, $.fn.i5ting_jquery_tab.defaults, options);
		var container = $(this);
		//如果is_tab_content_btn_show=yes，则显示上下箭头
		add_tab_content_btn_if_need($(this),opts);

	    return this.each(function() {
			opts.current_tab_content = $('.i5ting_tab_content');
				//$(container).find('.i5ting_tab_content div.i5ting_tab_content_item').eq( opts.current_tab_index );
				// container = $(this);
				//$(container).find('.i5ting_tab_list li').eq(opts.current_tab_index).addClass('current');
			$(container).find('.i5ting_tab_list li').last().addClass('last');

			$(container).find('.i5ting_tab_content div.i5ting_tab_content_item').eq( opts.current_tab_index ).show().siblings().hide();

			switch(opts.event_trigger_type){
				case 'hover':
					//tab 头点击处理
					 $(container).find('.i5ting_tab_list li').hover(function(e){

					 opts.current_tab_index = $(this).prevAll().length;

					  console.log(opts.current_index );

					  $(this).addClass('current').siblings().removeClass('current');
						  console.log($('.i5ting_tab_content div.i5ting_tab_content_item'));
					 	  $(container).find('.i5ting_tab_content div.i5ting_tab_content_item').eq( opts.current_tab_index ).show().siblings().hide();
					  });
					  break;
				 case 'click':
					   //tab 头点击处理
					   $(container).find('.i5ting_tab_list li').click(function(e){

					    opts.current_tab_index = $(this).prevAll().length;

					    console.log(opts.current_index );

					  	$(this).addClass('current').siblings().removeClass('current');

					  	$(container).find('.i5ting_tab_content div.i5ting_tab_content_item').eq( opts.current_tab_index ).show().siblings().hide();
				     });
				  default:
					  break;

			  }

			  i5ting_tab_list_li_hover();

			  tab_content_btn_show_or_hide($(this));

			  add_fix_height_if_need(container,opts);

	  });//end return
  };//end i5ting_jquery_tab

  $.fn.i5ting_jquery_tab.defaults = {
		current_tab_index: 0, //从0开始
		is_tab_content_btn_show: true, /*显示上下箭头*/
    	// fix_height :'200px',  /*如果没有配置fix_height，则自适应*/
		event_trigger_type:'click', /*现在支持2种类型：  click | hover */
  };

}(jQuery));
<commit_msg>format<commit_after>/*
 * i5ting-jquery-tab
 * https://github.com/i5ting/i5ting-jquery-tab
 *
 * Copyright (c) 2013 i5ting
 * Licensed under the MIT license.
 */

(function($) {

	function i5ting_tab_list_li_hover(){
	    //tab 头hover时间处理
	  	$(".i5ting_tab_list li").hover(function(){
	  		$(this).addClass("over");
	  	},function(){
	  		$(this).removeClass("over");
	  	});
	}

	function tab_content_btn_show_or_hide(container){
		$(container).find('.i5ting_tab_list #up_sen').click(function(){
			if($(this).hasClass('up')){
				$(this).removeClass('up').addClass('down');
				$(container).find('.i5ting_tab_content').hide();
			}else{
				$(this).removeClass('down').addClass('up');
				$(container).find('.i5ting_tab_content').show();
			}
		});
	}

	function add_tab_content_btn_if_need(container,opts){
		if(opts.is_tab_content_btn_show){
			$('',
				{
				 	'href'  : "javascript:;",
					'class' : 'up',
					'id'    : 'up_sen'
				}
			).prependTo(
				$(container).find('.i5ting_tab_list')
			);
		}
	}

	function add_fix_height_if_need(container,opts){
		if(opts.fix_height){
			$(container).find('.i5ting_tab_content_item').css('height',opts.fix_height);
		}
	}

  	$.fn.i5ting_jquery_tab = function(options) {
	  	var opts = $.extend({}, $.fn.i5ting_jquery_tab.defaults, options);
		var container = $(this);
		//如果is_tab_content_btn_show=yes，则显示上下箭头
		add_tab_content_btn_if_need($(this),opts);

	    return this.each(function() {
			opts.current_tab_content = $('.i5ting_tab_content');

			$(container).find('.i5ting_tab_list li').last().addClass('last');

			$(container).find('.i5ting_tab_content div.i5ting_tab_content_item').eq( opts.current_tab_index ).show().siblings().hide();

			switch(opts.event_trigger_type){
				case 'hover':
					//tab 头点击处理
					 $(container).find('.i5ting_tab_list li').hover(function(e){

					 opts.current_tab_index = $(this).prevAll().length;

					  console.log(opts.current_index );

					  $(this).addClass('current').siblings().removeClass('current');
						  console.log($('.i5ting_tab_content div.i5ting_tab_content_item'));
					 	  $(container).find('.i5ting_tab_content div.i5ting_tab_content_item').eq( opts.current_tab_index ).show().siblings().hide();
					  });
					  break;
				 case 'click':
					   //tab 头点击处理
					   $(container).find('.i5ting_tab_list li').click(function(e){

					    opts.current_tab_index = $(this).prevAll().length;

					    console.log(opts.current_index );

					  	$(this).addClass('current').siblings().removeClass('current');

					  	$(container).find('.i5ting_tab_content div.i5ting_tab_content_item').eq( opts.current_tab_index ).show().siblings().hide();
				     });
				  default:
					  break;

			  }

			  i5ting_tab_list_li_hover();

			  tab_content_btn_show_or_hide($(this));

			  add_fix_height_if_need(container,opts);

	  });//end return
  };//end i5ting_jquery_tab

  	$.fn.i5ting_jquery_tab.defaults = {
		current_tab_index: 0, //从0开始
		is_tab_content_btn_show: true, /*显示上下箭头*/
    	// fix_height :'200px',  /*如果没有配置fix_height，则自适应*/
		event_trigger_type:'click', /*现在支持2种类型：  click | hover */
  	};

}(jQuery));
<|endoftext|>


<commit_before>const request = require("request");
const logger = require("../modules/logger")("getImage");
const fs = require("fs");
const url = require("url");

const cacheFile = (queryData, res, cacheName, cb) => {
  logger.info(`Creating cached image for ${cacheName}`);
  let cacheData = "";
  if (queryData.url) {
    request({
      url: queryData.url,
    })
      .on("error", (e) => {
          logger.error(`Error creating cached image for ${cacheName}`, e);
        res.end(e);
      })
      .on("end", (e) => {
        if (cacheData) {
          fs.writeFile(
            `public/data/cache/${cacheName}`,
            cacheData,
            "binary",
            (err, data) => {
              if (err) {
                logger.error(`Error creating cached image for ${cacheName}`, err);
              } else {
                cb();
              }
            }
          );
        }
      })
      .on("response", (r) => {
        r.setEncoding("binary");
        r.on("data", (chunk) => {
          cacheData += chunk;
        });
      });
  }
};

const returnPath = (path, req, res) => {
  res.writeHead(200, {"Content-Type": "application/json"});
  res.end(JSON.stringify({
    url: `//${req.headers.host}/data/cache/${path}`
  }));
}

module.exports = (req, res) => {
  const queryData = url.parse(req.url, true).query;
  const mbid = queryData.mbid;
  if (mbid) {
    const files = fs.readdirSync(`public/data/cache`);
    const matchedFiles = files.filter(file => file.includes(mbid));
    if (matchedFiles.length > 0) {
      returnPath(matchedFiles[0], req, res);
    } else {
      res.writeHead(404, "Not found");
      res.end();
    }
    return;
  }
  let cacheName = queryData.url;
  if (cacheName.includes("https://")) {
    cacheName = cacheName.substring(8)
  }
  if (cacheName.includes("http://")) {
    cacheName = cacheName.substring(7)
  }
  cacheName = cacheName.replace(/\//g,`_`);
  if (fs.existsSync(`public/data/cache/${cacheName}`)) {
    returnPath(cacheName, req, res);
  } else {
    cacheFile(queryData, res, cacheName, () => {
      returnPath(cacheName, req, res);
    });
  }
};
<commit_msg>Prevent caching of 404 results<commit_after>const request = require("request");
const logger = require("../modules/logger")("getImage");
const fs = require("fs");
const url = require("url");

const cacheFile = (queryData, res, cacheName, cb) => {
  logger.info(`Creating cached image for ${cacheName}`);
  let cacheData = "";
  if (queryData.url) {
    request({
      url: queryData.url,
    })
      .on("error", (e) => {
          logger.error(`Error creating cached image for ${cacheName}`, e);
        res.end(e);
      })
      .on("end", (e) => {
        if (cacheData) {
          fs.writeFile(
            `public/data/cache/${cacheName}`,
            cacheData,
            "binary",
            (err, data) => {
              if (err) {
                logger.error(`Error creating cached image for ${cacheName}`, err);
              } else {
                cb();
              }
            }
          );
        }
      })
      .on("response", (r) => {
        r.setEncoding("binary");
        r.on("data", (chunk) => {
          cacheData += chunk;
        });
      });
  }
};

const returnPath = (path, req, res) => {
  res.writeHead(200, {"Content-Type": "application/json"});
  res.end(JSON.stringify({
    url: `//${req.headers.host}/data/cache/${path}`
  }));
}

module.exports = (req, res) => {
  const queryData = url.parse(req.url, true).query;
  const mbid = queryData.mbid;
  if (mbid) {
    const files = fs.readdirSync(`public/data/cache`);
    const matchedFiles = files.filter(file => file.includes(mbid));
    if (matchedFiles.length > 0) {
      returnPath(matchedFiles[0], req, res);
    } else {
      // don't cache 404 results please
      res.removeHeader("Cache-Control");
      res.setHeader("Expires", new Date(new Date.getTime() - 1).toUTCString());
      res.writeHead(404, "Not found");
      res.end();
    }
    return;
  }
  let cacheName = queryData.url;
  if (cacheName.includes("https://")) {
    cacheName = cacheName.substring(8)
  }
  if (cacheName.includes("http://")) {
    cacheName = cacheName.substring(7)
  }
  cacheName = cacheName.replace(/\//g,`_`);
  if (fs.existsSync(`public/data/cache/${cacheName}`)) {
    returnPath(cacheName, req, res);
  } else {
    cacheFile(queryData, res, cacheName, () => {
      returnPath(cacheName, req, res);
    });
  }
};
<|endoftext|>


<commit_before>/*jslint newcap: true */
/*global XMLHttpRequest: false, FormData: false */
/*
 * Inline Text Attachment
 *
 * Author: Roy van Kaathoven
 * Contact: ik@royvankaathoven.nl
 */
(function(document, window) {
  'use strict';

  var inlineAttachment = function(options, instance) {
    this.settings = inlineAttachment.util.merge(options, inlineAttachment.defaults);
    this.editor = instance;
    this.filenameTag = '{filename}';
    this.lastValue = null;
  };

  /**
   * Will holds the available editors
   *
   * @type {Object}
   */
  inlineAttachment.editors = {};

  /**
   * Utility functions
   */
  inlineAttachment.util = {

    /**
     * Simple function to merge the given objects
     *
     * @param {Object[]} object Multiple object parameters
     * @returns {Object}
     */
    merge: function() {
      var result = {};
      for (var i = arguments.length - 1; i >= 0; i--) {
        var obj = arguments[i];
        for (var k in obj) {
          if (obj.hasOwnProperty(k)) {
            result[k] = obj[k];
          }
        }
      }
      return result;
    },

    /**
     * Append a line of text at the bottom, ensuring there aren't unnecessary newlines
     *
     * @param {String} appended Current content
     * @param {String} previous Value which should be appended after the current content
     */
    appendInItsOwnLine: function(previous, appended) {
      return (previous + "\n\n[[D]]" + appended)
        .replace(/(\n{2,})\[\[D\]\]/, "\n\n")
        .replace(/^(\n*)/, "");
    },

    /**
     * Inserts the given value at the current cursor position of the textarea element
     *
     * @param  {HtmlElement} el
     * @param  {String} value Text which will be inserted at the cursor position
     */
    insertTextAtCursor: function(el, text) {
      var scrollPos = el.scrollTop,
        strPos = 0,
        browser = false,
        range;

      if ((el.selectionStart || el.selectionStart === '0')) {
        browser = "ff";
      } else if (document.selection) {
        browser = "ie";
      }

      if (browser === "ie") {
        el.focus();
        range = document.selection.createRange();
        range.moveStart('character', -el.value.length);
        strPos = range.text.length;
      } else if (browser === "ff") {
        strPos = el.selectionStart;
      }

      var front = (el.value).substring(0, strPos);
      var back = (el.value).substring(strPos, el.value.length);
      el.value = front + text + back;
      strPos = strPos + text.length;
      if (browser === "ie") {
        el.focus();
        range = document.selection.createRange();
        range.moveStart('character', -el.value.length);
        range.moveStart('character', strPos);
        range.moveEnd('character', 0);
        range.select();
      } else if (browser === "ff") {
        el.selectionStart = strPos;
        el.selectionEnd = strPos;
        el.focus();
      }
      el.scrollTop = scrollPos;
    }
  };

  /**
   * Default configuration options
   *
   * @type {Object}
   */
  inlineAttachment.defaults = {
    /**
     * URL where the file will be send
     */
    uploadUrl: 'upload_attachment.php',

    /**
     * Which method will be used to send the file to the upload URL
     */
    uploadMethod: 'POST',

    /**
     * Name in which the file will be placed
     */
    uploadFieldName: 'file',

    /**
     * Extension which will be used when a file extension could not
     * be detected
     */
    defaultExtension: 'png',

    /**
     * JSON field which refers to the uploaded file URL
     */
    jsonFieldName: 'filename',

    /**
     * Allowed MIME types
     */
    allowedTypes: [
      'image/jpeg',
      'image/png',
      'image/jpg',
      'image/gif'
    ],

    /**
     * Text which will be inserted when dropping or pasting a file.
     * Acts as a placeholder which will be replaced when the file is done with uploading
     */
    progressText: '![Uploading file...]()',

    /**
     * When a file has successfully been uploaded the progressText
     * will be replaced by the urlText, the {filename} tag will be replaced
     * by the filename that has been returned by the server
     */
    urlText: "![file]({filename})",

    /**
     * Text which will be used when uploading has failed
     */
    errorText: "Error uploading file",

    /**
     * Extra parameters which will be send when uploading a file
     */
    extraParams: {},

    /**
     * Extra headers which will be send when uploading a file
     */
    extraHeaders: {},

    /**
     * Before the file is send
     */
    beforeFileUpload: function() {
      return true;
    },

    /**
     * Triggers when a file is dropped or pasted
     */
    onFileReceived: function() {},

    /**
     * Custom upload handler
     *
     * @return {Boolean} when false is returned it will prevent default upload behavior
     */
    onFileUploadResponse: function() {
      return true;
    },

    /**
     * Custom error handler. Runs after removing the placeholder text and before the alert().
     * Return false from this function to prevent the alert dialog.
     *
     * @return {Boolean} when false is returned it will prevent default error behavior
     */
    onFileUploadError: function() {
      return true;
    },

    /**
     * When a file has succesfully been uploaded
     */
    onFileUploaded: function() {}
  };

  /**
   * Uploads the blob
   *
   * @param  {Blob} file blob data received from event.dataTransfer object
   * @return {XMLHttpRequest} request object which sends the file
   */
  inlineAttachment.prototype.uploadFile = function(file) {
    var me = this,
      formData = new FormData(),
      xhr = new XMLHttpRequest(),
      settings = this.settings,
      extension = settings.defaultExtension;

    if (typeof settings.setupFormData === 'function') {
      settings.setupFormData(formData, file);
    }

    // Attach the file. If coming from clipboard, add a default filename (only works in Chrome for now)
    // http://stackoverflow.com/questions/6664967/how-to-give-a-blob-uploaded-as-formdata-a-file-name
    if (file.name) {
      var fileNameMatches = file.name.match(/\.(.+)$/);
      if (fileNameMatches) {
        extension = fileNameMatches[1];
      }
    }

    var remoteFilename = "image-" + Date.now() + "." + extension;
    if (typeof settings.remoteFilename === 'function') {
      remoteFilename = settings.remoteFilename(file);
    }

    formData.append(settings.uploadFieldName, file, remoteFilename);

    // Append the extra parameters to the formdata
    if (typeof settings.extraParams === "object") {
      for (var key in settings.extraParams) {
        if (settings.extraParams.hasOwnProperty(key)) {
          formData.append(key, settings.extraParams[key]);
        }
      }
    }

    xhr.open('POST', settings.uploadUrl);

    // Add any available extra headers
    if (typeof settings.extraHeaders === "object") {
        for (var header in settings.extraHeaders) {
            if (settings.extraHeaders.hasOwnProperty(header)) {
                xhr.setRequestHeader(header, settings.extraHeaders[header]);
            }
        }
    }

    xhr.onload = function() {
      // If HTTP status is OK or Created
      if (xhr.status === 200 || xhr.status === 201) {
        me.onFileUploadResponse(xhr);
      } else {
        me.onFileUploadError(xhr);
      }
    };
    if (settings.beforeFileUpload(xhr) !== false) {
      xhr.send(formData);
    }
    return xhr;
  };

  /**
   * Returns if the given file is allowed to handle
   *
   * @param {File} clipboard data file
   */
  inlineAttachment.prototype.isFileAllowed = function(file) {
    if (this.settings.allowedTypes.indexOf('*') === 0){
      return true;
    } else {
      return this.settings.allowedTypes.indexOf(file.type) >= 0;
    }
  };

  /**
   * Handles upload response
   *
   * @param  {XMLHttpRequest} xhr
   * @return {Void}
   */
  inlineAttachment.prototype.onFileUploadResponse = function(xhr) {
    if (this.settings.onFileUploadResponse.call(this, xhr) !== false) {
      var result = JSON.parse(xhr.responseText),
        filename = result[this.settings.jsonFieldName];

      if (result && filename) {
        var newValue = this.settings.urlText.replace(this.filenameTag, filename);
        var text = this.editor.getValue().replace(this.lastValue, newValue);
        this.editor.setValue(text);
        this.settings.onFileUploaded.call(this, filename);
      }
    }
  };

  /**
   * Called when a file has failed to upload
   *
   * @param  {XMLHttpRequest} xhr
   * @return {Void}
   */
  inlineAttachment.prototype.onFileUploadError = function(xhr) {
    if (this.settings.onFileUploadError.call(this, xhr) !== false) {
      var text = this.editor.getValue().replace(this.lastValue, "");
      this.editor.setValue(text);
    }
  };

  /**
   * Called when a file has been inserted, either by drop or paste
   *
   * @param  {File} file
   * @return {Void}
   */
  inlineAttachment.prototype.onFileInserted = function(file) {
    if (this.settings.onFileReceived.call(this, file) !== false) {
      this.lastValue = this.settings.progressText;
      this.editor.insertValue(this.lastValue);
    }
  };

  /**
   * Called when a paste event occured
   * @param  {Event} e
   * @return {Boolean} if the event was handled
   */
  inlineAttachment.prototype.onPaste = function(e) {
    var result = false,
      clipboardData = e.clipboardData,
      items;

    if (typeof clipboardData === "object") {
      items = clipboardData.items || clipboardData.files || [];

      for (var i = 0; i < items.length; i++) {
        var item = items[i];
        if (this.isFileAllowed(item)) {
          result = true;
          this.onFileInserted(item.getAsFile());
          this.uploadFile(item.getAsFile());
        }
      }
    }

    if (result) { e.preventDefault(); }

    return result;
  };

  /**
   * Called when a drop event occures
   * @param  {Event} e
   * @return {Boolean} if the event was handled
   */
  inlineAttachment.prototype.onDrop = function(e) {
    var result = false;
    for (var i = 0; i < e.dataTransfer.files.length; i++) {
      var file = e.dataTransfer.files[i];
      if (this.isFileAllowed(file)) {
        result = true;
        this.onFileInserted(file);
        this.uploadFile(file);
      }
    }

    return result;
  };

  window.inlineAttachment = inlineAttachment;

})(document, window);
<commit_msg>made the s/defualt/default/ change backwards compatible<commit_after>/*jslint newcap: true */
/*global XMLHttpRequest: false, FormData: false */
/*
 * Inline Text Attachment
 *
 * Author: Roy van Kaathoven
 * Contact: ik@royvankaathoven.nl
 */
(function(document, window) {
  'use strict';

  var inlineAttachment = function(options, instance) {
    this.settings = inlineAttachment.util.merge(options, inlineAttachment.defaults);
    this.editor = instance;
    this.filenameTag = '{filename}';
    this.lastValue = null;
  };

  /**
   * Will holds the available editors
   *
   * @type {Object}
   */
  inlineAttachment.editors = {};

  /**
   * Utility functions
   */
  inlineAttachment.util = {

    /**
     * Simple function to merge the given objects
     *
     * @param {Object[]} object Multiple object parameters
     * @returns {Object}
     */
    merge: function() {
      var result = {};
      for (var i = arguments.length - 1; i >= 0; i--) {
        var obj = arguments[i];
        for (var k in obj) {
          if (obj.hasOwnProperty(k)) {
            result[k] = obj[k];
          }
        }
      }
      return result;
    },

    /**
     * Append a line of text at the bottom, ensuring there aren't unnecessary newlines
     *
     * @param {String} appended Current content
     * @param {String} previous Value which should be appended after the current content
     */
    appendInItsOwnLine: function(previous, appended) {
      return (previous + "\n\n[[D]]" + appended)
        .replace(/(\n{2,})\[\[D\]\]/, "\n\n")
        .replace(/^(\n*)/, "");
    },

    /**
     * Inserts the given value at the current cursor position of the textarea element
     *
     * @param  {HtmlElement} el
     * @param  {String} value Text which will be inserted at the cursor position
     */
    insertTextAtCursor: function(el, text) {
      var scrollPos = el.scrollTop,
        strPos = 0,
        browser = false,
        range;

      if ((el.selectionStart || el.selectionStart === '0')) {
        browser = "ff";
      } else if (document.selection) {
        browser = "ie";
      }

      if (browser === "ie") {
        el.focus();
        range = document.selection.createRange();
        range.moveStart('character', -el.value.length);
        strPos = range.text.length;
      } else if (browser === "ff") {
        strPos = el.selectionStart;
      }

      var front = (el.value).substring(0, strPos);
      var back = (el.value).substring(strPos, el.value.length);
      el.value = front + text + back;
      strPos = strPos + text.length;
      if (browser === "ie") {
        el.focus();
        range = document.selection.createRange();
        range.moveStart('character', -el.value.length);
        range.moveStart('character', strPos);
        range.moveEnd('character', 0);
        range.select();
      } else if (browser === "ff") {
        el.selectionStart = strPos;
        el.selectionEnd = strPos;
        el.focus();
      }
      el.scrollTop = scrollPos;
    }
  };

  /**
   * Default configuration options
   *
   * @type {Object}
   */
  inlineAttachment.defaults = {
    /**
     * URL where the file will be send
     */
    uploadUrl: 'upload_attachment.php',

    /**
     * Which method will be used to send the file to the upload URL
     */
    uploadMethod: 'POST',

    /**
     * Name in which the file will be placed
     */
    uploadFieldName: 'file',

    /**
     * Extension which will be used when a file extension could not
     * be detected
     */
    defaultExtension: 'png',

    /**
     * JSON field which refers to the uploaded file URL
     */
    jsonFieldName: 'filename',

    /**
     * Allowed MIME types
     */
    allowedTypes: [
      'image/jpeg',
      'image/png',
      'image/jpg',
      'image/gif'
    ],

    /**
     * Text which will be inserted when dropping or pasting a file.
     * Acts as a placeholder which will be replaced when the file is done with uploading
     */
    progressText: '![Uploading file...]()',

    /**
     * When a file has successfully been uploaded the progressText
     * will be replaced by the urlText, the {filename} tag will be replaced
     * by the filename that has been returned by the server
     */
    urlText: "![file]({filename})",

    /**
     * Text which will be used when uploading has failed
     */
    errorText: "Error uploading file",

    /**
     * Extra parameters which will be send when uploading a file
     */
    extraParams: {},

    /**
     * Extra headers which will be send when uploading a file
     */
    extraHeaders: {},

    /**
     * Before the file is send
     */
    beforeFileUpload: function() {
      return true;
    },

    /**
     * Triggers when a file is dropped or pasted
     */
    onFileReceived: function() {},

    /**
     * Custom upload handler
     *
     * @return {Boolean} when false is returned it will prevent default upload behavior
     */
    onFileUploadResponse: function() {
      return true;
    },

    /**
     * Custom error handler. Runs after removing the placeholder text and before the alert().
     * Return false from this function to prevent the alert dialog.
     *
     * @return {Boolean} when false is returned it will prevent default error behavior
     */
    onFileUploadError: function() {
      return true;
    },

    /**
     * When a file has succesfully been uploaded
     */
    onFileUploaded: function() {}
  };

  /**
   * Uploads the blob
   *
   * @param  {Blob} file blob data received from event.dataTransfer object
   * @return {XMLHttpRequest} request object which sends the file
   */
  inlineAttachment.prototype.uploadFile = function(file) {
    var me = this,
      formData = new FormData(),
      xhr = new XMLHttpRequest(),
      settings = this.settings,
      extension = settings.defaultExtension || settings.defualtExtension;

    if (typeof settings.setupFormData === 'function') {
      settings.setupFormData(formData, file);
    }

    // Attach the file. If coming from clipboard, add a default filename (only works in Chrome for now)
    // http://stackoverflow.com/questions/6664967/how-to-give-a-blob-uploaded-as-formdata-a-file-name
    if (file.name) {
      var fileNameMatches = file.name.match(/\.(.+)$/);
      if (fileNameMatches) {
        extension = fileNameMatches[1];
      }
    }

    var remoteFilename = "image-" + Date.now() + "." + extension;
    if (typeof settings.remoteFilename === 'function') {
      remoteFilename = settings.remoteFilename(file);
    }

    formData.append(settings.uploadFieldName, file, remoteFilename);

    // Append the extra parameters to the formdata
    if (typeof settings.extraParams === "object") {
      for (var key in settings.extraParams) {
        if (settings.extraParams.hasOwnProperty(key)) {
          formData.append(key, settings.extraParams[key]);
        }
      }
    }

    xhr.open('POST', settings.uploadUrl);

    // Add any available extra headers
    if (typeof settings.extraHeaders === "object") {
        for (var header in settings.extraHeaders) {
            if (settings.extraHeaders.hasOwnProperty(header)) {
                xhr.setRequestHeader(header, settings.extraHeaders[header]);
            }
        }
    }

    xhr.onload = function() {
      // If HTTP status is OK or Created
      if (xhr.status === 200 || xhr.status === 201) {
        me.onFileUploadResponse(xhr);
      } else {
        me.onFileUploadError(xhr);
      }
    };
    if (settings.beforeFileUpload(xhr) !== false) {
      xhr.send(formData);
    }
    return xhr;
  };

  /**
   * Returns if the given file is allowed to handle
   *
   * @param {File} clipboard data file
   */
  inlineAttachment.prototype.isFileAllowed = function(file) {
    if (this.settings.allowedTypes.indexOf('*') === 0){
      return true;
    } else {
      return this.settings.allowedTypes.indexOf(file.type) >= 0;
    }
  };

  /**
   * Handles upload response
   *
   * @param  {XMLHttpRequest} xhr
   * @return {Void}
   */
  inlineAttachment.prototype.onFileUploadResponse = function(xhr) {
    if (this.settings.onFileUploadResponse.call(this, xhr) !== false) {
      var result = JSON.parse(xhr.responseText),
        filename = result[this.settings.jsonFieldName];

      if (result && filename) {
        var newValue = this.settings.urlText.replace(this.filenameTag, filename);
        var text = this.editor.getValue().replace(this.lastValue, newValue);
        this.editor.setValue(text);
        this.settings.onFileUploaded.call(this, filename);
      }
    }
  };

  /**
   * Called when a file has failed to upload
   *
   * @param  {XMLHttpRequest} xhr
   * @return {Void}
   */
  inlineAttachment.prototype.onFileUploadError = function(xhr) {
    if (this.settings.onFileUploadError.call(this, xhr) !== false) {
      var text = this.editor.getValue().replace(this.lastValue, "");
      this.editor.setValue(text);
    }
  };

  /**
   * Called when a file has been inserted, either by drop or paste
   *
   * @param  {File} file
   * @return {Void}
   */
  inlineAttachment.prototype.onFileInserted = function(file) {
    if (this.settings.onFileReceived.call(this, file) !== false) {
      this.lastValue = this.settings.progressText;
      this.editor.insertValue(this.lastValue);
    }
  };

  /**
   * Called when a paste event occured
   * @param  {Event} e
   * @return {Boolean} if the event was handled
   */
  inlineAttachment.prototype.onPaste = function(e) {
    var result = false,
      clipboardData = e.clipboardData,
      items;

    if (typeof clipboardData === "object") {
      items = clipboardData.items || clipboardData.files || [];

      for (var i = 0; i < items.length; i++) {
        var item = items[i];
        if (this.isFileAllowed(item)) {
          result = true;
          this.onFileInserted(item.getAsFile());
          this.uploadFile(item.getAsFile());
        }
      }
    }

    if (result) { e.preventDefault(); }

    return result;
  };

  /**
   * Called when a drop event occures
   * @param  {Event} e
   * @return {Boolean} if the event was handled
   */
  inlineAttachment.prototype.onDrop = function(e) {
    var result = false;
    for (var i = 0; i < e.dataTransfer.files.length; i++) {
      var file = e.dataTransfer.files[i];
      if (this.isFileAllowed(file)) {
        result = true;
        this.onFileInserted(file);
        this.uploadFile(file);
      }
    }

    return result;
  };

  window.inlineAttachment = inlineAttachment;

})(document, window);
<|endoftext|>


<commit_before>/*
	MIT License http://www.opensource.org/licenses/mit-license.php
	Author Tobias Koppers @sokra
*/
var stylesInDom = {},
	memoize = function(fn) {
		var memo;
		return function () {
			if (typeof memo === "undefined") memo = fn.apply(this, arguments);
			return memo;
		};
	},
	isOldIE = memoize(function() {
		return /msie [6-9]\b/.test(self.navigator.userAgent.toLowerCase());
	}),
	getElement = (function(fn) {
		var memo = {};
		return function(selector) {
			if (typeof memo[selector] === "undefined") {
				memo[selector] = fn.call(this, selector);
			}
			return memo[selector]
		};
	})(function (styleTarget) {
		return document.querySelector(styleTarget)
	}),
	singletonElement = null,
	singletonCounter = 0,
	styleElementsInsertedAtTop = [],
	fixUrls = require("./fixUrls");

module.exports = function(list, options) {
	if(typeof DEBUG !== "undefined" && DEBUG) {
		if(typeof document !== "object") throw new Error("The style-loader cannot be used in a non-browser environment");
	}

	options = options || {};
	options.attrs = typeof options.attrs === "object" ? options.attrs : {};

	// Force single-tag solution on IE6-9, which has a hard limit on the # of <style>
	// tags it will allow on a page
	if (typeof options.singleton === "undefined") options.singleton = isOldIE();

	// By default, add <style> tags to the <head> element
	if (typeof options.insertInto === "undefined") options.insertInto = "head";

	// By default, add <style> tags to the bottom of the target
	if (typeof options.insertAt === "undefined") options.insertAt = "bottom";

	var styles = listToStyles(list);
	addStylesToDom(styles, options);

	return function update(newList) {
		var mayRemove = [];
		for(var i = 0; i < styles.length; i++) {
			var item = styles[i];
			var domStyle = stylesInDom[item.id];
			domStyle.refs--;
			mayRemove.push(domStyle);
		}
		if(newList) {
			var newStyles = listToStyles(newList);
			addStylesToDom(newStyles, options);
		}
		for(var i = 0; i < mayRemove.length; i++) {
			var domStyle = mayRemove[i];
			if(domStyle.refs === 0) {
				for(var j = 0; j < domStyle.parts.length; j++)
					domStyle.parts[j]();
				delete stylesInDom[domStyle.id];
			}
		}
	};
};

function addStylesToDom(styles, options) {
	for(var i = 0; i < styles.length; i++) {
		var item = styles[i];
		var domStyle = stylesInDom[item.id];
		if(domStyle) {
			domStyle.refs++;
			for(var j = 0; j < domStyle.parts.length; j++) {
				domStyle.parts[j](item.parts[j]);
			}
			for(; j < item.parts.length; j++) {
				domStyle.parts.push(addStyle(item.parts[j], options));
			}
		} else {
			var parts = [];
			for(var j = 0; j < item.parts.length; j++) {
				parts.push(addStyle(item.parts[j], options));
			}
			stylesInDom[item.id] = {id: item.id, refs: 1, parts: parts};
		}
	}
}

function listToStyles(list) {
	var styles = [];
	var newStyles = {};
	for(var i = 0; i < list.length; i++) {
		var item = list[i];
		var id = item[0];
		var css = item[1];
		var media = item[2];
		var sourceMap = item[3];
		var part = {css: css, media: media, sourceMap: sourceMap};
		if(!newStyles[id])
			styles.push(newStyles[id] = {id: id, parts: [part]});
		else
			newStyles[id].parts.push(part);
	}
	return styles;
}

function insertStyleElement(options, styleElement) {
	var styleTarget = getElement(options.insertInto)
	if (!styleTarget) {
		throw new Error("Couldn't find a style target. This probably means that the value for the 'insertInto' parameter is invalid.");
	}
	var lastStyleElementInsertedAtTop = styleElementsInsertedAtTop[styleElementsInsertedAtTop.length - 1];
	if (options.insertAt === "top") {
		if(!lastStyleElementInsertedAtTop) {
			styleTarget.insertBefore(styleElement, styleTarget.firstChild);
		} else if(lastStyleElementInsertedAtTop.nextSibling) {
			styleTarget.insertBefore(styleElement, lastStyleElementInsertedAtTop.nextSibling);
		} else {
			styleTarget.appendChild(styleElement);
		}
		styleElementsInsertedAtTop.push(styleElement);
	} else if (options.insertAt === "bottom") {
		styleTarget.appendChild(styleElement);
	} else {
		throw new Error("Invalid value for parameter 'insertAt'. Must be 'top' or 'bottom'.");
	}
}

function removeStyleElement(styleElement) {
	styleElement.parentNode.removeChild(styleElement);
	var idx = styleElementsInsertedAtTop.indexOf(styleElement);
	if(idx >= 0) {
		styleElementsInsertedAtTop.splice(idx, 1);
	}
}

function createStyleElement(options) {
	var styleElement = document.createElement("style");
	options.attrs.type = "text/css";

	attachTagAttrs(styleElement, options.attrs);
	insertStyleElement(options, styleElement);
	return styleElement;
}

function createLinkElement(options) {
	var linkElement = document.createElement("link");
	options.attrs.type = "text/css";
	options.attrs.rel = "stylesheet";

	attachTagAttrs(linkElement, options.attrs);
	insertStyleElement(options, linkElement);
	return linkElement;
}

function attachTagAttrs(element, attrs) {
	Object.keys(attrs).forEach(function (key) {
		element.setAttribute(key, attrs[key]);
	});
}

function addStyle(obj, options) {
	var styleElement, update, remove;

	if (options.singleton) {
		var styleIndex = singletonCounter++;
		styleElement = singletonElement || (singletonElement = createStyleElement(options));
		update = applyToSingletonTag.bind(null, styleElement, styleIndex, false);
		remove = applyToSingletonTag.bind(null, styleElement, styleIndex, true);
	} else if(obj.sourceMap &&
		typeof URL === "function" &&
		typeof URL.createObjectURL === "function" &&
		typeof URL.revokeObjectURL === "function" &&
		typeof Blob === "function" &&
		typeof btoa === "function") {
		styleElement = createLinkElement(options);
		update = updateLink.bind(null, styleElement, options);
		remove = function() {
			removeStyleElement(styleElement);
			if(styleElement.href)
				URL.revokeObjectURL(styleElement.href);
		};
	} else {
		styleElement = createStyleElement(options);
		update = applyToTag.bind(null, styleElement);
		remove = function() {
			removeStyleElement(styleElement);
		};
	}

	update(obj);

	return function updateStyle(newObj) {
		if(newObj) {
			if(newObj.css === obj.css && newObj.media === obj.media && newObj.sourceMap === obj.sourceMap)
				return;
			update(obj = newObj);
		} else {
			remove();
		}
	};
}

var replaceText = (function () {
	var textStore = [];

	return function (index, replacement) {
		textStore[index] = replacement;
		return textStore.filter(Boolean).join('\n');
	};
})();

function applyToSingletonTag(styleElement, index, remove, obj) {
	var css = remove ? "" : obj.css;

	if (styleElement.styleSheet) {
		styleElement.styleSheet.cssText = replaceText(index, css);
	} else {
		var cssNode = document.createTextNode(css);
		var childNodes = styleElement.childNodes;
		if (childNodes[index]) styleElement.removeChild(childNodes[index]);
		if (childNodes.length) {
			styleElement.insertBefore(cssNode, childNodes[index]);
		} else {
			styleElement.appendChild(cssNode);
		}
	}
}

function applyToTag(styleElement, obj) {
	var css = obj.css;
	var media = obj.media;

	if(media) {
		styleElement.setAttribute("media", media)
	}

	if(styleElement.styleSheet) {
		styleElement.styleSheet.cssText = css;
	} else {
		while(styleElement.firstChild) {
			styleElement.removeChild(styleElement.firstChild);
		}
		styleElement.appendChild(document.createTextNode(css));
	}
}

function updateLink(linkElement, options, obj) {
	var css = obj.css;
	var sourceMap = obj.sourceMap;

	/* If convertToAbsoluteUrls isn't defined, but sourcemaps are enabled
	and there is no publicPath defined then lets turn convertToAbsoluteUrls
	on by default.  Otherwise default to the convertToAbsoluteUrls option
	directly
	*/
	var autoFixUrls = options.convertToAbsoluteUrls === undefined && sourceMap;

	if (options.convertToAbsoluteUrls || autoFixUrls){
		css = fixUrls(css);
	}

	if(sourceMap) {
		// http://stackoverflow.com/a/26603875
		css += "\n/*# sourceMappingURL=data:application/json;base64," + btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap)))) + " */";
	}

	var blob = new Blob([css], { type: "text/css" });

	var oldSrc = linkElement.href;

	linkElement.href = URL.createObjectURL(blob);

	if(oldSrc)
		URL.revokeObjectURL(oldSrc);
}
<commit_msg>fix(addStyles): update for test for old IE versions (#196)<commit_after>/*
	MIT License http://www.opensource.org/licenses/mit-license.php
	Author Tobias Koppers @sokra
*/
var stylesInDom = {},
	memoize = function(fn) {
		var memo;
		return function () {
			if (typeof memo === "undefined") memo = fn.apply(this, arguments);
			return memo;
		};
	},
	isOldIE = memoize(function() {
		// Test for IE <= 9 as proposed by Browserhacks
		// @see http://browserhacks.com/#hack-e71d8692f65334173fee715c222cb805
		// Tests for existence of standard globals is to allow style-loader
		// to operate correctly into non-standard environments
		// @see https://github.com/webpack-contrib/style-loader/issues/177
		return window && document && document.all && !window.atob;
	}),
	getElement = (function(fn) {
		var memo = {};
		return function(selector) {
			if (typeof memo[selector] === "undefined") {
				memo[selector] = fn.call(this, selector);
			}
			return memo[selector]
		};
	})(function (styleTarget) {
		return document.querySelector(styleTarget)
	}),
	singletonElement = null,
	singletonCounter = 0,
	styleElementsInsertedAtTop = [],
	fixUrls = require("./fixUrls");

module.exports = function(list, options) {
	if(typeof DEBUG !== "undefined" && DEBUG) {
		if(typeof document !== "object") throw new Error("The style-loader cannot be used in a non-browser environment");
	}

	options = options || {};
	options.attrs = typeof options.attrs === "object" ? options.attrs : {};

	// Force single-tag solution on IE6-9, which has a hard limit on the # of <style>
	// tags it will allow on a page
	if (typeof options.singleton === "undefined") options.singleton = isOldIE();

	// By default, add <style> tags to the <head> element
	if (typeof options.insertInto === "undefined") options.insertInto = "head";

	// By default, add <style> tags to the bottom of the target
	if (typeof options.insertAt === "undefined") options.insertAt = "bottom";

	var styles = listToStyles(list);
	addStylesToDom(styles, options);

	return function update(newList) {
		var mayRemove = [];
		for(var i = 0; i < styles.length; i++) {
			var item = styles[i];
			var domStyle = stylesInDom[item.id];
			domStyle.refs--;
			mayRemove.push(domStyle);
		}
		if(newList) {
			var newStyles = listToStyles(newList);
			addStylesToDom(newStyles, options);
		}
		for(var i = 0; i < mayRemove.length; i++) {
			var domStyle = mayRemove[i];
			if(domStyle.refs === 0) {
				for(var j = 0; j < domStyle.parts.length; j++)
					domStyle.parts[j]();
				delete stylesInDom[domStyle.id];
			}
		}
	};
};

function addStylesToDom(styles, options) {
	for(var i = 0; i < styles.length; i++) {
		var item = styles[i];
		var domStyle = stylesInDom[item.id];
		if(domStyle) {
			domStyle.refs++;
			for(var j = 0; j < domStyle.parts.length; j++) {
				domStyle.parts[j](item.parts[j]);
			}
			for(; j < item.parts.length; j++) {
				domStyle.parts.push(addStyle(item.parts[j], options));
			}
		} else {
			var parts = [];
			for(var j = 0; j < item.parts.length; j++) {
				parts.push(addStyle(item.parts[j], options));
			}
			stylesInDom[item.id] = {id: item.id, refs: 1, parts: parts};
		}
	}
}

function listToStyles(list) {
	var styles = [];
	var newStyles = {};
	for(var i = 0; i < list.length; i++) {
		var item = list[i];
		var id = item[0];
		var css = item[1];
		var media = item[2];
		var sourceMap = item[3];
		var part = {css: css, media: media, sourceMap: sourceMap};
		if(!newStyles[id])
			styles.push(newStyles[id] = {id: id, parts: [part]});
		else
			newStyles[id].parts.push(part);
	}
	return styles;
}

function insertStyleElement(options, styleElement) {
	var styleTarget = getElement(options.insertInto)
	if (!styleTarget) {
		throw new Error("Couldn't find a style target. This probably means that the value for the 'insertInto' parameter is invalid.");
	}
	var lastStyleElementInsertedAtTop = styleElementsInsertedAtTop[styleElementsInsertedAtTop.length - 1];
	if (options.insertAt === "top") {
		if(!lastStyleElementInsertedAtTop) {
			styleTarget.insertBefore(styleElement, styleTarget.firstChild);
		} else if(lastStyleElementInsertedAtTop.nextSibling) {
			styleTarget.insertBefore(styleElement, lastStyleElementInsertedAtTop.nextSibling);
		} else {
			styleTarget.appendChild(styleElement);
		}
		styleElementsInsertedAtTop.push(styleElement);
	} else if (options.insertAt === "bottom") {
		styleTarget.appendChild(styleElement);
	} else {
		throw new Error("Invalid value for parameter 'insertAt'. Must be 'top' or 'bottom'.");
	}
}

function removeStyleElement(styleElement) {
	styleElement.parentNode.removeChild(styleElement);
	var idx = styleElementsInsertedAtTop.indexOf(styleElement);
	if(idx >= 0) {
		styleElementsInsertedAtTop.splice(idx, 1);
	}
}

function createStyleElement(options) {
	var styleElement = document.createElement("style");
	options.attrs.type = "text/css";

	attachTagAttrs(styleElement, options.attrs);
	insertStyleElement(options, styleElement);
	return styleElement;
}

function createLinkElement(options) {
	var linkElement = document.createElement("link");
	options.attrs.type = "text/css";
	options.attrs.rel = "stylesheet";

	attachTagAttrs(linkElement, options.attrs);
	insertStyleElement(options, linkElement);
	return linkElement;
}

function attachTagAttrs(element, attrs) {
	Object.keys(attrs).forEach(function (key) {
		element.setAttribute(key, attrs[key]);
	});
}

function addStyle(obj, options) {
	var styleElement, update, remove;

	if (options.singleton) {
		var styleIndex = singletonCounter++;
		styleElement = singletonElement || (singletonElement = createStyleElement(options));
		update = applyToSingletonTag.bind(null, styleElement, styleIndex, false);
		remove = applyToSingletonTag.bind(null, styleElement, styleIndex, true);
	} else if(obj.sourceMap &&
		typeof URL === "function" &&
		typeof URL.createObjectURL === "function" &&
		typeof URL.revokeObjectURL === "function" &&
		typeof Blob === "function" &&
		typeof btoa === "function") {
		styleElement = createLinkElement(options);
		update = updateLink.bind(null, styleElement, options);
		remove = function() {
			removeStyleElement(styleElement);
			if(styleElement.href)
				URL.revokeObjectURL(styleElement.href);
		};
	} else {
		styleElement = createStyleElement(options);
		update = applyToTag.bind(null, styleElement);
		remove = function() {
			removeStyleElement(styleElement);
		};
	}

	update(obj);

	return function updateStyle(newObj) {
		if(newObj) {
			if(newObj.css === obj.css && newObj.media === obj.media && newObj.sourceMap === obj.sourceMap)
				return;
			update(obj = newObj);
		} else {
			remove();
		}
	};
}

var replaceText = (function () {
	var textStore = [];

	return function (index, replacement) {
		textStore[index] = replacement;
		return textStore.filter(Boolean).join('\n');
	};
})();

function applyToSingletonTag(styleElement, index, remove, obj) {
	var css = remove ? "" : obj.css;

	if (styleElement.styleSheet) {
		styleElement.styleSheet.cssText = replaceText(index, css);
	} else {
		var cssNode = document.createTextNode(css);
		var childNodes = styleElement.childNodes;
		if (childNodes[index]) styleElement.removeChild(childNodes[index]);
		if (childNodes.length) {
			styleElement.insertBefore(cssNode, childNodes[index]);
		} else {
			styleElement.appendChild(cssNode);
		}
	}
}

function applyToTag(styleElement, obj) {
	var css = obj.css;
	var media = obj.media;

	if(media) {
		styleElement.setAttribute("media", media)
	}

	if(styleElement.styleSheet) {
		styleElement.styleSheet.cssText = css;
	} else {
		while(styleElement.firstChild) {
			styleElement.removeChild(styleElement.firstChild);
		}
		styleElement.appendChild(document.createTextNode(css));
	}
}

function updateLink(linkElement, options, obj) {
	var css = obj.css;
	var sourceMap = obj.sourceMap;

	/* If convertToAbsoluteUrls isn't defined, but sourcemaps are enabled
	and there is no publicPath defined then lets turn convertToAbsoluteUrls
	on by default.  Otherwise default to the convertToAbsoluteUrls option
	directly
	*/
	var autoFixUrls = options.convertToAbsoluteUrls === undefined && sourceMap;

	if (options.convertToAbsoluteUrls || autoFixUrls){
		css = fixUrls(css);
	}

	if(sourceMap) {
		// http://stackoverflow.com/a/26603875
		css += "\n/*# sourceMappingURL=data:application/json;base64," + btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap)))) + " */";
	}

	var blob = new Blob([css], { type: "text/css" });

	var oldSrc = linkElement.href;

	linkElement.href = URL.createObjectURL(blob);

	if(oldSrc)
		URL.revokeObjectURL(oldSrc);
}
<|endoftext|>


<commit_before>(function(){
"use strict";
angular.module("uk.ac.soton.ecs.videogular.plugins.analytics", [])
	.directive(
		"vgAnalytics", ["VG_STATES", "$http", "$timeout",
			function(VG_STATES, $http, $timeout) {
				return {
					restrict: "E",
					require: "^videogular",
					scope: {
						servers: "=vgAnalyticsServers",
					},
					link: function($scope, elem, attr, API) {

						$scope.$parent.$on("analytics", function(event, name, details) {
							sendEvent(name, details);
						});

						function sendEvent(name, details) {
							var now = new Date();

							var content = {
								time: now.toISOString(),
								name: name,
							};

							if (typeof(details) !== "undefined") {
								content.details = details;
							}

							$scope.servers.forEach(function(server) {
								$http.post(server + 'log', content)
									.success(function(data, status, headers, config) {
									// this callback will be called asynchronously
									// when the response is available
								}).error(function(data, status, headers, config) {
									console.error("error reporting event to the server");
									// called asynchronously if an error occurs
									// or server returns response with an error status.
								});
							});
						}

						function reportMediaElementEvent(event) {
							sendEvent(event.type, { time: API.currentTime });
						}

						var eventsToReport = ['play', 'pause', 'stop'];

						for (var i in eventsToReport) {
							API.mediaElement[0].addEventListener(eventsToReport[i], reportMediaElementEvent);
						}
					},
				};
			}
		]
	);
})();
<commit_msg>Added a UUID attached to all events.<commit_after>(function(){
"use strict";
angular.module("uk.ac.soton.ecs.videogular.plugins.analytics", [])
	.directive(
		"vgAnalytics", ["VG_STATES", "$http", "$timeout",
			function(VG_STATES, $http, $timeout) {
				return {
					restrict: "E",
					require: "^videogular",
					scope: {
						servers: "=vgAnalyticsServers",
					},
					link: function($scope, elem, attr, API) {

						$scope.$parent.$on("analytics", function(event, name, details) {
							sendEvent(name, details);
						});

						function sendEvent(name, details) {
							var now = new Date();

							var content = {
								time: now.toISOString(),
								name: name,
								uuid: $scope.uuid,
							};

							if (typeof(details) !== "undefined") {
								content.details = details;
							}

							$scope.servers.forEach(function(server) {
								$http.post(server + 'log', content)
									.success(function(data, status, headers, config) {
									// this callback will be called asynchronously
									// when the response is available
								}).error(function(data, status, headers, config) {
									console.error("error reporting event to the server");
									// called asynchronously if an error occurs
									// or server returns response with an error status.
								});
							});
						}

						function reportMediaElementEvent(event) {
							sendEvent(event.type, { time: API.currentTime });
						}

						var eventsToReport = ['play', 'pause', 'stop'];

						for (var i in eventsToReport) {
							API.mediaElement[0].addEventListener(eventsToReport[i], reportMediaElementEvent);
						}

						//generateUUID function uses this stack overflow answer http://stackoverflow.com/a/8809472
						function generateUUID(){
							var d = new Date().getTime();
							var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
								var r = (d + Math.random()*16)%16 | 0;
								d = Math.floor(d/16);
								return (c=='x' ? r : (r&0x3|0x8)).toString(16);
							});
							return uuid;
						}

						$scope.uuid = generateUUID();

					},
				};
			}
		]
	);
})();
<|endoftext|>