<commit_before>// start-source: core/ready.js
/*debug*///log.info('executing core/ready.js');
Glow.provide(
	function(glow) {
		var readyQueue = [],
			domReadyQueue = [],
			blockersActive = 0,
			processingReadyQueue = false;

		glow._readyBlockers = {};

 		/*debug*///log.info('overwriting Glow ready with glow.ready');
		glow.ready = function (f) { /*debug*///log.info('glow.ready()');
			if (this.isReady) {
				f();
			}
			else {
				readyQueue.push(f);
			}
			return glow;
		};

		glow.onDomReady = function(f) {
			//just run function if already ready
			if (glow.isDomReady) {
				f();
			}
			else {
				domReadyQueue.push(f);
			}
		};

		glow._addReadyBlock = function(name) { /*debug*///log.info('_addReadyBlock('+name+')');
			if (typeof glow._readyBlockers[name] === 'undefined') { glow._readyBlockers[name] = 0; }
			glow._readyBlockers[name]++;
			glow.isReady = false;
			blockersActive++; /*debug*///log.info('  » blockersActive '+blockersActive+'.');
			return glow;
		}

		glow._removeReadyBlock = function(name) { /*debug*///log.info('_removeReadyBlock('+name+')');
			if (glow._readyBlockers[name]) {
				glow._readyBlockers[name]--;
				blockersActive--;  /*debug*///log.info('  » blockersActive '+blockersActive+'.');
				// if we're out of blockers
				if (!blockersActive) {
					// call our queue
					glow.isReady = true;
					runReadyQueue();
				}
			}
			return glow;
		}

		// add blockers for any packages that started loading before core (this package) was built
		for (var i = 0, len = glow._build.loading.length; i < len; i++) {
			glow._addReadyBlock('glow_loading_'+glow._build.loading[i]);
		}

		function runDomReadyQueue() {
			glow.isDomReady = true;
			// run all functions in the array
			for (var i = 0, len = domReadyQueue.length; i < len; i++) {
				domReadyQueue[i]();
			}
		}

		function runReadyQueue() { /*debug*///console.log('runReadyQueue()');
			// if we're already processing the queue, just exit, the other instance will take care of it
			if (processingReadyQueue) { return; }

			processingReadyQueue = true;
			while (readyQueue.length) {
				(readyQueue.shift())(glow);

				// check if the previous function has created a blocker
				if (blockersActive) { break; }
			}
			processingReadyQueue = false;
		}

		/**
			@private
			@function
			@name bindReady
			@description Add listener to document to detect when page is ready.
		 */
		function bindReady() { /*debug*///log.info('bindReady()');
			//don't do this stuff if the dom is already ready
			if (glow.isDomReady) { return; }
			glow._addReadyBlock('glow_domReady'); // wait for dom to be ready

			function onReady() { /*debug*///log.info('onReady()');
				runReadyQueue();
				glow._removeReadyBlock('glow_domReady');
			}

			if (document.readyState == 'complete') { // already here!
				 /*debug*///log.info('already complete');
				onReady();
			}
			else if (glow.env.ie && document.attachEvent) { /*debug*///log.info('bindready() - document.attachEvent');
				// like IE

				// not an iframe...
				if (document.documentElement.doScroll && window == top) {
					(function() {  /*debug*///log.info('doScroll');
						try {
							document.documentElement.doScroll('left');
						}
						catch(error) {
							setTimeout(arguments.callee, 0);
							return;
						}

						// and execute any waiting functions
						onReady();
					})();
				}
				else {
					// an iframe...
					document.attachEvent(
						'onreadystatechange',
						function() { /*debug*///log.info('onreadystatechange');
							if (document.readyState == 'complete') {
								document.detachEvent('onreadystatechange', arguments.callee);
								onReady();
							}
						}
					);
				}
			}
			else if (document.readyState) { /*debug*///log.info('bindready() - document.readyState');
				// like pre Safari
				(function() { /*debug*///log.info('loaded|complete');
					if ( /loaded|complete/.test(document.readyState) ) {
						onReady();
					}
					else {
						setTimeout(arguments.callee, 0);
					}
				})();
			}
			else if (document.addEventListener) {/*debug*///log.info('bindready() - document.addEventListener');
				// like Mozilla, Opera and recent webkit
				document.addEventListener(
					'DOMContentLoaded',
					function(){ /*debug*///log.info('glow DOMContentLoaded');
						document.removeEventListener('DOMContentLoaded', arguments.callee, false);
						onReady();
					},
					false
				);
			}
			else {
				throw new Error('Unable to bind glow ready listener to document.');
			}
		}

		glow.notSupported = ( // here are the browsers we don't support
			glow.env.ie < 6 ||
			(glow.env.gecko < 1.9 && !/^1\.8\.1/.test(env.version)) ||
			glow.env.opera < 9 ||
			glow.env.webkit < 412
		);
		// deprecated
		glow.isSupported = !glow.notSupported;

		// block 'ready' if browser isn't supported
		if (glow.notSupported) {
			glow._addReadyBlock('glow_browserSupport');
		}

		bindReady();
	}
);
// end-source: core/ready.js<commit_msg>Fix for ready failing in the case where big Glow is not used.<commit_after>// start-source: core/ready.js
/*debug*///log.info('executing core/ready.js');
Glow.provide(
	function(glow) {
		var readyQueue = [],
			domReadyQueue = [],
			blockersActive = 0,
			processingReadyQueue = false;

		glow._readyBlockers = {};

 		/*debug*///log.info('overwriting Glow ready with glow.ready');
		glow.ready = function (f) { /*debug*///log.info('glow.ready()');
			if (this.isReady) {
				f();
			}
			else {
				readyQueue.push(f);
			}
			return glow;
		};

		glow.onDomReady = function(f) {
			//just run function if already ready
			if (glow.isDomReady) {
				f();
			}
			else {
				domReadyQueue.push(f);
			}
		};

		glow._addReadyBlock = function(name) { /*debug*///log.info('_addReadyBlock('+name+')');
			if (typeof glow._readyBlockers[name] === 'undefined') { glow._readyBlockers[name] = 0; }
			glow._readyBlockers[name]++;
			glow.isReady = false;
			blockersActive++; /*debug*///log.info('  » blockersActive '+blockersActive+'.');
			return glow;
		}

		glow._removeReadyBlock = function(name) { /*debug*///log.info('_removeReadyBlock('+name+')');
			if (glow._readyBlockers[name]) {
				glow._readyBlockers[name]--;
				blockersActive--;  /*debug*///log.info('  » blockersActive '+blockersActive+'.');
				// if we're out of blockers
				if (!blockersActive) {
					// call our queue
					glow.isReady = true;
					runReadyQueue();
				}
			}
			return glow;
		}

		// add blockers for any packages that started loading before core (this package) was built
		if (glow._build) { // only defined when using big Glow
			for (var i = 0, len = glow._build.loading.length; i < len; i++) {
				glow._addReadyBlock('glow_loading_'+glow._build.loading[i]);
			}
		}

		function runDomReadyQueue() {
			glow.isDomReady = true;
			// run all functions in the array
			for (var i = 0, len = domReadyQueue.length; i < len; i++) {
				domReadyQueue[i]();
			}
		}

		function runReadyQueue() { /*debug*///console.log('runReadyQueue()');
			// if we're already processing the queue, just exit, the other instance will take care of it
			if (processingReadyQueue) { return; }

			processingReadyQueue = true;
			while (readyQueue.length) {
				(readyQueue.shift())(glow);

				// check if the previous function has created a blocker
				if (blockersActive) { break; }
			}
			processingReadyQueue = false;
		}

		/**
			@private
			@function
			@name bindReady
			@description Add listener to document to detect when page is ready.
		 */
		function bindReady() { /*debug*///log.info('bindReady()');
			//don't do this stuff if the dom is already ready
			if (glow.isDomReady) { return; }
			glow._addReadyBlock('glow_domReady'); // wait for dom to be ready

			function onReady() { /*debug*///log.info('onReady()');
				runReadyQueue();
				glow._removeReadyBlock('glow_domReady');
			}

			if (document.readyState == 'complete') { // already here!
				 /*debug*///log.info('already complete');
				onReady();
			}
			else if (glow.env.ie && document.attachEvent) { /*debug*///log.info('bindready() - document.attachEvent');
				// like IE

				// not an iframe...
				if (document.documentElement.doScroll && window == top) {
					(function() {  /*debug*///log.info('doScroll');
						try {
							document.documentElement.doScroll('left');
						}
						catch(error) {
							setTimeout(arguments.callee, 0);
							return;
						}

						// and execute any waiting functions
						onReady();
					})();
				}
				else {
					// an iframe...
					document.attachEvent(
						'onreadystatechange',
						function() { /*debug*///log.info('onreadystatechange');
							if (document.readyState == 'complete') {
								document.detachEvent('onreadystatechange', arguments.callee);
								onReady();
							}
						}
					);
				}
			}
			else if (document.readyState) { /*debug*///log.info('bindready() - document.readyState');
				// like pre Safari
				(function() { /*debug*///log.info('loaded|complete');
					if ( /loaded|complete/.test(document.readyState) ) {
						onReady();
					}
					else {
						setTimeout(arguments.callee, 0);
					}
				})();
			}
			else if (document.addEventListener) {/*debug*///log.info('bindready() - document.addEventListener');
				// like Mozilla, Opera and recent webkit
				document.addEventListener(
					'DOMContentLoaded',
					function(){ /*debug*///log.info('glow DOMContentLoaded');
						document.removeEventListener('DOMContentLoaded', arguments.callee, false);
						onReady();
					},
					false
				);
			}
			else {
				throw new Error('Unable to bind glow ready listener to document.');
			}
		}

		glow.notSupported = ( // here are the browsers we don't support
			glow.env.ie < 6 ||
			(glow.env.gecko < 1.9 && !/^1\.8\.1/.test(env.version)) ||
			glow.env.opera < 9 ||
			glow.env.webkit < 412
		);
		// deprecated
		glow.isSupported = !glow.notSupported;

		// block 'ready' if browser isn't supported
		if (glow.notSupported) {
			glow._addReadyBlock('glow_browserSupport');
		}

		bindReady();
	}
);
// end-source: core/ready.js<|endoftext|>


