<commit_before>/** @preserve
jsPDF addImage plugin (JPEG only at this time)
Copyright (c) 2012 https://github.com/siefkenj/
*/

/**
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 * ====================================================================
 */

;(function(jsPDFAPI) {
'use strict'

var namespace = 'addImage_'

// takes a string imgData containing the raw bytes of
// a jpeg image and returns [width, height]
// Algorithm from: http://www.64lines.com/jpeg-width-height
var getJpegSize = function(imgData) {
	'use strict'
	var width, height;
	// Verify we have a valid jpeg header 0xff,0xd8,0xff,0xe0,?,?,'J','F','I','F',0x00
	if (!imgData.charCodeAt(0) === 0xff ||
		!imgData.charCodeAt(1) === 0xd8 ||
		!imgData.charCodeAt(2) === 0xff ||
		!imgData.charCodeAt(3) === 0xe0 ||
		!imgData.charCodeAt(6) === 'J'.charCodeAt(0) ||
		!imgData.charCodeAt(7) === 'F'.charCodeAt(0) ||
		!imgData.charCodeAt(8) === 'I'.charCodeAt(0) ||
		!imgData.charCodeAt(9) === 'F'.charCodeAt(0) ||
		!imgData.charCodeAt(10) === 0x00) {
			throw new Error('getJpegSize requires a binary jpeg file')
	}
	var blockLength = imgData.charCodeAt(4)*256 + imgData.charCodeAt(5);
	var i = 4, len = imgData.length;
	while ( i < len ) {
		i += blockLength;
		if (imgData.charCodeAt(i) !== 0xff) {
			throw new Error('getJpegSize could not find the size of the image');
		}
		if (imgData.charCodeAt(i+1) === 0xc0) {
			height = imgData.charCodeAt(i+5)*256 + imgData.charCodeAt(i+6);
			width = imgData.charCodeAt(i+7)*256 + imgData.charCodeAt(i+8);
			return [width, height];
		} else {
			i += 2;
			blockLength = imgData.charCodeAt(i)*256 + imgData.charCodeAt(i+1)
		}
	}
}
// Image functionality ported from pdf.js
, putImage = function(img) {
	var objectNumber = this.internal.newObject()
	, out = this.internal.write
	, putStream = this.internal.putStream

	img['n'] = objectNumber

	out('<</Type /XObject')
	out('/Subtype /Image')
	out('/Width ' + img['w'])
	out('/Height ' + img['h'])
	if (img['cs'] === 'Indexed') {
		out('/ColorSpace [/Indexed /DeviceRGB '
				+ (img['pal'].length / 3 - 1) + ' ' + (objectNumber + 1)
				+ ' 0 R]');
	} else {
		out('/ColorSpace /' + img['cs']);
		if (img['cs'] === 'DeviceCMYK') {
			out('/Decode [1 0 1 0 1 0 1 0]');
		}
	}
	out('/BitsPerComponent ' + img['bpc']);
	if ('f' in img) {
		out('/Filter /' + img['f']);
	}
	if ('dp' in img) {
		out('/DecodeParms <<' + img['dp'] + '>>');
	}
	if ('trns' in img && img['trns'].constructor == Array) {
		var trns = '';
		for ( var i = 0; i < img['trns'].length; i++) {
			trns += (img[trns][i] + ' ' + img['trns'][i] + ' ');
			out('/Mask [' + trns + ']');
		}
	}
	if ('smask' in img) {
		out('/SMask ' + (objectNumber + 1) + ' 0 R');
	}
	out('/Length ' + img['data'].length + '>>');

	putStream(img['data']);

	out('endobj');
}
, putResourcesCallback = function() {
	var images = this.internal.collections[namespace + 'images']
	for ( var i in images ) {
		putImage.call(this, images[i])
	}
}
, putXObjectsDictCallback = function(){
	var images = this.internal.collections[namespace + 'images']
	, out = this.internal.write
	, image
	for (var i in images) {
		image = images[i]
		out(
			'/I' + image['i']
			, image['n']
			, '0'
			, 'R'
		)
	}
}

jsPDFAPI.addImage = function(imageData, format, x, y, w, h) {
	'use strict'

	if (format.toUpperCase() !== 'JPEG') {
		throw new Error('addImage currently only supports format \'JPEG\', not \''+format+'\'');
	}

	var imageIndex
	, images = this.internal.collections[namespace + 'images']
	, coord = this.internal.getCoordinateString
	, vcoord = this.internal.getVerticalCoordinateString

	if (images){
		// this is NOT the first time this method is ran on this instance of jsPDF object.
		imageIndex = Object.keys ?
		Object.keys(images).length :
		(function(o){
			var i = 0
			for (var e in o){if(o.hasOwnProperty(e)){ i++ }}
			return i
		})(images)
	} else {
		// this is the first time this method is ran on this instance of jsPDF object.
		imageIndex = 0
		this.internal.collections[namespace + 'images'] = images = {}
		this.internal.events.subscribe('putResources', putResourcesCallback)
		this.internal.events.subscribe('putXobjectDict', putXObjectsDictCallback)
	}

	var dims = getJpegSize(imageData);
	var info = {
		w : dims[0],
		h : dims[1],
		cs : 'DeviceRGB',
		bpc : 8,
		f : 'DCTDecode',
		i : imageIndex,
		data : imageData
		// n: objectNumber will be added by putImage code

	};
	images[imageIndex] = info
	if (!w && !h) {
		w = -96;
		h = -96;
	}
	if (w < 0) {
		w = (-1) * info['w'] * 72 / w / this.internal.scaleFactor;
	}
	if (h < 0) {
		h = (-1) * info['h'] * 72 / h / this.internal.scaleFactor;
	}
	if (w === 0) {
		w = h * info['w'] / info['h'];
	}
	if (h === 0) {
		h = w * info['h'] / info['w'];
	}

	this.internal.write(
		'q'
		, coord(w)
		, '0 0'
		, coord(h) // TODO: check if this should be shifted by vcoord
		, coord(x)
		, vcoord(y + h)
		, 'cm /I'+info['i']
		, 'Do Q'
	)

	return this
}

})(jsPDF.API)
<commit_msg>Add data URL support to addImage<commit_after>/** @preserve
jsPDF addImage plugin (JPEG only at this time)
Copyright (c) 2012 https://github.com/siefkenj/
*/

/**
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 * ====================================================================
 */

;(function(jsPDFAPI) {
'use strict'

var namespace = 'addImage_'

// takes a string imgData containing the raw bytes of
// a jpeg image and returns [width, height]
// Algorithm from: http://www.64lines.com/jpeg-width-height
var getJpegSize = function(imgData) {
	'use strict'
	var width, height;
	// Verify we have a valid jpeg header 0xff,0xd8,0xff,0xe0,?,?,'J','F','I','F',0x00
	if (!imgData.charCodeAt(0) === 0xff ||
		!imgData.charCodeAt(1) === 0xd8 ||
		!imgData.charCodeAt(2) === 0xff ||
		!imgData.charCodeAt(3) === 0xe0 ||
		!imgData.charCodeAt(6) === 'J'.charCodeAt(0) ||
		!imgData.charCodeAt(7) === 'F'.charCodeAt(0) ||
		!imgData.charCodeAt(8) === 'I'.charCodeAt(0) ||
		!imgData.charCodeAt(9) === 'F'.charCodeAt(0) ||
		!imgData.charCodeAt(10) === 0x00) {
			throw new Error('getJpegSize requires a binary jpeg file')
	}
	var blockLength = imgData.charCodeAt(4)*256 + imgData.charCodeAt(5);
	var i = 4, len = imgData.length;
	while ( i < len ) {
		i += blockLength;
		if (imgData.charCodeAt(i) !== 0xff) {
			throw new Error('getJpegSize could not find the size of the image');
		}
		if (imgData.charCodeAt(i+1) === 0xc0) {
			height = imgData.charCodeAt(i+5)*256 + imgData.charCodeAt(i+6);
			width = imgData.charCodeAt(i+7)*256 + imgData.charCodeAt(i+8);
			return [width, height];
		} else {
			i += 2;
			blockLength = imgData.charCodeAt(i)*256 + imgData.charCodeAt(i+1)
		}
	}
}
// Image functionality ported from pdf.js
, putImage = function(img) {
	var objectNumber = this.internal.newObject()
	, out = this.internal.write
	, putStream = this.internal.putStream

	img['n'] = objectNumber

	out('<</Type /XObject')
	out('/Subtype /Image')
	out('/Width ' + img['w'])
	out('/Height ' + img['h'])
	if (img['cs'] === 'Indexed') {
		out('/ColorSpace [/Indexed /DeviceRGB '
				+ (img['pal'].length / 3 - 1) + ' ' + (objectNumber + 1)
				+ ' 0 R]');
	} else {
		out('/ColorSpace /' + img['cs']);
		if (img['cs'] === 'DeviceCMYK') {
			out('/Decode [1 0 1 0 1 0 1 0]');
		}
	}
	out('/BitsPerComponent ' + img['bpc']);
	if ('f' in img) {
		out('/Filter /' + img['f']);
	}
	if ('dp' in img) {
		out('/DecodeParms <<' + img['dp'] + '>>');
	}
	if ('trns' in img && img['trns'].constructor == Array) {
		var trns = '';
		for ( var i = 0; i < img['trns'].length; i++) {
			trns += (img[trns][i] + ' ' + img['trns'][i] + ' ');
			out('/Mask [' + trns + ']');
		}
	}
	if ('smask' in img) {
		out('/SMask ' + (objectNumber + 1) + ' 0 R');
	}
	out('/Length ' + img['data'].length + '>>');

	putStream(img['data']);

	out('endobj');
}
, putResourcesCallback = function() {
	var images = this.internal.collections[namespace + 'images']
	for ( var i in images ) {
		putImage.call(this, images[i])
	}
}
, putXObjectsDictCallback = function(){
	var images = this.internal.collections[namespace + 'images']
	, out = this.internal.write
	, image
	for (var i in images) {
		image = images[i]
		out(
			'/I' + image['i']
			, image['n']
			, '0'
			, 'R'
		)
	}
}

jsPDFAPI.addImage = function(imageData, format, x, y, w, h) {
	'use strict'

	if (format.toUpperCase() !== 'JPEG') {
		throw new Error('addImage currently only supports format \'JPEG\', not \''+format+'\'');
	}

	var imageIndex
	, images = this.internal.collections[namespace + 'images']
	, coord = this.internal.getCoordinateString
	, vcoord = this.internal.getVerticalCoordinateString;

	// Detect if the imageData is raw binary or Data URL
	if (imageData.substring(0, 23) === 'data:image/jpeg;base64,') {
		imageData = atob(imageData.replace('data:image/jpeg;base64,', ''));
	}

	if (images){
		// this is NOT the first time this method is ran on this instance of jsPDF object.
		imageIndex = Object.keys ?
		Object.keys(images).length :
		(function(o){
			var i = 0
			for (var e in o){if(o.hasOwnProperty(e)){ i++ }}
			return i
		})(images)
	} else {
		// this is the first time this method is ran on this instance of jsPDF object.
		imageIndex = 0
		this.internal.collections[namespace + 'images'] = images = {}
		this.internal.events.subscribe('putResources', putResourcesCallback)
		this.internal.events.subscribe('putXobjectDict', putXObjectsDictCallback)
	}

	var dims = getJpegSize(imageData);
	var info = {
		w : dims[0],
		h : dims[1],
		cs : 'DeviceRGB',
		bpc : 8,
		f : 'DCTDecode',
		i : imageIndex,
		data : imageData
		// n: objectNumber will be added by putImage code

	};
	images[imageIndex] = info
	if (!w && !h) {
		w = -96;
		h = -96;
	}
	if (w < 0) {
		w = (-1) * info['w'] * 72 / w / this.internal.scaleFactor;
	}
	if (h < 0) {
		h = (-1) * info['h'] * 72 / h / this.internal.scaleFactor;
	}
	if (w === 0) {
		w = h * info['w'] / info['h'];
	}
	if (h === 0) {
		h = w * info['h'] / info['w'];
	}

	this.internal.write(
		'q'
		, coord(w)
		, '0 0'
		, coord(h) // TODO: check if this should be shifted by vcoord
		, coord(x)
		, vcoord(y + h)
		, 'cm /I'+info['i']
		, 'Do Q'
	)

	return this
}

})(jsPDF.API)
<|endoftext|>


