<commit_before>/* global require, process, Buffer, module */
'use strict';

var fs = require("fs");
var path = require("path");
var join = path.join;
var crypto = require("crypto");
var RSA = require("node-rsa");
var archiver = require("archiver");
var Promise = require("es6-promise").Promise;
var resolve = require("./resolver.js");

function ChromeExtension(attrs) {
  if ((this instanceof ChromeExtension) !== true) {
    return new ChromeExtension(attrs);
  }

  /*
   Defaults
   */
  this.appId = null;

  this.rootDirectory = '';

  this.publicKey = null;

  this.privateKey = null;

  this.codebase = null;

  this.path = null;

  this.src = '**';

  /*
  Copying attributes
   */
  for (var name in attrs) {
    this[name] = attrs[name];
  }

  this.loaded = false;
}

ChromeExtension.prototype = {

  /**
   * Packs the content of the extension in a crx file.
   *
   * @param {Buffer=} contentsBuffer
   * @returns {Promise}
   * @example
   *
   * crx.pack().then(function(crxContent){
   *  // do something with the crxContent binary data
   * });
   *
   */
  pack: function (contentsBuffer) {
    if (!this.loaded) {
      return this.load().then(this.pack.bind(this, contentsBuffer));
    }

    var selfie = this;
    var packP = [
      this.generatePublicKey(),
      contentsBuffer || selfie.loadContents()
    ];

    return Promise.all(packP).then(function(outputs){
      var publicKey = outputs[0];
      var contents = outputs[1];

      selfie.publicKey = publicKey;

      var signature = selfie.generateSignature(contents);

      return selfie.generatePackage(signature, publicKey, contents);
    });
  },

  /**
   * Loads extension manifest and copies its content to a workable path.
   *
   * @param {string=} path
   * @returns {Promise}
   */
  load: function (path) {
    var selfie = this;

    return resolve(path || selfie.rootDirectory)
      .then(function(metadata){
        selfie.path = metadata.path;
        selfie.src = metadata.src;

        var manifestPath = join(selfie.path, 'manifest.json')
        delete require.cache[manifestPath];

        selfie.manifest = require(manifestPath);
        selfie.loaded = true;

        return selfie;
      });
  },

  /**
   * Writes data into the extension workable directory.
   *
   * @deprecated
   * @param {string} path
   * @param {*} data
   * @returns {Promise}
   */
  writeFile: function (path, data) {
    var absPath = join(this.path, path);

    /* istanbul ignore next */
    return new Promise(function(resolve, reject){
      fs.writeFile(absPath, data, function (err) {
        if (err) {
          return reject(err);
        }

        resolve();
      });
    });
  },

  /**
   * Generates a public key.
   *
   * BC BREAK `this.publicKey` is not stored anymore (since 1.0.0)
   * BC BREAK callback parameter has been removed in favor to the promise interface.
   *
   * @returns {Promise} Resolves to {Buffer} containing the public key
   * @example
   *
   * crx.generatePublicKey(function(publicKey){
   *   // do something with publicKey
   * });
   */
  generatePublicKey: function () {
    var privateKey = this.privateKey;

    return new Promise(function(resolve, reject){
      if (!privateKey) {
        return reject('Impossible to generate a public key: privateKey option has not been defined or is empty.');
      }

      var key = new RSA(privateKey);

      resolve(key.exportKey('pkcs8-public-der'));
    });
  },

  /**
   * Generates a SHA1 package signature.
   *
   * BC BREAK `this.signature` is not stored anymore (since 1.0.0)
   *
   * @param {Buffer} contents
   * @returns {Buffer}
   */
  generateSignature: function (contents) {
    return new Buffer(
      crypto
        .createSign("sha1")
        .update(contents)
        .sign(this.privateKey),
      "binary"
    );
  },

  /**
   *
   * BC BREAK `this.contents` is not stored anymore (since 1.0.0)
   *
   * @returns {Promise}
   */
  loadContents: function () {
    var selfie = this;

    return new Promise(function(resolve, reject){
      var archive = archiver('zip');
      var contents = new Buffer('');

      if (!selfie.loaded) {
	      throw new Error('crx.load needs to be called first in order to prepare the workspace.');
      }

      archive.on('error', reject);

      /*
        TODO: Remove in v4.
        It will be better to resolve an archive object
        rather than fitting everything in memory.

        @see https://github.com/oncletom/crx/issues/61
      */
      archive.on('data', function (buf) {
        contents = Buffer.concat([contents, buf]);
      });

      archive.on('finish', function () {
        resolve(contents);
      });

      archive
        .glob(selfie.src, {
          cwd: selfie.path,
          matchBase: true,
          ignore: ['*.pem', '.git', '*.crx']
        })
        .finalize();
    });
  },

  /**
   * Generates and returns a signed package from extension content.
   *
   * BC BREAK `this.package` is not stored anymore (since 1.0.0)
   *
   * @param {Buffer} signature
   * @param {Buffer} publicKey
   * @param {Buffer} contents
   * @returns {Buffer}
   */
  generatePackage: function (signature, publicKey, contents) {
    var keyLength = publicKey.length;
    var sigLength = signature.length;
    var zipLength = contents.length;
    var length = 16 + keyLength + sigLength + zipLength;

    var crx = new Buffer(length);

    crx.write("Cr24" + new Array(13).join("\x00"), "binary");

    crx[4] = 2;
    crx.writeUInt32LE(keyLength, 8);
    crx.writeUInt32LE(sigLength, 12);

    publicKey.copy(crx, 16);
    signature.copy(crx, 16 + keyLength);
    contents.copy(crx, 16 + keyLength + sigLength);

    return crx;
  },

  /**
   * Generates an appId from the publicKey.
   * Public key has to be set for this to work, otherwise an error is thrown.
   *
   * BC BREAK `this.appId` is not stored anymore (since 1.0.0)
   * BC BREAK introduced `publicKey` parameter as it is not stored any more since 2.0.0
   *
   * @param {Buffer|string} [publicKey] the public key to use to generate the app ID
   * @returns {string}
   */
  generateAppId: function (publicKey) {
    publicKey = publicKey || this.publicKey;
    if (typeof publicKey !== 'string' && !(publicKey instanceof Buffer)) {
      throw new Error('Public key is neither set, nor given');
    }
    return crypto
      .createHash("sha256")
      .update(publicKey)
      .digest("hex")
      .slice(0, 32)
      .replace(/./g, function (x) {
        return (parseInt(x, 16) + 10).toString(26);
      });
  },

  /**
   * Generates an updateXML file from the extension content.
   *
   * BC BREAK `this.updateXML` is not stored anymore (since 1.0.0)
   *
   * @returns {Buffer}
   */
  generateUpdateXML: function () {
    if (!this.codebase) {
      throw new Error("No URL provided for update.xml.");
    }

    return new Buffer(
      "<?xml version='1.0' encoding='UTF-8'?>\n" +
      "<gupdate xmlns='http://www.google.com/update2/response' protocol='2.0'>\n" +
      "  <app appid='" + (this.appId || this.generateAppId()) + "'>\n" +
      "    <updatecheck codebase='" + this.codebase + "' version='" + this.manifest.version + "' />\n" +
      "  </app>\n" +
      "</gupdate>"
    );
  }
};

module.exports = ChromeExtension;
<commit_msg>Fix extension ID calculation from path<commit_after>/* global require, process, Buffer, module */
'use strict';

var fs = require("fs");
var path = require("path");
var join = path.join;
var crypto = require("crypto");
var RSA = require("node-rsa");
var archiver = require("archiver");
var Promise = require("es6-promise").Promise;
var resolve = require("./resolver.js");

function ChromeExtension(attrs) {
  if ((this instanceof ChromeExtension) !== true) {
    return new ChromeExtension(attrs);
  }

  /*
   Defaults
   */
  this.appId = null;

  this.rootDirectory = '';

  this.publicKey = null;

  this.privateKey = null;

  this.codebase = null;

  this.path = null;

  this.src = '**';

  /*
  Copying attributes
   */
  for (var name in attrs) {
    this[name] = attrs[name];
  }

  this.loaded = false;
}