<commit_before>export function search(node, selector) {
  var result = [], tagsArray;

  if (typeof selector === "string") {
    tagsArray = selector.replace(/\./g, " ").trim().split(" ");
  }

  var searchIterator = function (node) {

    if (typeof selector === "string") {

      if (!node.__data__) {
        return;
      }

      for (var i = 0; i < tagsArray.length; i++) {
        if (node.__tags__.indexOf(tagsArray[i]) < 0) {
          return;
        }
      }
    } else {
      for (var s in selector) {
        if (node[s] !== selector[s]) {
          return;
        }
      }
    }

    return result.push(node);
  };

  node.traverse(searchIterator);

  return result;
}

export function array(list) {
  return Array.prototype.slice.call(list);
}

function ToObject(val) {
  if (val == null) {
    throw new TypeError('Object.assign cannot be called with null or undefined');
  }

  return Object(val);
}

export function assign(target, source) {
  var pendingException;
  var from;
  var keys;
  var to = ToObject(target);

  if (!source) {
    throw new Error("No source(s) provided to assign.");
  }

  for (var s = 1; s < arguments.length; s++) {
    from = arguments[s];
    keys = Object.keys(Object(from));

    for (var i = 0; i < keys.length; i++) {
      try {
        to[keys[i]] = from[keys[i]];
      } catch (err) {
        if (pendingException === undefined) {
          pendingException = err;
        }
      }
    }
  }

  if (pendingException) {
    throw pendingException;
  }

  return to;
}
<commit_msg>formatting<commit_after>export function search(node, selector) {
  var result = [], tagsArray;

  if (typeof selector === "string") {
    tagsArray = selector.replace(/\./g, " ").trim().split(" ");
  }

  var searchIterator = function (node) {

    if (typeof selector === "string") {

      if (!node.__data__) {
        return;
      }

      for (var i = 0; i < tagsArray.length; i++) {
        if (node.__tags__.indexOf(tagsArray[i]) < 0) {
          return;
        }
      }
    } else {
      for (var s in selector) {
        if (node[s] !== selector[s]) {
          return;
        }
      }
    }

    return result.push(node);
  };

  node.traverse(searchIterator);

  return result;
}

export function array(list) {
  return Array.prototype.slice.call(list);
}

function ToObject(val) {
  if (val == null) {
    throw new TypeError('Object.assign cannot be called with null or undefined');
  }

  return Object(val);
}

export function assign(target, source) {
  var pendingException;
  var from;
  var keys;
  var to = ToObject(target);

  if (!source) {
    throw new Error("No source(s) provided to assign.");
  }

  for (var s = 1; s < arguments.length; s++) {
    from = arguments[s];
    keys = Object.keys(Object(from));

    for (var i = 0; i < keys.length; i++) {
      try {
        to[keys[i]] = from[keys[i]];
      } catch (err) {
        if (pendingException === undefined) {
          pendingException = err;
        }
      }
    }
  }

  if (pendingException) {
    throw pendingException;
  }

  return to;
}<|endoftext|>


<commit_before>module.exports = ({ Plugin, types: t }) => {
  function mangleNames(refs, charset, depth) {
    const entries = Array.from(refs.entries());
    entries.forEach(([binding, paths]) => {
      if (binding.scope.getFunctionParent().path.isProgram()) {
        return;
      }

      if (binding.path.isLabeledStatement()) {
        return;
      }

      const scopes = new Set();
      scopes.add(binding.scope.getFunctionParent());
      for (let path of paths) {
        scopes.add(path.scope.getFunctionParent());
      }

      let newName;
      let i = 0;

      do {
        newName = charset.getIdentifier(i);
        i += 1;
      } while (!(t.isValidIdentifier(newName)
          && canUse(newName, binding.scope.getFunctionParent(), scopes, refs)));

      // WARNING: this is a destructive operation, use scope.rename for
      // a safer operation, however, it does full tree traversal for every
      // rename:
      delete binding.scope.bindings[paths[0].node.name];
      binding.scope.bindings[newName] = binding;
      for (let path of paths) {
        path.node.name = newName;
      }
    });
  }

  function canUse(name, originalBindingScope, scopes, refsMap) {
    if (originalBindingScope.hasGlobal(name)) {
      return false;
    }

    for (let scope of scopes) {
      if (scope.hasGlobal(name)) {
        return false;
      }

      // Competing binding in the definition scope.
      const competingBinding = scope.getBinding(name);
      if (competingBinding) {
        // If the competing binding shadows the originalBinding then we can't use it.
        if (competingBinding.scope.path.find(({ node }) => node === originalBindingScope.path.node)) {
          return false;
        }

        /**
         * Go through all references then crawl their scopes upwards,
         * looking to see if one of these references is in this scope.
         */
        const bindingRefs = refsMap.get(competingBinding);

        // If we don't have references for this binding then it must be a new one
        // introduced by this transform. This will rarely happen so it's fine to ignore it.
        if (!bindingRefs) {
          return false;
        }

        for (let ref of bindingRefs) {
          let myScope = ref.scope;
          do {
            if (myScope.path.node === scope.path.node) {
              return false;
            }
            myScope = myScope.parent;
          } while (myScope);
        }
      }
    }
    return true;
  }

  const hop = Object.prototype.hasOwnProperty;

  const collectVisitor = {
    Scope({ scope }) {
      let i = 0;
      let parent = scope.parent;
      while (parent) {
        i++;
        parent = parent.parent;
      }

      this.depth.set(scope, i);
    },

    'ReferencedIdentifier|BindingIdentifier'(path) {
      const { scope, node } = path;

      if (path.parentPath.isLabeledStatement()) {
        return;
      }

      // Ignore break and continue statements.
      if (path.parentPath.isContinueStatement() || path.parentPath.isBreakStatement()) {
        if (path.parent.label === node) {
          return;
        }
      }

      // A function decleration name maybe shadowed by a variable
      // in the scope. So we get it from the upper scope.
      let binding;
      if (t.isFunctionDeclaration(path.parent, { id: node })) {
        binding = scope.parent.getBinding(node.name);
      } else {
        binding = scope.getBinding(node.name);
      }

      if (!binding) {
        return;
      }

      if (!hop.call(this.blacklist, node.name)) {
        recordRef(this.refs, binding, path);
      }
    },

    Literal({ node }) {
      this.charset.consider(String(node.value));
    },

    Identifier(path) {
      if ((path.parentPath.isMemberExpression() && path.key === 'property') ||
          (path.parentPath.isObjectProperty() && path.key === 'key')
      ) {
        this.charset.consider(path.node.name);
      }
    },
  };

  return {
    visitor: {
      Program(path) {
        // If the source code is small then we're going to assume that the user
        // is running on this on single files before bundling. Therefore we
        // need to achieve as much determinisim and we will not do any frequency
        // sorting on the character set. Currently the number is pretty arbitrary.
        const shouldConsiderSource = path.getSource().length > 70000;

        const charset = new Charset(shouldConsiderSource);
        // We want to take control of the sequencing and make sure our visitors
        // run in isolation.
        const refs = new Map();
        const depth = new Map();
        path.traverse(collectVisitor, {
          refs,
          charset,
          depth,
          blacklist: this.opts.mangleBlacklist || Object.create(null),
        });

        charset.sort();

        mangleNames(refs, charset, depth);
      },
    },
  };

  function recordRef(refs, binding, refPath) {
    if (!refs.has(binding)) {
      refs.set(binding, []);
    }
    refs.get(binding).push(refPath);
  }
};

