<commit_before>(function (module) {
	"use strict";

	if( ! window.skrollr) {
		console.error('`skrollr` not found');
	} else {
		window.skrollr.decks = module(window, document, window.skrollr);
	}

} (function (window, document, skrollr) {
	"use strict";

	var defaults = {
		decks: '.skrollr-deck',
		offset: 15,
		duration: 600,
		easing: 'quadratic',
		delay: 500,
		autoscroll: true
	}, callbacks = {
		render: null,
		change: null
	};

	var setTimeout = window.setTimeout,
		clearTimeout = window.clearTimeout,
		isInitialized = false,
		settings = {},
		segments = {},
		segmentsList = [],
		nav = document.createElement('ul'),
		currentDeck;

	// Stop animating on scroll keys
	document.addEventListener('keydown', function (e) {
		var inst = skrollr.get(),
			keys = [38, 40,
					33, 34,
					36, 35];

		if(keys.indexOf(e.keyCode) > -1) {
			if(inst && inst.isAnimatingTo()) {
				inst.stopAnimateTo();
			}
		}
	}, false);

	// Auto initialize
	document.addEventListener('DOMContentLoaded', function () {
		var el = document.querySelector('.skrollr-decks-init');
		if(el && el.tagName === 'BODY') {
			init();

			// Auto resize
			window.addEventListener('load', resizeDecks, false);
			window.addEventListener('load', update, false);
			window.addEventListener('resize', resizeDecks, false);
			window.addEventListener('resize', update, false);
		}
	}, false);

	return {
		init: init,
		animateTo: animateTo,
		refresh: resizeDecks,
		on: on
	};

	function on(name, cb) {
		if(name in callbacks) {
			callbacks[name] = cb;
		}
	}

	function trigger(name, data) {
		var fn = callbacks[name];
		typeof fn === 'function' && fn.apply({}, data);
	}

	// Initialize
	function init (user) {
		if(isInitialized) {
			return false;
		} else {
			isInitialized = true;
		}

		var key, inst, renderTimer;

		user = typeof user === 'object' ? user : {};
		for(key in defaults) if(defaults.hasOwnProperty(key)) {
			settings[key] = user[key] || defaults[key];
		}

		var onRender = settings.onRender,
			onChange = settings.onChange,

		inst = skrollr.init({
			forceHeight: false
		});

		segments = findDecks(settings.decks, segmentsList);
		nav = createNav(segmentsList);

		resizeDecks();

		document.body.appendChild(nav);

		inst.refresh(nav.children);

		inst.on('render', function (e) {
			var el = update(e);

			clearTimeout(renderTimer);
			renderTimer = setTimeout(function () {
				settings.autoscroll && el && inst.animateTo(inst.relativeToAbsolute(el, 'top', 'top') + 1, settings);
			}, settings.delay);

			trigger('render', [e]);
		});
	}

	// Goto
	function animateTo(anchor, noAnim) {
		var el = segments[anchor],
			inst = skrollr.get(),
			top, active, index;

		if(inst) {
			inst.stopAnimateTo();

			if( ! el) {
				active = nav.getElementsByClassName('skrollable-between');
				if(active.length === 1) {
					index = Number(active[0].getAttribute('data-skrollr-decks-index'));
					el = segmentsList[anchor === 'up' ? index - 1 : anchor === 'down' ? index + 1 : -1];
				}

				if(active.length === 2) {
					active = active[anchor === 'up' ? 0 : anchor === 'down' ? 1 : -1];
					el = active ? segments[active.getAttribute('data-anchor-target')] : null;
				}
			}

			if(el) {
				top = inst.relativeToAbsolute(el, 'top', 'top') + 1;
				if(noAnim) {
					inst.setScrollTop(top, true);
				} else {
					inst.animateTo(top, settings);
				}

				return true;
			}
		}

		return false;
	}

	// Update decks size
	function resizeDecks() {
		var wndHeight = window.innerHeight + 2,
			deck, deckHeight, key;
		for(key in segments) if(segments.hasOwnProperty(key)) {
			deck = segments[key];
			deck.style.height = 'auto';
			deckHeight = deck.offsetHeight;
			deck.style.height = (deckHeight < wndHeight ? wndHeight : deckHeight) + 'px';
		}
	}

	// Autoscroll
	function update(e) {
		var inst = skrollr.get(),
			active = nav.getElementsByClassName('skrollable-between'),
			el, before, after;

		if(active.length === 1) {
			el = segments[active[0].getAttribute('data-anchor-target')];
			currentDeck !== el && trigger('change', [el])
			currentDeck = el;
		}

		if(active.length === 2) {
			before = e.direction === 'up' ? 0 : 1;
			after = e.direction === 'up' ? 1 : 0;

			el = segments[active[before].getAttribute('data-anchor-target')];
			if( ! isVol(el)) {
				el = segments[active[after].getAttribute('data-anchor-target')];
			}

			currentDeck !== el && trigger('change', [el])
			currentDeck = el;

			return el;
		}
	}

	function isVol(el) {
		var inst = skrollr.get(),
			top = inst.getScrollTop(),
			height = window.innerHeight,
			bottom = top + height,
			elTop = inst.relativeToAbsolute(el, 'top', 'top'),
			elBottom = inst.relativeToAbsolute(el, 'top', 'bottom'),
			offset =  height * settings.offset / 100;

		return elTop + offset < bottom  && elBottom - offset > top;
	}

	// Layout
	function findDecks(selector, list) {
		var items = {},
			start = +new Date(),
			el, i, id;

		if(typeof selector === 'string') {
			selector = document.querySelectorAll(selector);
			for(i = selector.length - 1; i > -1; i--) {
				el = selector[i];
				list.unshift(el);
				el.id = !! el.id ? el.id : 'skroll-deck-id-' + i;
				items['#' + el.id] = el;
			}
		}

		return items;
	}

	function createNav(list) {
		var frag = document.createElement('ul'),
			item = document.createElement('li'),
			i, max, el;

		frag.style.display = 'none';
		item.setAttribute('data-top-bottom', '');
		item.setAttribute('data-bottom-top', '');

		for(i = 0, max = list.length; i < max; i++) {
			el = item.cloneNode();
			el.setAttribute('data-skrollr-decks-index', i);
			el.setAttribute('data-anchor-target', '#' + list[i].id);
			frag.appendChild(el);
		}

		return frag;
	}

}));
<commit_msg>Fixed window listeners<commit_after>(function (module) {
	"use strict";

	if( ! window.skrollr) {
		console.error('`skrollr` not found');
	} else {
		window.skrollr.decks = module(window, document, window.skrollr);
	}

} (function (window, document, skrollr) {
	"use strict";

	var defaults = {
		decks: '.skrollr-deck',
		offset: 15,
		duration: 600,
		easing: 'quadratic',
		delay: 500,
		autoscroll: true
	}, callbacks = {
		render: null,
		change: null
	};

	var setTimeout = window.setTimeout,
		clearTimeout = window.clearTimeout,
		isInitialized = false,
		settings = {},
		segments = {},
		segmentsList = [],
		nav = document.createElement('ul'),
		currentDeck;

	// Stop animating on scroll keys
	document.addEventListener('keydown', function (e) {
		var inst = skrollr.get(),
			keys = [38, 40,
					33, 34,
					36, 35];

		if(keys.indexOf(e.keyCode) > -1) {
			if(inst && inst.isAnimatingTo()) {
				inst.stopAnimateTo();
			}
		}
	}, false);

	// Auto initialize
	document.addEventListener('DOMContentLoaded', function () {
		var el = document.querySelector('.skrollr-decks-init');
		if(el && el.tagName === 'BODY') {
			init();
		}
	}, false);

	// Auto resize
	window.addEventListener('load', resizeDecks, false);
	window.addEventListener('load', update, false);
	window.addEventListener('resize', resizeDecks, false);
	window.addEventListener('resize', update, false);

	return {
		init: init,
		animateTo: animateTo,
		refresh: resizeDecks,
		on: on
	};

	function on(name, cb) {
		if(name in callbacks) {
			callbacks[name] = cb;
		}
	}

	function trigger(name, data) {
		var fn = callbacks[name];
		typeof fn === 'function' && fn.apply({}, data);
	}

	// Initialize
	function init (user) {
		if(isInitialized) {
			return false;
		} else {
			isInitialized = true;
		}

		var key, inst, renderTimer;

		user = typeof user === 'object' ? user : {};
		for(key in defaults) if(defaults.hasOwnProperty(key)) {
			settings[key] = user[key] || defaults[key];
		}

		var onRender = settings.onRender,
			onChange = settings.onChange,

		inst = skrollr.init({
			forceHeight: false
		});

		segments = findDecks(settings.decks, segmentsList);
		nav = createNav(segmentsList);

		resizeDecks();

		document.body.appendChild(nav);

		inst.refresh(nav.children);

		inst.on('render', function (e) {
			var el = update(e);

			clearTimeout(renderTimer);
			renderTimer = setTimeout(function () {
				settings.autoscroll && el && inst.animateTo(inst.relativeToAbsolute(el, 'top', 'top') + 1, settings);
			}, settings.delay);

			trigger('render', [e]);
		});
	}

	// Goto
	function animateTo(anchor, noAnim) {
		var el = segments[anchor],
			inst = skrollr.get(),
			top, active, index;

		if(inst) {
			inst.stopAnimateTo();

			if( ! el) {
				active = nav.getElementsByClassName('skrollable-between');
				if(active.length === 1) {
					index = Number(active[0].getAttribute('data-skrollr-decks-index'));
					el = segmentsList[anchor === 'up' ? index - 1 : anchor === 'down' ? index + 1 : -1];
				}

				if(active.length === 2) {
					active = active[anchor === 'up' ? 0 : anchor === 'down' ? 1 : -1];
					el = active ? segments[active.getAttribute('data-anchor-target')] : null;
				}
			}

			if(el) {
				top = inst.relativeToAbsolute(el, 'top', 'top') + 1;
				if(noAnim) {
					inst.setScrollTop(top, true);
				} else {
					inst.animateTo(top, settings);
				}

				return true;
			}
		}

		return false;
	}

	// Update decks size
	function resizeDecks() {
		var wndHeight = window.innerHeight + 2,
			deck, deckHeight, key;
		for(key in segments) if(segments.hasOwnProperty(key)) {
			deck = segments[key];
			deck.style.height = 'auto';
			deckHeight = deck.offsetHeight;
			deck.style.height = (deckHeight < wndHeight ? wndHeight : deckHeight) + 'px';
		}
	}

	// Autoscroll
	function update(e) {
		var inst = skrollr.get(),
			active = nav.getElementsByClassName('skrollable-between'),
			el, before, after;

		if(active.length === 1) {
			el = segments[active[0].getAttribute('data-anchor-target')];
			currentDeck !== el && trigger('change', [el])
			currentDeck = el;
		}

		if(active.length === 2) {
			before = e.direction === 'up' ? 0 : 1;
			after = e.direction === 'up' ? 1 : 0;

			el = segments[active[before].getAttribute('data-anchor-target')];
			if( ! isVol(el)) {
				el = segments[active[after].getAttribute('data-anchor-target')];
			}

			currentDeck !== el && trigger('change', [el])
			currentDeck = el;

			return el;
		}
	}

	function isVol(el) {
		var inst = skrollr.get(),
			top = inst.getScrollTop(),
			height = window.innerHeight,
			bottom = top + height,
			elTop = inst.relativeToAbsolute(el, 'top', 'top'),
			elBottom = inst.relativeToAbsolute(el, 'top', 'bottom'),
			offset =  height * settings.offset / 100;

		return elTop + offset < bottom  && elBottom - offset > top;
	}

	// Layout
	function findDecks(selector, list) {
		var items = {},
			start = +new Date(),
			el, i, id;

		if(typeof selector === 'string') {
			selector = document.querySelectorAll(selector);
			for(i = selector.length - 1; i > -1; i--) {
				el = selector[i];
				list.unshift(el);
				el.id = !! el.id ? el.id : 'skroll-deck-id-' + i;
				items['#' + el.id] = el;
			}
		}

		return items;
	}

	function createNav(list) {
		var frag = document.createElement('ul'),
			item = document.createElement('li'),
			i, max, el;

		frag.style.display = 'none';
		item.setAttribute('data-top-bottom', '');
		item.setAttribute('data-bottom-top', '');

		for(i = 0, max = list.length; i < max; i++) {
			el = item.cloneNode();
			el.setAttribute('data-skrollr-decks-index', i);
			el.setAttribute('data-anchor-target', '#' + list[i].id);
			frag.appendChild(el);
		}

		return frag;
	}

}));
<|endoftext|>