ChromeExtension.prototype = {

  /**
   * Packs the content of the extension in a crx file.
   *
   * @param {Buffer=} contentsBuffer
   * @returns {Promise}
   * @example
   *
   * crx.pack().then(function(crxContent){
   *  // do something with the crxContent binary data
   * });
   *
   */
  pack: function (contentsBuffer) {
    if (!this.loaded) {
      return this.load().then(this.pack.bind(this, contentsBuffer));
    }

    var selfie = this;
    var packP = [
      this.generatePublicKey(),
      contentsBuffer || selfie.loadContents()
    ];

    return Promise.all(packP).then(function(outputs){
      var publicKey = outputs[0];
      var contents = outputs[1];

      selfie.publicKey = publicKey;

      var signature = selfie.generateSignature(contents);

      return selfie.generatePackage(signature, publicKey, contents);
    });
  },

  /**
   * Loads extension manifest and copies its content to a workable path.
   *
   * @param {string=} path
   * @returns {Promise}
   */
  load: function (path) {
    var selfie = this;

    return resolve(path || selfie.rootDirectory)
      .then(function(metadata){
        selfie.path = metadata.path;
        selfie.src = metadata.src;

        var manifestPath = join(selfie.path, 'manifest.json')
        delete require.cache[manifestPath];

        selfie.manifest = require(manifestPath);
        selfie.loaded = true;

        return selfie;
      });
  },

  /**
   * Writes data into the extension workable directory.
   *
   * @deprecated
   * @param {string} path
   * @param {*} data
   * @returns {Promise}
   */
  writeFile: function (path, data) {
    var absPath = join(this.path, path);

    /* istanbul ignore next */
    return new Promise(function(resolve, reject){
      fs.writeFile(absPath, data, function (err) {
        if (err) {
          return reject(err);
        }

        resolve();
      });
    });
  },

  /**
   * Generates a public key.
   *
   * BC BREAK `this.publicKey` is not stored anymore (since 1.0.0)
   * BC BREAK callback parameter has been removed in favor to the promise interface.
   *
   * @returns {Promise} Resolves to {Buffer} containing the public key
   * @example
   *
   * crx.generatePublicKey(function(publicKey){
   *   // do something with publicKey
   * });
   */
  generatePublicKey: function () {
    var privateKey = this.privateKey;

    return new Promise(function(resolve, reject){
      if (!privateKey) {
        return reject('Impossible to generate a public key: privateKey option has not been defined or is empty.');
      }

      var key = new RSA(privateKey);

      resolve(key.exportKey('pkcs8-public-der'));
    });
  },

  /**
   * Generates a SHA1 package signature.
   *
   * BC BREAK `this.signature` is not stored anymore (since 1.0.0)
   *
   * @param {Buffer} contents
   * @returns {Buffer}
   */
  generateSignature: function (contents) {
    return new Buffer(
      crypto
        .createSign("sha1")
        .update(contents)
        .sign(this.privateKey),
      "binary"
    );
  },

  /**
   *
   * BC BREAK `this.contents` is not stored anymore (since 1.0.0)
   *
   * @returns {Promise}
   */
  loadContents: function () {
    var selfie = this;

    return new Promise(function(resolve, reject){
      var archive = archiver('zip');
      var contents = new Buffer('');

      if (!selfie.loaded) {
	      throw new Error('crx.load needs to be called first in order to prepare the workspace.');
      }

      archive.on('error', reject);

      /*
        TODO: Remove in v4.
        It will be better to resolve an archive object
        rather than fitting everything in memory.

        @see https://github.com/oncletom/crx/issues/61
      */
      archive.on('data', function (buf) {
        contents = Buffer.concat([contents, buf]);
      });

      archive.on('finish', function () {
        resolve(contents);
      });

      archive
        .glob(selfie.src, {
          cwd: selfie.path,
          matchBase: true,
          ignore: ['*.pem', '.git', '*.crx']
        })
        .finalize();
    });
  },

  /**
   * Generates and returns a signed package from extension content.
   *
   * BC BREAK `this.package` is not stored anymore (since 1.0.0)
   *
   * @param {Buffer} signature
   * @param {Buffer} publicKey
   * @param {Buffer} contents
   * @returns {Buffer}
   */
  generatePackage: function (signature, publicKey, contents) {
    var keyLength = publicKey.length;
    var sigLength = signature.length;
    var zipLength = contents.length;
    var length = 16 + keyLength + sigLength + zipLength;

    var crx = new Buffer(length);

    crx.write("Cr24" + new Array(13).join("\x00"), "binary");

    crx[4] = 2;
    crx.writeUInt32LE(keyLength, 8);
    crx.writeUInt32LE(sigLength, 12);

    publicKey.copy(crx, 16);
    signature.copy(crx, 16 + keyLength);
    contents.copy(crx, 16 + keyLength + sigLength);

    return crx;
  },

  /**
   * Generates an appId from the publicKey.
   * Public key has to be set for this to work, otherwise an error is thrown.
   *
   * BC BREAK `this.appId` is not stored anymore (since 1.0.0)
   * BC BREAK introduced `publicKey` parameter as it is not stored any more since 2.0.0
   *
   * @param {Buffer|string} [publicKey] the public key to use to generate the app ID
   * @returns {string}
   */
  generateAppId: function (publicKey) {
    publicKey = publicKey || this.publicKey;
    if (typeof publicKey !== 'string' && !(publicKey instanceof Buffer)) {
      throw new Error('Public key is neither set, nor given');
    }
    return crypto
      .createHash("sha256")
      .update(Buffer.from(path.replace(/^[a-z]/g, function(match) { return match.toUpperCase() }), "utf16le"))
      .digest()
      .toString("hex")
      .split("")
      .map(x => String.fromCharCode(parseInt(x,16) + 'a'.charCodeAt(0)))
      .join("")
      .slice(0,32);
  },

  /**
   * Generates an updateXML file from the extension content.
   *
   * BC BREAK `this.updateXML` is not stored anymore (since 1.0.0)
   *
   * @returns {Buffer}
   */
  generateUpdateXML: function () {
    if (!this.codebase) {
      throw new Error("No URL provided for update.xml.");
    }

    return new Buffer(
      "<?xml version='1.0' encoding='UTF-8'?>\n" +
      "<gupdate xmlns='http://www.google.com/update2/response' protocol='2.0'>\n" +
      "  <app appid='" + (this.appId || this.generateAppId()) + "'>\n" +
      "    <updatecheck codebase='" + this.codebase + "' version='" + this.manifest.version + "' />\n" +
      "  </app>\n" +
      "</gupdate>"
    );
  }
};

module.exports = ChromeExtension;
<|endoftext|>


<commit_before>/* @flow */
/**
 *  Copyright (c) 2015, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  LICENSE file in the root directory of this source tree. An additional grant
 *  of patent rights can be found in the PATENTS file in the same directory.
 */

import invariant from '../jsutils/invariant';
import { GraphQLError } from '../error';
import { visit, getVisitFn } from '../language/visitor';
import * as Kind from '../language/kinds';
import type {
  Document,
  OperationDefinition,
  Variable,
  SelectionSet,
  FragmentSpread,
  FragmentDefinition,
} from '../language/ast';
import { GraphQLSchema } from '../type/schema';
import type {
  GraphQLInputType,
  GraphQLOutputType,
  GraphQLCompositeType,
  GraphQLFieldDefinition,
  GraphQLArgument
} from '../type/definition';
import type { GraphQLDirective } from '../type/directives';
import { TypeInfo } from '../utilities/TypeInfo';
import { specifiedRules } from './specifiedRules';

/**
 * Implements the "Validation" section of the spec.
 *
 * Validation runs synchronously, returning an array of encountered errors, or
 * an empty array if no errors were encountered and the document is valid.
 *
 * A list of specific validation rules may be provided. If not provided, the
 * default list of rules defined by the GraphQL specification will be used.
 *
 * Each validation rules is a function which returns a visitor
 * (see the language/visitor API). Visitor methods are expected to return
 * GraphQLErrors, or Arrays of GraphQLErrors when invalid.
 *
 * Visitors can also supply `visitSpreadFragments: true` which will alter the
 * behavior of the visitor to skip over top level defined fragments, and instead
 * visit those fragments at every point a spread is encountered.
 */
export function validate(
  schema: GraphQLSchema,
  ast: Document,
  rules?: Array<any>
): Array<GraphQLError> {
  invariant(schema, 'Must provide schema');
  invariant(ast, 'Must provide document');
  invariant(
    schema instanceof GraphQLSchema,
    'Schema must be an instance of GraphQLSchema. Also ensure that there are ' +
    'not multiple versions of GraphQL installed in your node_modules directory.'
  );
  var typeInfo = new TypeInfo(schema);
  return visitUsingRules(schema, typeInfo, ast, rules || specifiedRules);
}

/**
 * This uses a specialized visitor which runs multiple visitors in parallel,
 * while maintaining the visitor skip and break API.
 *
 * @internal
 */
export function visitUsingRules(
  schema: GraphQLSchema,
  typeInfo: TypeInfo,
  documentAST: Document,
  rules: Array<any>
): Array<GraphQLError> {
  var context = new ValidationContext(schema, documentAST, typeInfo);
  var errors = [];

  function visitInstance(ast, instance) {
    visit(ast, {
      enter(node, key) {
        // Collect type information about the current position in the AST.
        typeInfo.enter(node);

        // Do not visit top level fragment definitions if this instance will
        // visit those fragments inline because it
        // provided `visitSpreadFragments`.
        var result;
        if (
          node.kind === Kind.FRAGMENT_DEFINITION &&
          key !== undefined &&
          instance.visitSpreadFragments
        ) {
          return false;
        }

        // Get the visitor function from the validation instance, and if it
        // exists, call it with the visitor arguments.
        var enter = getVisitFn(instance, false, node.kind);
        result = enter ? enter.apply(instance, arguments) : undefined;

        // If the visitor returned an error, log it and do not visit any
        // deeper nodes.
        if (result && isError(result)) {
          append(errors, result);
          result = false;
        }

        // If any validation instances provide the flag `visitSpreadFragments`
        // and this node is a fragment spread, visit the fragment definition
        // from this point.
        if (result === undefined &&
            instance.visitSpreadFragments &&
            node.kind === Kind.FRAGMENT_SPREAD) {
          var fragment = context.getFragment(node.name.value);
          if (fragment) {
            visitInstance(fragment, instance);
          }
        }

        // If the result is "false", we're not visiting any descendent nodes,
        // but need to update typeInfo.
        if (result === false) {
          typeInfo.leave(node);
        }

        return result;
      },
      leave(node) {
        // Get the visitor function from the validation instance, and if it
        // exists, call it with the visitor arguments.
        var leave = getVisitFn(instance, true, node.kind);
        var result = leave ? leave.apply(instance, arguments) : undefined;

        // If the visitor returned an error, log it and do not visit any
        // deeper nodes.
        if (result && isError(result)) {
          append(errors, result);
          result = false;
        }

        // Update typeInfo.
        typeInfo.leave(node);

        return result;
      }
    });
  }

  // Visit the whole document with each instance of all provided rules.
  var instances = rules.map(rule => rule(context));
  instances.forEach(instance => {
    visitInstance(documentAST, instance);
  });

  return errors;
}

function isError(value) {
  return Array.isArray(value) ?
    value.every(item => item instanceof GraphQLError) :
    value instanceof GraphQLError;
}

function append(arr, items) {
  if (Array.isArray(items)) {
    arr.push.apply(arr, items);
  } else {
    arr.push(items);
  }
}

type HasSelectionSet = OperationDefinition | FragmentDefinition;
type VariableUsage = { node: Variable, type: ?GraphQLInputType };

/**
 * An instance of this class is passed as the "this" context to all validators,
 * allowing access to commonly useful contextual information from within a
 * validation rule.
 */
export class ValidationContext {
  _schema: GraphQLSchema;
  _ast: Document;
  _typeInfo: TypeInfo;
  _fragments: {[name: string]: FragmentDefinition};
  _fragmentSpreads: Map<HasSelectionSet, Array<FragmentSpread>>;
  _recursivelyReferencedFragments:
    Map<OperationDefinition, Array<FragmentDefinition>>;
  _variableUsages: Map<HasSelectionSet, Array<VariableUsage>>;
  _recursiveVariableUsages: Map<OperationDefinition, Array<VariableUsage>>;

  constructor(schema: GraphQLSchema, ast: Document, typeInfo: TypeInfo) {
    this._schema = schema;
    this._ast = ast;
    this._typeInfo = typeInfo;
    this._fragmentSpreads = new Map();
    this._recursivelyReferencedFragments = new Map();
    this._variableUsages = new Map();
    this._recursiveVariableUsages = new Map();
  }

  getSchema(): GraphQLSchema {
    return this._schema;
  }

  getDocument(): Document {
    return this._ast;
  }

  getFragment(name: string): ?FragmentDefinition {
    var fragments = this._fragments;
    if (!fragments) {
      this._fragments = fragments =
        this.getDocument().definitions.reduce((frags, statement) => {
          if (statement.kind === Kind.FRAGMENT_DEFINITION) {
            frags[statement.name.value] = statement;
          }
          return frags;
        }, {});
    }
    return fragments[name];
  }

  getFragmentSpreads(node: HasSelectionSet): Array<FragmentSpread> {
    let spreads = this._fragmentSpreads.get(node);
    if (!spreads) {
      spreads = [];
      gatherSpreads(spreads, node.selectionSet);
      this._fragmentSpreads.set(node, spreads);
    }
    return spreads;
  }