const CHARSET = ('abcdefghijklmnopqrstuvwxyz' +
                 'ABCDEFGHIJKLMNOPQRSTUVWXYZ$_').split('');

class Charset {
  constructor(shouldConsider) {
    this.shouldConsider = shouldConsider;
    this.chars = CHARSET.slice();
    this.frequency = {};
    this.chars.forEach(c => this.frequency[c] = 0);
    this.finalized = false;
  }

  consider(str) {
    if (!this.shouldConsider) {
      return;
    }

    str.split('').forEach(c => {
      if (this.frequency[c] != null) {
        this.frequency[c]++;
      }
    });
  }

  sort() {
    if (this.shouldConsider) {
      this.chars = this.chars.sort(
        (a, b) => this.frequency[b] - this.frequency[a]
      );
    }

    this.finalized = true;
  }

  getIdentifier(num) {
    if (!this.finalized) {
      throw new Error('Should sort first');
    }

    let ret = '';
    num++;
    do {
      num--;
      ret += this.chars[num % this.chars.length];
      num = Math.floor(num / this.chars.length);
    } while (num > 0);
    return ret;
  }
}
<commit_msg>Rewrite mangle names to be more robust<commit_after>module.exports = ({ Plugin, types: t }) => {
  const hop = Object.prototype.hasOwnProperty;

  class Mangler {
    constructor(charset, program, blacklist) {
      this.charset = charset;
      this.program = program;
      this.blacklist = blacklist;

      // scope => {name => true}
      this.scopeToRefNames = new Map();
      // binding => [path, path ...]
      this.bindingToRefPaths = new Map();
      // scope => set(scopes)
      this.scopeToChildren = new Map();
    }

    recordRefName(scope, name) {
      const { scopeToRefNames } = this;

      const refNames = scopeToRefNames.get(scope) || Object.create(null);
      scope = scope.getFunctionParent();
      if (!scopeToRefNames.has(scope)) {
        scopeToRefNames.set(scope, refNames);
      }
      refNames[name] = true;
    }

    recordBindingPath(binding, path) {
      const { bindingToRefPaths, scopeToChildren } = this;
      const paths = bindingToRefPaths.get(binding) || [];
      if (!bindingToRefPaths.has(binding)) {
        bindingToRefPaths.set(binding, paths);
      }
      paths.push(path);

      const scope = binding.scope.getFunctionParent();

      this.recordRefName(scope, binding.identifier.name);
      this.recordRefName(path.scope, binding.identifier.name);

      // Program scope.
      if (!scope.parent) {
        return;
      }

      const parentScope = scope.parent.getFunctionParent();
      const scopes = scopeToChildren.get(parentScope) || new Set();
      if (!scopeToChildren.has(parentScope)) {
        scopeToChildren.set(parentScope, scopes);
      }
      scopes.add(scope);
    }

    run() {
      this.program.traverse(collectVisitor, {
        recordBindingPath: (binding, path) => this.recordBindingPath(binding, path),
        charset: this.charset,
      });

      this.charset.sort();
      this.mangle();
    }

    canUse(newName, binding) {
      const { scopeToRefNames, scopeToChildren } = this;

      const visited = new Set();
      const toVisit = [binding.scope.getFunctionParent()];

      while (toVisit.length) {
        const scope = toVisit.pop();

        if (scope.hasGlobal(newName)) {
          return false;
        }

        const scopeReferences = scopeToRefNames.get(scope);
        if (!scopeReferences) {
          throw new Error('Encountered a scope with no information about');
        }

        if (scopeReferences[newName]) {
          return false;
        }

        visited.add(scope);

        const children = scopeToChildren.get(scope);
        if (children) {
          for (let scopeToVisit of children) {
            if (!visited.has(scopeToVisit)) {
              toVisit.push(scopeToVisit);
            }
          }
        }
      }

      return true;
    }

    updateScopeInfo(binding, newName) {
      const scope = binding.scope.getFunctionParent();
      const scopeReferences = this.scopeToRefNames.get(scope);
      if (!scopeReferences) {
        throw new Error('Encountered a scope with no information about');
      }

      const oldName = binding.identifier.name;
      delete scopeReferences[oldName];
      scopeReferences[newName] = true;

      const paths = this.bindingToRefPaths.get(binding);
      for (let path of paths) {
        const refNames = this.scopeToRefNames.get(path.scope.getFunctionParent());
        delete refNames[oldName];
        refNames[newName] = true;
        path.node.name = newName;
      }

      delete binding.scope.bindings[oldName];
      binding.identifier.name = newName;
      scope.bindings[newName] = binding;
    }

    mangle() {
      const { bindingToRefPaths, charset } = this;
      const entries = Array.from(bindingToRefPaths.entries());
      entries.forEach(([binding, paths]) => {
        if (hop.call(this.blacklist, binding.identifier.name)) {
          return;
        }

        if (binding.scope.getFunctionParent().path.isProgram()) {
          return;
        }
        if (binding.path.isLabeledStatement()) {
          return;
        }

        let newName;
        let i = 0;

        do {
          newName = charset.getIdentifier(i);
          i += 1;
        } while (!t.isValidIdentifier(newName) || !this.canUse(newName, binding));

        this.updateScopeInfo(binding, newName);
      });
    }
  }

  const collectVisitor = {
    'ReferencedIdentifier|BindingIdentifier'(path) {
      const { scope, node } = path;

      if (path.parentPath.isLabeledStatement()) {
        return;
      }

      // Ignore break and continue statements.
      if (path.parentPath.isContinueStatement() || path.parentPath.isBreakStatement()) {
        if (path.parent.label === node) {
          return;
        }
      }

      // A function declaration name maybe shadowed by a variable
      // in the scope. So we get it from the upper scope.
      let binding;
      if (t.isFunctionDeclaration(path.parent, { id: node })) {
        binding = scope.parent.getBinding(node.name);
      } else {
        binding = scope.getBinding(node.name);
      }

      if (!binding) {
        return;
      }

      this.recordBindingPath(binding, path);
    },

    Literal({ node }) {
      this.charset.consider(String(node.value));
    },

    Identifier(path) {
      if ((path.parentPath.isMemberExpression() && path.key === 'property') ||
          (path.parentPath.isObjectProperty() && path.key === 'key')
      ) {
        this.charset.consider(path.node.name);
      }
    },
  };

  return {
    visitor: {
      Program(path) {
        // If the source code is small then we're going to assume that the user
        // is running on this on single files before bundling. Therefore we
        // need to achieve as much determinisim and we will not do any frequency
        // sorting on the character set. Currently the number is pretty arbitrary.
        const shouldConsiderSource = path.getSource().length > 70000;

        const charset = new Charset(shouldConsiderSource);
        const mangler = new Mangler(charset, path, this.opts.mangleBlacklist || {});
        mangler.run();
      },
    },
  };
};

const CHARSET = ('abcdefghijklmnopqrstuvwxyz' +
                 'ABCDEFGHIJKLMNOPQRSTUVWXYZ$_').split('');

class Charset {
  constructor(shouldConsider) {
    this.shouldConsider = shouldConsider;
    this.chars = CHARSET.slice();
    this.frequency = {};
    this.chars.forEach(c => this.frequency[c] = 0);
    this.finalized = false;
  }

  consider(str) {
    if (!this.shouldConsider) {
      return;
    }

    str.split('').forEach(c => {
      if (this.frequency[c] != null) {
        this.frequency[c]++;
      }
    });
  }

  sort() {
    if (this.shouldConsider) {
      this.chars = this.chars.sort(
        (a, b) => this.frequency[b] - this.frequency[a]
      );
    }

    this.finalized = true;
  }