<commit_before>var path = require('path');
var fs = require('fs');

/**
 * Generate a shim for a library that does not have top level indexes.
 *
 * @param modName (string) - The name of the module to shim.
 *
 * @param sourceFolder (string) - The source folder.
 *
 * @param indexOnly (boolean) - Whether to only shim index files.
 *
 * @returns A promise that resolves when the file is created.
 */
function shimmer(modName, sourceFolder, regex) {
  var modPath = require.resolve(modName + '/package.json');
  sourceFolder = sourceFolder || 'lib';
  regex = regex || /.*\.js$/;
  modPath = path.join(path.dirname(modPath), sourceFolder);
  var lines = ['var ' + modName + ' = {}'];

  var entries = fs.readdirSync(modPath);
  // Algorithm:
  // search for index.js file
  // if one exists, use it, otherwise use an empty object to initialize
  // then, for each, if it is a directory, recurse-and-add
  // if it is a file that matches our regex, add it
  var lines = getLines(modName, sourceFolder, modPath, modPath, regex);
  lines.push('module.exports = ' + modName);
  return lines.join('\n');
}

/**
 * Get the appropriate shim lines for the items in a folder.
 */
function getLines(modName, sourceFolder, basePath, currentPath, regex) {
  var lines = [];
  var entries = fs.readdirSync(currentPath);
  var relPath = path.relative(basePath, currentPath);
  var parts = relPath.split('/');
  var modPath = path.join(modName, sourceFolder, relPath);

  // Initialize with the index file if available, else an empty object.
  if (entries.indexOf('index.js') !== -1) {
    if (!parts[0]) {
      lines.push('var ' + modName + ' = require("' + modPath + '");');
    } else {
      lines.push(modName + '["' + parts.join('"]["') + '"] = require("' + modPath + '");');
    }
  } else {
    if (!parts[0]) {
      lines.push('var ' + modName + ' = {};');
    } else {
      lines.push(modName + '["' + parts.join('"]["') + '"] = {};');
    }
  }
  // Otherwise, handle each file.
  for (var i = 0; i < entries.length; i++) {
    var entry = entries[i];
    // Handle directories recursively.
    var isDirectory = fs.statSync(path.join(currentPath, entry)).isDirectory();
    if (isDirectory) {
      lines = lines.concat(getLines(modName, sourceFolder, basePath, path.join(currentPath, entry), regex));
    } else if (regex.test(entry)) {
      // Skip index files, already taken care of.
      if (path.basename(entry) === 'index.js') {
        continue;
      }
      entry = entry.replace(path.extname(entry), '');
      var entryParts = parts.slice();
      entryParts.push(entry);
      if (!entryParts[0]) {
        entryParts = entryParts.slice(1);
      }
      lines.push(modName + '["' + entryParts.join('"]["') + '"] = require("' + path.join(modPath, entry) + '");');
    }
  }
  return lines;
}

module.exports = shimmer;
<commit_msg>Add copyright<commit_after>// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.

var path = require('path');
var fs = require('fs');

/**
 * Generate a shim for a library that does not have top level indexes.
 *
 * @param modName (string) - The name of the module to shim.
 *
 * @param sourceFolder (string) - The source folder.
 *
 * @param indexOnly (boolean) - Whether to only shim index files.
 *
 * @returns A promise that resolves when the file is created.
 */
function shimmer(modName, sourceFolder, regex) {
  var modPath = require.resolve(modName + '/package.json');
  sourceFolder = sourceFolder || 'lib';
  regex = regex || /.*\.js$/;
  modPath = path.join(path.dirname(modPath), sourceFolder);
  var lines = ['var ' + modName + ' = {}'];

  var entries = fs.readdirSync(modPath);
  // Algorithm:
  // search for index.js file
  // if one exists, use it, otherwise use an empty object to initialize
  // then, for each, if it is a directory, recurse-and-add
  // if it is a file that matches our regex, add it
  var lines = getLines(modName, sourceFolder, modPath, modPath, regex);
  lines.push('module.exports = ' + modName);
  return lines.join('\n');
}

/**
 * Get the appropriate shim lines for the items in a folder.
 */
function getLines(modName, sourceFolder, basePath, currentPath, regex) {
  var lines = [];
  var entries = fs.readdirSync(currentPath);
  var relPath = path.relative(basePath, currentPath);
  var parts = relPath.split('/');
  var modPath = path.join(modName, sourceFolder, relPath);

  // Initialize with the index file if available, else an empty object.
  if (entries.indexOf('index.js') !== -1) {
    if (!parts[0]) {
      lines.push('var ' + modName + ' = require("' + modPath + '");');
    } else {
      lines.push(modName + '["' + parts.join('"]["') + '"] = require("' + modPath + '");');
    }
  } else {
    if (!parts[0]) {
      lines.push('var ' + modName + ' = {};');
    } else {
      lines.push(modName + '["' + parts.join('"]["') + '"] = {};');
    }
  }
  // Otherwise, handle each file.
  for (var i = 0; i < entries.length; i++) {
    var entry = entries[i];
    // Handle directories recursively.
    var isDirectory = fs.statSync(path.join(currentPath, entry)).isDirectory();
    if (isDirectory) {
      lines = lines.concat(getLines(modName, sourceFolder, basePath, path.join(currentPath, entry), regex));
    } else if (regex.test(entry)) {
      // Skip index files, already taken care of.
      if (path.basename(entry) === 'index.js') {
        continue;
      }
      entry = entry.replace(path.extname(entry), '');
      var entryParts = parts.slice();
      entryParts.push(entry);
      if (!entryParts[0]) {
        entryParts = entryParts.slice(1);
      }
      lines.push(modName + '["' + entryParts.join('"]["') + '"] = require("' + path.join(modPath, entry) + '");');
    }
  }
  return lines;
}

module.exports = shimmer;
<|endoftext|>


<commit_before>// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.

var path = require('path');
var fs = require('fs');

/**
 * Generate a shim for a library that does not have top level indexes.
 *
 * @param modName (string) - The name of the module to shim.
 *
 * @param sourceFolder (string) - The source folder.
 *
 * @param indexOnly (boolean) - Whether to only shim index files.
 *
 * @returns A promise that resolves when the file is created.
 */
function shimmer(modName, sourceFolder, regex) {
  var modPath = require.resolve(modName + '/package.json');
  sourceFolder = sourceFolder || 'lib';
  regex = regex || /.*\.js$/;
  modPath = path.join(path.dirname(modPath), sourceFolder);
  var lines = ['var ' + modName + ' = {}'];

  var entries = fs.readdirSync(modPath);
  // Algorithm:
  // search for index.js file
  // if one exists, use it, otherwise use an empty object to initialize
  // then, for each, if it is a directory, recurse-and-add
  // if it is a file that matches our regex, add it
  var lines = getLines(modName, sourceFolder, modPath, modPath, regex);
  lines.push('module.exports = ' + modName);
  return lines.join('\n');
}

/**
 * Get the appropriate shim lines for the items in a folder.
 */
function getLines(modName, sourceFolder, basePath, currentPath, regex) {
  var lines = [];
  var entries = fs.readdirSync(currentPath);
  var relPath = path.relative(basePath, currentPath);
  var parts = relPath.split('/');
  var modPath = path.join(modName, sourceFolder, relPath);

  // Initialize with the index file if available, else an empty object.
  if (entries.indexOf('index.js') !== -1) {
    if (!parts[0]) {
      lines.push('var ' + modName + ' = require("' + modPath + '");');
    } else {
      lines.push(modName + '["' + parts.join('"]["') + '"] = require("' + modPath + '");');
    }
  } else {
    if (!parts[0]) {
      lines.push('var ' + modName + ' = {};');
    } else {
      lines.push(modName + '["' + parts.join('"]["') + '"] = {};');
    }
  }
  // Otherwise, handle each file.
  for (var i = 0; i < entries.length; i++) {
    var entry = entries[i];
    // Handle directories recursively.
    var isDirectory = fs.statSync(path.join(currentPath, entry)).isDirectory();
    if (isDirectory) {
      lines = lines.concat(getLines(modName, sourceFolder, basePath, path.join(currentPath, entry), regex));
    } else if (regex.test(entry)) {
      // Skip index files, already taken care of.
      if (path.basename(entry) === 'index.js') {
        continue;
      }
      entry = entry.replace(path.extname(entry), '');
      var entryParts = parts.slice();
      entryParts.push(entry);
      if (!entryParts[0]) {
        entryParts = entryParts.slice(1);
      }
      lines.push(modName + '["' + entryParts.join('"]["') + '"] = require("' + path.join(modPath, entry) + '");');
    }
  }
  return lines;
}

module.exports = shimmer;
<commit_msg>update docs<commit_after>// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.

var path = require('path');
var fs = require('fs');

/**
 * Generate a shim for a library that does not have top level indexes.
 *
 * @param modName (string) - The name of the module to shim.
 *
 * @param sourceFolder (string) - The source folder.
 *
 * @param regex - A regular expresion used to match files - the default
 *  is all `.js` files.
 *
 * @returns A promise that resolves when the file is created.
 */
function shimmer(modName, sourceFolder, regex) {
  var modPath = require.resolve(modName + '/package.json');
  sourceFolder = sourceFolder || 'lib';
  regex = regex || /.*\.js$/;
  modPath = path.join(path.dirname(modPath), sourceFolder);
  var lines = ['var ' + modName + ' = {}'];

  var entries = fs.readdirSync(modPath);
  // Algorithm:
  // search for index.js file
  // if one exists, use it, otherwise use an empty object to initialize
  // then, for each, if it is a directory, recurse-and-add
  // if it is a file that matches our regex, add it
  var lines = getLines(modName, sourceFolder, modPath, modPath, regex);
  lines.push('module.exports = ' + modName);
  return lines.join('\n');
}

/**
 * Get the appropriate shim lines for the items in a folder.
 */
function getLines(modName, sourceFolder, basePath, currentPath, regex) {
  var lines = [];
  var entries = fs.readdirSync(currentPath);
  var relPath = path.relative(basePath, currentPath);
  var parts = relPath.split('/');
  var modPath = path.join(modName, sourceFolder, relPath);

  // Initialize with the index file if available, else an empty object.
  if (entries.indexOf('index.js') !== -1) {
    if (!parts[0]) {
      lines.push('var ' + modName + ' = require("' + modPath + '");');
    } else {
      lines.push(modName + '["' + parts.join('"]["') + '"] = require("' + modPath + '");');
    }
  } else {
    if (!parts[0]) {
      lines.push('var ' + modName + ' = {};');
    } else {
      lines.push(modName + '["' + parts.join('"]["') + '"] = {};');
    }
  }
  // Otherwise, handle each file.
  for (var i = 0; i < entries.length; i++) {
    var entry = entries[i];
    // Handle directories recursively.
    var isDirectory = fs.statSync(path.join(currentPath, entry)).isDirectory();
    if (isDirectory) {
      lines = lines.concat(getLines(modName, sourceFolder, basePath, path.join(currentPath, entry), regex));
    } else if (regex.test(entry)) {
      // Skip index files, already taken care of.
      if (path.basename(entry) === 'index.js') {
        continue;
      }
      entry = entry.replace(path.extname(entry), '');
      var entryParts = parts.slice();
      entryParts.push(entry);
      if (!entryParts[0]) {
        entryParts = entryParts.slice(1);
      }
      lines.push(modName + '["' + entryParts.join('"]["') + '"] = require("' + path.join(modPath, entry) + '");');
    }
  }
  return lines;
}