  getRecursivelyReferencedFragments(
    operation: OperationDefinition
  ): Array<FragmentDefinition> {
    let fragments = this._recursivelyReferencedFragments.get(operation);
    if (!fragments) {
      fragments = [];
      const collectedNames = Object.create(null);
      const nodesToVisit: Array<HasSelectionSet> = [ operation ];
      while (nodesToVisit.length !== 0) {
        const node = nodesToVisit.pop();
        const spreads = this.getFragmentSpreads(node);
        for (let i = 0; i < spreads.length; i++) {
          const fragName = spreads[i].name.value;
          if (collectedNames[fragName] !== true) {
            collectedNames[fragName] = true;
            const fragment = this.getFragment(fragName);
            if (fragment) {
              fragments.push(fragment);
              nodesToVisit.push(fragment);
            }
          }
        }
      }
      this._recursivelyReferencedFragments.set(operation, fragments);
    }
    return fragments;
  }

  getVariableUsages(node: HasSelectionSet): Array<VariableUsage> {
    let usages = this._variableUsages.get(node);
    if (!usages) {
      usages = [];
      const typeInfo = new TypeInfo(this._schema);
      visit(node, {
        enter(subnode) {
          typeInfo.enter(subnode);
          if (subnode.kind === Kind.VARIABLE_DEFINITION) {
            return false;
          } else if (subnode.kind === Kind.VARIABLE) {
            usages.push({ node: subnode, type: typeInfo.getInputType() });
          }
        },
        leave(subnode) {
          typeInfo.leave(subnode);
        }
      });
      this._variableUsages.set(node, usages);
    }
    return usages;
  }

  getRecursiveVariableUsages(
    operation: OperationDefinition
  ): Array<VariableUsage> {
    let usages = this._recursiveVariableUsages.get(operation);
    if (!usages) {
      usages = this.getVariableUsages(operation);
      const fragments = this.getRecursivelyReferencedFragments(operation);
      for (let i = 0; i < fragments.length; i++) {
        Array.prototype.push.apply(
          usages,
          this.getVariableUsages(fragments[i])
        );
      }
      this._recursiveVariableUsages.set(operation, usages);
    }
    return usages;
  }

  getType(): ?GraphQLOutputType {
    return this._typeInfo.getType();
  }

  getParentType(): ?GraphQLCompositeType {
    return this._typeInfo.getParentType();
  }

  getInputType(): ?GraphQLInputType {
    return this._typeInfo.getInputType();
  }

  getFieldDef(): ?GraphQLFieldDefinition {
    return this._typeInfo.getFieldDef();
  }

  getDirective(): ?GraphQLDirective {
    return this._typeInfo.getDirective();
  }

  getArgument(): ?GraphQLArgument {
    return this._typeInfo.getArgument();
  }
}

/**
 * Given a selection set, gather all the named spreads defined within.
 */
function gatherSpreads(spreads: Array<FragmentSpread>, node: SelectionSet) {
  for (let i = 0; i < node.selections.length; i++) {
    const selection = node.selections[i];
    if (selection.kind === Kind.FRAGMENT_SPREAD) {
      spreads.push(selection);
    } else if (selection.selectionSet) {
      gatherSpreads(spreads, selection.selectionSet);
    }
  }
}
<commit_msg>[Validation] Unwrap recursion for collecting fragment spreads<commit_after>/* @flow */
/**
 *  Copyright (c) 2015, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  LICENSE file in the root directory of this source tree. An additional grant
 *  of patent rights can be found in the PATENTS file in the same directory.
 */

import invariant from '../jsutils/invariant';
import { GraphQLError } from '../error';
import { visit, getVisitFn } from '../language/visitor';
import * as Kind from '../language/kinds';
import type {
  Document,
  OperationDefinition,
  Variable,
  SelectionSet,
  FragmentSpread,
  FragmentDefinition,
} from '../language/ast';
import { GraphQLSchema } from '../type/schema';
import type {
  GraphQLInputType,
  GraphQLOutputType,
  GraphQLCompositeType,
  GraphQLFieldDefinition,
  GraphQLArgument
} from '../type/definition';
import type { GraphQLDirective } from '../type/directives';
import { TypeInfo } from '../utilities/TypeInfo';
import { specifiedRules } from './specifiedRules';

/**
 * Implements the "Validation" section of the spec.
 *
 * Validation runs synchronously, returning an array of encountered errors, or
 * an empty array if no errors were encountered and the document is valid.
 *
 * A list of specific validation rules may be provided. If not provided, the
 * default list of rules defined by the GraphQL specification will be used.
 *
 * Each validation rules is a function which returns a visitor
 * (see the language/visitor API). Visitor methods are expected to return
 * GraphQLErrors, or Arrays of GraphQLErrors when invalid.
 *
 * Visitors can also supply `visitSpreadFragments: true` which will alter the
 * behavior of the visitor to skip over top level defined fragments, and instead
 * visit those fragments at every point a spread is encountered.
 */
export function validate(
  schema: GraphQLSchema,
  ast: Document,
  rules?: Array<any>
): Array<GraphQLError> {
  invariant(schema, 'Must provide schema');
  invariant(ast, 'Must provide document');
  invariant(
    schema instanceof GraphQLSchema,
    'Schema must be an instance of GraphQLSchema. Also ensure that there are ' +
    'not multiple versions of GraphQL installed in your node_modules directory.'
  );
  var typeInfo = new TypeInfo(schema);
  return visitUsingRules(schema, typeInfo, ast, rules || specifiedRules);
}

/**
 * This uses a specialized visitor which runs multiple visitors in parallel,
 * while maintaining the visitor skip and break API.
 *
 * @internal
 */
export function visitUsingRules(
  schema: GraphQLSchema,
  typeInfo: TypeInfo,
  documentAST: Document,
  rules: Array<any>
): Array<GraphQLError> {
  var context = new ValidationContext(schema, documentAST, typeInfo);
  var errors = [];

  function visitInstance(ast, instance) {
    visit(ast, {
      enter(node, key) {
        // Collect type information about the current position in the AST.
        typeInfo.enter(node);

        // Do not visit top level fragment definitions if this instance will
        // visit those fragments inline because it
        // provided `visitSpreadFragments`.
        var result;
        if (
          node.kind === Kind.FRAGMENT_DEFINITION &&
          key !== undefined &&
          instance.visitSpreadFragments
        ) {
          return false;
        }

        // Get the visitor function from the validation instance, and if it
        // exists, call it with the visitor arguments.
        var enter = getVisitFn(instance, false, node.kind);
        result = enter ? enter.apply(instance, arguments) : undefined;

        // If the visitor returned an error, log it and do not visit any
        // deeper nodes.
        if (result && isError(result)) {
          append(errors, result);
          result = false;
        }

        // If any validation instances provide the flag `visitSpreadFragments`
        // and this node is a fragment spread, visit the fragment definition
        // from this point.
        if (result === undefined &&
            instance.visitSpreadFragments &&
            node.kind === Kind.FRAGMENT_SPREAD) {
          var fragment = context.getFragment(node.name.value);
          if (fragment) {
            visitInstance(fragment, instance);
          }
        }

        // If the result is "false", we're not visiting any descendent nodes,
        // but need to update typeInfo.
        if (result === false) {
          typeInfo.leave(node);
        }

        return result;
      },
      leave(node) {
        // Get the visitor function from the validation instance, and if it
        // exists, call it with the visitor arguments.
        var leave = getVisitFn(instance, true, node.kind);
        var result = leave ? leave.apply(instance, arguments) : undefined;

        // If the visitor returned an error, log it and do not visit any
        // deeper nodes.
        if (result && isError(result)) {
          append(errors, result);
          result = false;
        }

        // Update typeInfo.
        typeInfo.leave(node);

        return result;
      }
    });
  }

  // Visit the whole document with each instance of all provided rules.
  var instances = rules.map(rule => rule(context));
  instances.forEach(instance => {
    visitInstance(documentAST, instance);
  });

  return errors;
}

function isError(value) {
  return Array.isArray(value) ?
    value.every(item => item instanceof GraphQLError) :
    value instanceof GraphQLError;
}

function append(arr, items) {
  if (Array.isArray(items)) {
    arr.push.apply(arr, items);
  } else {
    arr.push(items);
  }
}

type HasSelectionSet = OperationDefinition | FragmentDefinition;
type VariableUsage = { node: Variable, type: ?GraphQLInputType };

/**
 * An instance of this class is passed as the "this" context to all validators,
 * allowing access to commonly useful contextual information from within a
 * validation rule.
 */
export class ValidationContext {
  _schema: GraphQLSchema;
  _ast: Document;
  _typeInfo: TypeInfo;
  _fragments: {[name: string]: FragmentDefinition};
  _fragmentSpreads: Map<HasSelectionSet, Array<FragmentSpread>>;
  _recursivelyReferencedFragments:
    Map<OperationDefinition, Array<FragmentDefinition>>;
  _variableUsages: Map<HasSelectionSet, Array<VariableUsage>>;
  _recursiveVariableUsages: Map<OperationDefinition, Array<VariableUsage>>;

  constructor(schema: GraphQLSchema, ast: Document, typeInfo: TypeInfo) {
    this._schema = schema;
    this._ast = ast;
    this._typeInfo = typeInfo;
    this._fragmentSpreads = new Map();
    this._recursivelyReferencedFragments = new Map();
    this._variableUsages = new Map();
    this._recursiveVariableUsages = new Map();
  }

  getSchema(): GraphQLSchema {
    return this._schema;
  }

  getDocument(): Document {
    return this._ast;
  }

  getFragment(name: string): ?FragmentDefinition {
    var fragments = this._fragments;
    if (!fragments) {
      this._fragments = fragments =
        this.getDocument().definitions.reduce((frags, statement) => {
          if (statement.kind === Kind.FRAGMENT_DEFINITION) {
            frags[statement.name.value] = statement;
          }
          return frags;
        }, {});
    }
    return fragments[name];
  }

  getFragmentSpreads(node: HasSelectionSet): Array<FragmentSpread> {
    let spreads = this._fragmentSpreads.get(node);
    if (!spreads) {
      spreads = [];
      const setsToVisit: Array<SelectionSet> = [ node.selectionSet ];
      while (setsToVisit.length !== 0) {
        const set = setsToVisit.pop();
        for (let i = 0; i < set.selections.length; i++) {
          const selection = set.selections[i];
          if (selection.kind === Kind.FRAGMENT_SPREAD) {
            spreads.push(selection);
          } else if (selection.selectionSet) {
            setsToVisit.push(selection.selectionSet);
          }
        }
      }
      this._fragmentSpreads.set(node, spreads);
    }
    return spreads;
  }