  getIdentifier(num) {
    if (!this.finalized) {
      throw new Error('Should sort first');
    }

    let ret = '';
    num++;
    do {
      num--;
      ret += this.chars[num % this.chars.length];
      num = Math.floor(num / this.chars.length);
    } while (num > 0);
    return ret;
  }
}
<|endoftext|>


<commit_before>/**
 * Pimcore
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://www.pimcore.org/license
 *
 * @copyright  Copyright (c) 2009-2014 pimcore GmbH (http://www.pimcore.org)
 * @license    http://www.pimcore.org/license     New BSD License
 */

pimcore.registerNS("pimcore.document.edit");
pimcore.document.edit = Class.create({

    initialize: function(document) {
        this.document = document;

    },

    getEditLink: function () {
        var date = new Date();
        var link =  this.document.data.path + this.document.data.key + '?pimcore_editmode=true&systemLocale='
                                                            + pimcore.settings.language+'&_dc=' + date.getTime();

        if(this.persona && this.persona.getValue()) {
            link += "&_ptp=" + this.persona.getValue();
        }

        return link;
    },

    getLayout: function (additionalConfig) {

        if (this.layout == null) {
            this.reloadInProgress = true;
            this.iframeName = 'document_iframe_' + this.document.id;

            var html = '';

            var cleanupFunction = function () {
                Ext.Ajax.request({
                    url: "/admin/page/clear-editable-data",
                    params: {
                        persona: this["persona"] ? this.persona.getValue() : "",
                        id: this.document.id
                    },
                    success: function () {
                        this.reload(true);
                    }.bind(this)
                });
            };

            var tbar = [{
                text: t("refresh"),
                iconCls: "pimcore_icon_reload",
                handler: this.reload.bind(this)
            },"-",{
                tooltip: t("highlight_editable_elements"),
                iconCls: "pimcore_icon_highlight",
                enableToggle: true,
                handler: function (el) {
                    var editables = this.frame.Ext.getBody().query(".pimcore_editable");
                    var ed;
                    for(var i=0; i<editables.length; i++) {
                        ed = Ext.get(editables[i]);
                        if(!ed.hasClass("pimcore_tag_inc") && !ed.hasClass("pimcore_tag_areablock")
                            && !ed.hasClass("pimcore_tag_block") && !ed.hasClass("pimcore_tag_area")) {
                            if(el.pressed) {
                                var mask = ed.mask();
                                mask.setStyle("background-color","#f5d833");
                            } else {
                                ed.unmask();
                            }
                        }
                    }
                }.bind(this)
            }, "-", {
                tooltip: t("clear_content_of_current_view"),
                iconCls: "pimcore_icon_cleanup",
                handler: cleanupFunction.bind(this)
            }];

            // add persona selection to toolbar
            if(this.document.getType() == "page" && pimcore.globalmanager.get("personas").getCount() > 0) {

                this.persona = new Ext.form.ComboBox({
                    displayField:'text',
                    valueField: "id",
                    store: {
                        xtype: "jsonstore",
                        url: "/admin/reports/targeting/persona-list/?add-default=true",
                        fields: ["id", "text"]
                    },
                    editable: false,
                    triggerAction: 'all',
                    width: 240,
                    cls: "pimcore_icon_persona_select",
                    emptyText: t("edit_content_for_persona"),
                    listeners: {
                        select: function (el) {
                            if(this.document.isDirty()) {
                                Ext.Msg.confirm(t('warning'), t('you_have_unsaved_changes')
                                    + "
" + t("continue") + "?",
                                    function(btn){
                                        if (btn == 'yes'){
                                            this.reload(true);
                                        }
                                    }.bind(this)
                                );
                            } else {
                                this.reload(true);
                            }
                        }.bind(this)
                    }
                });

                tbar.push("->", this.persona, {
                    tooltip: t("clear_content_of_selected_persona"),
                    iconCls: "pimcore_icon_cleanup",
                    handler: cleanupFunction.bind(this)
                });
            }

            // edit panel configuration
            var config = {
                id: "document_content_" + this.document.id,
                html: html,
                title: t('edit'),
                autoScroll: true,
                bodyStyle: "-webkit-overflow-scrolling:touch;",
                forceLayout: true,
                hideMode: "offsets",
                iconCls: "pimcore_icon_tab_edit",
                tbar: tbar
            };

            if(typeof additionalConfig == "object") {
                config = Ext.apply(config, additionalConfig);
            }

            this.layout = new Ext.Panel(config);
            this.layout.on("resize", this.onLayoutResize.bind(this));

            this.layout.on("afterrender", function () {

                // unfortunately we have to do this in jQuery, because Ext doesn'T offer this functionality
                $("#" + this.iframeName).load(function () {
                    // this is to hide the mask if edit/startup.js isn't executed (eg. in case an error is shown)
                    // otherwise edit/startup.js will disable the loading mask
                    if(!this["frame"]) {
                        this.loadMask.hide();
                    }
                }.bind(this));

                this.loadMask = new Ext.LoadMask(this.layout.body, {msg: t("please_wait")});
                this.loadMask.enable();
                this.loadMask.show();
            }.bind(this));
        }

        return this.layout;

    },

    /*
    iFrameLoaded: function () {
        console.log("finished");
    },
    */

    onLayoutResize: function (el, width, height, rWidth, rHeight) {
        this.setLayoutFrameDimensions(width, height);
    },

    setLayoutFrameDimensions: function (width, height) {
        Ext.get(this.iframeName).setStyle({
            height: (height-32) + "px"
        });
    },

    onClose: function () {

        try {
            this.reloadInProgress = true;
            window[this.iframeName].location.href = "about:blank";
            Ext.get(this.iframeName).remove();
            delete window[this.iframeName];

        } catch (e) {
        }
    },

    protectLocation: function () {
        if (this.reloadInProgress != true) {
            window.setTimeout(this.reload.bind(this), 200);
        }
    },

    iframeOnbeforeunload: function () {
        if(!this.reloadInProgress && !pimcore.globalmanager.get("pimcore_reload_in_progress")) {
            return t("do_you_really_want_to_leave_the_editmode");
        }
    },

    reload: function (disableSaveToSession) {

        if (this.reloadInProgress) {
            disableSaveToSession = true;
        }

        this.reloadInProgress = true;

        try {
            if(this["frame"]) {
                this.lastScrollposition = this.frame.Ext.getBody().getScroll();
            }
        }
        catch (e) {
            console.log(e);
        }

        this.loadMask.show();

        if (disableSaveToSession === true) {
            this.frame = null;
            Ext.get(this.iframeName).dom.src = this.getEditLink();
        }
        else {
            this.document.saveToSession(function () {
                this.frame = null;
                Ext.get(this.iframeName).dom.src = this.getEditLink();
            }.bind(this));
        }
    },

    maskFrames: function () {

        // this is for dnd over iframes, with this method it's not nessercery to register the dnd manager in each
        // iframe (wysiwyg)
        var width;
        var height;
        var offset;
        var element;
        var i;

        // mask frames (iframes)
        try {
            // mask iframes
            if (typeof this.frame.Ext != "object") {
                return;
            }

            var iFrames = this.frame.document.getElementsByTagName("iframe");
            for (i = 0; i < iFrames.length; i++) {
                width = Ext.get(iFrames[i]).getWidth();
                height = Ext.get(iFrames[i]).getHeight();

                offset = Ext.get(iFrames[i]).getOffsetsTo(this.frame.Ext.getBody());

                element = this.frame.Ext.getBody().createChild({
                    tag: "div",
                    id: Ext.id()
                });

                element.setStyle({
                    width: width + "px",
                    height: height + "px",
                    left: offset[0] + "px",
                    top: offset[1] + "px"
                });

                element.addClass("pimcore_iframe_mask");
            }
        }
        catch (e) {
            console.log(e);
            console.log("there is no frame to mask");
        }
    },

    getValues: function () {

        var values = {};

        if (!this.frame || !this.frame.editables) {
            throw "edit not available";
        }

        try {
            var editables = this.frame.editables;
            var editableName = "";

            for (var i = 0; i < editables.length; i++) {
                try {
                    if (editables[i].getName() && !editables[i].getInherited()) {
                        editableName = editables[i].getName();
                        values[editableName] = {};
                        values[editableName].data = editables[i].getValue();
                        values[editableName].type = editables[i].getType();
                    }
                } catch (e) {
                }
            }
        }
        catch (e2) {
        }

        return values;
    }

});

<commit_msg>document editmode: iframe overlay/mask fix<commit_after>/**
 * Pimcore
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://www.pimcore.org/license
 *
 * @copyright  Copyright (c) 2009-2014 pimcore GmbH (http://www.pimcore.org)
 * @license    http://www.pimcore.org/license     New BSD License
 */