module.exports = shimmer;
<|endoftext|>


<commit_before>/*
 * Iris
 * (c) 2016+ Buenos Aires City Government
 * License: https://opensource.org/licenses/MIT
 *
 */

'use strict';

const shortid = require('shortid');

class Data {
    constructor() {
        this._id = shortid.generate();
    }

    get id() {
        return this._id;
    }
}

module.exports = Data;<commit_msg>Added data structure<commit_after>/*
 * Iris
 * (c) 2016+ Buenos Aires City Government
 * License: https://opensource.org/licenses/MIT
 *
 */

'use strict';

const shortid = require('shortid');

class Data {
    constructor(tags, data) {
        this._tags = tags;
        this._data = data;
        this._id = shortid.generate();
    }

    get id() {
        return this._id;
    }

    get tags() {
        return this._tags;
    }

    get data() {
        return this._data;
    }
}

module.exports = Data;<|endoftext|>


<commit_before>
var serveModule = require("../../lib/phonegap/serve"),
    project = require("../../lib/phonegap/util/project"),
    serve;

describe("PhoneGap serve", function () {

    describe("module", function () {

        it("should export at object", function() {
            expect(serveModule).toEqual(any(Object));
        });

        it("should export an object with a create parameter", function() {
            expect(serveModule.create).toEqual(any(Function));
        });

    });

    describe("when called", function() {
        var validOptions,
            wrapper = {
                emit: function(){}
            };

        beforeEach(function() {
            validOptions = {
                port:3939,
                autoreload:true,
                localtunnel:false
            };

            serve = serveModule.create(wrapper);

            spyOn(project,'cd');

        });

        it("should be a function", function() {
            expect(serve).toEqual(any(Function));
        });

        it("should return the wrapper object given to create", function () {
            var ret = serve({});
            expect(ret).toEqual(wrapper);
        });

        it("should require options parameter", function(){
            expect(function() {
                serve();
            }).toThrow();
        });

        it("should accept empty options", function(){
            expect(function() {
                serve({});
            }).not.toThrow();
        });

        it("should not require callback parameter", function(){
            expect(function() {
                serve(validOptions);
            }).not.toThrow();
        });

        it("should not require options.port", function(){
            expect(function() {
                serve({port:undefined});
            }).not.toThrow();
        });

        it("should not require options.port", function(){
            expect(function() {
                serve({port:undefined});
            }).not.toThrow();
        });

        it("should change to the project directory", function (){
            serve(validOptions);
            expect(project.cd).toHaveBeenCalled();
        });

    });

});
<commit_msg>additional tests spec behaviour of execute portion in module<commit_after>
var serveModule = require("../../lib/phonegap/serve"),
    http = require("http"),
    server = require("connect-phonegap"),
    project = require("../../lib/phonegap/util/project"),
    serve;

describe("PhoneGap serve", function () {

    describe("module", function () {

        it("should export at object", function() {
            expect(serveModule).toEqual(any(Object));
        });

        it("should export an object with a create parameter", function() {
            expect(serveModule.create).toEqual(any(Function));
        });

    });

    describe("when called", function() {
        var validOptions,
            wrapper;

        beforeEach(function() {
            // valid options
            validOptions = {
                port:3939,
                autoreload:true,
                localtunnel:false
            };

            // define wrapper as a stub
            wrapper = {
                emit: function(){},
                on: function(){}
            };

            // initialize the serve function with wrapper
            serve = serveModule.create(wrapper);

            // declare spies
            spyOn(project,'cd').andReturn(true);
            spyOn(server,'listen').andReturn({on:function(){return this}});
        });

        it("should be a function", function() {
            expect(serve).toEqual(any(Function));
        });

        it("should return the wrapper object given to create", function () {
            var ret = serve({});
            expect(ret).toEqual(wrapper);
        });

        it("should require options parameter", function(){
            expect(function() {
                serve();
            }).toThrow();
        });

        it("should accept empty options", function(){
            expect(function() {
                serve({});
            }).not.toThrow();
        });

        it("should not require callback parameter", function(){
            expect(function() {
                serve(validOptions);
            }).not.toThrow();
        });

        it("should not require options.port", function(){
            expect(function() {
                serve({port:undefined});
            }).not.toThrow();
        });

        it("should not require options.port", function(){
            expect(function() {
                serve({port:undefined});
            }).not.toThrow();
        });

        it("should change to the project directory", function (){
            serve(validOptions);
            expect(project.cd).toHaveBeenCalled();
        });

        it("should call connect-phonegap library", function (){
            serve(validOptions);
            expect(server.listen).toHaveBeenCalled();
        });

    });

});
<|endoftext|>


<commit_before>window.onload = function() {
    var $body = $("body"),
        $window = $(window),
        $window_width = $window.width(),
        $window_height = $window.height(),
        $section_2 = $(".section-2"),
        $skill_row = $section_2.find(".skill-row"),
        $init_loading = $("#init-loading"),
        $loading_div = $init_loading.find("div"),
        $loading_div_height = $loading_div.height(),
        $skill_item = $(".skill-row").find(".item"),
        $skill_item_width = 0,
        $skill_row_height = 0,
        $section_2_padding_height = 0;

    $init_loading.css("height", 0).css("top", -$loading_div_height+"px");

    setTimeout(function() {
        $loading_div.css("display", "none");
        $body.removeClass("loading");
    }, 3000);

    if($skill_item.length > 0) {
        console.log($skill_row_height);
        console.log($skill_row.width()*0.2);
        if($skill_row_height > $skill_row.width()*0.2) {
            //가로 길이 사용
            console.log("가로사용");
            $skill_item_width = $skill_item.width();
            $skill_item.height($skill_item_width);
        } else {
            //세로 길이 사용
            console.log("세로사용");
            $section_2.addClass("height");
            $section_2_padding_height = $section_2.closest(".section").find(".border-section").innerHeight();
            $section_2.css("padding-top", $section_2_padding_height+"px");
            $skill_row_height = $skill_row.height();
            $skill_item.width($skill_row_height);
            $skill_item.height($skill_row_height);

        }

    }

};

$(function() {

    $(".main").onepage_scroll({
        sectionContainer: "section",     // sectionContainer accepts any kind of selector in case you don't want to use section
        easing: "ease",                  // Easing options accepts the CSS3 easing animation such "ease", "linear", "ease-in",
                                         // "ease-out", "ease-in-out", or even cubic bezier value such as "cubic-bezier(0.175, 0.885, 0.420, 1.310)"
        animationTime: 1000,             // AnimationTime let you define how long each section takes to animate
        pagination: true,                // You can either show or hide the pagination. Toggle true for show, false for hide.
        updateURL: false,                // Toggle this true if you want the URL to be updated automatically when the user scroll to each page.
        beforeMove: function(index) {},  // This option accepts a callback function. The function will be called before the page moves.
        afterMove: function(index) {},   // This option accepts a callback function. The function will be called after the page moves.
        loop: false,                     // You can have the page loop back to the top/bottom when the user navigates at up/down on the first/last page.
        keyboard: true,                  // You can activate the keyboard controls
        responsiveFallback: false,        // You can fallback to normal page scroll by defining the width of the browser in which
        // you want the responsive fallback to be triggered. For example, set this to 600 and whenever
        // the browser's width is less than 600, the fallback will kick in.
        direction: "vertical"            // You can now define the direction of the One Page Scroll animation. Options available are "vertical" and "horizontal". The default value is "vertical".
    });

    // drawer menu open/close
    // touch 이벤트 유무에 따라서 이벤트 다르게 적용
    if ('ontouchstart' in document.documentElement) {
        $(".ssc-btn-sidebar").on("touchstart", function() {
            var $menu = $(".menu"),
                $navbar_backdrop = $("#ssc-navbar-backdrop"),
                $ssc_right_to = $(this).find(".ssc-right-to");
            $(this).removeClass("touch-end");
            if($menu.length > 0) {
                if($menu.hasClass("open")) {
                    $menu.removeClass("open");
                    $navbar_backdrop.removeClass("open");
                    $(this).removeClass("active");
                    //$ssc_right_to.css("transform", "rotate(45deg)");
                } else {
                    $menu.addClass("open");
                    $navbar_backdrop.addClass("open");
                    $(this).addClass("active");
                    //$ssc_right_to.css("transform", "rotate(225deg)");
                }
            }
        }).on("touchend", function() {
            var $this_btn = $(this);
            setTimeout(function() {
                $this_btn.addClass("touch-end");
            }, 1200);
        });
    } else {
        $(".ssc-btn-sidebar").on("click", function() {
            var $menu = $(".menu"),
                $navbar_backdrop = $("#ssc-navbar-backdrop"),
                $ssc_right_to = $(this).find(".ssc-right-to");
            if($menu.length > 0) {
                if($menu.hasClass("open")) {
                    $menu.removeClass("open");
                    $navbar_backdrop.removeClass("open");
                    $(this).removeClass("active");
                    //$ssc_right_to.css("transform", "rotate(45deg)");
                } else {
                    $menu.addClass("open");
                    $navbar_backdrop.addClass("open");
                    $(this).addClass("active");
                    //$ssc_right_to.css("transform", "rotate(225deg)");
                }
            }
        });
    }

    $(window).resize(function() {
        var $window_width = $(window).width(),
            $menu = $(".menu");
        if($window_width <= 767) {
            $menu.addClass("opening");
            setTimeout(function() {
                $menu.removeClass("opening");
            }, 100);

        }
    });
});<commit_msg>메인 > 스킬 영역 스타일 개선 완료<commit_after>window.onload = function() {
    var $body = $("body"),
        $window = $(window),
        $window_width = $window.width(),
        $window_height = $window.height(),
        $section_2 = $(".section-2"),
        $skill_row = $section_2.find(".skill-row"),
        $init_loading = $("#init-loading"),
        $loading_div = $init_loading.find("div"),
        $loading_div_height = $loading_div.height(),
        $skill_item = $(".skill-row").find(".item"),
        $skill_item_width = 0,
        $skill_row_height = 0,
        $section_2_padding_height = 0;

    $init_loading.css("height", 0).css("top", -$loading_div_height+"px");

    setTimeout(function() {
        $loading_div.css("display", "none");
        $body.removeClass("loading");
    }, 3000);

    if($skill_item.length > 0) {
        $section_2_padding_height = $section_2.closest(".section").find(".border-section").innerHeight();
        $section_2.css("padding-top", $section_2_padding_height+"px");
        if($section_2.height() * 0.3 > $skill_row.width()*0.2) {
            //가로 길이 사용
            $skill_item_width = $skill_item.width();
            $skill_item.height($skill_item_width);
        } else {
            //세로 길이 사용
            $section_2.addClass("height");
            $skill_row_height = $skill_row.height();
            $skill_item.width($skill_row_height);
            $skill_item.height($skill_row_height);

        }

    }

};