  getRecursivelyReferencedFragments(
    operation: OperationDefinition
  ): Array<FragmentDefinition> {
    let fragments = this._recursivelyReferencedFragments.get(operation);
    if (!fragments) {
      fragments = [];
      const collectedNames = Object.create(null);
      const nodesToVisit: Array<HasSelectionSet> = [ operation ];
      while (nodesToVisit.length !== 0) {
        const node = nodesToVisit.pop();
        const spreads = this.getFragmentSpreads(node);
        for (let i = 0; i < spreads.length; i++) {
          const fragName = spreads[i].name.value;
          if (collectedNames[fragName] !== true) {
            collectedNames[fragName] = true;
            const fragment = this.getFragment(fragName);
            if (fragment) {
              fragments.push(fragment);
              nodesToVisit.push(fragment);
            }
          }
        }
      }
      this._recursivelyReferencedFragments.set(operation, fragments);
    }
    return fragments;
  }

  getVariableUsages(node: HasSelectionSet): Array<VariableUsage> {
    let usages = this._variableUsages.get(node);
    if (!usages) {
      usages = [];
      const typeInfo = new TypeInfo(this._schema);
      visit(node, {
        enter(subnode) {
          typeInfo.enter(subnode);
          if (subnode.kind === Kind.VARIABLE_DEFINITION) {
            return false;
          } else if (subnode.kind === Kind.VARIABLE) {
            usages.push({ node: subnode, type: typeInfo.getInputType() });
          }
        },
        leave(subnode) {
          typeInfo.leave(subnode);
        }
      });
      this._variableUsages.set(node, usages);
    }
    return usages;
  }

  getRecursiveVariableUsages(
    operation: OperationDefinition
  ): Array<VariableUsage> {
    let usages = this._recursiveVariableUsages.get(operation);
    if (!usages) {
      usages = this.getVariableUsages(operation);
      const fragments = this.getRecursivelyReferencedFragments(operation);
      for (let i = 0; i < fragments.length; i++) {
        Array.prototype.push.apply(
          usages,
          this.getVariableUsages(fragments[i])
        );
      }
      this._recursiveVariableUsages.set(operation, usages);
    }
    return usages;
  }

  getType(): ?GraphQLOutputType {
    return this._typeInfo.getType();
  }

  getParentType(): ?GraphQLCompositeType {
    return this._typeInfo.getParentType();
  }

  getInputType(): ?GraphQLInputType {
    return this._typeInfo.getInputType();
  }

  getFieldDef(): ?GraphQLFieldDefinition {
    return this._typeInfo.getFieldDef();
  }

  getDirective(): ?GraphQLDirective {
    return this._typeInfo.getDirective();
  }

  getArgument(): ?GraphQLArgument {
    return this._typeInfo.getArgument();
  }
}
<|endoftext|>


<commit_before>/*
 * MelonJS Game Engine
 * Copyright (C) 2011 - 2014 Olivier Biot, Jason Oster, Aaron McLeod
 * http://www.melonjs.org
 */