pimcore.registerNS("pimcore.document.edit");
pimcore.document.edit = Class.create({

    initialize: function(document) {
        this.document = document;

    },

    getEditLink: function () {
        var date = new Date();
        var link =  this.document.data.path + this.document.data.key + '?pimcore_editmode=true&systemLocale='
                                                            + pimcore.settings.language+'&_dc=' + date.getTime();

        if(this.persona && this.persona.getValue()) {
            link += "&_ptp=" + this.persona.getValue();
        }

        return link;
    },

    getLayout: function (additionalConfig) {

        if (this.layout == null) {
            this.reloadInProgress = true;
            this.iframeName = 'document_iframe_' + this.document.id;

            var html = '';

            var cleanupFunction = function () {
                Ext.Ajax.request({
                    url: "/admin/page/clear-editable-data",
                    params: {
                        persona: this["persona"] ? this.persona.getValue() : "",
                        id: this.document.id
                    },
                    success: function () {
                        this.reload(true);
                    }.bind(this)
                });
            };

            var tbar = [{
                text: t("refresh"),
                iconCls: "pimcore_icon_reload",
                handler: this.reload.bind(this)
            },"-",{
                tooltip: t("highlight_editable_elements"),
                iconCls: "pimcore_icon_highlight",
                enableToggle: true,
                handler: function (el) {
                    var editables = this.frame.Ext.getBody().query(".pimcore_editable");
                    var ed;
                    for(var i=0; i<editables.length; i++) {
                        ed = Ext.get(editables[i]);
                        if(!ed.hasClass("pimcore_tag_inc") && !ed.hasClass("pimcore_tag_areablock")
                            && !ed.hasClass("pimcore_tag_block") && !ed.hasClass("pimcore_tag_area")) {
                            if(el.pressed) {
                                var mask = ed.mask();
                                mask.setStyle("background-color","#f5d833");
                            } else {
                                ed.unmask();
                            }
                        }
                    }
                }.bind(this)
            }, "-", {
                tooltip: t("clear_content_of_current_view"),
                iconCls: "pimcore_icon_cleanup",
                handler: cleanupFunction.bind(this)
            }];

            // add persona selection to toolbar
            if(this.document.getType() == "page" && pimcore.globalmanager.get("personas").getCount() > 0) {

                this.persona = new Ext.form.ComboBox({
                    displayField:'text',
                    valueField: "id",
                    store: {
                        xtype: "jsonstore",
                        url: "/admin/reports/targeting/persona-list/?add-default=true",
                        fields: ["id", "text"]
                    },
                    editable: false,
                    triggerAction: 'all',
                    width: 240,
                    cls: "pimcore_icon_persona_select",
                    emptyText: t("edit_content_for_persona"),
                    listeners: {
                        select: function (el) {
                            if(this.document.isDirty()) {
                                Ext.Msg.confirm(t('warning'), t('you_have_unsaved_changes')
                                    + "
" + t("continue") + "?",
                                    function(btn){
                                        if (btn == 'yes'){
                                            this.reload(true);
                                        }
                                    }.bind(this)
                                );
                            } else {
                                this.reload(true);
                            }
                        }.bind(this)
                    }
                });

                tbar.push("->", this.persona, {
                    tooltip: t("clear_content_of_selected_persona"),
                    iconCls: "pimcore_icon_cleanup",
                    handler: cleanupFunction.bind(this)
                });
            }

            // edit panel configuration
            var config = {
                id: "document_content_" + this.document.id,
                html: html,
                title: t('edit'),
                autoScroll: true,
                bodyStyle: "-webkit-overflow-scrolling:touch;",
                forceLayout: true,
                hideMode: "offsets",
                iconCls: "pimcore_icon_tab_edit",
                tbar: tbar
            };

            if(typeof additionalConfig == "object") {
                config = Ext.apply(config, additionalConfig);
            }

            this.layout = new Ext.Panel(config);
            this.layout.on("resize", this.onLayoutResize.bind(this));

            this.layout.on("afterrender", function () {

                // unfortunately we have to do this in jQuery, because Ext doesn'T offer this functionality
                $("#" + this.iframeName).load(function () {
                    // this is to hide the mask if edit/startup.js isn't executed (eg. in case an error is shown)
                    // otherwise edit/startup.js will disable the loading mask
                    if(!this["frame"]) {
                        this.loadMask.hide();
                    }
                }.bind(this));

                this.loadMask = new Ext.LoadMask(this.layout.body, {msg: t("please_wait")});
                this.loadMask.enable();
                this.loadMask.show();
            }.bind(this));
        }

        return this.layout;

    },

    /*
    iFrameLoaded: function () {
        console.log("finished");
    },
    */

    onLayoutResize: function (el, width, height, rWidth, rHeight) {
        this.setLayoutFrameDimensions(width, height);
    },

    setLayoutFrameDimensions: function (width, height) {
        Ext.get(this.iframeName).setStyle({
            height: (height-32) + "px"
        });
    },

    onClose: function () {

        try {
            this.reloadInProgress = true;
            window[this.iframeName].location.href = "about:blank";
            Ext.get(this.iframeName).remove();
            delete window[this.iframeName];

        } catch (e) {
        }
    },

    protectLocation: function () {
        if (this.reloadInProgress != true) {
            window.setTimeout(this.reload.bind(this), 200);
        }
    },

    iframeOnbeforeunload: function () {
        if(!this.reloadInProgress && !pimcore.globalmanager.get("pimcore_reload_in_progress")) {
            return t("do_you_really_want_to_leave_the_editmode");
        }
    },

    reload: function (disableSaveToSession) {

        if (this.reloadInProgress) {
            disableSaveToSession = true;
        }

        this.reloadInProgress = true;

        try {
            if(this["frame"]) {
                this.lastScrollposition = this.frame.Ext.getBody().getScroll();
            }
        }
        catch (e) {
            console.log(e);
        }

        this.loadMask.show();

        if (disableSaveToSession === true) {
            this.frame = null;
            Ext.get(this.iframeName).dom.src = this.getEditLink();
        }
        else {
            this.document.saveToSession(function () {
                this.frame = null;
                Ext.get(this.iframeName).dom.src = this.getEditLink();
            }.bind(this));
        }
    },

    maskFrames: function () {

        // this is for dnd over iframes, with this method it's not nessercery to register the dnd manager in each
        // iframe (wysiwyg)
        var width;
        var height;
        var offset;
        var element;
        var i;

        // mask frames (iframes)
        try {
            // mask iframes
            if (typeof this.frame.Ext != "object") {
                return;
            }

            var iFrames = this.frame.document.getElementsByTagName("iframe");
            for (i = 0; i < iFrames.length; i++) {
                width = Ext.get(iFrames[i]).getWidth();
                height = Ext.get(iFrames[i]).getHeight();

                offset = Ext.get(iFrames[i]).getOffsetsTo(this.frame.Ext.getBody());

                var parentElement = this.frame.Ext.get(iFrames[i]).parent();

                parentElement.applyStyles({
                    position: "relative"
                });

                element = parentElement.createChild({
                    tag: "div",
                    id: Ext.id()
                });

                element.setStyle({
                    width: width + "px",
                    height: height + "px",
                    left: 0,
                    top: 0
                });

                element.addClass("pimcore_iframe_mask");
            }
        }
        catch (e) {
            console.log(e);
            console.log("there is no frame to mask");
        }
    },

    getValues: function () {

        var values = {};

        if (!this.frame || !this.frame.editables) {
            throw "edit not available";
        }

        try {
            var editables = this.frame.editables;
            var editableName = "";

            for (var i = 0; i < editables.length; i++) {
                try {
                    if (editables[i].getName() && !editables[i].getInherited()) {
                        editableName = editables[i].getName();
                        values[editableName] = {};
                        values[editableName].data = editables[i].getValue();
                        values[editableName].type = editables[i].getType();
                    }
                } catch (e) {
                }
            }
        }
        catch (e2) {
        }

        return values;
    }

});

<|endoftext|>