$(function() {

    $(".main").onepage_scroll({
        sectionContainer: "section",     // sectionContainer accepts any kind of selector in case you don't want to use section
        easing: "ease",                  // Easing options accepts the CSS3 easing animation such "ease", "linear", "ease-in",
                                         // "ease-out", "ease-in-out", or even cubic bezier value such as "cubic-bezier(0.175, 0.885, 0.420, 1.310)"
        animationTime: 1000,             // AnimationTime let you define how long each section takes to animate
        pagination: true,                // You can either show or hide the pagination. Toggle true for show, false for hide.
        updateURL: false,                // Toggle this true if you want the URL to be updated automatically when the user scroll to each page.
        beforeMove: function(index) {},  // This option accepts a callback function. The function will be called before the page moves.
        afterMove: function(index) {},   // This option accepts a callback function. The function will be called after the page moves.
        loop: false,                     // You can have the page loop back to the top/bottom when the user navigates at up/down on the first/last page.
        keyboard: true,                  // You can activate the keyboard controls
        responsiveFallback: false,        // You can fallback to normal page scroll by defining the width of the browser in which
        // you want the responsive fallback to be triggered. For example, set this to 600 and whenever
        // the browser's width is less than 600, the fallback will kick in.
        direction: "vertical"            // You can now define the direction of the One Page Scroll animation. Options available are "vertical" and "horizontal". The default value is "vertical".
    });

    // drawer menu open/close
    // touch 이벤트 유무에 따라서 이벤트 다르게 적용
    if ('ontouchstart' in document.documentElement) {
        $(".ssc-btn-sidebar").on("touchstart", function() {
            var $menu = $(".menu"),
                $navbar_backdrop = $("#ssc-navbar-backdrop"),
                $ssc_right_to = $(this).find(".ssc-right-to");
            $(this).removeClass("touch-end");
            if($menu.length > 0) {
                if($menu.hasClass("open")) {
                    $menu.removeClass("open");
                    $navbar_backdrop.removeClass("open");
                    $(this).removeClass("active");
                    //$ssc_right_to.css("transform", "rotate(45deg)");
                } else {
                    $menu.addClass("open");
                    $navbar_backdrop.addClass("open");
                    $(this).addClass("active");
                    //$ssc_right_to.css("transform", "rotate(225deg)");
                }
            }
        }).on("touchend", function() {
            var $this_btn = $(this);
            setTimeout(function() {
                $this_btn.addClass("touch-end");
            }, 1200);
        });
    } else {
        $(".ssc-btn-sidebar").on("click", function() {
            var $menu = $(".menu"),
                $navbar_backdrop = $("#ssc-navbar-backdrop"),
                $ssc_right_to = $(this).find(".ssc-right-to");
            if($menu.length > 0) {
                if($menu.hasClass("open")) {
                    $menu.removeClass("open");
                    $navbar_backdrop.removeClass("open");
                    $(this).removeClass("active");
                    //$ssc_right_to.css("transform", "rotate(45deg)");
                } else {
                    $menu.addClass("open");
                    $navbar_backdrop.addClass("open");
                    $(this).addClass("active");
                    //$ssc_right_to.css("transform", "rotate(225deg)");
                }
            }
        });
    }

    $(window).resize(function() {
        var $window_width = $(window).width(),
            $menu = $(".menu");
        if($window_width <= 767) {
            $menu.addClass("opening");
            setTimeout(function() {
                $menu.removeClass("opening");
            }, 100);

        }
    });
});<|endoftext|>


<commit_before>if ( typeof Promise == 'undefined' ) { var _P = require('../lib/promise.min.js').Promise; } else { var _P = Promise; }
var Session = require('session.js');
var Excp = require('excp.js');

/**
 * 常用工具
 * @param {[type]} option [description]
 */

function Utils( option ) {

	option = option || {};
	this.ss = new Session( option );
	this.host = option['https'] || option['host'];
	this.cid =  option.app || '';

	// 1.5.1 新增: 用户数据处理云端应用
	this.appid = option['appid']; // 小程序ID，可不填写
	this.secret = option['secret']; // 云端鉴权 secret 格式为 appid|secret

	this.send = function( data ) {
		var api  = 'https://' +  this.host + '/_a/baas/utils/send';
			data = data || {};
		return this.request( 'POST', api, data);
	}

	/**
	 * 获取小程序页面二维码 ( 1.0  RC6 )
	 * @param  string path 小程序页面
	 * @param  number width 二维码的宽度 默认 430
	 */
	this.qrcode = function( path, width, option ) {
		option = option || {};
		var api  = 'https://' +  this.host + '/_a/baas/utils/pageqr';
		width = width || 430;

		var query = option || {}, queryString = [];
		query['path'] = escape(path);
		query['width'] = width;
		query["_sid"] = this.ss.id();
		query["_cid"] = option['app'] || this.cid;

		for( var key in query  ) {
			queryString.push( key + '=' + query[key]);
		}
		return api + '?' + queryString.join('&');

	}

	/**
	 * 获取二维码地址 ( 1.0 RC5 )
	 * @param  string content 二维码内容
	 * @param  object option 配置项
	 *         number option.size   大小(长宽) 默认 300
	 *         number option.padding  边距  默认 10
	 *         string option.foreground  前景色 默认 '0,0,0,0' r,g,b,a 白色不透明
	 *         string option.background  背景色 默认  '255,255,255,0' r,g,b,a 黑色不透明
	 *         string option.font  字体 默认 'LantingQianHei.ttf'  有效值 ('elephant.ttf'/'LantingQianHei.ttf'/'LantingHei.ttc'/'STHeitiLight.ttc'/'STHeitiMedium.ttc')
	 *         number option.fontsize  字体大小 默认 14
	 *         string option.label  底部标签名称 默认 '扫描二维码'
	 *
	 * @return string 二维码图片地址
	 */
	this.qrImageUrl = function( content, option ) {
		var api  = 'https://' +  this.host + '/_a/baas/utils/qrcode';
		var query = option || {}, queryString = [];
		query['code'] = escape(content);
		query['size'] = query['size'] || 300;
		query['padding'] =  query['padding'] || 10;
		query['foreground'] = query['foreground']  || '0,0,0,0';
		query['background'] = query['background']  || '255,255,255,0';
		query['font'] = query['font']  || 'LantingQianHei.ttf';
		query['fontsize'] = query['fontsize']  || 14;
		query['label'] = query['label']  || '扫描二维码';
		query["_sid"] = this.ss.id();
		query["_cid"] = this.cid;

		for( var key in query  ) {
			queryString.push( key + '=' + query[key]);
		}
		return api + '?' + queryString.join('&');

	}

	/**
	 * 生成一个 Guid
	 * @return GUID
	 */
	this.guid = function(){

		function S4() {
    		return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
  		}
  		return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4());
	}

	/**
	 * 抓取远程地址
	 *
	 * @param  string url 地址
	 * @param  object option 请求选项
	 *
	 * @param  string option.method 请求方法 GET/POST/PUT/DELETE
	 * @param  object option.query  GET 参数
	 * @param  object option.data   POST 参数
	 * @param  object option.header   Request Header
	 * @param  string option.type     HTTP REQUEST TYPE 默认为 form . 有效数值 form/json/raw/media
	 * @param  string option.datatype HTTP RESPONSE TYPE 默认为 html. 有效值 json/html/auto/xml
	 *
	 *
	 * @return Promise
	 */
	this.fetch = function ( url, option ) {

		var api  = 'https://' +  this.host + '/_a/baas/route/url';
			option = option || {};
			option['datatype'] = option['datatype'] || 'html';
			option['type'] =  option['type'] || 'form';
			option['header'] = option['header']  || {};

		return this.request( 'POST', api, {
			'url': url,
			'method':option['method'] || 'GET',
			'option':option || {}
		}, {
			'dataType': option['datatype'] || 'text',
			'header': option['header']
		});

	}

	/**
	 * 上传文件到云端
	 * @param  string tmpFile 上传文件资源的路径
	 * @param  string api     云端API地址
	 * @param  string name     文件对应的 key , 开发者在服务器端通过这个 key 可以获取到文件二进制内容
	 * @param  object data    formData
	 * @param  object option 选项
	 * @return Promise
	 */
	this.upload = function( tmpFile, name, api, data, option ) {

		option = option || {};
		data = data || {};
		option['header'] = option['header'] || {'content-type': 'application/json'};
		option['dataType'] = option['dataType'] || 'json';

		return new _P(function (resolve, reject) {
			wx.uploadFile({
				url:api,
				filePath:tmpFile,
				name:name,
				formData:data,
				header:option['header'],
				success:function(res){

					if ( res.statusCode != 200 ) {
						reject(new Excp('上传文件失败', res.statusCode, {
							res:res,
							tmpFile:tmpFile,
							name:name,
							api:api,
							data:data,
							option:option
						}));
						return;
					}

					if ( typeof res['data'] == 'string' && option['dataType'] == 'json') {

						try { res['data'] = JSON.parse(res['data']) } catch(e){
							reject(new Excp('上传文件失败 ' + e.message ,500, {
								res:res,
								tmpFile:tmpFile,
								name:name,
								type:typeof res['data'],
								api:api,
								data:data,
								option:option
							}));
						}

					}

					if ( typeof res['data'] != 'object' && option['dataType'] == 'json') {
						reject(new Excp('上传文件失败 Json Error',500, {
							res:res,
							tmpFile:tmpFile,
							name:name,
							type:typeof res['data'],
							api:api,
							data:data,
							option:option
						}));
						return;
					}

					if ( typeof res['data']['code'] != 'undefined' &&  res['data']['code']  != 0 ) {
						res['data']['message'] = res['data']['message'] || 'Server Gone';

						reject(new Excp('上传文件失败 ' + res['data']['message'],500, {
							res:res,
							tmpFile:tmpFile,
							name:name,
							api:api,
							data:data,
							option:option
						}));
						return;
					}

					resolve( res['data'] );

				},
				fail:function(res){
					reject(new Excp('上传文件失败',500, {
						res:res,
						tmpFile:tmpFile,
						name:name,
						api:api,
						data:data,
						option:option
					}));
				}
			});

		});

	}

	/**
	 *
	 * 发起 Request 请求
	 *
	 * @param  string method 方法 GET/POST/PUT/DELETE ..
	 * @param  string api API 地址
	 * @param  object data  post data
	 * @param  object option 选项
	 * @return Promise
	 */
	this.request = function( method, api, data, option ) {
		option = option || {};
		option['header'] = option['header'] || {'content-type': 'application/json'};
		option['dataType'] = option['dataType'] || 'json';
		data = data || {};
		var queryAdd = {};

		queryAdd["_sid"] = this.ss.id();
		queryAdd["_cid"] = this.cid;
		queryAdd["_appid"] = this.appid;
		queryAdd["_secret"] = this.secret;

		var query = [], queryString ='';
		for( var field in queryAdd ) {
			query.push(field + '=' + queryAdd[field]);
		}

		var queryString = query.join('&');

		if ( api.indexOf('?') === -1 ) {
			api = api + '?' + queryString;
		} else {
			api = api + '&' + queryString;
		}

		return new _P(function (resolve, reject) {

			wx.request({
				url: api,
				data: data,
				header: option['header'],
				method: method,
				success: function (res){

					if ( res.statusCode != 200 ) {
						reject(new Excp('请求API失败', res.statusCode, {
							res:res,
							method:method,
							api:api,
							data:data,
							option:option
						}));
						return;
					}

					if ( typeof res['data']['code'] != 'undefined' &&  res['data']['code']  != 0 ) {

					   res.data = res.data || {};
				       var message = res.data.message || '请求API失败';
				       if ( typeof res.data == 'string' ) {
				       		message = res.data;
				       }

						reject(new Excp( message, 500, {
							res:res,
							method:method,
							api:api,
							data:data,
							option:option
						}));
						return;
					}

					if ( typeof res['data'] != 'object' && option['dataType'] == 'json') {

					   res.data = res.data || {};
				       var message = res.data.message || '请求API失败';
				       if ( typeof res.data == 'string' ) {
				       		message = res.data;
				       }

						reject(new Excp(message,500, {
							res:res,
							method:method,
							api:api,
							data:data,
							option:option
						}));
						return;
					}
					resolve( res['data'] );
				},

				fail: function (res) {
					reject(new Excp('请求API失败',500, {
						res:res,
						method:method,
						api:api,
						data:data,
						option:option
					}));
				}
			});
		});
	}

	/**
	 * 计算时间差值
	 * @param  datetime start 开始时间
	 * @param  datetime end   结束时间
	 * @param  string unit 时间单位 （ 默认 second,  有效值  day hour  minute second ）
	 * @return 时间差值
	 */
	this.timediff = function( start, end, unit ) {
		var u = unit || 'second'; // day hour  minute second

		if ( typeof start == 'string') {
			start = start.replace(/-/g, "/");
		}

		if ( typeof end == 'string') {
			end = end.replace(/-/g, "/");
		}

		var startDate = new Date( start );
		var endDate   = new Date( end );

		var seconds = (endDate.getTime() - startDate.getTime()) / 1000;

		if ( u == 'second' ) {
			return seconds;
		} else if ( u == 'minute' ) {
			return seconds / 60
		} else if ( u == 'hour' ) {
			return seconds / 3600
		} else if ( u == 'day' ) {
			return seconds / 86400
		}

		return seconds;
	}

	/**
	 * 检查变量是否为空
	 * @param  mix obj string/number/object/array
	 * @return boolen 为空返回 true 否则返回 false
	 */
	this.empty = function( obj ) {

		if ( typeof obj == 'undefined' ) {
			return true;
		} else if ( typeof obj == 'string' && obj == '' ) {
			return true;
		} else if ( typeof obj == 'object' && Object.keys(obj).length == 0 ) {
			return true;
		} else if ( typeof obj == 'number' && Number.isNaN(obj) ) {
			return true;
		}

		return false;

	}

	/**
	 * 合并多个Object
	 * @param  Object  n1, n2...  待合并的 Object
	 * @return Object
	 */
	this.merge = function() {

	    var to = new Object();

	    // 遍历剩余所有参数
	    for (var i = 0; i < arguments.length; i++) {
	      var nextSource = arguments[i];
	      // 参数为空，则跳过，继续下一个
	      if (nextSource === undefined || nextSource === null) {
	        continue;
	      }
	      nextSource = Object(nextSource);

	      // 获取改参数的所有key值，并遍历
	      var keysArray = Object.keys(nextSource);
	      for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) {
	        var nextKey = keysArray[nextIndex];
	        var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey);
	        // 如果不为空且可枚举，则直接浅拷贝赋值
	        if (desc !== undefined && desc.enumerable) {
	          to[nextKey] = nextSource[nextKey];
	        }
	      }
	    }

	    return to;
	}

	/**
	 * 获取当前的地理位置
	 * @param  string type 默认为 wgs84  返回 gps 坐标
	 * @return Promise
	 */
	this.getLocation = function( type ) {

		type = type || 'wgs84';
		return new _P(function (resolve, reject) {
			wx.getLocation({
				type:type,
				success:function( data ) {
					resolve(data);
					return;
				},

				fail: function( res ) {
					reject(new Excp('调用地位信息失败',500, {
						res:res,
						type:type
					}));
					return;
				}
			});
		});
	}

	/**
	 * 打开地图选择位置
	 * @return Promise
	 */
	this.chooseLocation = function() {

		return new _P(function (resolve, reject) {
			wx.chooseLocation({

				success:function( data ) {
					resolve(data);
					return;
				},

				fail: function( res ) {
					if ( res.errMsg=='chooseLocation:fail cancel') {
						reject(new Excp('用户取消调用',501, {
							res:res
						}));

					} else {
						reject(new Excp('打开地图选择位置接口失败',500, {
							res:res
						}));
					}
					return;
				}
			});
		});
	}

}