<commit_before>import * as NavigationStateUtils from 'NavigationStateUtils'

// Actions
export const NAVIGATE = 'NAVIGATE'
export const NAV_PUSH = 'NAV_PUSH'
export const NAV_POP = 'NAV_POP'
export const NAV_JUMP_TO_KEY = 'NAV_JUMP_TO_KEY'
export const NAV_JUMP_TO_INDEX = 'NAV_JUMP_TO_INDEX'
export const NAV_RESET = 'NAV_RESET'

const initialNavState = {
	index: 0,
	routes: [
		{ key: 'First', title: 'First' }
	]
}

// Action Creators
export function navigatePush(state) {
	state = typeof state === 'string' ? { key: state, title: state } : state
	return {
		type: NAV_PUSH,
		state
	}
}
export function navigatePop() {
	return {
		type: NAV_POP
	}
}

export function navigateJumpToKey(key) {
	return {
		type: NAV_JUMP_TO_KEY,
		key
	}
}

export function navigateJumpToIndex(index) {
	return {
		type: NAV_JUMP_TO_INDEX,
		index
	}
}

export function navigateReset(routes, index) {
	return {
		type: NAV_RESET,
		index,
		routes
	}
}

export default function reducer(state = initialNavState, action) {
	switch (action.type) {
	case NAV_PUSH:
		if (state.routes[state.index].key === (action.state && action.state.key)) return state
		return NavigationStateUtils.push(state, action.state)

	case NAV_POP:
		if (state.index === 0 || state.routes.length === 1) return state
		return NavigationStateUtils.pop(state)

	case NAV_JUMP_TO_KEY:
		return NavigationStateUtils.jumpTo(state, action.key)

	case NAV_JUMP_TO_INDEX:
		return NavigationStateUtils.jumpToIndex(state, action.index)

	case NAV_RESET:
		return {
			...state,
			index: action.index,
			routes: action.routes
		}

	default:
		return state
	}
}
<commit_msg>should use `NavigationStateUtils.reset`<commit_after>import * as NavigationStateUtils from 'NavigationStateUtils'

// Actions
export const NAVIGATE = 'NAVIGATE'
export const NAV_PUSH = 'NAV_PUSH'
export const NAV_POP = 'NAV_POP'
export const NAV_JUMP_TO_KEY = 'NAV_JUMP_TO_KEY'
export const NAV_JUMP_TO_INDEX = 'NAV_JUMP_TO_INDEX'
export const NAV_RESET = 'NAV_RESET'

const initialNavState = {
	index: 0,
	routes: [
		{ key: 'First', title: 'First' }
	]
}

// Action Creators
export function navigatePush(state) {
	state = typeof state === 'string' ? { key: state, title: state } : state
	return {
		type: NAV_PUSH,
    state
	}
}
export function navigatePop() {
	return {
		type: NAV_POP
	}
}

export function navigateJumpToKey(key) {
	return {
		type: NAV_JUMP_TO_KEY,
		key
	}
}

export function navigateJumpToIndex(index) {
	return {
		type: NAV_JUMP_TO_INDEX,
		index
	}
}

export function navigateReset(routes, index) {
	return {
		type: NAV_RESET,
		index,
		routes
	}
}

export default function reducer(state = initialNavState, action) {
	switch (action.type) {
	case NAV_PUSH:
		if (state.routes[state.index].key === (action.state && action.state.key)) return state
		return NavigationStateUtils.push(state, action.state)

	case NAV_POP:
		if (state.index === 0 || state.routes.length === 1) return state
		return NavigationStateUtils.pop(state)

	case NAV_JUMP_TO_KEY:
		return NavigationStateUtils.jumpTo(state, action.key)

	case NAV_JUMP_TO_INDEX:
		return NavigationStateUtils.jumpToIndex(state, action.index)

	case NAV_RESET:
    return NavigationStateUtils.reset(state, action.routes, action.index);

	default:
		return state
	}
}
<|endoftext|>


<commit_before>var angular = require('angular');

function isNode(node) {
  if( typeof Node === 'object' && node instanceof Node ) return true;
  if( typeof node.nodeType === 'number' && node.nodeName ) return true;
  return false;
}

function isElement(node) {
  if( typeof Element === 'object' && node instanceof Element ) return true;
  if( node.nodeType === 1 && node.tagName && typeof node.setAttribute === 'function' ) return true;
  return false;
}

function isArrayLike(o) {
  if( o && typeof o === 'object' && typeof o.length === 'number' ) return true;
  return false;
}

function ensure(scope, done) {
  done = done || function(err) { if( err ) console.error(err) };
  if( isElement(scope) ) scope = angular.element(scope).scope();
  if( !scope ) return done(new TypeError('not found scope'));
  if( !scope.$root ) return done(new TypeError('invalid scope (scope.$root not found)'));
  if( !scope.$apply ) return done(new TypeError('invalid scope (scope.$apply not found)'));

  if( scope.$root.$$phase != '$digest' && scope.$root.$$phase != '$apply' ) {
    scope.$apply(function() {done && done(null, scope);});
  } else {
    done && done(null, scope);
  }
}

function pack(parent, elements, done) {
  done = done || function(err) { if( err ) console.error(err) };
  if( !elements ) return done(new TypeError('missing elements'));
  if( isNode(elements) ) elements = [elements];
  if( !isArrayLike(elements) ) return done(new TypeError('unknwon type of elements: ' +  elements));

  if( typeof parent === 'string' ) el = document.querySelector(parent);
  if( !parent ) return done(new Error('not found parent scope element'));
  if( !isElement(parent) ) return done(new Error('parent must be an element'));

  parent = angular.element(parent);
  var parentscope = parent.scope();
  var injector = parent.injector();

  if( !parentscope ) return done(new Error('not found parent scope'));
  if( !injector ) return done(new Error('not found parent scope injector'));

  [].forEach.call(elements, function(el) {
    if( el.__packed__ ) return;
    el.__packed__ = true;

    injector.invoke(['$compile', '$rootScope', function(compile, root) {
      compile(el)(parentscope || root);
      root.$digest();
    }]);
  });

  done(null, elements);
}

function scope(el, controller) {
  if( typeof el === 'string' ) el = document.querySelector(el);
  if( !el ) return console.error('not found element', el);
  if( !isElement(el) ) return console.error('element must be an element or selector', arguments[0]);
  if( controller ) el = el.querySelector('*[ng-controller=\"' + controller + '\"]');
  if( !el ) return console.error('not found controller', controller);
  //if( !el.hasAttribute('ng-app') && !el.hasAttribute('ng-controller') ) el = el.querySelector('*[ng-controller]');
  //if( !el ) return console.error('not found scoped element', arguments[0]);

  return angular.element(el).scope();
}

function scopes(el) {
  if( typeof el === 'string' ) el = document.querySelector(el);
  if( !el ) return console.error('not found element', el);
  if( !isElement(el) ) return console.error('element must be an element or selector', arguments[0]);

  var els = el.querySelectorAll('*[ng-controller]');
  var scopes = [];
  var elements = scopes.elements = [];
  [].forEach.call(els, function(node) {
    var controller = node.getAttribute('ng-controller');
    var scope = angular.element(node).scope();
    scopes.push(scope);
    scopes[controller] = scope;

    elements.push(node);
    elements[controller] = node;
  });
  return scopes;
}

function parentelement(el) {
  function find() {
    if( !el ) return null;
    var scope = angular.element(el).scope();
    if( scope ) return el;
    el = el.parentNode;
    find();
  }

  return find();
}

function parentscope(el) {
  var pel = parentelement(el);
  return pel && angular.element(pel).scope();
}

var cache = {};
function engine(defaults) {
  defaults = defaults || {};

  var root = function() {
    return defaults.app ? document.querySelector('[ng-app="' + defaults.app + '"]') : document.querySelector('[ng-app]');
  };

  return function(src, options, done) {
    var singleton = options.singleton;
    var target = options.target;
    var parent = options.parent;
    var controller = options.controller;
    var flat = options.flat || defaults.flat;

    if( controller && typeof controller === 'string' ) {
      var el = document.createElement('div');
      el.setAttribute('ng-controller', controller);

      target.innerHTML = '';
      target.appendChild(el);
      //pack(parent || parentelement(target) || root(), el);
      parent = target = el;
    }

    if( !('singleton' in options) ) singleton = defaults.singleton;
    if( !('parent' in options) ) {
      parent = defaults.parent;
      if( !parent && !flat ) parent = parentelement(target);
    }

    if( !parent ) parent = root();

    if( singleton && cache[src] ) {
      return (function() {
        target.innerHTML = '';
        [].forEach.call(cache[src].els, function(node) {
          target.appendChild(node);
        });
        done(null, cache[src].scopes);
      })();
    }

    this.util.ajax(src, function(err, html) {
      if( err ) return done(err);

      var els = this.util.evalhtml(html);
      target.innerHTML = '';
      [].forEach.call(els, function(node) {
        target.appendChild(node);
      });

      pack(parent, els, function(err) {
        if( err ) return done(err);

        var sc = scopes(target);
        if( singleton ) cache[src] = {
          els: els,
          scopes: sc
        };
        done(null, sc);
      });
    });
  };
};

function middleware(options) {
  options = options || {};
  var app = options.app;

  var root = function() {
    return app ? document.querySelector('[ng-app="' + app + '"]') : document.querySelector('[ng-app]');
  };

  return function(req, res, next) {
    res.ensure = function(scope, done) {
      ensure(scope, done);
      return this;
    };

    res.pack = function(elements, done) {
      pack(root(), elements, done);
      return this;
    };

    res.scope = function(el, controller) {
      if( !arguments.length ) return scope(root());
      return scope(el, controller);
    };

    res.scopes = function(el) {
      return scopes(el);
    };

    next();
  };
};

engine.ensure = ensure;
engine.pack = pack;
engine.scope = scope;
engine.scopes = scopes;
engine.parentelement = parentelement;
engine.parentscope = parentscope;
engine.middleware = middleware;
module.exports = engine;

angular.module('xRouterAngular', [])
.service('xRouterAngular', function() {
  return {
    ensure: ensure,
    pack: pack,
    scope: scope,
    scopes: scopes,
    parentelement: parentelement,
    parentscope: parentscope
  };
});<commit_msg>fix singleton cache reset when option changed<commit_after>var angular = require('angular');

function isNode(node) {
  if( typeof Node === 'object' && node instanceof Node ) return true;
  if( typeof node.nodeType === 'number' && node.nodeName ) return true;
  return false;
}

function isElement(node) {
  if( typeof Element === 'object' && node instanceof Element ) return true;
  if( node.nodeType === 1 && node.tagName && typeof node.setAttribute === 'function' ) return true;
  return false;
}

function isArrayLike(o) {
  if( o && typeof o === 'object' && typeof o.length === 'number' ) return true;
  return false;
}

function ensure(scope, done) {
  done = done || function(err) { if( err ) console.error(err) };
  if( isElement(scope) ) scope = angular.element(scope).scope();
  if( !scope ) return done(new TypeError('not found scope'));
  if( !scope.$root ) return done(new TypeError('invalid scope (scope.$root not found)'));
  if( !scope.$apply ) return done(new TypeError('invalid scope (scope.$apply not found)'));

  if( scope.$root.$$phase != '$digest' && scope.$root.$$phase != '$apply' ) {
    scope.$apply(function() {done && done(null, scope);});
  } else {
    done && done(null, scope);
  }
}