(function () {

    // Handy constants
    var VERTEX_SIZE = 2;
    var COLOR_SIZE = 4;
    var TEXTURE_SIZE = 1;
    var REGION_SIZE = 2;

    var ELEMENT_SIZE = VERTEX_SIZE + COLOR_SIZE + TEXTURE_SIZE + REGION_SIZE;
    var ELEMENT_OFFSET = ELEMENT_SIZE * Float32Array.BYTES_PER_ELEMENT;

    var VERTEX_ELEMENT = 0;
    var COLOR_ELEMENT = VERTEX_ELEMENT + VERTEX_SIZE;
    var TEXTURE_ELEMENT = COLOR_ELEMENT + COLOR_SIZE;
    var REGION_ELEMENT = TEXTURE_ELEMENT + TEXTURE_SIZE;

    var VERTEX_OFFSET = VERTEX_ELEMENT * Float32Array.BYTES_PER_ELEMENT;
    var COLOR_OFFSET = COLOR_ELEMENT * Float32Array.BYTES_PER_ELEMENT;
    var TEXTURE_OFFSET = TEXTURE_ELEMENT * Float32Array.BYTES_PER_ELEMENT;
    var REGION_OFFSET = REGION_ELEMENT * Float32Array.BYTES_PER_ELEMENT;

    var ELEMENTS_PER_RECT = 4;
    var INDICES_PER_RECT = 6;

    var MAX_LENGTH = 16000;

    /**
     * A WebGL texture batcher object. This class handles all of the WebGL state

     * Pushes texture regions into WebGL buffers, automatically flushes to GPU
     * @extends Object
     * @namespace me.WebGLRenderer.Batcher
     * @memberOf me
     * @constructor
     * @param {WebGLContext} gl Destination WebGL Context
     * @param {me.Matrix2d} matrix Global transformation matrix
     * @param {me.Color} color Global color
     */
    me.WebGLRenderer.Batcher = Object.extend(
    /** @scope me.WebGLRenderer.Batcher.prototype */
    {
        /**
         * @ignore
         */
        init : function (gl, matrix, color) {
            /**
             * The number of rectangles held in the batch
             * @name length
             * @memberOf me.WebGLRenderer.Batcher
             * @type Number
             * @readonly
             */
            this.length = 0;

            // Hash map of texture units
            this.units = [];

            // WebGL context
            this.gl = gl;

            // Global transformation matrix
            this.matrix = matrix;

            // Global color
            this.color = color;

            // Uniform projection matrix
            this.uMatrix = new me.Matrix2d();

            // Rectangle buffer
            this.buffer = new Float32Array(ELEMENT_SIZE * ELEMENTS_PER_RECT);

            // Load and create shader program
            this.shader = this.createShader();

            // Stream buffer
            this.sb = gl.createBuffer();
            gl.bindBuffer(gl.ARRAY_BUFFER, this.sb);
            gl.bufferData(gl.ARRAY_BUFFER, MAX_LENGTH * ELEMENT_OFFSET, gl.DYNAMIC_DRAW);
            this.sbIndex = 0;

            // Index buffer
            this.ib = gl.createBuffer();
            gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.ib);
            gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.createIB(), gl.STATIC_DRAW);

            // Bind attribute pointers
            gl.vertexAttribPointer(
                this.shader.attributes.aVertex,
                VERTEX_SIZE,
                gl.FLOAT,
                false,
                ELEMENT_OFFSET,
                VERTEX_OFFSET
            );
            gl.vertexAttribPointer(
                this.shader.attributes.aColor,
                COLOR_SIZE,
                gl.FLOAT,
                false,
                ELEMENT_OFFSET,
                COLOR_OFFSET
            );
            gl.vertexAttribPointer(
                this.shader.attributes.aTexture,
                TEXTURE_SIZE,
                gl.FLOAT,
                false,
                ELEMENT_OFFSET,
                TEXTURE_OFFSET
            );
            gl.vertexAttribPointer(
                this.shader.attributes.aRegion,
                REGION_SIZE,
                gl.FLOAT,
                false,
                ELEMENT_OFFSET,
                REGION_OFFSET
            );

            this.reset();
            this.setProjection(gl.canvas.width, gl.canvas.height);
        },

        /**
         * @ignore
         */
        createShader : function () {
            // WebGL shader program
            return me.video.shader.createShader(
                this.gl,
                [
                    "aVertex",
                    "aColor",
                    "aTexture",
                    "aRegion",
                ],
                {
                    "uMatrix"   : "mat3",
                    "uSampler"  : "sampler2D",
                }
            );
        },

        /**
         * Sets the projection matrix with the given size
         * @name setProjection
         * @memberOf me.WebGLRenderer.Batcher
         * @function
         * @param {Number} w WebGL Canvas width
         * @param {Number} h WebGL Canvas height
         */
        setProjection : function (w, h) {
            this.gl.viewport(0, 0, w, h);
            this.uMatrix.set(
                2 / w,  0,      0,
                0,      -2 / h, 0,
                -1,     1,      1
            );
            this.shader.uniforms.uMatrix = this.uMatrix.val;
        },

        /**
         * @ignore
         */
        uploadTexture : function (unit, texture, w, h, b) {
            if (!this.units[unit]) {
                this.units[unit] = true;
                this.shader.createTexture(this.gl, unit, texture.texture, w, h, b);
            }
        },

        /**
         * @ignore
         */
        reset : function () {
            // TODO
            this.sbIndex = 0;
            this.length = 0;

            var samplers = [];

            var units = this.gl.getParameter(this.gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS);
            for (var i = 0; i < units; i++) {
                this.units[i] = false;
                samplers[i] = i;
            }

            this.shader.uniforms.uSampler = samplers;
        },

        /**
         * @ignore
         */
        createIB : function () {
            var indices = [
                0, 1, 2,
                2, 1, 3
            ];

            // ~128KB index buffer
            var data = new Array(MAX_LENGTH * INDICES_PER_RECT);
            for (var i = 0; i < data.length; i++) {
                data[i] = indices[i % INDICES_PER_RECT] +
                    ~~(i / INDICES_PER_RECT) * ELEMENTS_PER_RECT;
            }

            return new Uint16Array(data);
        },

        /**
         * Add a texture region
         * @name add
         * @memberOf me.WebGLRenderer.Batcher
         * @function
         * @param {me.video.renderer.Texture} texture Source texture
         * @param {Number} sx Source x-coordinate
         * @param {Number} sy Source y-coordinate
         * @param {Number} sw Source width
         * @param {Number} sh Source height
         * @param {Number} x Destination x-coordinate
         * @param {Number} y Destination y-coordinate
         * @param {Number} w Destination width
         * @param {Number} h Destination height
         */
        add : function (texture, sx, sy, sw, sh, x, y, w, h) {
            if (this.length >= MAX_LENGTH) {
                this.flush();
            }

            // TODO: Replace the function signature with:
            // add(texture, region, x, y, w, h)
            // This can only be done after TextureAtlas is used on tilesets
            var region;
            if (arguments.length === 6) {
                h = x;
                w = sh;
                y = sw;
                x = sy;
                region = texture.getRegion(sx);
            }
            else {
                // TODO: Remove this cache lookup and the assignment in Texture
                var key = sx + "," + sy + "," + sw + "," + sh;
                region = texture.getRegion(key);
                if (typeof(region) === "undefined") {
                    // TODO: Require proper atlas regions instead of caching arbitrary regions
                    region = texture._insertRegion(key, sx, sy, sw, sh);
                }
            }

            var gl = this.gl;
            var m = this.matrix;

            // Upload the texture if necessary
            var unit = me.video.renderer.cache.getUnit(texture);
            this.uploadTexture(unit, texture);

            // Transform vertices
            var v0 = m.vectorMultiply(me.pool.pull("me.Vector2d", x, y));
            var v1 = m.vectorMultiply(me.pool.pull("me.Vector2d", x + w, y));
            var v2 = m.vectorMultiply(me.pool.pull("me.Vector2d", x, y + h));
            var v3 = m.vectorMultiply(me.pool.pull("me.Vector2d", x + w, y + h));

            // Fill vertex buffer
            // FIXME: Pack each vertex vector into single float
            this.buffer[VERTEX_ELEMENT + ELEMENT_SIZE * 0 + 0] = v0.x;
            this.buffer[VERTEX_ELEMENT + ELEMENT_SIZE * 0 + 1] = v0.y;
            this.buffer[VERTEX_ELEMENT + ELEMENT_SIZE * 1 + 0] = v1.x;
            this.buffer[VERTEX_ELEMENT + ELEMENT_SIZE * 1 + 1] = v1.y;
            this.buffer[VERTEX_ELEMENT + ELEMENT_SIZE * 2 + 0] = v2.x;
            this.buffer[VERTEX_ELEMENT + ELEMENT_SIZE * 2 + 1] = v2.y;
            this.buffer[VERTEX_ELEMENT + ELEMENT_SIZE * 3 + 0] = v3.x;
            this.buffer[VERTEX_ELEMENT + ELEMENT_SIZE * 3 + 1] = v3.y;

            // Push vertices back into pool
            me.pool.push(v0);
            me.pool.push(v1);
            me.pool.push(v2);
            me.pool.push(v3);

            // Fill color buffer
            // FIXME: Pack color vector into single float
            var color = this.color.toGL();
            this.buffer.set(color, COLOR_ELEMENT + ELEMENT_SIZE * 0);
            this.buffer.set(color, COLOR_ELEMENT + ELEMENT_SIZE * 1);
            this.buffer.set(color, COLOR_ELEMENT + ELEMENT_SIZE * 2);
            this.buffer.set(color, COLOR_ELEMENT + ELEMENT_SIZE * 3);

            // Fill texture index buffer
            // FIXME: Can the texture index be packed into another element?
            this.buffer[TEXTURE_ELEMENT + ELEMENT_SIZE * 0] =
            this.buffer[TEXTURE_ELEMENT + ELEMENT_SIZE * 1] =
            this.buffer[TEXTURE_ELEMENT + ELEMENT_SIZE * 2] =
            this.buffer[TEXTURE_ELEMENT + ELEMENT_SIZE * 3] = unit;

            // Fill texture coordinates buffer
            // FIXME: Pack each texture coordinate into single floats
            var stMap = region.stMap;
            this.buffer[REGION_ELEMENT + ELEMENT_SIZE * 0 + 0] = stMap[0];
            this.buffer[REGION_ELEMENT + ELEMENT_SIZE * 0 + 1] = stMap[1];
            this.buffer[REGION_ELEMENT + ELEMENT_SIZE * 1 + 0] = stMap[2];
            this.buffer[REGION_ELEMENT + ELEMENT_SIZE * 1 + 1] = stMap[1];
            this.buffer[REGION_ELEMENT + ELEMENT_SIZE * 2 + 0] = stMap[0];
            this.buffer[REGION_ELEMENT + ELEMENT_SIZE * 2 + 1] = stMap[3];
            this.buffer[REGION_ELEMENT + ELEMENT_SIZE * 3 + 0] = stMap[2];
            this.buffer[REGION_ELEMENT + ELEMENT_SIZE * 3 + 1] = stMap[3];

            // Copy data into stream buffer
            gl.bufferSubData(gl.ARRAY_BUFFER, this.sbIndex, this.buffer);

            this.sbIndex += ELEMENT_OFFSET * ELEMENTS_PER_RECT;
            this.length++;
        },

        /**
         * Flush batched texture operations to the GPU
         * @name flush
         * @memberOf me.WebGLRenderer.Batcher
         * @function
         */
        flush : function () {
            if (this.length) {
                var gl = this.gl;
                gl.drawElements(
                    gl.TRIANGLES,
                    this.length * ELEMENTS_PER_RECT * INDICES_PER_RECT,
                    gl.UNSIGNED_SHORT,
                    0
                );

                this.sbIndex = 0;
                this.length = 0;
            }
        }
    });
})();
<commit_msg>[#591] HUGE permformance win for WebGL<commit_after>/*
 * MelonJS Game Engine
 * Copyright (C) 2011 - 2014 Olivier Biot, Jason Oster, Aaron McLeod
 * http://www.melonjs.org
 */
(function () {

    // Handy constants
    var VERTEX_SIZE = 2;
    var COLOR_SIZE = 4;
    var TEXTURE_SIZE = 1;
    var REGION_SIZE = 2;

    var ELEMENT_SIZE = VERTEX_SIZE + COLOR_SIZE + TEXTURE_SIZE + REGION_SIZE;
    var ELEMENT_OFFSET = ELEMENT_SIZE * Float32Array.BYTES_PER_ELEMENT;

    var VERTEX_ELEMENT = 0;
    var COLOR_ELEMENT = VERTEX_ELEMENT + VERTEX_SIZE;
    var TEXTURE_ELEMENT = COLOR_ELEMENT + COLOR_SIZE;
    var REGION_ELEMENT = TEXTURE_ELEMENT + TEXTURE_SIZE;

    var VERTEX_OFFSET = VERTEX_ELEMENT * Float32Array.BYTES_PER_ELEMENT;
    var COLOR_OFFSET = COLOR_ELEMENT * Float32Array.BYTES_PER_ELEMENT;
    var TEXTURE_OFFSET = TEXTURE_ELEMENT * Float32Array.BYTES_PER_ELEMENT;
    var REGION_OFFSET = REGION_ELEMENT * Float32Array.BYTES_PER_ELEMENT;

    var ELEMENTS_PER_RECT = 4;
    var INDICES_PER_RECT = 6;

    var MAX_LENGTH = 16000;

    /**
     * A WebGL texture batcher object. This class handles all of the WebGL state

     * Pushes texture regions into WebGL buffers, automatically flushes to GPU
     * @extends Object
     * @namespace me.WebGLRenderer.Batcher
     * @memberOf me
     * @constructor
     * @param {WebGLContext} gl Destination WebGL Context
     * @param {me.Matrix2d} matrix Global transformation matrix
     * @param {me.Color} color Global color
     */
    me.WebGLRenderer.Batcher = Object.extend(
    /** @scope me.WebGLRenderer.Batcher.prototype */
    {
        /**
         * @ignore
         */
        init : function (gl, matrix, color) {
            /**
             * The number of rectangles held in the batch
             * @name length
             * @memberOf me.WebGLRenderer.Batcher
             * @type Number
             * @readonly
             */
            this.length = 0;

            // Hash map of texture units
            this.units = [];

            // WebGL context
            this.gl = gl;

            // Global transformation matrix
            this.matrix = matrix;

            // Global color
            this.color = color;

            // Uniform projection matrix
            this.uMatrix = new me.Matrix2d();

            // Rectangle buffer
            this.buffer = new Float32Array(ELEMENT_SIZE * ELEMENTS_PER_RECT);

            // Load and create shader program
            this.shader = this.createShader();

            // Stream buffer
            this.sb = gl.createBuffer();
            gl.bindBuffer(gl.ARRAY_BUFFER, this.sb);
            gl.bufferData(gl.ARRAY_BUFFER, MAX_LENGTH * ELEMENT_OFFSET, gl.DYNAMIC_DRAW);
            this.sbIndex = 0;

            // Index buffer
            this.ib = gl.createBuffer();
            gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.ib);
            gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.createIB(), gl.STATIC_DRAW);

            // Bind attribute pointers
            gl.vertexAttribPointer(
                this.shader.attributes.aVertex,
                VERTEX_SIZE,
                gl.FLOAT,
                false,
                ELEMENT_OFFSET,
                VERTEX_OFFSET
            );
            gl.vertexAttribPointer(
                this.shader.attributes.aColor,
                COLOR_SIZE,
                gl.FLOAT,
                false,
                ELEMENT_OFFSET,
                COLOR_OFFSET
            );
            gl.vertexAttribPointer(
                this.shader.attributes.aTexture,
                TEXTURE_SIZE,
                gl.FLOAT,
                false,
                ELEMENT_OFFSET,
                TEXTURE_OFFSET
            );
            gl.vertexAttribPointer(
                this.shader.attributes.aRegion,
                REGION_SIZE,
                gl.FLOAT,
                false,
                ELEMENT_OFFSET,
                REGION_OFFSET
            );

            this.reset();
            this.setProjection(gl.canvas.width, gl.canvas.height);
        },

        /**
         * @ignore
         */
        createShader : function () {
            // WebGL shader program
            return me.video.shader.createShader(
                this.gl,
                [
                    "aVertex",
                    "aColor",
                    "aTexture",
                    "aRegion",
                ],
                {
                    "uMatrix"   : "mat3",
                    "uSampler"  : "sampler2D",
                }
            );
        },

        /**
         * Sets the projection matrix with the given size
         * @name setProjection
         * @memberOf me.WebGLRenderer.Batcher
         * @function
         * @param {Number} w WebGL Canvas width
         * @param {Number} h WebGL Canvas height
         */
        setProjection : function (w, h) {
            this.gl.viewport(0, 0, w, h);
            this.uMatrix.set(
                2 / w,  0,      0,
                0,      -2 / h, 0,
                -1,     1,      1
            );
            this.shader.uniforms.uMatrix = this.uMatrix.val;
        },

        /**
         * @ignore
         */
        uploadTexture : function (unit, texture, w, h, b) {
            if (!this.units[unit]) {
                this.units[unit] = true;
                this.shader.createTexture(this.gl, unit, texture.texture, w, h, b);
            }
        },

        /**
         * @ignore
         */
        reset : function () {
            // TODO
            this.sbIndex = 0;
            this.length = 0;

            var samplers = [];

            var units = this.gl.getParameter(this.gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS);
            for (var i = 0; i < units; i++) {
                this.units[i] = false;
                samplers[i] = i;
            }

            this.shader.uniforms.uSampler = samplers;
        },

        /**
         * @ignore
         */
        createIB : function () {
            var indices = [
                0, 1, 2,
                2, 1, 3
            ];

            // ~128KB index buffer
            var data = new Array(MAX_LENGTH * INDICES_PER_RECT);
            for (var i = 0; i < data.length; i++) {
                data[i] = indices[i % INDICES_PER_RECT] +
                    ~~(i / INDICES_PER_RECT) * ELEMENTS_PER_RECT;
            }

            return new Uint16Array(data);
        },

        /**
         * Add a texture region
         * @name add
         * @memberOf me.WebGLRenderer.Batcher
         * @function
         * @param {me.video.renderer.Texture} texture Source texture
         * @param {Number} sx Source x-coordinate
         * @param {Number} sy Source y-coordinate
         * @param {Number} sw Source width
         * @param {Number} sh Source height
         * @param {Number} dx Destination x-coordinate
         * @param {Number} dy Destination y-coordinate
         * @param {Number} dw Destination width
         * @param {Number} dh Destination height
         */
        add : function (texture, sx, sy, sw, sh, dx, dy, dw, dh) {
            if (this.length >= MAX_LENGTH) {
                this.flush();
            }

            // TODO: Replace the function signature with:
            // add(texture, region, x, y, w, h)
            // This can only be done after TextureAtlas is used on tilesets
            var region,
                x = dx,
                y = dy,
                w = dw,
                h = dh;

            if (arguments.length === 6) {
                h = dx;
                w = sh;
                y = sw;
                x = sy;
                region = texture.getRegion(sx);
            }
            else {
                // TODO: Remove this cache lookup and the assignment in Texture
                var key = sx + "," + sy + "," + sw + "," + sh;
                region = texture.getRegion(key);
                if (typeof(region) === "undefined") {
                    // TODO: Require proper atlas regions instead of caching arbitrary regions
                    region = texture._insertRegion(key, sx, sy, sw, sh);
                }
            }

            var gl = this.gl;
            var m = this.matrix;

            // Upload the texture if necessary
            var unit = me.video.renderer.cache.getUnit(texture);
            this.uploadTexture(unit, texture);

            // Transform vertices
            var v0 = m.vectorMultiply(new me.Vector2d(x, y));
            var v1 = m.vectorMultiply(new me.Vector2d(x + w, y));
            var v2 = m.vectorMultiply(new me.Vector2d(x, y + h));
            var v3 = m.vectorMultiply(new me.Vector2d(x + w, y + h));

            // Fill vertex buffer
            // FIXME: Pack each vertex vector into single float
            this.buffer[VERTEX_ELEMENT + ELEMENT_SIZE * 0 + 0] = v0.x;
            this.buffer[VERTEX_ELEMENT + ELEMENT_SIZE * 0 + 1] = v0.y;
            this.buffer[VERTEX_ELEMENT + ELEMENT_SIZE * 1 + 0] = v1.x;
            this.buffer[VERTEX_ELEMENT + ELEMENT_SIZE * 1 + 1] = v1.y;
            this.buffer[VERTEX_ELEMENT + ELEMENT_SIZE * 2 + 0] = v2.x;
            this.buffer[VERTEX_ELEMENT + ELEMENT_SIZE * 2 + 1] = v2.y;
            this.buffer[VERTEX_ELEMENT + ELEMENT_SIZE * 3 + 0] = v3.x;
            this.buffer[VERTEX_ELEMENT + ELEMENT_SIZE * 3 + 1] = v3.y;

            // Fill color buffer
            // FIXME: Pack color vector into single float
            var color = this.color.toGL();
            this.buffer.set(color, COLOR_ELEMENT + ELEMENT_SIZE * 0);
            this.buffer.set(color, COLOR_ELEMENT + ELEMENT_SIZE * 1);
            this.buffer.set(color, COLOR_ELEMENT + ELEMENT_SIZE * 2);
            this.buffer.set(color, COLOR_ELEMENT + ELEMENT_SIZE * 3);

            // Fill texture index buffer
            // FIXME: Can the texture index be packed into another element?
            this.buffer[TEXTURE_ELEMENT + ELEMENT_SIZE * 0] =
            this.buffer[TEXTURE_ELEMENT + ELEMENT_SIZE * 1] =
            this.buffer[TEXTURE_ELEMENT + ELEMENT_SIZE * 2] =
            this.buffer[TEXTURE_ELEMENT + ELEMENT_SIZE * 3] = unit;

            // Fill texture coordinates buffer
            // FIXME: Pack each texture coordinate into single floats
            var stMap = region.stMap;
            this.buffer[REGION_ELEMENT + ELEMENT_SIZE * 0 + 0] = stMap[0];
            this.buffer[REGION_ELEMENT + ELEMENT_SIZE * 0 + 1] = stMap[1];
            this.buffer[REGION_ELEMENT + ELEMENT_SIZE * 1 + 0] = stMap[2];
            this.buffer[REGION_ELEMENT + ELEMENT_SIZE * 1 + 1] = stMap[1];
            this.buffer[REGION_ELEMENT + ELEMENT_SIZE * 2 + 0] = stMap[0];
            this.buffer[REGION_ELEMENT + ELEMENT_SIZE * 2 + 1] = stMap[3];
            this.buffer[REGION_ELEMENT + ELEMENT_SIZE * 3 + 0] = stMap[2];
            this.buffer[REGION_ELEMENT + ELEMENT_SIZE * 3 + 1] = stMap[3];

            // Copy data into stream buffer
            gl.bufferSubData(gl.ARRAY_BUFFER, this.sbIndex, this.buffer);

            this.sbIndex += ELEMENT_OFFSET * ELEMENTS_PER_RECT;
            this.length++;
        },

        /**
         * Flush batched texture operations to the GPU
         * @name flush
         * @memberOf me.WebGLRenderer.Batcher
         * @function
         */
        flush : function () {
            if (this.length) {
                var gl = this.gl;
                gl.drawElements(
                    gl.TRIANGLES,
                    this.length * ELEMENTS_PER_RECT * INDICES_PER_RECT,
                    gl.UNSIGNED_SHORT,
                    0
                );

                this.sbIndex = 0;
                this.length = 0;
            }
        }
    });
})();
<|endoftext|>