module.exports = Utils;<commit_msg>REQUEST<commit_after>if ( typeof Promise == 'undefined' ) { var _P = require('../lib/promise.min.js').Promise; } else { var _P = Promise; }
var Session = require('session.js');
var Excp = require('excp.js');

/**
 * 常用工具
 * @param {[type]} option [description]
 */

function Utils( option ) {

	option = option || {};
	this.ss = new Session( option );
	this.host = option['https'] || option['host'];
	this.cid =  option.app || '';

	// 1.5.1 新增: 用户数据处理云端应用
	this.appid = option['appid']; // 小程序ID，可不填写
	this.secret = option['secret']; // 云端鉴权 secret 格式为 appid|secret

	this.send = function( data ) {
		var api  = 'https://' +  this.host + '/_a/baas/utils/send';
			data = data || {};
		return this.request( 'POST', api, data);
	}

	/**
	 * 获取小程序页面二维码 ( 1.0  RC6 )
	 * @param  string path 小程序页面
	 * @param  number width 二维码的宽度 默认 430
	 */
	this.qrcode = function( path, width, option ) {
		option = option || {};
		var api  = 'https://' +  this.host + '/_a/baas/utils/pageqr';
		width = width || 430;

		var query = option || {}, queryString = [];
		query['path'] = escape(path);
		query['width'] = width;
		query["_sid"] = this.ss.id();
		query["_cid"] = option['app'] || this.cid;

		for( var key in query  ) {
			queryString.push( key + '=' + query[key]);
		}
		return api + '?' + queryString.join('&');

	}

	/**
	 * 获取二维码地址 ( 1.0 RC5 )
	 * @param  string content 二维码内容
	 * @param  object option 配置项
	 *         number option.size   大小(长宽) 默认 300
	 *         number option.padding  边距  默认 10
	 *         string option.foreground  前景色 默认 '0,0,0,0' r,g,b,a 白色不透明
	 *         string option.background  背景色 默认  '255,255,255,0' r,g,b,a 黑色不透明
	 *         string option.font  字体 默认 'LantingQianHei.ttf'  有效值 ('elephant.ttf'/'LantingQianHei.ttf'/'LantingHei.ttc'/'STHeitiLight.ttc'/'STHeitiMedium.ttc')
	 *         number option.fontsize  字体大小 默认 14
	 *         string option.label  底部标签名称 默认 '扫描二维码'
	 *
	 * @return string 二维码图片地址
	 */
	this.qrImageUrl = function( content, option ) {
		var api  = 'https://' +  this.host + '/_a/baas/utils/qrcode';
		var query = option || {}, queryString = [];
		query['code'] = escape(content);
		query['size'] = query['size'] || 300;
		query['padding'] =  query['padding'] || 10;
		query['foreground'] = query['foreground']  || '0,0,0,0';
		query['background'] = query['background']  || '255,255,255,0';
		query['font'] = query['font']  || 'LantingQianHei.ttf';
		query['fontsize'] = query['fontsize']  || 14;
		query['label'] = query['label']  || '扫描二维码';
		query["_sid"] = this.ss.id();
		query["_cid"] = this.cid;

		for( var key in query  ) {
			queryString.push( key + '=' + query[key]);
		}
		return api + '?' + queryString.join('&');

	}

	/**
	 * 生成一个 Guid
	 * @return GUID
	 */
	this.guid = function(){

		function S4() {
    		return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
  		}
  		return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4());
	}

	/**
	 * 抓取远程地址
	 *
	 * @param  string url 地址
	 * @param  object option 请求选项
	 *
	 * @param  string option.method 请求方法 GET/POST/PUT/DELETE
	 * @param  object option.query  GET 参数
	 * @param  object option.data   POST 参数
	 * @param  object option.header   Request Header
	 * @param  string option.type     HTTP REQUEST TYPE 默认为 form . 有效数值 form/json/raw/media
	 * @param  string option.datatype HTTP RESPONSE TYPE 默认为 html. 有效值 json/html/auto/xml
	 *
	 *
	 * @return Promise
	 */
	this.fetch = function ( url, option ) {

		var api  = 'https://' +  this.host + '/_a/baas/route/url';
			option = option || {};
			option['datatype'] = option['datatype'] || 'html';
			option['type'] =  option['type'] || 'form';
			option['header'] = option['header']  || {};

		return this.request( 'POST', api, {
			'url': url,
			'method':option['method'] || 'GET',
			'option':option || {}
		}, {
			'dataType': option['datatype'] || 'text',
			'header': option['header']
		});

	}

	/**
	 * 上传文件到云端
	 * @param  string tmpFile 上传文件资源的路径
	 * @param  string api     云端API地址
	 * @param  string name     文件对应的 key , 开发者在服务器端通过这个 key 可以获取到文件二进制内容
	 * @param  object data    formData
	 * @param  object option 选项
	 * @return Promise
	 */
	this.upload = function( tmpFile, name, api, data, option ) {

		option = option || {};
		data = data || {};
		option['header'] = option['header'] || {'content-type': 'application/json'};
		option['dataType'] = option['dataType'] || 'json';

		return new _P(function (resolve, reject) {
			wx.uploadFile({
				url:api,
				filePath:tmpFile,
				name:name,
				formData:data,
				header:option['header'],
				success:function(res){

					if ( res.statusCode != 200 ) {
						reject(new Excp('上传文件失败', res.statusCode, {
							res:res,
							tmpFile:tmpFile,
							name:name,
							api:api,
							data:data,
							option:option
						}));
						return;
					}

					if ( typeof res['data'] == 'string' && option['dataType'] == 'json') {

						try { res['data'] = JSON.parse(res['data']) } catch(e){
							reject(new Excp('上传文件失败 ' + e.message ,500, {
								res:res,
								tmpFile:tmpFile,
								name:name,
								type:typeof res['data'],
								api:api,
								data:data,
								option:option
							}));
						}

					}

					if ( typeof res['data'] != 'object' && option['dataType'] == 'json') {
						reject(new Excp('上传文件失败 Json Error',500, {
							res:res,
							tmpFile:tmpFile,
							name:name,
							type:typeof res['data'],
							api:api,
							data:data,
							option:option
						}));
						return;
					}

					if ( typeof res['data']['code'] != 'undefined' &&  res['data']['code']  != 0 ) {
						res['data']['message'] = res['data']['message'] || 'Server Gone';

						reject(new Excp('上传文件失败 ' + res['data']['message'],500, {
							res:res,
							tmpFile:tmpFile,
							name:name,
							api:api,
							data:data,
							option:option
						}));
						return;
					}

					resolve( res['data'] );

				},
				fail:function(res){
					reject(new Excp('上传文件失败',500, {
						res:res,
						tmpFile:tmpFile,
						name:name,
						api:api,
						data:data,
						option:option
					}));
				}
			});

		});

	}

	/**
	 *
	 * 发起 Request 请求
	 *
	 * @param  string method 方法 GET/POST/PUT/DELETE ..
	 * @param  string api API 地址
	 * @param  object data  post data
	 * @param  object option 选项
	 * @return Promise
	 */
	this.request = function( method, api, data, option ) {
		option = option || {};
		option['header'] = option['header'] || {'content-type': 'application/json'};
		option['dataType'] = option['dataType'] || 'json';
		data = data || {};
		var queryAdd = {};

		queryAdd["_sid"] = this.ss.id();
		queryAdd["_cid"] = this.cid;
		queryAdd["_appid"] = this.appid;
		queryAdd["_secret"] = this.secret;

		var query = [], queryString ='';
		for( var field in queryAdd ) {
			query.push(field + '=' + queryAdd[field]);
		}

		var queryString = query.join('&');

		if ( api.indexOf('?') === -1 ) {
			api = api + '?' + queryString;
		} else {
			api = api + '&' + queryString;
		}

		return new _P(function (resolve, reject) {

			wx.request({
				url: api,
				data: data,
				header: option['header'],
				method: method,
				success: function (res){

					if ( res.statusCode != 200 ) {
						reject(new Excp('请求API失败', res.statusCode, {
							res:res,
							method:method,
							api:api,
							data:data,
							option:option
						}));
						return;
					}

					if ( typeof res['data']['code']  != null && typeof res['data']['code'] != 'undefined' && typeof res['data']['message'] != 'undefined' &&  res['data']['code']  != 0 ) {

					   res.data = res.data || {};
				       var message = res.data.message || '请求API失败';
				       if ( typeof res.data == 'string' ) {
				       		message = res.data;
				       }

						reject(new Excp( message, 500, {
							res:res,
							method:method,
							api:api,
							data:data,
							option:option
						}));
						return;
					}

					if ( typeof res['data'] != 'object' && option['dataType'] == 'json') {

					   res.data = res.data || {};
				       var message = res.data.message || '请求API失败';
				       if ( typeof res.data == 'string' ) {
				       		message = res.data;
				       }

						reject(new Excp(message,500, {
							res:res,
							method:method,
							api:api,
							data:data,
							option:option
						}));
						return;
					}
					resolve( res['data'] );
				},

				fail: function (res) {
					reject(new Excp('请求API失败',500, {
						res:res,
						method:method,
						api:api,
						data:data,
						option:option
					}));
				}
			});
		});
	}

	/**
	 * 计算时间差值
	 * @param  datetime start 开始时间
	 * @param  datetime end   结束时间
	 * @param  string unit 时间单位 （ 默认 second,  有效值  day hour  minute second ）
	 * @return 时间差值
	 */
	this.timediff = function( start, end, unit ) {
		var u = unit || 'second'; // day hour  minute second

		if ( typeof start == 'string') {
			start = start.replace(/-/g, "/");
		}

		if ( typeof end == 'string') {
			end = end.replace(/-/g, "/");
		}

		var startDate = new Date( start );
		var endDate   = new Date( end );

		var seconds = (endDate.getTime() - startDate.getTime()) / 1000;

		if ( u == 'second' ) {
			return seconds;
		} else if ( u == 'minute' ) {
			return seconds / 60
		} else if ( u == 'hour' ) {
			return seconds / 3600
		} else if ( u == 'day' ) {
			return seconds / 86400
		}

		return seconds;
	}

	/**
	 * 检查变量是否为空
	 * @param  mix obj string/number/object/array
	 * @return boolen 为空返回 true 否则返回 false
	 */
	this.empty = function( obj ) {

		if ( typeof obj == 'undefined' ) {
			return true;
		} else if ( typeof obj == 'string' && obj == '' ) {
			return true;
		} else if ( typeof obj == 'object' && Object.keys(obj).length == 0 ) {
			return true;
		} else if ( typeof obj == 'number' && Number.isNaN(obj) ) {
			return true;
		}

		return false;

	}

	/**
	 * 合并多个Object
	 * @param  Object  n1, n2...  待合并的 Object
	 * @return Object
	 */
	this.merge = function() {

	    var to = new Object();

	    // 遍历剩余所有参数
	    for (var i = 0; i < arguments.length; i++) {
	      var nextSource = arguments[i];
	      // 参数为空，则跳过，继续下一个
	      if (nextSource === undefined || nextSource === null) {
	        continue;
	      }
	      nextSource = Object(nextSource);

	      // 获取改参数的所有key值，并遍历
	      var keysArray = Object.keys(nextSource);
	      for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) {
	        var nextKey = keysArray[nextIndex];
	        var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey);
	        // 如果不为空且可枚举，则直接浅拷贝赋值
	        if (desc !== undefined && desc.enumerable) {
	          to[nextKey] = nextSource[nextKey];
	        }
	      }
	    }

	    return to;
	}

	/**
	 * 获取当前的地理位置
	 * @param  string type 默认为 wgs84  返回 gps 坐标
	 * @return Promise
	 */
	this.getLocation = function( type ) {

		type = type || 'wgs84';
		return new _P(function (resolve, reject) {
			wx.getLocation({
				type:type,
				success:function( data ) {
					resolve(data);
					return;
				},

				fail: function( res ) {
					reject(new Excp('调用地位信息失败',500, {
						res:res,
						type:type
					}));
					return;
				}
			});
		});
	}

	/**
	 * 打开地图选择位置
	 * @return Promise
	 */
	this.chooseLocation = function() {

		return new _P(function (resolve, reject) {
			wx.chooseLocation({

				success:function( data ) {
					resolve(data);
					return;
				},

				fail: function( res ) {
					if ( res.errMsg=='chooseLocation:fail cancel') {
						reject(new Excp('用户取消调用',501, {
							res:res
						}));

					} else {
						reject(new Excp('打开地图选择位置接口失败',500, {
							res:res
						}));
					}
					return;
				}
			});
		});
	}

}