function pack(parent, elements, done) {
  done = done || function(err) { if( err ) console.error(err) };
  if( !elements ) return done(new TypeError('missing elements'));
  if( isNode(elements) ) elements = [elements];
  if( !isArrayLike(elements) ) return done(new TypeError('unknwon type of elements: ' +  elements));

  if( typeof parent === 'string' ) el = document.querySelector(parent);
  if( !parent ) return done(new Error('not found parent scope element'));
  if( !isElement(parent) ) return done(new Error('parent must be an element'));

  parent = angular.element(parent);
  var parentscope = parent.scope();
  var injector = parent.injector();

  if( !parentscope ) return done(new Error('not found parent scope'));
  if( !injector ) return done(new Error('not found parent scope injector'));

  [].forEach.call(elements, function(el) {
    if( el.__packed__ ) return;
    el.__packed__ = true;

    injector.invoke(['$compile', '$rootScope', function(compile, root) {
      compile(el)(parentscope || root);
      root.$digest();
    }]);
  });

  done(null, elements);
}

function scope(el, controller) {
  if( typeof el === 'string' ) el = document.querySelector(el);
  if( !el ) return console.error('not found element', el);
  if( !isElement(el) ) return console.error('element must be an element or selector', arguments[0]);
  if( controller ) el = el.querySelector('*[ng-controller=\"' + controller + '\"]');
  if( !el ) return console.error('not found controller', controller);
  //if( !el.hasAttribute('ng-app') && !el.hasAttribute('ng-controller') ) el = el.querySelector('*[ng-controller]');
  //if( !el ) return console.error('not found scoped element', arguments[0]);

  return angular.element(el).scope();
}

function scopes(el) {
  if( typeof el === 'string' ) el = document.querySelector(el);
  if( !el ) return console.error('not found element', el);
  if( !isElement(el) ) return console.error('element must be an element or selector', arguments[0]);

  var els = el.querySelectorAll('*[ng-controller]');
  var scopes = [];
  var elements = scopes.elements = [];
  [].forEach.call(els, function(node) {
    var controller = node.getAttribute('ng-controller');
    var scope = angular.element(node).scope();
    scopes.push(scope);
    scopes[controller] = scope;

    elements.push(node);
    elements[controller] = node;
  });
  return scopes;
}

function parentelement(el) {
  function find() {
    if( !el ) return null;
    var scope = angular.element(el).scope();
    if( scope ) return el;
    el = el.parentNode;
    find();
  }

  return find();
}

function parentscope(el) {
  var pel = parentelement(el);
  return pel && angular.element(pel).scope();
}

var cache = {};
function engine(defaults) {
  defaults = defaults || {};

  var root = function() {
    return defaults.app ? document.querySelector('[ng-app="' + defaults.app + '"]') : document.querySelector('[ng-app]');
  };

  return function(src, options, done) {
    var singleton = options.singleton;
    var target = options.target;
    var parent = options.parent;
    var controller = options.controller;
    var flat = options.flat || defaults.flat;

    if( controller && typeof controller === 'string' ) {
      var el = document.createElement('div');
      el.setAttribute('ng-controller', controller);

      target.innerHTML = '';
      target.appendChild(el);
      //pack(parent || parentelement(target) || root(), el);
      parent = target = el;
    }

    if( !('singleton' in options) ) singleton = defaults.singleton;
    if( !('parent' in options) ) {
      parent = defaults.parent;
      if( !parent && !flat ) parent = parentelement(target);
    }

    if( !parent ) parent = root();

    if( singleton && cache[src] ) {
      return (function() {
        target.innerHTML = '';
        [].forEach.call(cache[src].els, function(node) {
          target.appendChild(node);
        });
        done(null, cache[src].scopes);
      })();
    } else {
      cache[src] = null;
      delete cache[src];
    }

    this.util.ajax(src, function(err, html) {
      if( err ) return done(err);

      var els = this.util.evalhtml(html);
      target.innerHTML = '';
      [].forEach.call(els, function(node) {
        target.appendChild(node);
      });

      pack(parent, els, function(err) {
        if( err ) return done(err);

        var sc = scopes(target);
        if( singleton ) cache[src] = {
          els: els,
          scopes: sc
        };
        done(null, sc);
      });
    });
  };
};

function middleware(options) {
  options = options || {};
  var app = options.app;

  var root = function() {
    return app ? document.querySelector('[ng-app="' + app + '"]') : document.querySelector('[ng-app]');
  };

  return function(req, res, next) {
    res.ensure = function(scope, done) {
      ensure(scope, done);
      return this;
    };

    res.pack = function(elements, done) {
      pack(root(), elements, done);
      return this;
    };

    res.scope = function(el, controller) {
      if( !arguments.length ) return scope(root());
      return scope(el, controller);
    };

    res.scopes = function(el) {
      return scopes(el);
    };

    next();
  };
};

engine.ensure = ensure;
engine.pack = pack;
engine.scope = scope;
engine.scopes = scopes;
engine.parentelement = parentelement;
engine.parentscope = parentscope;
engine.middleware = middleware;
module.exports = engine;

angular.module('xRouterAngular', [])
.service('xRouterAngular', function() {
  return {
    ensure: ensure,
    pack: pack,
    scope: scope,
    scopes: scopes,
    parentelement: parentelement,
    parentscope: parentscope
  };
});<|endoftext|>


<commit_before>
var fs = require('fs');
var outputHome = './live-demo/'
var demoHome = getDemoHome();
var template = getTemplate();

var outputHome = process.argv.length > 2 ? process.argv[2] : './live-demo/';
if (fs.existsSync(outputHome)) {
    console.error("ERROR: remove output dir and try again: " + outputHome);
    process.exit(1);
}
outputHome = outputHome ? outputHome.trim() : './live-demo/';
outputHome = outputHome.match(/[\/\\]$/) ? outputHome : outputHome + '/';

makeAllPlunkers(demoHome);

function makeAllPlunkers(demoHome) {
    var demoSetFolders = fs.readdirSync(demoHome);
    demoSetFolders.forEach(demoFolder => {
        var pathname = demoHome + demoFolder;
        var stat = fs.lstatSync(pathname);
        if (stat.isDirectory()) {
            processDemoSet(pathname + '/');
        }
    });
}

function processDemoSet(demoSetFolder) {
    var demoFolders = fs.readdirSync(demoSetFolder);
    demoFolders.forEach(demoFolder => {
        var pathname = demoSetFolder + demoFolder;
        var stat = fs.lstatSync(pathname);
        if (stat.isDirectory()) {
            makePlunker(pathname + '/');
        }
    });
}

function makePlunker(demoFolder) {
    var content = [];
    var mockDatas = [];
    readDemoContent(content, demoFolder);
    var compContentIndex = -1;
    var moduleContentIndex = -1;
    content.forEach((item, idx) => {
        item.path = 'app/' + item.path.substring(demoFolder.length);
        if (item.path.match(/.+\.ts$/i)) {
            item.code = fixImport(item.code);
            item.code = fixTemplateUrl(item.code);
            item.code = fixStyleUrls(item.code);
            item.code = fixCodeForDemoOnly(item.code);
            findMockDatas(item.code).forEach(mockData => mockDatas.push(mockData));
        }
        if (item.path == 'app/app.component.ts') {
            compContentIndex = idx;
        } else if (item.path == 'app/app.module.ts') {
            moduleContentIndex = idx;
        }
    });
    if (compContentIndex == -1) {
        console.error('ERROR: need this file: ' + demoFolder + 'app.module.ts');
        process.exit(100);
    }
    if (moduleContentIndex == -1) {
        console.error('ERROR: need this file: ' + demoFolder + 'app.component.ts');
        process.exit(100);
    }

    var moduleItem = content[moduleContentIndex];
    moduleItem.code = fixAppModuleTs(moduleItem.code);
    var compItem = content[compContentIndex];
    compItem.code = fixAppComponentTs(compItem.code, moduleItem.code);

    mockDatas.forEach(mockData => content.push(mockData));

    content.push(getIndexHtml());
    content.push(getMainTs());
    content.push(getSystemJsConfig());
    content.push(getSystemJsAngularLoader());

    var html = '';
    content.forEach(item => {
        var path = item.path;
        item.code = escapeCode(item.code);
        html += `<input type="hidden" name="entries[${path}][encoding]" value="utf8" />\n`;
        html += `<input type="hidden" name="entries[${path}][content]" value="${item.code}" />\n`;
    });

    var plunker = template
        .replace('<!-- replace-by-content -->', html)
        .replace('<!-- replace-by-title -->', demoFolder.substring(demoHome.length, demoFolder.length-1));

    var saveTo = outputHome + demoFolder.substring(demoHome.length);
    makeDirs(saveTo);
    saveTo += 'index.html';
    fs.writeFileSync(saveTo, plunker);
    console.log('made plunker to ' + saveTo);
}

function readDemoContent(content, demoFolder) {
    var demoFiles = fs.readdirSync(demoFolder);
    demoFiles.forEach(demoFile => {
        var pathname = demoFolder + demoFile;
        var stat = fs.lstatSync(pathname);
        if (stat.isDirectory()) {
            readDemoContent(content, pathname + '/');
        } else {
            content.push({path: pathname, code: readCode(pathname)});
        }
    });
}

function fixTemplateUrl(code) {
    return code.replace(/\btemplateUrl\s*:\s*['"]\s*(.*?)\s*['"]/g, (found, templateUrl) => {
        if (templateUrl.substring(0, 2) !== './') {
            templateUrl = './' + templateUrl;
        }
        return 'templateUrl: "' + templateUrl + '"';
    });
}

function fixStyleUrls(code) {
    return code.replace(/\bstyleUrls\s*:\s*(\[[\s\S]*?\])/g, (found, urlString) => {
        var urls = eval(urlString);
        urls = urls.map(url => {
            if (url.substring(0, 2) !== './') {
                url = './' + url;
            }
            return url;
        });
        return 'styleUrls: ["' + urls.join('", "') + '"]';
    });
}