<commit_before>$(function() {
    var treeDOMTarget = '#materialTree';

    /**
     * Clears the DOM element for the graph and then initiates a jqPlot render
     * in the target area.
     *
     * @param source - JSON of dates: {total / unique} hits
     */
    var draw_jqplot_graph = function(source, DOMTarget, replot) {
        $('#' + DOMTarget).html('');

        var plotOptions = {
            axes: {
                xaxis: {
                    renderer: $.jqplot.DateAxisRenderer,
                    min: window.piwikReportDates.start,
                    max: window.piwikReportDates.end
                },
                yaxis: {
                    min: 0,
                    numberTicks: 10
                }
            },
            cursor: {
                show: true,
                zoom: true,
                showTooltip: false
            },
            highlighter: {
                show: true,
                sizeAdjust: 5
            },
            legend: {
                show: true,
                location: 'nw'
            },
            grid: {
                background: '#FFFFFF',
                shadow: false
            },
            series: [{
                showMarker:false,
                lineWidth: 1,
                color: '#CCCCCC',
                label: $T('Total Hits')
            }, {
                showMarker:false,
                lineWidth: 1,
                color: '#0B63A5',
                label: $T('Unique Hits')
            }]
        };

        if (replot) {
            $.jqplot(DOMTarget, source, plotOptions).replot();
        } else {
            $.jqplot(DOMTarget, source, plotOptions);
        }
    };

    /**
     * Draw a customized jqTree
     */
    var draw_jqTree = function(treeData) {
        $(treeDOMTarget).tree({
            data: treeData,
            autoOpen: 0,
            saveState: true,
            onCanSelectNode: function(node) {
                // Leaf node (material) can be selected
                return (node.children.length === 0);
            },
            onCreateLi: function(node, $li) {
                if (node.id !== undefined) {
                    $li.find('.title').addClass('selectableNode');
                }
            }
        });
    };

    /**
     * Get base values for API requests
     */
    var get_api_params = function() {
        var params = {'confId': $('#confId').val(),
                      'start_date' : $('#statsFilterStartDate').val(),
                      'end_date' : $('#statsFilterEndDate').val()};

        var contrib_id = $('#contribId').val();
        if (contrib_id != 'None') {
            params.contrib_id = contrib_id;
        }

        return params;
    };

    /**
     * Build URI for page update
     */
    var get_updated_uri = function() {
        var params = {'start_date' : $('#statsFilterStartDate').val(),
                      'end_date' : $('#statsFilterEndDate').val()};
        var contrib_id = $('#updateContribution').val();
        if (contrib_id != 'None') {
            params.contrib_id = contrib_id;
        }
        return $.param(params);
    };

    /**
     * Extract data from a JSON object returned via the API into
     * the jqPlot array format. If 'with_date' is true
     * each element will be a key-pair value of date-hits.
     */
    var get_jqplot_array_values = function(data, key, with_date) {
        output = [];
        with_date = typeof with_date !== 'undefined' ? with_date : true;

        for (var date in data) {
            hits = data[date];
            value = (with_date) ? [date, hits[key]] : hits[key];
            output.push(value);
        }

        return output;
    };

    /**
     * Load material downloads data via AJAX and draw its graph
     */
    var load_material_graph = function(uri, replot) {
        replot = typeof replot !== 'undefined' ? replot : false;
        var DOMTarget = 'materialDownloadChart';
        var graph_params = get_api_params();
        graph_params.download_url = uri;

        $.ajax({
            url: build_url(PiwikPlugin.urls.data_downloads, graph_params),
            type: 'POST',
            dataType: 'json',
            success: function(data) {
                if (handleAjaxError(data)) {
                    return;
                }
                var materialHits = [get_jqplot_array_values(data.metrics.downloads.individual, 'total'),
                                    get_jqplot_array_values(data.metrics.downloads.individual, 'unique')];
                draw_jqplot_graph(materialHits, DOMTarget, replot);
                $('#materialTotalDownloads').html(data.metrics.downloads.cumulative.total);
            }
        });
    };

    /**
     * Load the material files data and draw its jqTree
     */
    var load_material_tree = function() {
        $(treeDOMTarget).html(progressIndicator(true, true).dom);

        $.ajax({
            url: build_url(PiwikPlugin.urls.material, get_api_params()),
            type: 'POST',
            dataType: 'json',
            success: function(data) {
                if (handleAjaxError(data)) {
                    return;
                }
                if (data.material.tree !== null) {
                    draw_jqTree(data.material.tree);
                } else {
                    $(treeDOMTarget).html($T('No material found'));
                }
            }
        });
    };

    /**
     * Loads visits data and draw its graph
     */
    var load_visits_graph = function(data) {
        var DOMTarget = 'visitorChart';
        $('#' + DOMTarget).html(progressIndicator(true, true).dom);

        $.ajax({
            url: build_url(PiwikPlugin.urls.data_visits, get_api_params()),
            type: 'POST',
            dataType: 'json',
            success: function(data) {
                if (handleAjaxError(data)) {
                    return;
                }
                var source = [get_jqplot_array_values(data.metrics, 'total'),
                              get_jqplot_array_values(data.metrics, 'unique')];
                draw_jqplot_graph(source, DOMTarget, false);
            }
        });
    };

    /**
     * Load static graphs via ajax
     */
    var load_graphs = function() {
        var graph_requests = [{'endpoint': 'graph_countries', 'report': 'countries'},
                              {'endpoint': 'graph_devices', 'report': 'devices'}];
        $.each(graph_requests, function(index, request) {
            $.ajax({
                url: build_url(PiwikPlugin.urls[request.endpoint], get_api_params()),
                type: 'POST',
                dataType: 'json',
                success: function(data) {
                    if (handleAjaxError(data)) {
                        return;
                    }
                    var graph_holder =  $('#' + request.endpoint);
                    if (data.graphs[request.report] !== null) {
                        graph_holder.attr('src', data.graphs[request.report]);
                    } else {
                        var error = $('<div>').text($T("No graph data received"));
                        graph_holder.replaceWith(error);
                    }
                }
            });
        });
    };

    var init = function() {
        $('#statsModify').click(function(e) {
            e.preventDefault();
            var $this = $(this);
            var filter = $('#statsFilter');
            if (filter.is(':visible')) {  // hiding it
                $this.text($this.data('msgShow'));
            } else {
                $this.text($this.data('msgHide'));
            }
            filter.slideToggle('fast');
        });

        $('.statsDates').datepicker({
            dateFormat : 'yy-mm-dd',
            defaultDate : $(this).attr('data-default')
        });

        $('#updateQuery').click(function() {
            var url = '?{0}'.format(get_updated_uri());
            window.location.href = url;
        });

        // Event handler for clicking 'selectable' elements from the jqTree.
        $(treeDOMTarget).bind('tree.click', function(event) {
            $('#materialTitle').html(event.node.name);
            $('#materialDownloadChart').html(progressIndicator(true, true).dom);
            load_material_graph(event.node.id, true);
        });

        // jQuery UI Dialog if no data is received via AJAX (timeout)
        $('#dialogNoGraphData').dialog({
            modal: true,
            resizable: false,
            autoOpen: false,
            buttons: {
                Ok: function() {
                    $(this).dialog('close');
                }
            }
        });

        load_graphs();
        load_visits_graph();
        load_material_tree();
    };

    init();
});
<commit_msg>Piwik: Use plugin JS i18n<commit_after>$(function() {
    var $t = $T.domain('piwik');
    var treeDOMTarget = '#materialTree';

    /**
     * Clears the DOM element for the graph and then initiates a jqPlot render
     * in the target area.
     *
     * @param source - JSON of dates: {total / unique} hits
     */
    var draw_jqplot_graph = function(source, DOMTarget, replot) {
        $('#' + DOMTarget).html('');

        var plotOptions = {
            axes: {
                xaxis: {
                    renderer: $.jqplot.DateAxisRenderer,
                    min: window.piwikReportDates.start,
                    max: window.piwikReportDates.end
                },
                yaxis: {
                    min: 0,
                    numberTicks: 10
                }
            },
            cursor: {
                show: true,
                zoom: true,
                showTooltip: false
            },
            highlighter: {
                show: true,
                sizeAdjust: 5
            },
            legend: {
                show: true,
                location: 'nw'
            },
            grid: {
                background: '#FFFFFF',
                shadow: false
            },
            series: [{
                showMarker:false,
                lineWidth: 1,
                color: '#CCCCCC',
                label: $t.gettext('Total Hits')
            }, {
                showMarker:false,
                lineWidth: 1,
                color: '#0B63A5',
                label: $t.gettext('Unique Hits')
            }]
        };

        if (replot) {
            $.jqplot(DOMTarget, source, plotOptions).replot();
        } else {
            $.jqplot(DOMTarget, source, plotOptions);
        }
    };

    /**
     * Draw a customized jqTree
     */
    var draw_jqTree = function(treeData) {
        $(treeDOMTarget).tree({
            data: treeData,
            autoOpen: 0,
            saveState: true,
            onCanSelectNode: function(node) {
                // Leaf node (material) can be selected
                return (node.children.length === 0);
            },
            onCreateLi: function(node, $li) {
                if (node.id !== undefined) {
                    $li.find('.title').addClass('selectableNode');
                }
            }
        });
    };

    /**
     * Get base values for API requests
     */
    var get_api_params = function() {
        var params = {'confId': $('#confId').val(),
                      'start_date' : $('#statsFilterStartDate').val(),
                      'end_date' : $('#statsFilterEndDate').val()};

        var contrib_id = $('#contribId').val();
        if (contrib_id != 'None') {
            params.contrib_id = contrib_id;
        }

        return params;
    };

    /**
     * Build URI for page update
     */
    var get_updated_uri = function() {
        var params = {'start_date' : $('#statsFilterStartDate').val(),
                      'end_date' : $('#statsFilterEndDate').val()};
        var contrib_id = $('#updateContribution').val();
        if (contrib_id != 'None') {
            params.contrib_id = contrib_id;
        }
        return $.param(params);
    };

    /**
     * Extract data from a JSON object returned via the API into
     * the jqPlot array format. If 'with_date' is true
     * each element will be a key-pair value of date-hits.
     */
    var get_jqplot_array_values = function(data, key, with_date) {
        output = [];
        with_date = typeof with_date !== 'undefined' ? with_date : true;

        for (var date in data) {
            hits = data[date];
            value = (with_date) ? [date, hits[key]] : hits[key];
            output.push(value);
        }

        return output;
    };

    /**
     * Load material downloads data via AJAX and draw its graph
     */
    var load_material_graph = function(uri, replot) {
        replot = typeof replot !== 'undefined' ? replot : false;
        var DOMTarget = 'materialDownloadChart';
        var graph_params = get_api_params();
        graph_params.download_url = uri;

        $.ajax({
            url: build_url(PiwikPlugin.urls.data_downloads, graph_params),
            type: 'POST',
            dataType: 'json',
            success: function(data) {
                if (handleAjaxError(data)) {
                    return;
                }
                var materialHits = [get_jqplot_array_values(data.metrics.downloads.individual, 'total'),
                                    get_jqplot_array_values(data.metrics.downloads.individual, 'unique')];
                draw_jqplot_graph(materialHits, DOMTarget, replot);
                $('#materialTotalDownloads').html(data.metrics.downloads.cumulative.total);
            }
        });
    };

    /**
     * Load the material files data and draw its jqTree
     */
    var load_material_tree = function() {
        $(treeDOMTarget).html(progressIndicator(true, true).dom);

        $.ajax({
            url: build_url(PiwikPlugin.urls.material, get_api_params()),
            type: 'POST',
            dataType: 'json',
            success: function(data) {
                if (handleAjaxError(data)) {
                    return;
                }
                if (data.material.tree !== null) {
                    draw_jqTree(data.material.tree);
                } else {
                    $(treeDOMTarget).html($t.gettext('No material found'));
                }
            }
        });
    };

    /**
     * Loads visits data and draw its graph
     */
    var load_visits_graph = function(data) {
        var DOMTarget = 'visitorChart';
        $('#' + DOMTarget).html(progressIndicator(true, true).dom);

        $.ajax({
            url: build_url(PiwikPlugin.urls.data_visits, get_api_params()),
            type: 'POST',
            dataType: 'json',
            success: function(data) {
                if (handleAjaxError(data)) {
                    return;
                }
                var source = [get_jqplot_array_values(data.metrics, 'total'),
                              get_jqplot_array_values(data.metrics, 'unique')];
                draw_jqplot_graph(source, DOMTarget, false);
            }
        });
    };

    /**
     * Load static graphs via ajax
     */
    var load_graphs = function() {
        var graph_requests = [{'endpoint': 'graph_countries', 'report': 'countries'},
                              {'endpoint': 'graph_devices', 'report': 'devices'}];
        $.each(graph_requests, function(index, request) {
            $.ajax({
                url: build_url(PiwikPlugin.urls[request.endpoint], get_api_params()),
                type: 'POST',
                dataType: 'json',
                success: function(data) {
                    if (handleAjaxError(data)) {
                        return;
                    }
                    var graph_holder =  $('#' + request.endpoint);
                    if (data.graphs[request.report] !== null) {
                        graph_holder.attr('src', data.graphs[request.report]);
                    } else {
                        var error = $('<div>').text($t.gettext("No graph data received"));
                        graph_holder.replaceWith(error);
                    }
                }
            });
        });
    };

    var init = function() {
        $('#statsModify').click(function(e) {
            e.preventDefault();
            var $this = $(this);
            var filter = $('#statsFilter');
            if (filter.is(':visible')) {  // hiding it
                $this.text($this.data('msgShow'));
            } else {
                $this.text($this.data('msgHide'));
            }
            filter.slideToggle('fast');
        });

        $('.statsDates').datepicker({
            dateFormat : 'yy-mm-dd',
            defaultDate : $(this).attr('data-default')
        });

        $('#updateQuery').click(function() {
            var url = '?{0}'.format(get_updated_uri());
            window.location.href = url;
        });

        // Event handler for clicking 'selectable' elements from the jqTree.
        $(treeDOMTarget).bind('tree.click', function(event) {
            $('#materialTitle').html(event.node.name);
            $('#materialDownloadChart').html(progressIndicator(true, true).dom);
            load_material_graph(event.node.id, true);
        });

        // jQuery UI Dialog if no data is received via AJAX (timeout)
        $('#dialogNoGraphData').dialog({
            modal: true,
            resizable: false,
            autoOpen: false,
            buttons: {
                Ok: function() {
                    $(this).dialog('close');
                }
            }
        });

        load_graphs();
        load_visits_graph();
        load_material_tree();
    };

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


<commit_before>/**
 * Forwards data from content script to freedom;
 * TODO(salomegeo): rewrite in typescript;
 * Figure out a way to avoid freeom -> add-on env -> content script -> add-on
 * for proxy setting and setiing a main icon.
 */

var proxyConfig = require('firefox_proxy_config.js').proxyConfig;
var xhr = require('firefox_xhr.js').xhr;

// TODO: rename uproxy.js/ts to uproxy-enums.js/ts
var uproxy_core_api = require('./interfaces/uproxy_core_api.js');
var { Ci, Cc, Cr } = require("chrome");
var self = require("sdk/self");
var events = require("sdk/system/events");
var notifications = require('sdk/notifications')
var pagemod = require('sdk/page-mod');

// TODO: rename freedom to uProxyFreedomModule
function setUpConnection(freedom, panel, button) {
  function connect(command, from, to) {
    from.on(command, function(data) {
      to.emit(command, data);
    })
  }

  function openURL(url) {
    var win = Cc['@mozilla.org/appshell/window-mediator;1']
        .getService(Ci.nsIWindowMediator)
        .getMostRecentWindow('navigator:browser');
    if (url.indexOf(':') < 0) {
      url = self.data.url(url);
    }
    win.gBrowser.selectedTab = win.gBrowser.addTab(url);
    panel.hide();
  }

  // Set up listeners between core and ui.
  for (var command in uproxy_core_api.Command) {
    if (typeof uproxy_core_api.Command[command] === 'number') {
      connect('' + uproxy_core_api.Command[command], panel.port, freedom);
    }
  }

  for (var update in uproxy_core_api.Update) {
    if (typeof uproxy_core_api.Update[update] === 'number') {
      connect('' + uproxy_core_api.Update[update], freedom, panel.port);
    }
  }

  panel.port.on('startUsingProxy', function(endpoint) {
    proxyConfig.startUsingProxy(endpoint);
  });

  panel.port.on('stopUsingProxy', function() {
    proxyConfig.stopUsingProxy();
  });

  panel.port.on('setIcon', function(iconFiles) {
    button.state("window", {
      icon : iconFiles
    });
  });

  panel.port.on('showPanel', function() {
    panel.show({
      position: button
    });
  });

  panel.port.on('openURL', function(url) {
    openURL(url);
  });

  panel.port.on('launchTabIfNotOpen', function(url) {
    // TODO: only launch if not open (https://github.com/uProxy/uproxy/issues/1124)
    openURL(url);
  });

  panel.port.on('showNotification', function(notification) {
    notifications.notify({
      text: notification.text,
      iconURL: './icons/128_online.png',
      data: notification.tag,
      onClick: function(data) {
        panel.port.emit('notificationClicked', data);
      }
    });
  });

  /**
   * Install a handler for promise emits received from the panel.
   * Promise emits return an ack or error to the panel.
   */
  function onPromiseEmit(message, handler) {
    console.log('on promise emit called');
    var promiseEmitHandler = function (args) {
      // Ensure promiseId is set for all requests
      if (!args.promiseId) {
        var err = 'onPromiseEmit called with message ' + message +
                  'with promiseId undefined';
        return Promise.reject(new Error(err));
      }

      // Call handler function, then return success or failure to the panel.
      handler(args.data).then(
        function (argsForCallback) {
          var fulfillData = {
            message: message,
            promiseId: args.promiseId,
            argsForCallback: argsForCallback
          };
          panel.port.emit('emitFulfilled', fulfillData);
        },
        function (errorForCallback) {
          var rejectionData = {
            promiseId: args.promiseId,
            errorForCallback: errorForCallback.toString()
          };
          panel.port.emit('emitRejected', rejectionData);
        }
      );
    }.bind(this);

    panel.port.on(message, function(args) {
      promiseEmitHandler(args);
    }.bind(this));
  };

  function post(data) {
    return xhr.frontedPost(data.data, data.externalDomain,
        data.cloudfrontDomain, data.cloudfrontPath);
  };

  // Ensure a fulfill or reject message will be sent back to the panel
  // when required by registering messages that initiate async behaviour.
  onPromiseEmit('frontedPost', post);

  /* Allow any pages in the addon to send messages to the UI or the core */
  pagemod.PageMod({
    include: self.data.url('*'),
    contentScriptFile: self.data.url('scripts/content-proxy.js'),
    onAttach: function(worker) {
      worker.port.on('update', function(data) {
        panel.port.emit(uproxy_core_api.Update[data.update], data.data);
      });

      worker.port.on('command', function(data) {
        freedom.emit(uproxy_core_api.Command[data.command], data.data);
      });

      // If we receive a getLogs message from a webpage (specifically
      // view-logs.html), make a call to get logs from the core, and intercept
      // the returned value when it is being passed to the UI with a
      // COMMAND_FULFILLED update.
      worker.port.on('getLogs', function(data) {
        freedom.emit(uproxy_core_api.Command.GET_LOGS, {data: data.data, promiseId: -1});
        var forwardLogsToContentScript = function(data) {
          if (data['command'] == uproxy_core_api.Command.GET_LOGS) {
            // Forward logs to content-proxy.js
            worker.port.emit('message', {
              logs: true,
              data: data.argsForCallback
            });
            freedom.off(uproxy_core_api.Update.COMMAND_FULFILLED,
              forwardLogsToContentScript);
          }
        };
        freedom.on(uproxy_core_api.Update.COMMAND_FULFILLED, forwardLogsToContentScript);
      });

      worker.port.on('showPanel', function(data) {
        panel.show({
          position: button
        });
      });
    }
  });
}

exports.setUpConnection = setUpConnection
<commit_msg>Update global Firefox icon instead of per-window state<commit_after>/**
 * Forwards data from content script to freedom;
 * TODO(salomegeo): rewrite in typescript;
 * Figure out a way to avoid freeom -> add-on env -> content script -> add-on
 * for proxy setting and setiing a main icon.
 */

var proxyConfig = require('firefox_proxy_config.js').proxyConfig;
var xhr = require('firefox_xhr.js').xhr;

// TODO: rename uproxy.js/ts to uproxy-enums.js/ts
var uproxy_core_api = require('./interfaces/uproxy_core_api.js');
var { Ci, Cc, Cr } = require("chrome");
var self = require("sdk/self");
var events = require("sdk/system/events");
var notifications = require('sdk/notifications')
var pagemod = require('sdk/page-mod');

// TODO: rename freedom to uProxyFreedomModule
function setUpConnection(freedom, panel, button) {
  function connect(command, from, to) {
    from.on(command, function(data) {
      to.emit(command, data);
    })
  }

  function openURL(url) {
    var win = Cc['@mozilla.org/appshell/window-mediator;1']
        .getService(Ci.nsIWindowMediator)
        .getMostRecentWindow('navigator:browser');
    if (url.indexOf(':') < 0) {
      url = self.data.url(url);
    }
    win.gBrowser.selectedTab = win.gBrowser.addTab(url);
    panel.hide();
  }

  // Set up listeners between core and ui.
  for (var command in uproxy_core_api.Command) {
    if (typeof uproxy_core_api.Command[command] === 'number') {
      connect('' + uproxy_core_api.Command[command], panel.port, freedom);
    }
  }

  for (var update in uproxy_core_api.Update) {
    if (typeof uproxy_core_api.Update[update] === 'number') {
      connect('' + uproxy_core_api.Update[update], freedom, panel.port);
    }
  }

  panel.port.on('startUsingProxy', function(endpoint) {
    proxyConfig.startUsingProxy(endpoint);
  });

  panel.port.on('stopUsingProxy', function() {
    proxyConfig.stopUsingProxy();
  });

  panel.port.on('setIcon', function(iconFiles) {
    button.icon = iconFiles;
  });

  panel.port.on('showPanel', function() {
    panel.show({
      position: button
    });
  });

  panel.port.on('openURL', function(url) {
    openURL(url);
  });

  panel.port.on('launchTabIfNotOpen', function(url) {
    // TODO: only launch if not open (https://github.com/uProxy/uproxy/issues/1124)
    openURL(url);
  });

  panel.port.on('showNotification', function(notification) {
    notifications.notify({
      text: notification.text,
      iconURL: './icons/128_online.png',
      data: notification.tag,
      onClick: function(data) {
        panel.port.emit('notificationClicked', data);
      }
    });
  });

  /**
   * Install a handler for promise emits received from the panel.
   * Promise emits return an ack or error to the panel.
   */
  function onPromiseEmit(message, handler) {
    console.log('on promise emit called');
    var promiseEmitHandler = function (args) {
      // Ensure promiseId is set for all requests
      if (!args.promiseId) {
        var err = 'onPromiseEmit called with message ' + message +
                  'with promiseId undefined';
        return Promise.reject(new Error(err));
      }

      // Call handler function, then return success or failure to the panel.
      handler(args.data).then(
        function (argsForCallback) {
          var fulfillData = {
            message: message,
            promiseId: args.promiseId,
            argsForCallback: argsForCallback
          };
          panel.port.emit('emitFulfilled', fulfillData);
        },
        function (errorForCallback) {
          var rejectionData = {
            promiseId: args.promiseId,
            errorForCallback: errorForCallback.toString()
          };
          panel.port.emit('emitRejected', rejectionData);
        }
      );
    }.bind(this);

    panel.port.on(message, function(args) {
      promiseEmitHandler(args);
    }.bind(this));
  };

  function post(data) {
    return xhr.frontedPost(data.data, data.externalDomain,
        data.cloudfrontDomain, data.cloudfrontPath);
  };

  // Ensure a fulfill or reject message will be sent back to the panel
  // when required by registering messages that initiate async behaviour.
  onPromiseEmit('frontedPost', post);

  /* Allow any pages in the addon to send messages to the UI or the core */
  pagemod.PageMod({
    include: self.data.url('*'),
    contentScriptFile: self.data.url('scripts/content-proxy.js'),
    onAttach: function(worker) {
      worker.port.on('update', function(data) {
        panel.port.emit(uproxy_core_api.Update[data.update], data.data);
      });

      worker.port.on('command', function(data) {
        freedom.emit(uproxy_core_api.Command[data.command], data.data);
      });

      // If we receive a getLogs message from a webpage (specifically
      // view-logs.html), make a call to get logs from the core, and intercept
      // the returned value when it is being passed to the UI with a
      // COMMAND_FULFILLED update.
      worker.port.on('getLogs', function(data) {
        freedom.emit(uproxy_core_api.Command.GET_LOGS, {data: data.data, promiseId: -1});
        var forwardLogsToContentScript = function(data) {
          if (data['command'] == uproxy_core_api.Command.GET_LOGS) {
            // Forward logs to content-proxy.js
            worker.port.emit('message', {
              logs: true,
              data: data.argsForCallback
            });
            freedom.off(uproxy_core_api.Update.COMMAND_FULFILLED,
              forwardLogsToContentScript);
          }
        };
        freedom.on(uproxy_core_api.Update.COMMAND_FULFILLED, forwardLogsToContentScript);
      });

      worker.port.on('showPanel', function(data) {
        panel.show({
          position: button
        });
      });
    }
  });
}

exports.setUpConnection = setUpConnection
<|endoftext|>