module.exports = Utils;<|endoftext|>


<commit_before>app.controller('ChartController', [
'$scope',
'$http',
function($scope, $http){
  var colors = ['#2196F3', '#4CAF50', '#FF9800', '#9C27B0',
   '#E91E63', '#FFEB3B', '#009688', '#F44336', '#3F51B5'];
  Highcharts.setOptions({
    lang: {
      loading: $('<iron-icon>').attr('icon', 'autorenew').addClass('fa-spin loading-spinner').prop('outerHTML')
    }
  });
  var chart;

  $scope.toggleSeriesTrendline = function(value) {
    $scope.highchartsNG.series.forEach(function(series) {
      if (series.name.match(/Series Trendline/)) {
        series.visible = !value;
      }
    })
  }

  $scope.toggleSeasonTrendline = function(value) {
    $scope.highchartsNG.series.forEach(function(series) {
      if (series.name.match(/Season \d+ Trendline/)) {
        series.visible = !value;
      }
    })
  }

  $scope.$watch('scale', function(newVal, oldVal) {
    if(newVal === 'auto') {
      chart.yAxis[0].setExtremes(null, 10);
    } else if(newVal === 'full') {
      chart.yAxis[0].setExtremes(0, 10);
    }
  });

  $scope.highchartsNG = {
    options: {
      chart: {
        backgroundColor: "white",
        spacingTop: 24,
        style: {
          fontSize: 12,
          fontFamily: 'Roboto',
          fontWeight: 300,
        }
      },
      navigation: {
        buttonOptions: {
          theme: {
            fill: '#333333',
            stroke: '#000',
            states: {
              hover: {
                fill: '#424242',
                stroke: '#333',
              },
              select: {
                fill: '#424242',
                stroke: '#333',
              }
            }
          }
        }
      },
      yAxis: {
        max: 10,
        title: {
          text: "IMDb Rating",
          style: {
            color: "#black",
            fontWeight: 300,
          }
        },
        labels: {
          style: {
            color: "black",
          }
        },
        gridLineColor: '#E0E0E0'
      },
      xAxis: {
        tickColor: "transparent", // hide ticks
        lineColor: "transparent", // hide line
        allowDecimals: false,
        title: {
          text: "Episode Number",
          style: {
            color: "black",
            fontWeight: 300,
          }
        },
        labels: {
          style: {
            color: "black",
          }
        }
      },
      legend: {
        enabled: false
      },
      credits: {
        enabled: false
      },
      tooltip: {
        useHTML: true,
        formatter: function() {
          if (this.series.options.type === "scatter") {
            return [
                    "", this.point.title, "", "
",
                    "Season ", this.point.season, ", Episode ", this.point.number, "
",
                    "Rating: ", this.point.rating
                    ].join("");
          } else if (this.series.options.type === "line") {
            return false;
          }
        },
        shape: 'square',
        backgroundColor: 'white',
        borderRadius: 0,
        borderWidth: 2
      },
      plotOptions: {
        scatter: {
          cursor: 'pointer',
          point: {
            events: {
              click: function() {
                window.open(
                  ["http://www.imdb.com/title/", this.options.imdb_id, "/"].join(""),
                '_blank');
              }
            }
          }
        }
      }
    },
    title: {
        useHTML: true,
        text: '' + title + '',
        style: {
          color: "black",
          font: '26px Roboto',
          fontWeight: 300,
        }
    },
    loading: true,
    size: {
      height: 500
    },
    series: [],
    func: function(retChart) {
      chart = retChart;
    }
  };

  $scope.scale = 'auto';

  $scope.seriesTrendline = false;
  $scope.seasonTrendline = true;

  $http.get("data/" + id).then(function successCallback(response) {
    $scope.highchartsNG.loading = false;
    $scope.highchartsNG.series = transform(response.data);
  }, function errorCallback(response) {
    $('.loading-spinner').attr('icon', 'error').removeClass('fa-spin').addClass('error');
  });

  function transform(data) {
    values = [];
    values.push(buildShowTrendline(data));
    data.seasons.forEach(function(season) {
      values.push(buildTrendline(season));
      values.push(buildScatter(season));
    })
    return values;
  }

  function buildTrendline(season) {
    trendline = {};
    trendline.type = 'line';
    trendline.color = colors[(season.number - 1) % colors.length];
    trendline.name = 'Season ' + season.number + ' Trendline';
    trendline.data = season.trendline;
    trendline.marker = {
      enabled: false,
      states: {
        hover: {
          enabled: false
        }
      }
    };
    trendline.zIndex = 2;
    trendline.visible = $scope.seasonTrendline;
    return trendline;
  }

  function buildShowTrendline(show) {
    trendline = {};
    trendline.type = 'line';
    trendline.color = '#9E9E9E';
    trendline.name = 'Series Trendline';
    trendline.data = show.trendline;
    trendline.marker = {
      enabled: false,
      states: {
        hover: {
          enabled: false
        }
      }
    };
    trendline.zIndex = 1;
    trendline.visible = $scope.seriesTrendline;
    return trendline;
  }

  function buildScatter(season) {
    scatter = {};
    scatter.type = 'scatter';
    scatter.color = colors[(season.number - 1) % colors.length];
    scatter.name = 'Season ' + season.number;
    scatter.marker = {
              radius: 4,
              symbol: "circle"
          };
    data = [];
    season.episodes.forEach(function(episode) {
      point = {};
      point.x = episode.number;
      point.y = episode.rating;
      point.imdb_id = episode.id;
      point.season = season.number;
      point.number = episode.numberInSeason;
      point.title = $('
').html(episode.title).text();
      point.rating = episode.rating;
      data.push(point);
    })
    scatter.zIndex = 3;
    scatter.data = data;
    return scatter;
  }

  function padZeros(number, max) {
    var str = String(number);

    while (str.length < max) {
        str = '0' + str;
    }

    return str;
  }
}]);<commit_msg>styling<commit_after>app.controller('ChartController', [
'$scope',
'$http',
function($scope, $http){
  var colors = ['#2196F3', '#4CAF50', '#FF9800', '#9C27B0',
   '#E91E63', '#FFEB3B', '#009688', '#F44336', '#3F51B5'];
  Highcharts.setOptions({
    lang: {
      loading: $('<iron-icon>').attr('icon', 'autorenew').addClass('fa-spin loading-spinner').prop('outerHTML')
    }
  });
  var chart;

  $scope.toggleSeriesTrendline = function(value) {
    $scope.highchartsNG.series.forEach(function(series) {
      if (series.name.match(/Series Trendline/)) {
        series.visible = !value;
      }
    })
  }

  $scope.toggleSeasonTrendline = function(value) {
    $scope.highchartsNG.series.forEach(function(series) {
      if (series.name.match(/Season \d+ Trendline/)) {
        series.visible = !value;
      }
    })
  }

  $scope.$watch('scale', function(newVal, oldVal) {
    if(newVal === 'auto') {
      chart.yAxis[0].setExtremes(null, 10);
    } else if(newVal === 'full') {
      chart.yAxis[0].setExtremes(0, 10);
    }
  });

  $scope.highchartsNG = {
    options: {
      chart: {
        backgroundColor: "white",
        spacingTop: 24,
        style: {
          fontSize: 12,
          fontFamily: 'Roboto',
          fontWeight: 300,
        }
      },
      navigation: {
        buttonOptions: {
          theme: {
            fill: '#333333',
            stroke: '#000',
            states: {
              hover: {
                fill: '#424242',
                stroke: '#333',
              },
              select: {
                fill: '#424242',
                stroke: '#333',
              }
            }
          }
        }
      },
      yAxis: {
        max: 10,
        title: {
          text: "IMDb Rating",
          style: {
            color: "#black",
            fontWeight: 300,
          }
        },
        labels: {
          style: {
            color: "black",
          }
        },
        gridLineColor: '#E0E0E0'
      },
      xAxis: {
        tickColor: "transparent", // hide ticks
        lineColor: "transparent", // hide line
        allowDecimals: false,
        title: {
          text: "Episode Number",
          style: {
            color: "black",
            fontWeight: 300,
          }
        },
        labels: {
          style: {
            color: "black",
          }
        }
      },
      legend: {
        enabled: false
      },
      credits: {
        enabled: false
      },
      tooltip: {
        useHTML: true,
        formatter: function() {
          if (this.series.options.type === "scatter") {
            return [
                    "", this.point.title, "", "
",
                    "Season ", this.point.season, ", Episode ", this.point.number, "
",
                    "Rating: ", this.point.rating
                    ].join("");
          } else if (this.series.options.type === "line") {
            return false;
          }
        },
        shape: 'square',
        backgroundColor: 'white',
        borderRadius: 0,
        borderWidth: 2
      },
      plotOptions: {
        scatter: {
          cursor: 'pointer',
          point: {
            events: {
              click: function() {
                window.open(
                  ["http://www.imdb.com/title/", this.options.imdb_id, "/"].join(""),
                '_blank');
              }
            }
          }
        }
      }
    },
    title: {
        useHTML: true,
        text: '' + title + '',
        style: {
          color: "black",
          font: '26px Roboto',
          fontWeight: 300,
        }
    },
    loading: true,
    size: {
      height: 500
    },
    series: [],
    func: function(retChart) {
      chart = retChart;
    }
  };

  $scope.scale = 'auto';

  $scope.seriesTrendline = false;
  $scope.seasonTrendline = true;

  $http.get("data/" + id).then(function successCallback(response) {
    $scope.highchartsNG.loading = false;
    $scope.highchartsNG.series = transform(response.data);
  }, function errorCallback(response) {
    $('.loading-spinner').attr('icon', 'error').removeClass('fa-spin').addClass('error');
  });

  function transform(data) {
    values = [];
    values.push(buildShowTrendline(data));
    data.seasons.forEach(function(season) {
      values.push(buildTrendline(season));
      values.push(buildScatter(season));
    })
    return values;
  }

  function buildTrendline(season) {
    trendline = {};
    trendline.type = 'line';
    trendline.color = colors[(season.number - 1) % colors.length];
    trendline.name = 'Season ' + season.number + ' Trendline';
    trendline.data = season.trendline;
    trendline.marker = {
      enabled: false,
      states: {
        hover: {
          enabled: false
        }
      }
    };
    trendline.states = {
      hover: {
        enabled: false
      }
    };
    trendline.zIndex = 2;
    trendline.visible = $scope.seasonTrendline;
    return trendline;
  }

  function buildShowTrendline(show) {
    trendline = {};
    trendline.type = 'line';
    trendline.color = '#9E9E9E';
    trendline.name = 'Series Trendline';
    trendline.data = show.trendline;
    trendline.marker = {
      enabled: false,
      states: {
        hover: {
          enabled: false
        }
      }
    };
    trendline.states = {
      hover: {
        enabled: false
      }
    };
    trendline.zIndex = 1;
    trendline.visible = $scope.seriesTrendline;
    return trendline;
  }

  function buildScatter(season) {
    scatter = {};
    scatter.type = 'scatter';
    scatter.color = colors[(season.number - 1) % colors.length];
    scatter.name = 'Season ' + season.number;
    scatter.marker = {
              radius: 4,
              symbol: "circle"
          };
    data = [];
    season.episodes.forEach(function(episode) {
      point = {};
      point.x = episode.number;
      point.y = episode.rating;
      point.imdb_id = episode.id;
      point.season = season.number;
      point.number = episode.numberInSeason;
      point.title = $('
').html(episode.title).text();
      point.rating = episode.rating;
      data.push(point);
    })
    scatter.zIndex = 3;
    scatter.data = data;
    return scatter;
  }

  function padZeros(number, max) {
    var str = String(number);

    while (str.length < max) {
        str = '0' + str;
    }

    return str;
  }
}]);<|endoftext|>


<commit_before>'use strict';

self.l4 = {};

importScripts('src/sw/messaging.js');

//importScripts('bundle.js');

importScripts('babel-core/browser.js');
importScripts('babel-core/browser-polyfill.js');

importScripts('serviceworker-cache-polyfill.js');

// --------------------------------------------------------------------
// Loaders
// --------------------------------------------------------------------
importScripts('loader/default.js');
importScripts('loader/eval.js');

(function() {
    var headers = new Headers();
    //headers.append();

    var request = new Request('https://code.jquery.com/jquery-2.1.4.js', {
        method: 'GET',
        headers: headers
    });

    fetch(request).then(function(response) {
        console.log('#############################################################');
        console.log(response);
    }).catch(function(error) {
        console.log('#############################################################');
        console.log(error);
    });
})();

/*
importScripts('loader/github/github.js');
importScripts('loader/github/credentials.js');

var github = new Github(GITHUB_CREDENTIALS),
    repo = github.getRepo('jquery', 'jquery');

repo.show(function showRepoCallback(err, repo) {
    console.log('----------------------------------------------------------');
    if(err) { console.log('ERROR', err); }
    console.log(repo);
    console.log('----------------------------------------------------------');
});

repo.fork(function(err, res) {
    console.log(err);
    console.log(res);
});
(new Github(GITHUB_CREDENTIALS))
    .getRepo('Lively4', 'manuallycreated')
    .read('master', 'README.md', function(err, data) {
        console.log(err, data);
    });
(new Github(GITHUB_CREDENTIALS))
    .getRepo('Lively4', 'manuallycreated')
    .write('master', 'README.md', `# manuallycreated
A repository to be deleted by Lively4.
THIS IS NEW!
`,
    'autocommitted by Lively4',
    function(err, data) {
        console.log(err, data);
    });

(new Github(GITHUB_CREDENTIALS))
    .getUser()
    .show('Lively4', function(err, data) {
        console.log('Lively4\'s User Profile', err, data);
    });

(new Github(GITHUB_CREDENTIALS))
    .getUser()
    .userRepos('Lively4', function(err, data) {
        console.log('Lively4\'s Repos', err, data);
    });

(new Github(GITHUB_CREDENTIALS))
    .getUser()
    .userRepos('Lively4', function(err, data) {
        console.log('Lively4\'s Repos #####################################');
        console.log(err, data);
    });

(new Github(GITHUB_CREDENTIALS))
    .getUser()
    .createRepo({
        name: 'autorepo',
        description: 'This repo is auto-generated',
        "private": false,
        "has_issues": true,
        "has_wiki": true,
        "has_downloads": true,
        auto_init: true
    }, function(err, data) {
        console.log('POSSIBLY CREATED A REPO', err, data);
        repo = github.getRepo('Lively4', 'autorepo');
        repo.deleteRepo(function(err, res) {
            if(err) {
                console.log('ERROR ON DELETING A REPO!!!!');
                console.log(err);
            } else {
                console.log('DELETED A REPO!!!!');
                console.log(res);
            }
        });
    });
*/

// --------------------------------------------------------------------
// Transformers
// --------------------------------------------------------------------
importScripts('transformer/identity.js');

class ApplySourceTransformation {
    match(response) {
        var blackList = [
            'babel-core/browser.js',
            'es6-module-loader/es6-module-loader-dev.src.js',
            'bootworker.js',
            'serviceworker.js',
            'system-polyfills.src.js',
            'system.src.js',
            'serviceworker-loader.js'
        ];

        var isJS = response.url.indexOf('.js') > -1;
        var inBlackList = false;
        for(var i = 0; i < blackList.length; i++) {
            inBlackList = inBlackList || response.url.indexOf(blackList[i]) > -1;
        }
        return isJS && !inBlackList;
    }

    transform(response) {
        return response.clone().blob()
            .then(function(blob) {
                function readBlob(blob) {
                    console.log('Read blob of type ' + blob.type);
                    return new Promise(function(resolve, reject) {
                        var reader = new FileReader();

                        reader.addEventListener("load", function() {
                            resolve(reader.result);
                        });
                        reader.addEventListener("error", function(err) {
                            reject(err, 'Error during reading Blob');
                        });

                        reader.readAsBinaryString(blob);
                    });
                }

                return readBlob(blob)
                    .then(function srcTransform(content) {
                        console.log("BEFORE TRANSFORM");
                        //console.log(content);
                        console.log("AFTER TRANSFORM");
                        var transformed = babel.transform(content, {
                            //modules: 'system'
                        }).code;
                        //console.log(transformed);

                        return transformed;
                    })
                    .then(function packNewBlob(newContent) {
                        return new Blob([newContent], {
                            type: blob.type
                        });
                    });
            })
            .then(function packNewResponse(newBlob) {
                // pack new Response from a Blob and the given Response
                return new Response(newBlob, {
                    status: response.status,
                    statusText: response.statusText,
                    headers: response.headers
                });
            });
    }
}

console.log('Service Worker: File Start');

self.addEventListener('install', function(event) {
    console.log('Service Worker: Install');
});

self.addEventListener('activate', function(event) {
    console.log('Service Worker: Activate');
    self.clients.claim();
});

function useGithub(event) {
    function getDataObject(url) {
        var obj = {};
        url.split('?')[1]
            .split('&')
            .forEach(function(datum) {
                var keyValue = datum.split('=');
                obj[keyValue[0]] = keyValue[1];
            });
        return obj;
    }

    // TODO: process request and call appropriate github functionality
    // Or use communication between Broswer and Worker to make this work
    console.log(' # # # # # # # ## # # # # # # #');
    console.log(event.request);
    return fetch(event.request);
}

self.addEventListener('fetch', function(event) {
    console.log('Service Worker: Fetch', event.request, event.request.url);
    l4.broadCastMessage('FETCHING THIS STUFF2: ' + event.request.url);

    var response = event.request.url.match(/^(https:\/\/githubapi\/)/) ?
        useGithub(event) :
        parseEvent(event)
            .then(applyLoaders)
            .then(applyTransformers);

    event.respondWith(response);
});

function parseEvent(event) {
    console.log(event.request.url);
    return new Promise(function(resolve, reject) {
        resolve(event.request);
    });
}

function applyLoaders(request) {
    console.log('Service Worker: Loader', request.url);

    var response;

    /*
    if(githubLoader.match(request)) {
        response = githubLoader.transform(request);
        return response;
    }
    */

    var evalScript = new EvalLoader();
    if(evalScript.match(request)) {
        response = evalScript.transform(request);
    } else {
        response = (new DefaultLoader()).transform(request);
    }

    return response;
}

function applyTransformers(response) {
    console.log('Service Worker: Transformer', response, response.url);

    var log = new ApplySourceTransformation();
    if(log.match(response)) {
        return log.transform(response);
    }

    return (new Identity()).transform(response);
}

/*
 TODO: broker/service locator for core modules
 https://github.com/mochajs/mocha/issues/1457
 make them interchangable
 var modules;

 var broker = function broker(name) {
 return (modules && modules[name]) || broker.defaults[name]();
 };

 broker.defaults = {
 runner: function() {
 return require('./lib/runner');
 },
 test: function() {
 return require('./lib/test') ;
 }
 };

 broker.init = function init(opts) {
 modules = opts;
 return broker;
 });

 module.exports = broker;

 then calling:
 require('broker').init(modules);
 require('broker')('runner');
 */

/*
 TODO: plugin architecture
 chai.use(require('some-chai-plugin'));
 mocha.use(require('plugin-module')(opts));

 thenable plugins
 l4.use().then();
 */

/*
TODO: configuration via code, not json as code is more powerful, e.g.
// karma.conf.js
module.exports = function(karma) {
  karma.set({
     browsers: [process.env.TRAVIS ? 'Firefox' : 'Chrome']
   });
 }

// angular
 'use strict';

 var angularFiles = require('./angularFiles');
 var sharedConfig = require('./karma-shared.conf');

 module.exports = function(config) {
 sharedConfig(config, {testName: 'AngularJS: jqLite', logFile: 'karma-jqlite.log'});

 config.set({
 files: angularFiles.mergeFilesFor('karma'),
 exclude: angularFiles.mergeFilesFor('karmaExclude'),

 junitReporter: {
 outputFile: 'test_out/jqlite.xml',
 suite: 'jqLite'
 }
 });
 };
 */

/*
 // TODO: gulp-like stream processing of requests
l4.task('github*', str => {
    l4.start(str)
        .then(loader(l4-github.request))
        .then(transform(l4_babel))
        .then(transform(l4_bbb))
        .then(l4_write())
});
*/

console.log('Service Worker: File End');
<commit_msg>implemented github api via message passing<commit_after>'use strict';

self.l4 = {};

importScripts('src/sw/messaging.js');

//importScripts('bundle.js');

importScripts('babel-core/browser.js');
importScripts('babel-core/browser-polyfill.js');

importScripts('serviceworker-cache-polyfill.js');

// --------------------------------------------------------------------
// Loaders
// --------------------------------------------------------------------
importScripts('loader/default.js');
importScripts('loader/eval.js');

(function() {
    var headers = new Headers();
    //headers.append();

    var request = new Request('https://code.jquery.com/jquery-2.1.4.js', {
        method: 'GET',
        headers: headers
    });

    fetch(request).then(function(response) {
        console.log('#############################################################');
        console.log(response);
    }).catch(function(error) {
        console.log('#############################################################');
        console.log(error);
    });
})();

importScripts('loader/github/github.js');
importScripts('loader/github/credentials.js');

l4.onCall(function match(event) {
    return hasPort(event) &&
        event.data &&
        event.data.meta &&
        event.data.meta.type === 'github api';
}, function react(event) {
    var topLevelAPIMapping = {
        getIssues: 'getIssues',
        getRepo: 'getRepo',
        getUser: 'getUser',
        getGist: 'getGist'
    };

    var callback = function(err, data) {
        console.log(err, data);

        getSource(event).postMessage({
            meta: {
                type: 'github api response',
                receivedMessage: event.data
            },
            message: data
        });
    };

    var message = event.data.message,
        credentials = message.credentials || GITHUB_CREDENTIALS,
        github = new Github(credentials),
        topLevelAPIName = topLevelAPIMapping[message.topLevelAPI],
        topLevelAPIFunction = github[topLevelAPIName],
        apiObject = topLevelAPIFunction.apply(github, message.topLevelArguments),
        methodName = message.method,
        methodFunction = apiObject[methodName],
        args = message.args.concat(callback);

    methodFunction.apply(apiObject, args);

    return true;
});

var github = new Github(GITHUB_CREDENTIALS),
    repo = github.getRepo('jquery', 'jquery');

repo.show(function showRepoCallback(err, repo) {
    console.log('----------------------------------------------------------');
    if(err) { console.log('ERROR', err); }
    console.log(repo);
    console.log('----------------------------------------------------------');
});
/*

repo.fork(function(err, res) {
    console.log(err);
    console.log(res);
});
(new Github(GITHUB_CREDENTIALS))
    .getRepo('Lively4', 'manuallycreated')
    .read('master', 'README.md', function(err, data) {
        console.log(err, data);
    });
(new Github(GITHUB_CREDENTIALS))
    .getRepo('Lively4', 'manuallycreated')
    .write('master', 'README.md', `# manuallycreated
A repository to be deleted by Lively4.
THIS IS NEW!
`,
    'autocommitted by Lively4',
    function(err, data) {
        console.log(err, data);
    });

(new Github(GITHUB_CREDENTIALS))
    .getUser()
    .show('Lively4', function(err, data) {
        console.log('Lively4\'s User Profile', err, data);
    });

(new Github(GITHUB_CREDENTIALS))
    .getUser()
    .userRepos('Lively4', function(err, data) {
        console.log('Lively4\'s Repos', err, data);
    });

(new Github(GITHUB_CREDENTIALS))
    .getUser()
    .userRepos('Lively4', function(err, data) {
        console.log('Lively4\'s Repos #####################################');
        console.log(err, data);
    });

(new Github(GITHUB_CREDENTIALS))
    .getUser()
    .createRepo({
        name: 'autorepo',
        description: 'This repo is auto-generated',
        "private": false,
        "has_issues": true,
        "has_wiki": true,
        "has_downloads": true,
        auto_init: true
    }, function(err, data) {
        console.log('POSSIBLY CREATED A REPO', err, data);
        repo = github.getRepo('Lively4', 'autorepo');
        repo.deleteRepo(function(err, res) {
            if(err) {
                console.log('ERROR ON DELETING A REPO!!!!');
                console.log(err);
            } else {
                console.log('DELETED A REPO!!!!');
                console.log(res);
            }
        });
    });
*/

// --------------------------------------------------------------------
// Transformers
// --------------------------------------------------------------------
importScripts('transformer/identity.js');

class ApplySourceTransformation {
    match(response) {
        var blackList = [
            'babel-core/browser.js',
            'es6-module-loader/es6-module-loader-dev.src.js',
            'bootworker.js',
            'serviceworker.js',
            'system-polyfills.src.js',
            'system.src.js',
            'serviceworker-loader.js'
        ];

        var isJS = response.url.indexOf('.js') > -1;
        var inBlackList = false;
        for(var i = 0; i < blackList.length; i++) {
            inBlackList = inBlackList || response.url.indexOf(blackList[i]) > -1;
        }
        return isJS && !inBlackList;
    }

    transform(response) {
        return response.clone().blob()
            .then(function(blob) {
                function readBlob(blob) {
                    console.log('Read blob of type ' + blob.type);
                    return new Promise(function(resolve, reject) {
                        var reader = new FileReader();

                        reader.addEventListener("load", function() {
                            resolve(reader.result);
                        });
                        reader.addEventListener("error", function(err) {
                            reject(err, 'Error during reading Blob');
                        });

                        reader.readAsBinaryString(blob);
                    });
                }

                return readBlob(blob)
                    .then(function srcTransform(content) {
                        console.log("BEFORE TRANSFORM");
                        //console.log(content);
                        console.log("AFTER TRANSFORM");
                        var transformed = babel.transform(content, {
                            //modules: 'system'
                        }).code;
                        //console.log(transformed);

                        return transformed;
                    })
                    .then(function packNewBlob(newContent) {
                        return new Blob([newContent], {
                            type: blob.type
                        });
                    });
            })
            .then(function packNewResponse(newBlob) {
                // pack new Response from a Blob and the given Response
                return new Response(newBlob, {
                    status: response.status,
                    statusText: response.statusText,
                    headers: response.headers
                });
            });
    }
}

console.log('Service Worker: File Start');

self.addEventListener('install', function(event) {
    console.log('Service Worker: Install');
});

self.addEventListener('activate', function(event) {
    console.log('Service Worker: Activate');
    self.clients.claim();
});

function useGithub(event) {
    function getDataObject(url) {
        var obj = {};
        url.split('?')[1]
            .split('&')
            .forEach(function(datum) {
                var keyValue = datum.split('=');
                obj[keyValue[0]] = keyValue[1];
            });
        return obj;
    }

    // TODO: process request and call appropriate github functionality
    // Or use communication between Broswer and Worker to make this work
    console.log(' # # # # # # # ## # # # # # # #');
    console.log(event.request);
    return fetch(event.request);
}

self.addEventListener('fetch', function(event) {
    console.log('Service Worker: Fetch', event.request, event.request.url);
    l4.broadCastMessage('FETCHING THIS STUFF2: ' + event.request.url);

    var response = event.request.url.match(/^(https:\/\/githubapi\/)/) ?
        useGithub(event) :
        parseEvent(event)
            .then(applyLoaders)
            .then(applyTransformers);

    event.respondWith(response);
});

function parseEvent(event) {
    console.log(event.request.url);
    return new Promise(function(resolve, reject) {
        resolve(event.request);
    });
}

function applyLoaders(request) {
    console.log('Service Worker: Loader', request.url);

    var response;

    /*
    if(githubLoader.match(request)) {
        response = githubLoader.transform(request);
        return response;
    }
    */

    var evalScript = new EvalLoader();
    if(evalScript.match(request)) {
        response = evalScript.transform(request);
    } else {
        response = (new DefaultLoader()).transform(request);
    }

    return response;
}

function applyTransformers(response) {
    console.log('Service Worker: Transformer', response, response.url);

    var log = new ApplySourceTransformation();
    if(log.match(response)) {
        return log.transform(response);
    }

    return (new Identity()).transform(response);
}

/*
 TODO: broker/service locator for core modules
 https://github.com/mochajs/mocha/issues/1457
 make them interchangable
 var modules;

 var broker = function broker(name) {
 return (modules && modules[name]) || broker.defaults[name]();
 };

 broker.defaults = {
 runner: function() {
 return require('./lib/runner');
 },
 test: function() {
 return require('./lib/test') ;
 }
 };

 broker.init = function init(opts) {
 modules = opts;
 return broker;
 });

 module.exports = broker;

 then calling:
 require('broker').init(modules);
 require('broker')('runner');
 */

/*
 TODO: plugin architecture
 chai.use(require('some-chai-plugin'));
 mocha.use(require('plugin-module')(opts));

 thenable plugins
 l4.use().then();
 */

/*
TODO: configuration via code, not json as code is more powerful, e.g.
// karma.conf.js
module.exports = function(karma) {
  karma.set({
     browsers: [process.env.TRAVIS ? 'Firefox' : 'Chrome']
   });
 }

// angular
 'use strict';

 var angularFiles = require('./angularFiles');
 var sharedConfig = require('./karma-shared.conf');

 module.exports = function(config) {
 sharedConfig(config, {testName: 'AngularJS: jqLite', logFile: 'karma-jqlite.log'});

 config.set({
 files: angularFiles.mergeFilesFor('karma'),
 exclude: angularFiles.mergeFilesFor('karmaExclude'),

 junitReporter: {
 outputFile: 'test_out/jqlite.xml',
 suite: 'jqLite'
 }
 });
 };
 */

/*
 // TODO: gulp-like stream processing of requests
l4.task('github*', str => {
    l4.start(str)
        .then(loader(l4-github.request))
        .then(transform(l4_babel))
        .then(transform(l4_bbb))
        .then(l4_write())
});
*/

console.log('Service Worker: File End');
<|endoftext|>