<commit_before>import React, {Component} from 'react';
import PropTypes from 'prop-types';
import {connect} from 'react-redux';
import {Link} from 'react-router-dom';
import {bindActionCreators} from 'redux';

import * as actions from 'reduxes/actions';

import IconButton from 'src/IconButton';
import IconAnchor from 'src/IconAnchor';

import 'sass/containers/app/navBar/brand/Brand.scss';

class Brand extends Component {

    constructor(props) {

        super(props);

        this.mousedownHandler = ::this.mousedownHandler;

    }

    mousedownHandler(e) {
        e.stopPropagation();
        this.props.toggleNavMenu();
    }

    render() {
        return (

                <IconButton className="menu-toggle-button"
                            iconCls="fas fa-bars"
                            onTouchTap={this.mousedownHandler}/>

                <Link className="brand-name"
                      to="/">

                    Alcedo-UI Examples
                </Link>

                <IconAnchor className="github-icon"
                            iconCls="fab fa-github"
                            href="https://github.com/alcedo-ui/alcedo-ui"/>

        );
    }
}

Brand.propTypes = {
    toggleNavMenu: PropTypes.func
};

export default connect(state => ({}), dispatch => bindActionCreators({
    toggleNavMenu: actions.toggleNavMenu
}, dispatch))(Brand);<commit_msg>update Brand<commit_after>import React, {Component} from 'react';
import PropTypes from 'prop-types';
import {connect} from 'react-redux';
import {Link} from 'react-router-dom';
import {bindActionCreators} from 'redux';

import * as actions from 'reduxes/actions';

import IconButton from 'src/IconButton';
import IconAnchor from 'src/IconAnchor';

import 'sass/containers/app/navBar/brand/Brand.scss';

class Brand extends Component {

    constructor(props) {
        super(props);
    }

    mousedownHandler = e => {
        e.stopPropagation();
        this.props.toggleNavMenu();
    };

    render() {
        return (

                <IconButton className="menu-toggle-button"
                            iconCls="fas fa-bars"
                            onTouchTap={this.mousedownHandler}/>

                <Link className="brand-name"
                      to="/">

                    Alcedo-UI Examples
                </Link>

                <IconAnchor className="github-icon"
                            iconCls="fab fa-github"
                            href="https://github.com/alcedo-ui/alcedo-ui"/>

        );
    }
}

Brand.propTypes = {
    toggleNavMenu: PropTypes.func
};

export default connect(state => ({}), dispatch => bindActionCreators({
    toggleNavMenu: actions.toggleNavMenu
}, dispatch))(Brand);<|endoftext|>


<commit_before>/**
 * Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 *  http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file 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.
 */