function fixImport(code) {
    var jigsawImports = [];
    var rawImports = [];
    while (true) {
        var match = code.match(/\bimport\s+\{([\s\S]+?)\}\s+from\s+['"](.+?)['"]/);
        if (!match) {
            break;
        }
        if (match[2].match(/jigsaw\/.+?/)) {
            var importString = match[1];
            var imports = importString.split(/,/g).map(item => item.trim());
            imports.forEach(item => jigsawImports.push(item));
        } else {
            rawImports.push(match[0]);
        }
        code = code.substring(match.index + match[0].length);
    }
    jigsawImports = jigsawImports.sort((a, b) => a.localeCompare(b));

    var jigsawImportString = '';
    if (jigsawImports.length > 0) {
        jigsawImportString = 'import {\n';
        for(var i = 0, len = jigsawImports.length; i < len; i += 3) {
           jigsawImportString += '    ' + jigsawImports.slice(i, i+3).join(', ') + ',\n';
        }
        jigsawImportString += '} from "@rdkmaster/jigsaw";';
    }

    return jigsawImportString + '\n' + rawImports.join(';\n') + code;
}

function fixCodeForDemoOnly(code) {
    return code.replace(/\/\* #for-live-demo-only#([\s\S]*?)\*\//g, (found, codeForDemo) => codeForDemo.trim());
}

function findMockDatas(code) {
    var ret = [];
    var match = code.match(/mock-data\/.*?['"]/g);
    if (!match) {
        return ret;
    }
    var map = {};
    match.forEach(path => {
        path = path.substring(0, path.length-1);
        map[path] = readCode(`${demoHome}/../../${path}`);
    });
    for (var path in map) {
        ret.push({path: path, code: map[path]})
    }
    return ret;
}

function fixAppComponentTs(compCode, moduleCode) {
    var match = moduleCode.match(/bootstrap\s*:\s*\[\s*(\w+?)\s*\]/);
    if (!match) {
        console.error('ERROR: need bootstrap property in the app.module.ts, the code is:\n' + moduleCode);
        process.exit(200);
    }

    var mainComp = match[1];
    return compCode.replace(/@Component\s*\(\s*\{([\s\S]*?)\}\s*\)[\s\S]*?export\s+class\s+(\w+?)\b/g,
        (found, props, className) => {
            if (className != mainComp) {
                return found;
            }
            if (found.match(/selector\s*:/)) {
                console.error('ERROR: more than one "selector" appears, remove the "selector" property, ' +
                    'the code is:\n' + found);
                process.exit(201);
            }
            return found.replace(/@Component\s*\(\s*\{/, '@Component({\n    selector: "jigsaw-live-demo",');
        });
}

function fixAppModuleTs(appModuleCode) {
    appModuleCode = "import {BrowserModule} from '@angular/platform-browser';\n" +
        "import {BrowserAnimationsModule} from '@angular/platform-browser/animations';\n" +
        "import {HttpModule} from '@angular/http';\n" +
        appModuleCode.replace(/export\s+class\s+(.+?)\s*{/, 'export class AppModule {');

    var re = /\bimports\s*:\s*\[([\s\S]*?)\]/;
    var match = appModuleCode.match(/\bimports\s*:\s*\[([\s\S]*?)\]/);
    if (match) {
        return appModuleCode.replace(re, (found, importString) => '' +
            '    imports: [\n' +
            '        BrowserModule, BrowserAnimationsModule, HttpModule,\n' +
            '        ' + importString.trim() + '\n' +
            '    ]'
        );
    } else {
        return appModuleCode.replace('@NgModule({',
            '@NgModule({\n    imports: [BrowserModule, BrowserAnimationsModule, HttpModule],');
    }

}

function escapeCode(code) {
    return code.replace(/</g, '<').replace(/>/g, '>')
               .replace(/"/g, '"').replace(/'/g, ''');
}

function makeDirs(path) {
    var pathPartials = path.split(/[\/\\]/g);
    for(var i = 1; i < pathPartials.length; i++) {
        var arr = pathPartials.slice(0, i);
        var p = arr.join('/');
        // p == '' if path is absolute
        if (fs.existsSync(p) || !p) {
            continue;
        }
        fs.mkdirSync(p, 755);
    }
}

function readCode(path) {
    return fs.readFileSync(path).toString();
}

function getDemoHome() {
    var pathPartials = __dirname.split(/[/\\]/g);
    pathPartials.pop();
    pathPartials.pop();
    var demoHome = pathPartials.join('/') + '/src/app/demo/';
    return demoHome;
}

function getTemplate() {
    return fs.readFileSync(__dirname + '/demo-independent-templates/plunker.embed.template.html').toString();
}

function getIndexHtml() {
    return {
        path: 'index.html',
        code: readCode(__dirname + '/demo-independent-templates/index.html')
    }
}

function getMainTs() {
    return {
        path: 'main.ts',
        code: readCode(__dirname + '/demo-independent-templates/main.ts')
    }
}

function getSystemJsAngularLoader() {
    return {
        path: 'systemjs-angular-loader.js',
        code: readCode(__dirname + '/demo-independent-templates/systemjs-angular-loader.js')
    }
}

function getSystemJsConfig() {
    return {
        path: 'systemjs.config.js',
        code: readCode(__dirname + '/demo-independent-templates/systemjs.config.js')
    }
}
<commit_msg>修正生成demo脚本错误<commit_after>
var fs = require('fs');
var outputHome = './live-demo/'
var demoHome = getDemoHome();
var template = getTemplate();

var outputHome = process.argv.length > 2 ? process.argv[2] : './live-demo/';
if (fs.existsSync(outputHome)) {
    console.error("ERROR: remove output dir and try again: " + outputHome);
    process.exit(1);
}
outputHome = outputHome ? outputHome.trim() : './live-demo/';
outputHome = outputHome.match(/[\/\\]$/) ? outputHome : outputHome + '/';

makeAllPlunkers(demoHome);

function makeAllPlunkers(demoHome) {
    var demoSetFolders = fs.readdirSync(demoHome);
    demoSetFolders.forEach(demoFolder => {
        var pathname = demoHome + demoFolder;
        var stat = fs.lstatSync(pathname);
        if (stat.isDirectory()) {
            processDemoSet(pathname + '/');
        }
    });
}

function processDemoSet(demoSetFolder) {
    var demoFolders = fs.readdirSync(demoSetFolder);
    demoFolders.forEach(demoFolder => {
        var pathname = demoSetFolder + demoFolder;
        var stat = fs.lstatSync(pathname);
        if (stat.isDirectory()) {
            makePlunker(pathname + '/');
        }
    });
}

function makePlunker(demoFolder) {
    var content = [];
    var mockDatas = [];
    readDemoContent(content, demoFolder);
    var compContentIndex = -1;
    var moduleContentIndex = -1;
    content.forEach((item, idx) => {
        item.path = 'app/' + item.path.substring(demoFolder.length);
        if (item.path.match(/.+\.ts$/i)) {
            item.code = fixImport(item.code);
            item.code = fixTemplateUrl(item.code);
            item.code = fixStyleUrls(item.code);
            item.code = fixCodeForDemoOnly(item.code);
            findMockDatas(item.code).forEach(mockData => mockDatas.push(mockData));
        }
        if (item.path == 'app/app.component.ts') {
            compContentIndex = idx;
        } else if (item.path == 'app/app.module.ts') {
            moduleContentIndex = idx;
        }
    });
    if (compContentIndex == -1) {
        console.error('ERROR: need this file: ' + demoFolder + 'app.module.ts');
        process.exit(100);
    }
    if (moduleContentIndex == -1) {
        console.error('ERROR: need this file: ' + demoFolder + 'app.component.ts');
        process.exit(100);
    }

    var moduleItem = content[moduleContentIndex];
    moduleItem.code = fixAppModuleTs(moduleItem.code);
    var compItem = content[compContentIndex];
    compItem.code = fixAppComponentTs(compItem.code, moduleItem.code);

    mockDatas.forEach(mockData => content.push(mockData));

    content.push(getIndexHtml());
    content.push(getMainTs());
    content.push(getSystemJsConfig());
    content.push(getSystemJsAngularLoader());

    var html = '';
    content.forEach(item => {
        var path = item.path;
        item.code = escapeCode(item.code);
        html += `<input type="hidden" name="entries[${path}][encoding]" value="utf8" />\n`;
        html += `<input type="hidden" name="entries[${path}][content]" value="${item.code}" />\n`;
    });

    var plunker = template
        .replace('<!-- replace-by-content -->', html)
        .replace('<!-- replace-by-title -->', demoFolder.substring(demoHome.length, demoFolder.length-1));

    var saveTo = outputHome + demoFolder.substring(demoHome.length);
    makeDirs(saveTo);
    saveTo += 'index.html';
    fs.writeFileSync(saveTo, plunker);
    console.log('made plunker to ' + saveTo);
}

function readDemoContent(content, demoFolder) {
    var demoFiles = fs.readdirSync(demoFolder);
    demoFiles.forEach(demoFile => {
        var pathname = demoFolder + demoFile;
        var stat = fs.lstatSync(pathname);
        if (stat.isDirectory()) {
            readDemoContent(content, pathname + '/');
        } else {
            content.push({path: pathname, code: readCode(pathname)});
        }
    });
}

function fixTemplateUrl(code) {
    return code.replace(/\btemplateUrl\s*:\s*['"]\s*(.*?)\s*['"]/g, (found, templateUrl) => {
        if (templateUrl.substring(0, 2) !== './') {
            templateUrl = './' + templateUrl;
        }
        return 'templateUrl: "' + templateUrl + '"';
    });
}

function fixStyleUrls(code) {
    return code.replace(/\bstyleUrls\s*:\s*(\[[\s\S]*?\])/g, (found, urlString) => {
        var urls = eval(urlString);
        urls = urls.map(url => {
            if (url.substring(0, 2) !== './') {
                url = './' + url;
            }
            return url;
        });
        return 'styleUrls: ["' + urls.join('", "') + '"]';
    });
}

function fixImport(code) {
    var jigsawImports = [];
    var rawImports = [];
    while (true) {
        var match = code.match(/\bimport\s+\{([\s\S]+?)\}\s+from\s+['"](.+?)['"]/);
        if (!match) {
            break;
        }
        if (match[2].match(/jigsaw\/.+?/)) {
            var importString = match[1];
            var imports = importString.split(/,/g).map(item => item.trim());
            imports.forEach(item => jigsawImports.push(item));
        } else {
            rawImports.push(match[0]);
        }
        code = code.substring(match.index + match[0].length);
    }
    jigsawImports = jigsawImports.sort((a, b) => a.localeCompare(b));

    var jigsawImportString = '';
    if (jigsawImports.length > 0) {
        jigsawImportString = 'import {\n';
        for(var i = 0, len = jigsawImports.length; i < len; i += 3) {
           jigsawImportString += '    ' + jigsawImports.slice(i, i+3).join(', ') + ',\n';
        }
        jigsawImportString += '} from "@rdkmaster/jigsaw";';
    }

    return jigsawImportString + '\n' + rawImports.join(';\n') + code;
}

function fixCodeForDemoOnly(code) {
    return code.replace(/\/\* #for-live-demo-only#([\s\S]*?)\*\//g, (found, codeForDemo) => codeForDemo.trim());
}

function findMockDatas(code) {
    var ret = [];
    var match = code.match(/mock-data\/.*?['"]/g);
    if (!match) {
        return ret;
    }
    var map = {};
    match.forEach(path => {
        path = path.substring(0, path.length-1);
        map[path] = readCode(`${demoHome}/../../${path}`);
    });
    for (var path in map) {
        ret.push({path: path, code: map[path]})
    }
    return ret;
}

function fixAppComponentTs(compCode, moduleCode) {
    var match = moduleCode.match(/bootstrap\s*:\s*\[\s*(\w+?)\s*\]/);
    if (!match) {
        console.error('ERROR: need bootstrap property in the app.module.ts, the code is:\n' + moduleCode);
        process.exit(200);
    }

    var mainComp = match[1];
    return compCode.replace(/@Component\s*\(\s*\{([\s\S]*?)\}\s*\)[\s\S]*?export\s+class\s+(\w+?)\b/g,
        (found, props, className) => {
            if (className != mainComp) {
                return found;
            }
            if (found.match(/selector\s*:/)) {
                console.error('ERROR: more than one "selector" appears, remove the "selector" property, ' +
                    'the code is:\n' + found);
                process.exit(201);
            }
            return found.replace(/@Component\s*\(\s*\{/, '@Component({\n    selector: "jigsaw-live-demo",');
        });
}

function fixAppModuleTs(appModuleCode) {
    appModuleCode = "import {BrowserModule} from '@angular/platform-browser';\n" +
        "import {BrowserAnimationsModule} from '@angular/platform-browser/animations';\n" +
        "import {HttpModule} from '@angular/http';\n" +
        appModuleCode.replace(/export\s+class\s+(.+?)\s*{/, 'export class AppModule {');

    var re = /\bimports\s*:\s*\[([\s\S]*?)\]/;
    var match = appModuleCode.match(/\bimports\s*:\s*\[([\s\S]*?)\]/);
    if (match) {
        return appModuleCode.replace(re, (found, importString) => 'imports: [\n' +
            '        BrowserModule, BrowserAnimationsModule, HttpModule,\n' +
            '        ' + importString.trim() + '\n' +
            '    ]'
        );
    } else {
        return appModuleCode.replace('@NgModule({',
            '@NgModule({\nimports: [BrowserModule, BrowserAnimationsModule, HttpModule],');
    }

}

function escapeCode(code) {
    return code.replace(/</g, '<').replace(/>/g, '>')
               .replace(/"/g, '"').replace(/'/g, ''');
}

function makeDirs(path) {
    var pathPartials = path.split(/[\/\\]/g);
    for(var i = 1; i < pathPartials.length; i++) {
        var arr = pathPartials.slice(0, i);
        var p = arr.join('/');
        // p == '' if path is absolute
        if (fs.existsSync(p) || !p) {
            continue;
        }
        fs.mkdirSync(p, 755);
    }
}

function readCode(path) {
    return fs.readFileSync(path).toString();
}

function getDemoHome() {
    var pathPartials = __dirname.split(/[/\\]/g);
    pathPartials.pop();
    pathPartials.pop();
    var demoHome = pathPartials.join('/') + '/src/app/demo/';
    return demoHome;
}

function getTemplate() {
    return fs.readFileSync(__dirname + '/demo-independent-templates/plunker.embed.template.html').toString();
}

function getIndexHtml() {
    return {
        path: 'index.html',
        code: readCode(__dirname + '/demo-independent-templates/index.html')
    }
}

function getMainTs() {
    return {
        path: 'main.ts',
        code: readCode(__dirname + '/demo-independent-templates/main.ts')
    }
}

function getSystemJsAngularLoader() {
    return {
        path: 'systemjs-angular-loader.js',
        code: readCode(__dirname + '/demo-independent-templates/systemjs-angular-loader.js')
    }
}

function getSystemJsConfig() {
    return {
        path: 'systemjs.config.js',
        code: readCode(__dirname + '/demo-independent-templates/systemjs.config.js')
    }
}
<|endoftext|>


<commit_before>define(["app/i18n"], function(i18n) {
    "use strict";

    // return `cookie` string if set
    var cookie = function(cookie) {
        return (document.cookie.match('(^|; )' + cookie + '=([^;]*)') || 0)[2];
    };

    var pad = function(n, width, z) {
        z = z || '0';
        n = n + '';
        return n.length >= width ? n : new Array(width - n.length + 1).join(z) + n;
    };

    var ago = function(localTime, date) {

        var secs = ((localTime.getTime() - date.getTime()) / 1000);

        if (isNaN(secs) || secs < 0 ) {
            secs = 0;
        }

        var mins = Math.ceil(secs / 60), hours = Math.ceil(mins / 60),
            days = Math.ceil(hours / 24);

        return secs  <=  45 && i18n.translate("date-now")  ||
               secs  <=  90 && i18n.pluralize("date-minute", 1) ||
               mins  <=  45 && i18n.pluralize("date-minute", mins) ||
               mins  <=  90 && i18n.pluralize("date-hour", 1) ||
               hours <=  22 && i18n.pluralize("date-hour", hours) ||
               hours <=  36 && i18n.pluralize("date-day", 1) ||
               days  <=   5 && i18n.pluralize("date-day", days) ||
               days  <=   8 && i18n.pluralize("date-week", 1) ||
               days  <=  21 && i18n.pluralize("date-week", Math.ceil(days / 7)) ||
               days  <=  45 && i18n.pluralize("date-month", 1) ||
               days  <= 345 && i18n.pluralize("date-month", Math.ceil(days / 30)) ||
               days  <= 547 && i18n.pluralize("date-year", 1) ||
                               i18n.pluralize("date-year", Math.ceil(days / 365.25));
    };

    var HTMLEntity = {
        "&": "&",
        "<": "<",
        ">": ">",
        '"': '"',
        "'": ''',
        "/": '/'
    };

    var escape = function(html) {
        return String(html).replace(/[&<>"'\/]/g, function (s) {
            return HTMLEntity[s];
        });
    };

    var text = function(html) {
        var _ = document.createElement("div");
        _.innerHTML = html.replace(/

<\/div>/gi, '
')
                          .replace(/
/gi,'
')
                          .replace(/
/gi, '\n')
                          .replace(/ /gi, ' ');
        return _.textContent.trim();
    };

    var detext = function(text) {
        text = escape(text);
        return text.replace(/\n\n/gi, '

')
                   .replace(/\n/gi, '
');
    };

    // Safari private browsing mode supports localStorage, but throws QUOTA_EXCEEDED_ERR
    var localStorageImpl;
    try {
        localStorage.setItem("x", "y");
        localStorage.removeItem("x");
        localStorageImpl = localStorage;
    } catch (ex) {
        localStorageImpl = (function(storage) {
            return {
                setItem: function(key, val) {
                    storage[key] = val;
                },
                getItem: function(key) {
                    return typeof(storage[key]) !== 'undefined' ? storage[key] : null;
                },
                removeItem: function(key) {
                    delete storage[key];
                }
            };
        })({});
    }

    return {
        cookie: cookie,
        pad: pad,
        ago: ago,
        text: text,
        detext: detext,
        localStorageImpl: localStorageImpl
    };
});
<commit_msg>display only complete units of time<commit_after>define(["app/i18n"], function(i18n) {
    "use strict";

    // return `cookie` string if set
    var cookie = function(cookie) {
        return (document.cookie.match('(^|; )' + cookie + '=([^;]*)') || 0)[2];
    };

    var pad = function(n, width, z) {
        z = z || '0';
        n = n + '';
        return n.length >= width ? n : new Array(width - n.length + 1).join(z) + n;
    };

    var ago = function(localTime, date) {

        var secs = ((localTime.getTime() - date.getTime()) / 1000);

        if (isNaN(secs) || secs < 0 ) {
            secs = 0;
        }

        var mins = Math.floor(secs / 60), hours = Math.floor(mins / 60),
            days = Math.floor(hours / 24);

        return secs  <=  45 && i18n.translate("date-now")  ||
               secs  <=  90 && i18n.pluralize("date-minute", 1) ||
               mins  <=  45 && i18n.pluralize("date-minute", mins) ||
               mins  <=  90 && i18n.pluralize("date-hour", 1) ||
               hours <=  22 && i18n.pluralize("date-hour", hours) ||
               hours <=  36 && i18n.pluralize("date-day", 1) ||
               days  <=   5 && i18n.pluralize("date-day", days) ||
               days  <=   8 && i18n.pluralize("date-week", 1) ||
               days  <=  21 && i18n.pluralize("date-week", Math.floor(days / 7)) ||
               days  <=  45 && i18n.pluralize("date-month", 1) ||
               days  <= 345 && i18n.pluralize("date-month", Math.floor(days / 30)) ||
               days  <= 547 && i18n.pluralize("date-year", 1) ||
                               i18n.pluralize("date-year", Math.floor(days / 365.25));
    };

    var HTMLEntity = {
        "&": "&",
        "<": "<",
        ">": ">",
        '"': '"',
        "'": ''',
        "/": '/'
    };

    var escape = function(html) {
        return String(html).replace(/[&<>"'\/]/g, function (s) {
            return HTMLEntity[s];
        });
    };

    var text = function(html) {
        var _ = document.createElement("div");
        _.innerHTML = html.replace(/

<\/div>/gi, '
')
                          .replace(/
/gi,'
')
                          .replace(/
/gi, '\n')
                          .replace(/ /gi, ' ');
        return _.textContent.trim();
    };

    var detext = function(text) {
        text = escape(text);
        return text.replace(/\n\n/gi, '

')
                   .replace(/\n/gi, '
');
    };

    // Safari private browsing mode supports localStorage, but throws QUOTA_EXCEEDED_ERR
    var localStorageImpl;
    try {
        localStorage.setItem("x", "y");
        localStorage.removeItem("x");
        localStorageImpl = localStorage;
    } catch (ex) {
        localStorageImpl = (function(storage) {
            return {
                setItem: function(key, val) {
                    storage[key] = val;
                },
                getItem: function(key) {
                    return typeof(storage[key]) !== 'undefined' ? storage[key] : null;
                },
                removeItem: function(key) {
                    delete storage[key];
                }
            };
        })({});
    }

    return {
        cookie: cookie,
        pad: pad,
        ago: ago,
        text: text,
        detext: detext,
        localStorageImpl: localStorageImpl
    };
});
<|endoftext|>


<commit_before>////	jQuery.leanModal2.js v2.5
// MIT Licensed by eustasy https://eustasy.org
// Based on leanModal v1.1 by Ray Stone - http://finelysliced.com.au

/*global jQuery*/

// ANONFUNC Wrap in an anonymous function.
(function($){

	////	Extend jQuery
	// EXTENDFUNC
	$.fn.extend({
		// EXTENDFUNC_LEANMODAL
		leanModal: function(options) {

			////	Default Options
			// Set some Defaults.
			var defaults = {
				top: '15vh',
				overlayOpacity: 0.7,
				closeButton: '.js-leanmodal-close',
				disableCloseOnOverlayClick: false,
				disableCloseOnEscape: false,
				fadeTime: 200,
				modalCenter: true,
			}
			// Merge in any passed options.
			options = $.extend(defaults, options)

			////	Close the Modal
			// FUNCTION: Fade out the overlay and a passed identifier.
			function leanModal_Close(modal_id) {
				$('.js-leanmodal-overlay').fadeOut(options.fadeTime)
				$(modal_id).fadeOut(options.fadeTime)
				$('.js-leanmodal-overlay').unbind('click')
				$(document).off('keyup')
			}

			////	There can be only one.
			// Overlay. If there isn't an overlay, add one.
			if ( $('.js-leanmodal-overlay').length == 0 ) {
				var style = 'background: #000; display: none; height: 100%; left: 0; position: fixed; top: 0; width: 100%; z-index: 100;'
				var overlay = $('

')
				$('body').append(overlay)
			}

			////	Everything is Linked
			// FOREACHLINK For each targeted link.
			return this.each(function() {
				// Force this to look like a link.
				$(this).css({ 'cursor': 'pointer' })

				////	Command Override
				// Override the existing click command,
				// and insert this new one.
				// CLICKOVERRIDE
				$(this).unbind('click').click(function(e) {

					////	Select the Modal Identifier
					// IFHREF Use data-open-modal if available
					var modal_id
					if ( $(this).attr('data-modal-id') ) {
						modal_id = $(this).attr('data-modal-id')
					// IFHREF Fall back to href
					} else if ( $(this).attr('href') ) {
						modal_id = $(this).attr('href')
					// IFHREF Fail entirely.
					} else {
						return false
					} // IFHREF

					////	Close with closeButton
					// If `closeButton` is set,
					// use it to call the close command.
					if ( options.closeButton ) {
						$(options.closeButton).click(function() {
							leanModal_Close(modal_id)
						})
					}

					////	Escape with `Esc`
					// Close the modal when someone presses `Esc`,
					// except when `disableCloseOnEscape` is set to `true`
					if ( !options.disableCloseOnEscape ) {
						$(document).on('keyup', function(evt) {
							if ( evt.keyCode == 27 ) {
								leanModal_Close(modal_id)
							}
						})
					}

					////	Envelope in Darkness
					// Close the modal when someone clicks on the overlay,
					// except when `disableCloseOnOverlayClick` is set to `true`
					if ( !options.disableCloseOnOverlayClick ) {
						$('.js-leanmodal-overlay').click(function() {
							leanModal_Close(modal_id)
						})
					}

					////	Modal Positioning
					// Position the modal centrally using JavaScript, because CSS sucks.
					// Actually it doesn't, but it is hard to globally position.
					//var modal_height = $(modal_id).innerHeight()
					//var modal_width = $(modal_id).innerWidth()
					if ( options.modalCenter ) {
						$(modal_id).css({
							'display': 'block',
							//'left': 50 + '%',
							//'margin-left': - ( modal_width / 2 ) + 'px',
							'margin-left': '50%',
							'transform': 'translateX(-50%)',
							'opacity': 0,
							'position': 'fixed',
							'top': options.top,
							'z-index': 11000,
						})
					}

					////	Curtain Up
					// Fade in the modal and overlay.
					$('.js-leanmodal-overlay').css({ 'display': 'block', opacity: 0 })
					$('.js-leanmodal-overlay').fadeTo(options.fadeTime, options.overlayOpacity)
					$(modal_id).fadeTo(options.fadeTime, 1)

					////	Default Prevention
					// Prevent whatever the default was (probably scrolling).
					e.preventDefault()

				}) // CLICKOVERRIDE
			}) // FOREACHLINK
		} // EXTENDFUNC_LEANMODAL
	}) // EXTENDFUNC
})(jQuery) // ANONFUNC This passes in `jQuery` as `$`
<commit_msg>comma dangle<commit_after>////	jQuery.leanModal2.js v2.5
// MIT Licensed by eustasy https://eustasy.org
// Based on leanModal v1.1 by Ray Stone - http://finelysliced.com.au

/*global jQuery*/

// ANONFUNC Wrap in an anonymous function.
(function($){

	////	Extend jQuery
	// EXTENDFUNC
	$.fn.extend({
		// EXTENDFUNC_LEANMODAL
		leanModal: function(options) {

			////	Default Options
			// Set some Defaults.
			var defaults = {
				top: '15vh',
				overlayOpacity: 0.7,
				closeButton: '.js-leanmodal-close',
				disableCloseOnOverlayClick: false,
				disableCloseOnEscape: false,
				fadeTime: 200,
				modalCenter: true,
			}
			// Merge in any passed options.
			options = $.extend(defaults, options)

			////	Close the Modal
			// FUNCTION: Fade out the overlay and a passed identifier.
			function leanModal_Close(modal_id) {
				$('.js-leanmodal-overlay').fadeOut(options.fadeTime)
				$(modal_id).fadeOut(options.fadeTime)
				$('.js-leanmodal-overlay').unbind('click')
				$(document).off('keyup')
			}

			////	There can be only one.
			// Overlay. If there isn't an overlay, add one.
			if ( $('.js-leanmodal-overlay').length == 0 ) {
				var style = 'background: #000; display: none; height: 100%; left: 0; position: fixed; top: 0; width: 100%; z-index: 100;'
				var overlay = $('

')
				$('body').append(overlay)
			}

			////	Everything is Linked
			// FOREACHLINK For each targeted link.
			return this.each(function() {
				// Force this to look like a link.
				$(this).css({ 'cursor': 'pointer' })

				////	Command Override
				// Override the existing click command,
				// and insert this new one.
				// CLICKOVERRIDE
				$(this).unbind('click').click(function(e) {

					////	Select the Modal Identifier
					// IFHREF Use data-open-modal if available
					var modal_id
					if ( $(this).attr('data-modal-id') ) {
						modal_id = $(this).attr('data-modal-id')
					// IFHREF Fall back to href
					} else if ( $(this).attr('href') ) {
						modal_id = $(this).attr('href')
					// IFHREF Fail entirely.
					} else {
						return false
					} // IFHREF

					////	Close with closeButton
					// If `closeButton` is set,
					// use it to call the close command.
					if ( options.closeButton ) {
						$(options.closeButton).click(function() {
							leanModal_Close(modal_id)
						})
					}

					////	Escape with `Esc`
					// Close the modal when someone presses `Esc`,
					// except when `disableCloseOnEscape` is set to `true`
					if ( !options.disableCloseOnEscape ) {
						$(document).on('keyup', function(evt) {
							if ( evt.keyCode == 27 ) {
								leanModal_Close(modal_id)
							}
						})
					}

					////	Envelope in Darkness
					// Close the modal when someone clicks on the overlay,
					// except when `disableCloseOnOverlayClick` is set to `true`
					if ( !options.disableCloseOnOverlayClick ) {
						$('.js-leanmodal-overlay').click(function() {
							leanModal_Close(modal_id)
						})
					}

					////	Modal Positioning
					// Position the modal centrally using JavaScript, because CSS sucks.
					// Actually it doesn't, but it is hard to globally position.
					//var modal_height = $(modal_id).innerHeight()
					//var modal_width = $(modal_id).innerWidth()
					if ( options.modalCenter ) {
						$(modal_id).css({
							'display': 'block',
							//'left': 50 + '%',
							//'margin-left': - ( modal_width / 2 ) + 'px',
							'margin-left': '50%',
							'transform': 'translateX(-50%)',
							'opacity': 0,
							'position': 'fixed',
							'top': options.top,
							'z-index': 11000,
						})
					}

					////	Curtain Up
					// Fade in the modal and overlay.
					$('.js-leanmodal-overlay').css({ 'display': 'block', opacity: 0 })
					$('.js-leanmodal-overlay').fadeTo(options.fadeTime, options.overlayOpacity)
					$(modal_id).fadeTo(options.fadeTime, 1)

					////	Default Prevention
					// Prevent whatever the default was (probably scrolling).
					e.preventDefault()

				}) // CLICKOVERRIDE
			}) // FOREACHLINK
		}, // EXTENDFUNC_LEANMODAL
	}) // EXTENDFUNC
})(jQuery) // ANONFUNC This passes in `jQuery` as `$`
<|endoftext|>


<commit_before>/**
 * This code is mostly from the old Etherpad. Please help us to comment this code.
 * This helps other people to understand this code better and helps them to improve it.
 * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED
 */

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

// requires: top
// requires: plugins
// requires: undefined

Ace2Editor.registry = {
  nextId: 1
};

var hooks = require('./pluginfw/hooks');
var _ = require('./underscore');

function Ace2Editor()
{
  var ace2 = Ace2Editor;

  var editor = {};
  var info = {
    editor: editor,
    id: (ace2.registry.nextId++)
  };
  var loaded = false;

  var actionsPendingInit = [];

  function pendingInit(func, optDoNow)
  {
    return function()
    {
      var that = this;
      var args = arguments;
      var action = function()
      {
        func.apply(that, args);
      }
      if (optDoNow)
      {
        optDoNow.apply(that, args);
      }
      if (loaded)
      {
        action();
      }
      else
      {
        actionsPendingInit.push(action);
      }
    };
  }

  function doActionsPendingInit()
  {
    _.each(actionsPendingInit, function(fn,i){
      fn()
    });
    actionsPendingInit = [];
  }

  ace2.registry[info.id] = info;

  // The following functions (prefixed by 'ace_')  are exposed by editor, but
  // execution is delayed until init is complete
  var aceFunctionsPendingInit = ['importText', 'importAText', 'focus',
  'setEditable', 'getFormattedCode', 'setOnKeyPress', 'setOnKeyDown',
  'setNotifyDirty', 'setProperty', 'setBaseText', 'setBaseAttributedText',
  'applyChangesToBase', 'applyPreparedChangesetToBase',
  'setUserChangeNotificationCallback', 'setAuthorInfo',
  'setAuthorSelectionRange', 'callWithAce', 'execCommand', 'replaceRange'];

  _.each(aceFunctionsPendingInit, function(fnName,i){
    var prefix = 'ace_';
    var name = prefix + fnName;
    editor[fnName] = pendingInit(function(){
      info[prefix + fnName].apply(this, arguments);
    });
  });

  editor.exportText = function()
  {
    if (!loaded) return "(awaiting init)\n";
    return info.ace_exportText();
  };

  editor.getFrame = function()
  {
    return info.frame || null;
  };

  editor.getDebugProperty = function(prop)
  {
    return info.ace_getDebugProperty(prop);
  };

  // prepareUserChangeset:
  // Returns null if no new changes or ACE not ready.  Otherwise, bundles up all user changes
  // to the latest base text into a Changeset, which is returned (as a string if encodeAsString).
  // If this method returns a truthy value, then applyPreparedChangesetToBase can be called
  // at some later point to consider these changes part of the base, after which prepareUserChangeset
  // must be called again before applyPreparedChangesetToBase.  Multiple consecutive calls
  // to prepareUserChangeset will return an updated changeset that takes into account the
  // latest user changes, and modify the changeset to be applied by applyPreparedChangesetToBase
  // accordingly.
  editor.prepareUserChangeset = function()
  {
    if (!loaded) return null;
    return info.ace_prepareUserChangeset();
  };

  editor.getUnhandledErrors = function()
  {
    if (!loaded) return [];
    // returns array of {error: <browser Error object>, time: +new Date()}
    return info.ace_getUnhandledErrors();
  };

  function sortFilesByEmbeded(files) {
    var embededFiles = [];
    var remoteFiles = [];

    if (Ace2Editor.EMBEDED) {
      for (var i = 0, ii = files.length; i < ii; i++) {
        var file = files[i];
        if (Object.prototype.hasOwnProperty.call(Ace2Editor.EMBEDED, file)) {
          embededFiles.push(file);
        } else {
          remoteFiles.push(file);
        }
      }
    } else {
      remoteFiles = files;
    }

    return {embeded: embededFiles, remote: remoteFiles};
  }
  function pushRequireScriptTo(buffer) {
    var KERNEL_SOURCE = '../static/js/require-kernel.js';
    var KERNEL_BOOT = '\
require.setRootURI("../javascripts/src");\n\
require.setLibraryURI("../javascripts/lib");\n\
require.setGlobalKeyPath("require");\n\
';
    if (Ace2Editor.EMBEDED && Ace2Editor.EMBEDED[KERNEL_SOURCE]) {
      buffer.push('');

      hooks.callAll("aceInitInnerdocbodyHead", {
        iframeHTML: iframeHTML
      });

      // For compatability's sake transform in and out.
      for (var i = 0, ii = iframeHTML.length; i < ii; i++) {
        iframeHTML[i] = JSON.stringify(iframeHTML[i]);
      }
      for (var i = 0, ii = iframeHTML.length; i < ii; i++) {
        iframeHTML[i] = JSON.parse(iframeHTML[i]);
      }

      // calls to these functions ($$INCLUDE_...)  are replaced when this file is processed
      // and compressed, putting the compressed code from the named file directly into the
      // source here.
      // these lines must conform to a specific format because they are passed by the build script:
      var includedCSS = [];
      var $$INCLUDE_CSS = function(filename) {includedCSS.push(filename)};
      $$INCLUDE_CSS("../static/css/iframe_editor.css");
      $$INCLUDE_CSS("../static/css/pad.css");
      $$INCLUDE_CSS("../static/custom/pad.css");

      var additionalCSS = _(hooks.callAll("aceEditorCSS")).map(function(path){ return '../static/plugins/' + path });
      includedCSS = includedCSS.concat(additionalCSS);

      pushStyleTagsFor(iframeHTML, includedCSS);

      var includedJS = [];
      var $$INCLUDE_JS = function(filename) {includedJS.push(filename)};
      pushRequireScriptTo(iframeHTML);
      // Inject my plugins into my child.
      iframeHTML.push('\
\
');
      pushScriptsTo(iframeHTML);

      iframeHTML.push('');
      iframeHTML.push(' ');

      // Expose myself to global for my child frame.
      var thisFunctionsName = "ChildAccessibleAce2Editor";
      (function () {return this}())[thisFunctionsName] = Ace2Editor;

      var outerScript = 'editorId = "' + info.id + '"; editorInfo = parent.' + thisFunctionsName + '.registry[editorId]; ' + 'window.onload = function() ' + '{ window.onload = null; setTimeout' + '(function() ' + '{ var iframe = document.createElement("IFRAME"); iframe.name = "ace_inner";' + 'iframe.scrolling = "no"; var outerdocbody = document.getElementById("outerdocbody"); ' + 'iframe.frameBorder = 0; iframe.allowTransparency = true; ' + // for IE
      'outerdocbody.insertBefore(iframe, outerdocbody.firstChild); ' + 'iframe.ace_outerWin = window; ' + 'readyFunc = function() { editorInfo.onEditorReady(); readyFunc = null; editorInfo = null; }; ' + 'var doc = iframe.contentWindow.document; doc.open(); var text = (' + JSON.stringify(iframeHTML.join('\n')) + ');doc.write(text); doc.close(); ' + '}, 0); }';

      var outerHTML = [doctype, '']

      var includedCSS = [];
      var $$INCLUDE_CSS = function(filename) {includedCSS.push(filename)};
      $$INCLUDE_CSS("../static/css/iframe_editor.css");
      $$INCLUDE_CSS("../static/css/pad.css");
      $$INCLUDE_CSS("../static/custom/pad.css");

      var additionalCSS = _(hooks.callAll("aceEditorCSS")).map(function(path){ return '../static/plugins/' + path });
      includedCSS = includedCSS.concat(additionalCSS);

      pushStyleTagsFor(outerHTML, includedCSS);

      // bizarrely, in FF2, a file with no "external" dependencies won't finish loading properly
      // (throbs busy while typing)
      outerHTML.push('<link rel="stylesheet" type="text/css" href="data:text/css,"/>', '\x3cscript>\n', outerScript.replace(/<\//g, '<\\/'), '\n\x3c/script>', '

x

');

      var outerFrame = document.createElement("IFRAME");
      outerFrame.name = "ace_outer";
      outerFrame.frameBorder = 0; // for IE
      info.frame = outerFrame;
      document.getElementById(containerId).appendChild(outerFrame);

      var editorDocument = outerFrame.contentWindow.document;

      editorDocument.open();
      editorDocument.write(outerHTML.join(''));
      editorDocument.close();
    })();
  };

  return editor;
}

exports.Ace2Editor = Ace2Editor;
<commit_msg>removing de-facto noop<commit_after>/**
 * This code is mostly from the old Etherpad. Please help us to comment this code.
 * This helps other people to understand this code better and helps them to improve it.
 * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED
 */

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

// requires: top
// requires: plugins
// requires: undefined

Ace2Editor.registry = {
  nextId: 1
};

var hooks = require('./pluginfw/hooks');
var _ = require('./underscore');

function Ace2Editor()
{
  var ace2 = Ace2Editor;

  var editor = {};
  var info = {
    editor: editor,
    id: (ace2.registry.nextId++)
  };
  var loaded = false;

  var actionsPendingInit = [];

  function pendingInit(func, optDoNow)
  {
    return function()
    {
      var that = this;
      var args = arguments;
      var action = function()
      {
        func.apply(that, args);
      }
      if (optDoNow)
      {
        optDoNow.apply(that, args);
      }
      if (loaded)
      {
        action();
      }
      else
      {
        actionsPendingInit.push(action);
      }
    };
  }

  function doActionsPendingInit()
  {
    _.each(actionsPendingInit, function(fn,i){
      fn()
    });
    actionsPendingInit = [];
  }

  ace2.registry[info.id] = info;

  // The following functions (prefixed by 'ace_')  are exposed by editor, but
  // execution is delayed until init is complete
  var aceFunctionsPendingInit = ['importText', 'importAText', 'focus',
  'setEditable', 'getFormattedCode', 'setOnKeyPress', 'setOnKeyDown',
  'setNotifyDirty', 'setProperty', 'setBaseText', 'setBaseAttributedText',
  'applyChangesToBase', 'applyPreparedChangesetToBase',
  'setUserChangeNotificationCallback', 'setAuthorInfo',
  'setAuthorSelectionRange', 'callWithAce', 'execCommand', 'replaceRange'];

  _.each(aceFunctionsPendingInit, function(fnName,i){
    var prefix = 'ace_';
    var name = prefix + fnName;
    editor[fnName] = pendingInit(function(){
      info[prefix + fnName].apply(this, arguments);
    });
  });

  editor.exportText = function()
  {
    if (!loaded) return "(awaiting init)\n";
    return info.ace_exportText();
  };

  editor.getFrame = function()
  {
    return info.frame || null;
  };

  editor.getDebugProperty = function(prop)
  {
    return info.ace_getDebugProperty(prop);
  };

  // prepareUserChangeset:
  // Returns null if no new changes or ACE not ready.  Otherwise, bundles up all user changes
  // to the latest base text into a Changeset, which is returned (as a string if encodeAsString).
  // If this method returns a truthy value, then applyPreparedChangesetToBase can be called
  // at some later point to consider these changes part of the base, after which prepareUserChangeset
  // must be called again before applyPreparedChangesetToBase.  Multiple consecutive calls
  // to prepareUserChangeset will return an updated changeset that takes into account the
  // latest user changes, and modify the changeset to be applied by applyPreparedChangesetToBase
  // accordingly.
  editor.prepareUserChangeset = function()
  {
    if (!loaded) return null;
    return info.ace_prepareUserChangeset();
  };

  editor.getUnhandledErrors = function()
  {
    if (!loaded) return [];
    // returns array of {error: <browser Error object>, time: +new Date()}
    return info.ace_getUnhandledErrors();
  };

  function sortFilesByEmbeded(files) {
    var embededFiles = [];
    var remoteFiles = [];

    if (Ace2Editor.EMBEDED) {
      for (var i = 0, ii = files.length; i < ii; i++) {
        var file = files[i];
        if (Object.prototype.hasOwnProperty.call(Ace2Editor.EMBEDED, file)) {
          embededFiles.push(file);
        } else {
          remoteFiles.push(file);
        }
      }
    } else {
      remoteFiles = files;
    }

    return {embeded: embededFiles, remote: remoteFiles};
  }
  function pushRequireScriptTo(buffer) {
    var KERNEL_SOURCE = '../static/js/require-kernel.js';
    var KERNEL_BOOT = '\
require.setRootURI("../javascripts/src");\n\
require.setLibraryURI("../javascripts/lib");\n\
require.setGlobalKeyPath("require");\n\
';
    if (Ace2Editor.EMBEDED && Ace2Editor.EMBEDED[KERNEL_SOURCE]) {
      buffer.push('');

      hooks.callAll("aceInitInnerdocbodyHead", {
        iframeHTML: iframeHTML
      });

      // calls to these functions ($$INCLUDE_...)  are replaced when this file is processed
      // and compressed, putting the compressed code from the named file directly into the
      // source here.
      // these lines must conform to a specific format because they are passed by the build script:
      var includedCSS = [];
      var $$INCLUDE_CSS = function(filename) {includedCSS.push(filename)};
      $$INCLUDE_CSS("../static/css/iframe_editor.css");
      $$INCLUDE_CSS("../static/css/pad.css");
      $$INCLUDE_CSS("../static/custom/pad.css");

      var additionalCSS = _(hooks.callAll("aceEditorCSS")).map(function(path){ return '../static/plugins/' + path });
      includedCSS = includedCSS.concat(additionalCSS);

      pushStyleTagsFor(iframeHTML, includedCSS);

      var includedJS = [];
      var $$INCLUDE_JS = function(filename) {includedJS.push(filename)};
      pushRequireScriptTo(iframeHTML);
      // Inject my plugins into my child.
      iframeHTML.push('\
\
');
      pushScriptsTo(iframeHTML);

      iframeHTML.push('');
      iframeHTML.push(' ');

      // Expose myself to global for my child frame.
      var thisFunctionsName = "ChildAccessibleAce2Editor";
      (function () {return this}())[thisFunctionsName] = Ace2Editor;

      var outerScript = 'editorId = "' + info.id + '"; editorInfo = parent.' + thisFunctionsName + '.registry[editorId]; ' + 'window.onload = function() ' + '{ window.onload = null; setTimeout' + '(function() ' + '{ var iframe = document.createElement("IFRAME"); iframe.name = "ace_inner";' + 'iframe.scrolling = "no"; var outerdocbody = document.getElementById("outerdocbody"); ' + 'iframe.frameBorder = 0; iframe.allowTransparency = true; ' + // for IE
      'outerdocbody.insertBefore(iframe, outerdocbody.firstChild); ' + 'iframe.ace_outerWin = window; ' + 'readyFunc = function() { editorInfo.onEditorReady(); readyFunc = null; editorInfo = null; }; ' + 'var doc = iframe.contentWindow.document; doc.open(); var text = (' + JSON.stringify(iframeHTML.join('\n')) + ');doc.write(text); doc.close(); ' + '}, 0); }';

      var outerHTML = [doctype, '']

      var includedCSS = [];
      var $$INCLUDE_CSS = function(filename) {includedCSS.push(filename)};
      $$INCLUDE_CSS("../static/css/iframe_editor.css");
      $$INCLUDE_CSS("../static/css/pad.css");
      $$INCLUDE_CSS("../static/custom/pad.css");

      var additionalCSS = _(hooks.callAll("aceEditorCSS")).map(function(path){ return '../static/plugins/' + path });
      includedCSS = includedCSS.concat(additionalCSS);

      pushStyleTagsFor(outerHTML, includedCSS);

      // bizarrely, in FF2, a file with no "external" dependencies won't finish loading properly
      // (throbs busy while typing)
      outerHTML.push('<link rel="stylesheet" type="text/css" href="data:text/css,"/>', '\x3cscript>\n', outerScript.replace(/<\//g, '<\\/'), '\n\x3c/script>', '

x

');

      var outerFrame = document.createElement("IFRAME");
      outerFrame.name = "ace_outer";
      outerFrame.frameBorder = 0; // for IE
      info.frame = outerFrame;
      document.getElementById(containerId).appendChild(outerFrame);

      var editorDocument = outerFrame.contentWindow.document;

      editorDocument.open();
      editorDocument.write(outerHTML.join(''));
      editorDocument.close();
    })();
  };

  return editor;
}

exports.Ace2Editor = Ace2Editor;
<|endoftext|>


<commit_before>var S3ViewModel = require('./s3UserConfig.js').S3ViewModel;
var S3UserConfig = require('./s3UserConfig.js').S3UserConfig;

// Endpoint for S3 user settings
var url = '/api/v1/settings/s3/accounts/';

var s3UserConfig = new S3UserConfig('#s3AddonScope', url);

// Load initial S3 data
s3UserConfig.viewModel.updateAccounts();
<commit_msg>Remove unused code<commit_after>var S3UserConfig = require('./s3UserConfig.js').S3UserConfig;

// Endpoint for S3 user settings
var url = '/api/v1/settings/s3/accounts/';

var s3UserConfig = new S3UserConfig('#s3AddonScope', url);
<|endoftext|>


<commit_before>/*

   mechanism for persisting (some parts of) the store:

   - `localStorage.kancepts` is used for storing everything in encoded JSON format.
     this way we don't need to worry (too much) about the same hosting site
     being used for other purposes so localStorage might be shared.

   - `localStorage.kancepts.version` is a number for data version

   - `persistPaths` in this module keeps a list of paths of the store needed to be
     saved, we use a selector to extract these paths using `_.get` and
     paths with non-`undefined` values are merged into a single Object using `_.set`,
     which is then saved as `localStorage.kancepts.state`.

   - `localStorage.kancepts.state` is used as preloadedState when initializing the store.

   - debounce saving action as we don't really need to save that often.
     (500ms should be fine I guess)

 */
import _ from 'lodash'
import { observer } from 'redux-observers'

import { shallowEqual } from '../utils'

const persistPaths = [
  'expedConfigs',
  'ui.planner.config',
]

// get non-undefined path values and merge them into another Object
const persistStateSelector = state => persistPaths
  .reduce(
    (curObj,path) => {
      const pathVal = _.get(state,path)
      if (typeof pathVal !== 'undefined') {
        _.set(curObj,path,pathVal)
      }
      return curObj
    },
    {})

const saveToLocalStorageImpl = state =>
  setTimeout(() => {
    const kanceptsData = {
      version: 1,
      state,
    }
    localStorage.kancepts = JSON.stringify(kanceptsData)
  })

const saveToLocalStorage = _.debounce(
  saveToLocalStorageImpl,
  500
)

const persistStateObserver = observer(
  persistStateSelector,
  (_dispatch, current, previous) => {
    if (!shallowEqual(current, previous)) {
      saveToLocalStorage(current)
    }
  })

// note that if we feed a persist state to persistStateSelector,
// it should produce another Object with exactly the same shape,
// we can use this property to "normalize" a persist state so it only contains
// those specified paths
const normalizePersistState = persistStateSelector

const loadPreparedState = () => {
  try {
    const raw = localStorage.kancepts
    // localStorage is empty, nothing to load.
    if (typeof raw === 'undefined')
      return {}

    const kanceptsData = JSON.parse(raw)
    if (kanceptsData.version !== 1)
      throw new Error(
        `version mismatched: expecting 1 `+
        `while getting ${kanceptsData.version}`)

    return normalizePersistState(kanceptsData.state)
  } catch (e) {
    console.error('error while loading preparedState from localStorage', e)
    return {}
  }
}

export {
  persistStateObserver,
  loadPreparedState,
}
<commit_msg>make table view & sort method persistent<commit_after>/*

   mechanism for persisting (some parts of) the store:

   - `localStorage.kancepts` is used for storing everything in encoded JSON format.
     this way we don't need to worry (too much) about the same hosting site
     being used for other purposes so localStorage might be shared.

   - `localStorage.kancepts.version` is a number for data version

   - `persistPaths` in this module keeps a list of paths of the store needed to be
     saved, we use a selector to extract these paths using `_.get` and
     paths with non-`undefined` values are merged into a single Object using `_.set`,
     which is then saved as `localStorage.kancepts.state`.

   - `localStorage.kancepts.state` is used as preloadedState when initializing the store.

   - debounce saving action as we don't really need to save that often.
     (500ms should be fine I guess)

 */
import _ from 'lodash'
import { observer } from 'redux-observers'

import { shallowEqual } from '../utils'

const persistPaths = [
  'expedConfigs',
  'ui.planner.config',
  'ui.table.view',
  'ui.table.sort',
]

// get non-undefined path values and merge them into another Object
const persistStateSelector = state => persistPaths
  .reduce(
    (curObj,path) => {
      const pathVal = _.get(state,path)
      if (typeof pathVal !== 'undefined') {
        _.set(curObj,path,pathVal)
      }
      return curObj
    },
    {})

const saveToLocalStorageImpl = state =>
  setTimeout(() => {
    const kanceptsData = {
      version: 1,
      state,
    }
    localStorage.kancepts = JSON.stringify(kanceptsData)
  })

const saveToLocalStorage = _.debounce(
  saveToLocalStorageImpl,
  500
)

const persistStateObserver = observer(
  persistStateSelector,
  (_dispatch, current, previous) => {
    if (!shallowEqual(current, previous)) {
      saveToLocalStorage(current)
    }
  })

// note that if we feed a persist state to persistStateSelector,
// it should produce another Object with exactly the same shape,
// we can use this property to "normalize" a persist state so it only contains
// those specified paths
const normalizePersistState = persistStateSelector

const loadPreparedState = () => {
  try {
    const raw = localStorage.kancepts
    // localStorage is empty, nothing to load.
    if (typeof raw === 'undefined')
      return {}

    const kanceptsData = JSON.parse(raw)
    if (kanceptsData.version !== 1)
      throw new Error(
        `version mismatched: expecting 1 `+
        `while getting ${kanceptsData.version}`)

    return normalizePersistState(kanceptsData.state)
  } catch (e) {
    console.error('error while loading preparedState from localStorage', e)
    return {}
  }
}

export {
  persistStateObserver,
  loadPreparedState,
}
<|endoftext|>


<commit_before>import { getContext } from '../context'
import { runPlugins } from '../plugins'

export const ATTACH_REDUCER = '@KEA/ATTACH_REDUCER'
export const DETACH_REDUCER = '@KEA/DETACH_REDUCER'

const defaultState = {}

function initRootReducerTree (pathStart) {
  const { reducers: { tree } } = getContext()
  if (!tree[pathStart]) {
    tree[pathStart] = {}
    regenerateRootReducer(pathStart)
  }
}

export function keaReducer (pathStart = 'scenes') {
  const { reducers: { roots } } = getContext()
  initRootReducerTree(pathStart)

  return (state = defaultState, action, fullState) => {
    return roots[pathStart] ? roots[pathStart](state, action, fullState) : state
  }
}

export function attachReducer (logic) {
  const { path, reducer } = logic
  const {
    reducers: { tree, combined },
    options: { attachStrategy },
    store
  } = getContext()

  const pathStart = path[0]

  initRootReducerTree(pathStart)

  let pointer = tree

  for (let i = 0; i < path.length; i++) {
    const pathPart = path[i]

    // last part of the path, so [..., pathPart] = path
    if (i === path.length - 1) {
      // there's already something here!
      if (pointer[pathPart]) {
        // if we're in the root level in the tree and it's an empty object
        if (i === 0 && typeof pointer[pathPart] === 'object' && Object.keys(pointer[pathPart]).length === 0) {
          // don't block here

        // if it's a function, assume it's a reducer and replacing it is fine
        // otherwise give an error
        } else if (typeof pointer[pathPart] !== 'function') {
          console.error(`[KEA] Can not add reducer to "${path.join('.')}". There is something in the way:`, pointer[pathPart])
          return
        }
      }

      pointer[pathPart] = reducer
    } else {
      if (!pointer[pathPart]) {
        pointer[pathPart] = {}
      }
      pointer = pointer[pathPart]
    }
  }

  regenerateRootReducer(pathStart)

  if (attachStrategy === 'dispatch' || attachStrategy === 'replace') {
    runPlugins('beforeAttach', logic)

    if (attachStrategy === 'dispatch') {
      store && store.dispatch({ type: ATTACH_REDUCER, payload: { path, reducer } })
    } else if (attachStrategy === 'replace') {
      store && store.replaceReducer(combined)
    }

    runPlugins('afterAttach', logic)
  }
}

export function detachReducer (logic) {
  const { path } = logic

  const {
    reducers: { tree, combined },
    options: { detachStrategy },
    store
  } = getContext()

  const pathStart = path[0]

  if (detachStrategy === 'persist') {
    return
  }

  initRootReducerTree(pathStart)

  let pointer = tree

  let detached = false

  // ['scenes', 'sceneName', 'page', 'key']
  for (let i = path.length - 2; i >= 0; i--) {
    let pointerToHere = pointer
    for (let j = 0; j <= i; j++) {
      pointerToHere = (pointerToHere && pointerToHere[path[j]]) || undefined
    }

    if (pointerToHere) {
      if (Object.keys(pointerToHere).length === 0) {
        // next
      } else if (Object.keys(pointerToHere).length >= 1 && i === path.length - 2 && typeof pointerToHere[path[i + 1]] === 'function') {
        delete pointerToHere[path[i + 1]]
        detached = true
      } else if (detached && Object.keys(pointerToHere).length >= 1 && i < path.length - 2 && Object.keys(pointerToHere[path[i + 1]]).length === 0) {
        delete pointerToHere[path[i + 1]]
      } else {
        break
      }
    }
  }

  regenerateRootReducer(pathStart)

  if (detached) {
    if (detachStrategy === 'dispatch' || detachStrategy === 'replace') {
      runPlugins('beforeDetach', logic)

      if (detachStrategy === 'dispatch') {
        store && store.dispatch({ type: DETACH_REDUCER, payload: { path } })
      } else if (detachStrategy === 'replace') {
        store && store.replaceReducer(combined)
      }

      runPlugins('afterDetach', logic)
    }
  }
}

export function regenerateRootReducer (pathStart) {
  const { reducers: { tree, roots } } = getContext()
  roots[pathStart] = recursiveCreateReducer(tree[pathStart])
}

export function recursiveCreateReducer (treeNode) {
  if (typeof treeNode === 'function') {
    return treeNode
  }

  let children = {}

  Object.keys(treeNode).forEach(key => {
    if (typeof treeNode[key] !== 'undefined') {
      children[key] = recursiveCreateReducer(treeNode[key])
    }
  })

  if (Object.keys(children).length > 0) {
    return combineKeaReducers(children)
  } else {
    const emptyObj = {}
    return () => emptyObj
  }
}

// We are using our own function for the tree nodes instead of redux's combineReducers beacause this way we will not
// get the constant 'Unexpected key "1" found in previous state received by the reducer' warnings when unmounting.
// Instead we'll simply discard the keys we don't need.
// Please note that logic store reducers are still built with redux's combineReducers.
export function combineKeaReducers (reducers) {
  const reducerKeys = Object.keys(reducers)

  return function combination (state = {}, action, fullState) {
    let stateChanged = Object.keys(state).length !== reducerKeys.length
    let nextState = {}

    for (let i = 0; i < reducerKeys.length; i++) {
      const key = reducerKeys[i]
      const reducer = reducers[key]
      const previousKeyState = state[key]
      const nextKeyState = reducer(previousKeyState, action, fullState || state)
      if (typeof nextKeyState === 'undefined') {
        throw new Error(`[KEA] Reducer "${key}" returned undefined for action "${action && action.type}"`)
      }
      nextState[key] = nextKeyState
      stateChanged = stateChanged || nextKeyState !== previousKeyState
    }

    return stateChanged ? nextState : state
  }
}
<commit_msg>update comment<commit_after>import { getContext } from '../context'
import { runPlugins } from '../plugins'

export const ATTACH_REDUCER = '@KEA/ATTACH_REDUCER'
export const DETACH_REDUCER = '@KEA/DETACH_REDUCER'

const defaultState = {}

function initRootReducerTree (pathStart) {
  const { reducers: { tree } } = getContext()
  if (!tree[pathStart]) {
    tree[pathStart] = {}
    regenerateRootReducer(pathStart)
  }
}

export function keaReducer (pathStart = 'scenes') {
  const { reducers: { roots } } = getContext()
  initRootReducerTree(pathStart)

  return (state = defaultState, action, fullState) => {
    return roots[pathStart] ? roots[pathStart](state, action, fullState) : state
  }
}

export function attachReducer (logic) {
  const { path, reducer } = logic
  const {
    reducers: { tree, combined },
    options: { attachStrategy },
    store
  } = getContext()

  const pathStart = path[0]

  initRootReducerTree(pathStart)

  let pointer = tree

  for (let i = 0; i < path.length; i++) {
    const pathPart = path[i]

    // last part of the path, so [..., pathPart] = path
    if (i === path.length - 1) {
      // there's already something here!
      if (pointer[pathPart]) {
        // if we're in the root level in the tree and it's an empty object
        if (i === 0 && typeof pointer[pathPart] === 'object' && Object.keys(pointer[pathPart]).length === 0) {
          // don't block here

        // if it's a function, assume it's a reducer and replacing it is fine
        // otherwise give an error
        } else if (typeof pointer[pathPart] !== 'function') {
          console.error(`[KEA] Can not add reducer to "${path.join('.')}". There is something in the way:`, pointer[pathPart])
          return
        }
      }

      pointer[pathPart] = reducer
    } else {
      if (!pointer[pathPart]) {
        pointer[pathPart] = {}
      }
      pointer = pointer[pathPart]
    }
  }

  regenerateRootReducer(pathStart)

  if (attachStrategy === 'dispatch' || attachStrategy === 'replace') {
    runPlugins('beforeAttach', logic)

    if (attachStrategy === 'dispatch') {
      store && store.dispatch({ type: ATTACH_REDUCER, payload: { path, reducer } })
    } else if (attachStrategy === 'replace') {
      store && store.replaceReducer(combined)
    }

    runPlugins('afterAttach', logic)
  }
}

export function detachReducer (logic) {
  const { path } = logic

  const {
    reducers: { tree, combined },
    options: { detachStrategy },
    store
  } = getContext()

  const pathStart = path[0]

  if (detachStrategy === 'persist') {
    return
  }

  initRootReducerTree(pathStart)

  let pointer = tree

  let detached = false

  // ['scenes', 'sceneName', 'page', 'key']
  for (let i = path.length - 2; i >= 0; i--) {
    let pointerToHere = pointer
    for (let j = 0; j <= i; j++) {
      pointerToHere = (pointerToHere && pointerToHere[path[j]]) || undefined
    }

    if (pointerToHere) {
      if (Object.keys(pointerToHere).length === 0) {
        // next
      } else if (Object.keys(pointerToHere).length >= 1 && i === path.length - 2 && typeof pointerToHere[path[i + 1]] === 'function') {
        delete pointerToHere[path[i + 1]]
        detached = true
      } else if (detached && Object.keys(pointerToHere).length >= 1 && i < path.length - 2 && Object.keys(pointerToHere[path[i + 1]]).length === 0) {
        delete pointerToHere[path[i + 1]]
      } else {
        break
      }
    }
  }

  regenerateRootReducer(pathStart)

  if (detached) {
    if (detachStrategy === 'dispatch' || detachStrategy === 'replace') {
      runPlugins('beforeDetach', logic)

      if (detachStrategy === 'dispatch') {
        store && store.dispatch({ type: DETACH_REDUCER, payload: { path } })
      } else if (detachStrategy === 'replace') {
        store && store.replaceReducer(combined)
      }

      runPlugins('afterDetach', logic)
    }
  }
}

export function regenerateRootReducer (pathStart) {
  const { reducers: { tree, roots } } = getContext()
  roots[pathStart] = recursiveCreateReducer(tree[pathStart])
}

export function recursiveCreateReducer (treeNode) {
  if (typeof treeNode === 'function') {
    return treeNode
  }

  let children = {}

  Object.keys(treeNode).forEach(key => {
    if (typeof treeNode[key] !== 'undefined') {
      children[key] = recursiveCreateReducer(treeNode[key])
    }
  })

  if (Object.keys(children).length > 0) {
    return combineKeaReducers(children)
  } else {
    const emptyObj = {}
    return () => emptyObj
  }
}

// We are using our own function for the tree nodes instead of redux's combineReducers beacause this way we will not
// get the constant 'Unexpected key "1" found in previous state received by the reducer' warnings when unmounting.
// Instead we'll simply discard the keys we don't need.
// Please note that logic reducers are still built with redux's combineReducers.
export function combineKeaReducers (reducers) {
  const reducerKeys = Object.keys(reducers)

  return function combination (state = {}, action, fullState) {
    let stateChanged = Object.keys(state).length !== reducerKeys.length
    let nextState = {}

    for (let i = 0; i < reducerKeys.length; i++) {
      const key = reducerKeys[i]
      const reducer = reducers[key]
      const previousKeyState = state[key]
      const nextKeyState = reducer(previousKeyState, action, fullState || state)
      if (typeof nextKeyState === 'undefined') {
        throw new Error(`[KEA] Reducer "${key}" returned undefined for action "${action && action.type}"`)
      }
      nextState[key] = nextKeyState
      stateChanged = stateChanged || nextKeyState !== previousKeyState
    }

    return stateChanged ? nextState : state
  }
}
<|endoftext|>