define(
    [
        'Magento_Checkout/js/model/quote',
        'Magento_Checkout/js/model/url-builder',
        'mage/storage',
        'mage/url',
        'Magento_Checkout/js/model/error-processor',
        'Magento_Customer/js/model/customer',
        'Magento_Checkout/js/model/full-screen-loader',
        'Amazon_Payment/js/model/storage'
    ],
    function (quote, urlBuilder, storage, url, errorProcessor, customer, fullScreenLoader, amazonStorage) {
        'use strict';

        return function (paymentData, redirectOnSuccess) {
            var serviceUrl,
                payload;

            redirectOnSuccess = redirectOnSuccess !== false;

            /** Checkout for guest and registered customer. */
            if (!customer.isLoggedIn()) {
                serviceUrl = urlBuilder.createUrl('/guest-carts/:quoteId/payment-information', {
                    quoteId: quote.getQuoteId()
                });
                payload = {
                    cartId: quote.getQuoteId(),
                    email: quote.guestEmail,
                    paymentMethod: paymentData,
                    billingAddress: quote.billingAddress()
                };
            } else {
                serviceUrl = urlBuilder.createUrl('/carts/mine/payment-information', {});
                payload = {
                    cartId: quote.getQuoteId(),
                    paymentMethod: paymentData,
                    billingAddress: quote.billingAddress()
                };
            }

            fullScreenLoader.startLoader();

            return storage.post(
                serviceUrl,
                JSON.stringify(payload)
            ).done(
                function () {
                    if (redirectOnSuccess) {
                        window.location.replace(url.build('checkout/onepage/success/'));
                    }
                }
            ).fail(
                function (response) {
                    errorProcessor.process(response);
                    amazonStorage.amazonDeclineCode(response.responseJSON.code);
                    fullScreenLoader.stopLoader(true);
                    if (response.responseJSON.code == 4273) {
                        var intervalId = setInterval(function () {
                            clearInterval(intervalId);
                            window.location.replace(url.build('checkout/cart/'));
                        }, 3000);
                    }
                }
            );
        };
    }
);
<commit_msg>Support issue ap 410 (#306)<commit_after>/**
 * Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 *  http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file 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.
 */

define(
    [
        'Magento_Checkout/js/model/quote',
        'Magento_Checkout/js/model/url-builder',
        'mage/storage',
        'mage/url',
        'Magento_Checkout/js/model/error-processor',
        'Magento_Customer/js/model/customer',
        'Magento_Checkout/js/model/full-screen-loader',
        'Amazon_Payment/js/model/storage'
    ],
    function (quote, urlBuilder, storage, url, errorProcessor, customer, fullScreenLoader, amazonStorage) {
        'use strict';

        return function (paymentData, redirectOnSuccess) {
            var serviceUrl,
                payload;

            redirectOnSuccess = redirectOnSuccess !== false;

            /** Checkout for guest and registered customer. */
            if (!customer.isLoggedIn()) {
                serviceUrl = urlBuilder.createUrl('/guest-carts/:quoteId/payment-information', {
                    quoteId: quote.getQuoteId()
                });
                payload = {
                    cartId: quote.getQuoteId(),
                    email: quote.guestEmail,
                    paymentMethod: paymentData,
                    billingAddress: quote.billingAddress()
                };
            } else {
                serviceUrl = urlBuilder.createUrl('/carts/mine/payment-information', {});
                payload = {
                    cartId: quote.getQuoteId(),
                    paymentMethod: paymentData,
                    billingAddress: quote.billingAddress()
                };
            }

            fullScreenLoader.startLoader();

            return storage.post(
                serviceUrl,
                JSON.stringify(payload)
            ).done(
                function () {
                    if (redirectOnSuccess) {
                        window.location.replace(url.build('checkout/onepage/success/'));
                    }
                }
            ).fail(
                function (response) {
                    errorProcessor.process(response);
                    amazonStorage.amazonDeclineCode(response.responseJSON.code);
                    fullScreenLoader.stopLoader(true);
                    if (response.responseJSON.code == 4273) {
                        var intervalId = setInterval(function () {
                            clearInterval(intervalId);
                            window.location.replace(url.build('checkout/cart/'));
                        }, 5000);
                    }
                }
            );
        };
    }
);
<|endoftext|>


<commit_before>(function($){
  $.fn.textareaAutoExpand = function(){
    return this.each(function(){
      var textarea = $(this);
      var height = textarea.height();
      var diff = parseInt(textarea.css('borderBottomWidth')) + parseInt(textarea.css('borderTopWidth')) +
                 parseInt(textarea.css('paddingBottom')) + parseInt(textarea.css('paddingTop'));
      var hasInitialValue = (this.value.replace(/\s/g, '').length > 0);

      if (textarea.css('box-sizing') === 'border-box' ||
          textarea.css('-moz-box-sizing') === 'border-box' ||
          textarea.css('-webkit-box-sizing') === 'border-box') {
        height = textarea.outerHeight();

        if (this.scrollHeight + diff == height) // special case for Firefox where scrollHeight isn't full height on border-box
          diff = 0;
      } else {
        diff = 0;
      }

      if (hasInitialValue) {
        textarea.height(this.scrollHeight);
      }

      textarea.on('scroll input', function(event){
        if (event.keyCode == 13 && !event.shiftKey) {
          // just allow default behavior to enter new line
          if (this.value.replace(/\s/g, '').length == 0) {
            event.stopImmediatePropagation();
            event.stopPropagation();
          }
        }

        textarea.height(0);
        //textarea.height(Math.max(height - diff, this.scrollHeight - diff));
        textarea.height(this.scrollHeight - diff);
      });
    });
  }
})(jQuery);
<commit_msg>Add keyup to event list to fix IE so it updates height properly when deleting text.<commit_after>(function($){
  $.fn.textareaAutoExpand = function(){
    return this.each(function(){
      var textarea = $(this);
      var height = textarea.height();
      var diff = parseInt(textarea.css('borderBottomWidth')) + parseInt(textarea.css('borderTopWidth')) +
                 parseInt(textarea.css('paddingBottom')) + parseInt(textarea.css('paddingTop'));
      var hasInitialValue = (this.value.replace(/\s/g, '').length > 0);

      if (textarea.css('box-sizing') === 'border-box' ||
          textarea.css('-moz-box-sizing') === 'border-box' ||
          textarea.css('-webkit-box-sizing') === 'border-box') {
        height = textarea.outerHeight();

        if (this.scrollHeight + diff == height) // special case for Firefox where scrollHeight isn't full height on border-box
          diff = 0;
      } else {
        diff = 0;
      }

      if (hasInitialValue) {
        textarea.height(this.scrollHeight);
      }

      textarea.on('scroll input keyup', function(event){ // keyup isn't necessary but when deleting text IE needs it to reset height properly
        if (event.keyCode == 13 && !event.shiftKey) {
          // just allow default behavior to enter new line
          if (this.value.replace(/\s/g, '').length == 0) {
            event.stopImmediatePropagation();
            event.stopPropagation();
          }
        }

        textarea.height(0);
        //textarea.height(Math.max(height - diff, this.scrollHeight - diff));
        textarea.height(this.scrollHeight - diff);
      });
    });
  }
})(jQuery);
<|endoftext|>


<commit_before>/**
 * Text = 3D Text
 *
 * parameters = {
 *  font: <THREE.Font>, // font
 *
 *  size: <float>, // size of the text
 *  height: <float>, // thickness to extrude text
 *  curveSegments: <int>, // number of points on the curves
 *
 *  bevelEnabled: <bool>, // turn on bevel
 *  bevelThickness: <float>, // how deep into text bevel goes
 *  bevelSize: <float>, // how far from text outline (including bevelOffset) is bevel
 *  bevelOffset: <float> // how far from text outline does bevel start
 * }
 */

import {
	BufferGeometry,
	ExtrudeGeometry
} from '../../../build/three.module.js';

class TextGeometry extends ExtrudeGeometry {

	constructor( text, parameters = {} ) {

		const font = parameters.font;

		if ( ! ( font && font.isFont ) ) {

			console.error( 'THREE.TextGeometry: font parameter is not an instance of THREE.Font.' );
			return new BufferGeometry();

		}

		const shapes = font.generateShapes( text, parameters.size );

		// translate parameters to ExtrudeGeometry API

		parameters.depth = parameters.height !== undefined ? parameters.height : 50;

		// defaults

		if ( parameters.bevelThickness === undefined ) parameters.bevelThickness = 10;
		if ( parameters.bevelSize === undefined ) parameters.bevelSize = 8;
		if ( parameters.bevelEnabled === undefined ) parameters.bevelEnabled = false;

		super( shapes, parameters );

		this.type = 'TextGeometry';

	}

}

export { TextGeometry };
<commit_msg>TextGeometry: Fix clone(). (#23269)<commit_after>/**
 * Text = 3D Text
 *
 * parameters = {
 *  font: <THREE.Font>, // font
 *
 *  size: <float>, // size of the text
 *  height: <float>, // thickness to extrude text
 *  curveSegments: <int>, // number of points on the curves
 *
 *  bevelEnabled: <bool>, // turn on bevel
 *  bevelThickness: <float>, // how deep into text bevel goes
 *  bevelSize: <float>, // how far from text outline (including bevelOffset) is bevel
 *  bevelOffset: <float> // how far from text outline does bevel start
 * }
 */

import {
	ExtrudeGeometry
} from '../../../build/three.module.js';

class TextGeometry extends ExtrudeGeometry {

	constructor( text, parameters = {} ) {

		const font = parameters.font;

		if ( font === undefined ) {

			super(); // generate default extrude geometry

		} else {

			const shapes = font.generateShapes( text, parameters.size );

			// translate parameters to ExtrudeGeometry API

			parameters.depth = parameters.height !== undefined ? parameters.height : 50;

			// defaults

			if ( parameters.bevelThickness === undefined ) parameters.bevelThickness = 10;
			if ( parameters.bevelSize === undefined ) parameters.bevelSize = 8;
			if ( parameters.bevelEnabled === undefined ) parameters.bevelEnabled = false;

			super( shapes, parameters );

		}

		this.type = 'TextGeometry';

	}

}

export { TextGeometry };
<|endoftext|>


<commit_before>
// function updateCount()  {

//$('#characters').text(cs);
// }
$("#id_lead").on("change paste keyup", function(){
    console.log("funktion");
    var cs = $("#id_lead").val().length;
    document.getElementById("characters").value = cs;
});

//$('#id_lead').on('input', updateCount());

<commit_msg>631 vi har börjat men det är inte klart<commit_after>
// function updateCount()  {

//$('#characters').text(cs);
// }
$("#id_lead").on("change paste keyup keypress", function(){
    console.log("funktion");
    var cs = $("#id_lead").val().length;
    document.getElementById("characters").value = cs;
});

//$('#id_lead').on('input', updateCount());

<|endoftext|>


<commit_before>(function () {
    'use strict';

    angular.module('ariaNg').run(['$rootScope', '$location', '$document', 'SweetAlert', 'ariaNgNotificationService', 'ariaNgSettingService', 'ariaNgLogService', 'aria2TaskService', function ($rootScope, $location, $document, SweetAlert, ariaNgNotificationService, ariaNgSettingService, ariaNgLogService, aria2TaskService) {
        var isUrlMatchUrl2 = function (url, url2) {
            if (url === url2) {
                return true;
            }

            var index = url2.indexOf(url);

            if (index !== 0) {
                return false;
            }

            var lastPart = url2.substring(url.length);

            if (lastPart.indexOf('/') === 0) {
                return true;
            }

            return false;
        };

        var initCheck = function () {
            var browserFeatures = ariaNgSettingService.getBrowserFeatures();

            if (!browserFeatures.localStroage) {
                ariaNgLogService.warn('[root.initCheck] LocalStorage is not supported!');
            }

            if (!browserFeatures.cookies) {
                ariaNgLogService.warn('[root.initCheck] Cookies is not supported!');
            }

            if (!ariaNgSettingService.isBrowserSupportStorage()) {
                angular.element('body').prepend('

');
                angular.element('.main-sidebar').addClass('blur');
                angular.element('.navbar').addClass('blur');
                angular.element('.content-body').addClass('blur');
                ariaNgNotificationService.notifyInPage('', 'You cannot use AriaNg because this browser does not support data storage.', {
                    type: 'error',
                    delay: false
                });
            }
        };

        var initNavbar = function () {
            angular.element('section.sidebar > ul > li[data-href-match] > a').click(function () {
                angular.element('section.sidebar > ul li').removeClass('active');
                angular.element(this).parent().addClass('active');
            });

            angular.element('section.sidebar > ul > li.treeview > ul.treeview-menu > li[data-href-match] > a').click(function () {
                angular.element('section.sidebar > ul li').removeClass('active');
                angular.element(this).parent().addClass('active').parent().parent().addClass('active');
            });
        };

        var setNavbarSelected = function (location) {
            angular.element('section.sidebar > ul li').removeClass('active');
            angular.element('section.sidebar > ul > li[data-href-match]').each(function (index, element) {
                var match = angular.element(element).attr('data-href-match');

                if (isUrlMatchUrl2(match, location)) {
                    angular.element(element).addClass('active');
                }
            });

            angular.element('section.sidebar > ul > li.treeview > ul.treeview-menu > li[data-href-match]').each(function (index, element) {
                var match = angular.element(element).attr('data-href-match');

                if (isUrlMatchUrl2(match, location)) {
                    angular.element(element).addClass('active').parent().parent().addClass('active');
                }
            });
        };

        var showSidebar = function () {
            angular.element('body').removeClass('sidebar-collapse').addClass('sidebar-open');
        };

        var hideSidebar = function () {
            angular.element('body').addClass('sidebar-collapse').removeClass('sidebar-open');
        };

        var isSidebarShowInSmallScreen = function () {
            return angular.element('body').hasClass('sidebar-open');
        };

        $rootScope.searchContext = {
            text: ''
        };

        $rootScope.taskContext = {
            rpcStatus: 'Connecting',
            list: [],
            selected: {},
            enableSelectAll: false,
            getSelectedTaskIds: function () {
                var result = [];

                if (!this.list || !this.selected || this.list.length < 1) {
                    return result;
                }

                for (var i = 0; i < this.list.length; i++) {
                    var task = this.list[i];

                    if (this.selected[task.gid]) {
                        result.push(task.gid);
                    }
                }

                return result;
            },
            getSelectedTasks: function () {
                var result = [];

                if (!this.list || !this.selected || this.list.length < 1) {
                    return result;
                }

                for (var i = 0; i < this.list.length; i++) {
                    var task = this.list[i];

                    if (this.selected[task.gid]) {
                        result.push(task);
                    }
                }

                return result;
            },
            selectAll: function () {
                if (!this.list || !this.selected || this.list.length < 1) {
                    return;
                }

                if (!this.enableSelectAll) {
                    return;
                }

                var isAllSelected = true;

                for (var i = 0; i < this.list.length; i++) {
                    var task = this.list[i];

                    if (!this.selected[task.gid]) {
                        isAllSelected = false;
                        break;
                    }
                }

                for (var i = 0; i < this.list.length; i++) {
                    var task = this.list[i];
                    this.selected[task.gid] = !isAllSelected;
                }
            }
        };

        $rootScope.swipeActions = {
            leftSwipe: function () {
                if (isSidebarShowInSmallScreen()) {
                    hideSidebar();
                    return;
                }

                if (!this.extentLeftSwipe ||
                    (angular.isFunction(this.extentLeftSwipe) && !this.extentLeftSwipe())) {
                    hideSidebar();
                }
            },
            rightSwipe: function () {
                if (!this.extentRightSwipe ||
                    (angular.isFunction(this.extentRightSwipe) && !this.extentRightSwipe())) {
                    showSidebar();
                }
            }
        };

        ariaNgSettingService.onFirstAccess(function () {
            ariaNgNotificationService.notifyInPage('', 'Tap to configure and get started with AriaNg.', {
                delay: false,
                onClose: function () {
                    $location.path('/settings/ariang');
                }
            });
        });

        aria2TaskService.onFirstSuccess(function (event) {
            ariaNgNotificationService.notifyInPage('', '{{name}} is connected', {
                type: 'success',
                contentParams: {
                    name: event.rpcName
                }
            });
        });

        aria2TaskService.onConnectSuccess(function () {
            $rootScope.taskContext.rpcStatus = 'Connected';
        });

        aria2TaskService.onConnectError(function () {
            $rootScope.taskContext.rpcStatus = 'Not Connected';
        });

        aria2TaskService.onTaskCompleted(function (event) {
            ariaNgNotificationService.notifyTaskComplete(event.task);
        });

        aria2TaskService.onBtTaskCompleted(function (event) {
            ariaNgNotificationService.notifyBtTaskComplete(event.task);
        });

        aria2TaskService.onTaskErrorOccur(function (event) {
            ariaNgNotificationService.notifyTaskError(event.task);
        });

        $rootScope.$on('$locationChangeStart', function (event) {
            SweetAlert.close();

            $rootScope.loadPromise = null;

            delete $rootScope.swipeActions.extentLeftSwipe;
            delete $rootScope.swipeActions.extentRightSwipe;

            if (angular.isArray($rootScope.taskContext.list) && $rootScope.taskContext.list.length > 0) {
                $rootScope.taskContext.list.length = 0;
            }

            if (angular.isObject($rootScope.taskContext.selected)) {
                $rootScope.taskContext.selected = {};
            }

            $rootScope.taskContext.enableSelectAll = false;
        });

        $rootScope.$on('$routeChangeStart', function (event, next, current) {
            var location = $location.path();

            setNavbarSelected(location);
            $document.unbind('keypress');
        });

        initCheck();
        initNavbar();
    }]);
}());
<commit_msg>throw error when browser does not support data storage<commit_after>(function () {
    'use strict';

    angular.module('ariaNg').run(['$rootScope', '$location', '$document', 'SweetAlert', 'ariaNgNotificationService', 'ariaNgSettingService', 'ariaNgLogService', 'aria2TaskService', function ($rootScope, $location, $document, SweetAlert, ariaNgNotificationService, ariaNgSettingService, ariaNgLogService, aria2TaskService) {
        var isUrlMatchUrl2 = function (url, url2) {
            if (url === url2) {
                return true;
            }

            var index = url2.indexOf(url);

            if (index !== 0) {
                return false;
            }

            var lastPart = url2.substring(url.length);

            if (lastPart.indexOf('/') === 0) {
                return true;
            }

            return false;
        };

        var initCheck = function () {
            var browserFeatures = ariaNgSettingService.getBrowserFeatures();

            if (!browserFeatures.localStroage) {
                ariaNgLogService.warn('[root.initCheck] LocalStorage is not supported!');
            }

            if (!browserFeatures.cookies) {
                ariaNgLogService.warn('[root.initCheck] Cookies is not supported!');
            }

            if (!ariaNgSettingService.isBrowserSupportStorage()) {
                angular.element('body').prepend('

');
                angular.element('.main-sidebar').addClass('blur');
                angular.element('.navbar').addClass('blur');
                angular.element('.content-body').addClass('blur');
                ariaNgNotificationService.notifyInPage('', 'You cannot use AriaNg because this browser does not support data storage.', {
                    type: 'error',
                    delay: false
                });

                throw new Error('You cannot use AriaNg because this browser does not support data storage.');
            }
        };

        var initNavbar = function () {
            angular.element('section.sidebar > ul > li[data-href-match] > a').click(function () {
                angular.element('section.sidebar > ul li').removeClass('active');
                angular.element(this).parent().addClass('active');
            });

            angular.element('section.sidebar > ul > li.treeview > ul.treeview-menu > li[data-href-match] > a').click(function () {
                angular.element('section.sidebar > ul li').removeClass('active');
                angular.element(this).parent().addClass('active').parent().parent().addClass('active');
            });
        };

        var setNavbarSelected = function (location) {
            angular.element('section.sidebar > ul li').removeClass('active');
            angular.element('section.sidebar > ul > li[data-href-match]').each(function (index, element) {
                var match = angular.element(element).attr('data-href-match');

                if (isUrlMatchUrl2(match, location)) {
                    angular.element(element).addClass('active');
                }
            });

            angular.element('section.sidebar > ul > li.treeview > ul.treeview-menu > li[data-href-match]').each(function (index, element) {
                var match = angular.element(element).attr('data-href-match');

                if (isUrlMatchUrl2(match, location)) {
                    angular.element(element).addClass('active').parent().parent().addClass('active');
                }
            });
        };

        var showSidebar = function () {
            angular.element('body').removeClass('sidebar-collapse').addClass('sidebar-open');
        };

        var hideSidebar = function () {
            angular.element('body').addClass('sidebar-collapse').removeClass('sidebar-open');
        };

        var isSidebarShowInSmallScreen = function () {
            return angular.element('body').hasClass('sidebar-open');
        };

        $rootScope.searchContext = {
            text: ''
        };

        $rootScope.taskContext = {
            rpcStatus: 'Connecting',
            list: [],
            selected: {},
            enableSelectAll: false,
            getSelectedTaskIds: function () {
                var result = [];

                if (!this.list || !this.selected || this.list.length < 1) {
                    return result;
                }

                for (var i = 0; i < this.list.length; i++) {
                    var task = this.list[i];

                    if (this.selected[task.gid]) {
                        result.push(task.gid);
                    }
                }

                return result;
            },
            getSelectedTasks: function () {
                var result = [];

                if (!this.list || !this.selected || this.list.length < 1) {
                    return result;
                }

                for (var i = 0; i < this.list.length; i++) {
                    var task = this.list[i];

                    if (this.selected[task.gid]) {
                        result.push(task);
                    }
                }

                return result;
            },
            selectAll: function () {
                if (!this.list || !this.selected || this.list.length < 1) {
                    return;
                }

                if (!this.enableSelectAll) {
                    return;
                }

                var isAllSelected = true;

                for (var i = 0; i < this.list.length; i++) {
                    var task = this.list[i];

                    if (!this.selected[task.gid]) {
                        isAllSelected = false;
                        break;
                    }
                }

                for (var i = 0; i < this.list.length; i++) {
                    var task = this.list[i];
                    this.selected[task.gid] = !isAllSelected;
                }
            }
        };

        $rootScope.swipeActions = {
            leftSwipe: function () {
                if (isSidebarShowInSmallScreen()) {
                    hideSidebar();
                    return;
                }

                if (!this.extentLeftSwipe ||
                    (angular.isFunction(this.extentLeftSwipe) && !this.extentLeftSwipe())) {
                    hideSidebar();
                }
            },
            rightSwipe: function () {
                if (!this.extentRightSwipe ||
                    (angular.isFunction(this.extentRightSwipe) && !this.extentRightSwipe())) {
                    showSidebar();
                }
            }
        };

        ariaNgSettingService.onFirstAccess(function () {
            ariaNgNotificationService.notifyInPage('', 'Tap to configure and get started with AriaNg.', {
                delay: false,
                onClose: function () {
                    $location.path('/settings/ariang');
                }
            });
        });

        aria2TaskService.onFirstSuccess(function (event) {
            ariaNgNotificationService.notifyInPage('', '{{name}} is connected', {
                type: 'success',
                contentParams: {
                    name: event.rpcName
                }
            });
        });

        aria2TaskService.onConnectSuccess(function () {
            $rootScope.taskContext.rpcStatus = 'Connected';
        });

        aria2TaskService.onConnectError(function () {
            $rootScope.taskContext.rpcStatus = 'Not Connected';
        });

        aria2TaskService.onTaskCompleted(function (event) {
            ariaNgNotificationService.notifyTaskComplete(event.task);
        });

        aria2TaskService.onBtTaskCompleted(function (event) {
            ariaNgNotificationService.notifyBtTaskComplete(event.task);
        });

        aria2TaskService.onTaskErrorOccur(function (event) {
            ariaNgNotificationService.notifyTaskError(event.task);
        });

        $rootScope.$on('$locationChangeStart', function (event) {
            SweetAlert.close();

            $rootScope.loadPromise = null;

            delete $rootScope.swipeActions.extentLeftSwipe;
            delete $rootScope.swipeActions.extentRightSwipe;

            if (angular.isArray($rootScope.taskContext.list) && $rootScope.taskContext.list.length > 0) {
                $rootScope.taskContext.list.length = 0;
            }

            if (angular.isObject($rootScope.taskContext.selected)) {
                $rootScope.taskContext.selected = {};
            }

            $rootScope.taskContext.enableSelectAll = false;
        });

        $rootScope.$on('$routeChangeStart', function (event, next, current) {
            var location = $location.path();

            setNavbarSelected(location);
            $document.unbind('keypress');
        });

        initCheck();
        initNavbar();
    }]);
}());
<|endoftext|>