<commit_before>var EXPORTED_SYMBOLS = ['GM_xmlhttpRequester'];

Components.utils.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
Components.utils.import("resource://greasemonkey/util.js");

function GM_xmlhttpRequester(wrappedContentWin, originUrl, sandbox) {
  this.wrappedContentWin = wrappedContentWin;
  this.originUrl = originUrl;
  this.sandboxPrincipal = Components.utils.getObjectPrincipal(sandbox);
  this.stringBundle = Components
    .classes["@mozilla.org/intl/stringbundle;1"]
    .getService(Components.interfaces.nsIStringBundleService)
    .createBundle("chrome://greasemonkey/locale/greasemonkey.properties");
}

// this function gets called by user scripts in content security scope to
// start a cross-domain xmlhttp request.
//
// details should look like:
// {method,url,onload,onerror,onreadystatechange,headers,data}
// headers should be in the form {name:value,name:value,etc}
// can't support mimetype because i think it's only used for forcing
// text/xml and we can't support that
GM_xmlhttpRequester.prototype.contentStartRequest = function(details) {
  try {
    // Validate and parse the (possibly relative) given URL.
    var uri = GM_util.uriFromUrl(details.url, this.originUrl);
    var url = uri.spec;
  } catch (e) {
    // A malformed URL won't be parsed properly.
    throw new Error(
        this.stringBundle.GetStringFromName('error.invalidUrl')
            .replace('%1', name)
        );
  }

  // This is important - without it, GM_xmlhttpRequest can be used to get
  // access to things like files and chrome. Careful.
  switch (uri.scheme) {
    case "http":
    case "https":
    case "ftp":
        var req = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"]
            .createInstance(Components.interfaces.nsIXMLHttpRequest);
        GM_util.hitch(this, "chromeStartRequest", url, details, req)();
      break;
    default:
      throw new Error(
          this.stringBundle.GetStringFromName('error.disallowedScheme')
              .replace('%1', details.url)
          );
  }

  var rv = {
    __exposedProps__: {
        finalUrl: "r",
        readyState: "r",
        responseHeaders: "r",
        responseText: "r",
        status: "r",
        statusText: "r",
        abort: "r"
        },
    abort: function () { return req.abort(); }
  };
  if (!!details.synchronous) {
    rv.finalUrl = req.finalUrl;
    rv.readyState = req.readyState;
    rv.responseHeaders = req.getAllResponseHeaders();
    rv.responseText = req.responseText;
    rv.status = req.status;
    rv.statusText = req.statusText;
  }
  return rv;
};

// this function is intended to be called in chrome's security context, so
// that it can access other domains without security warning
GM_xmlhttpRequester.prototype.chromeStartRequest =
function(safeUrl, details, req) {
  this.setupReferer(details, req);

  var setupRequestEvent = GM_util.hitch(
      this, 'setupRequestEvent', this.wrappedContentWin);

  setupRequestEvent(req, "abort", details);
  setupRequestEvent(req, "error", details);
  setupRequestEvent(req, "load", details);
  setupRequestEvent(req, "progress", details);
  setupRequestEvent(req, "readystatechange", details);
  setupRequestEvent(req, "timeout", details);
  if (details.upload) {
    setupRequestEvent(req.upload, "abort", details.upload);
    setupRequestEvent(req.upload, "error", details.upload);
    setupRequestEvent(req.upload, "load", details.upload);
    setupRequestEvent(req.upload, "progress", details.upload);
  }

  req.mozBackgroundRequest = !!details.mozBackgroundRequest;

  req.open(details.method, safeUrl,
      !details.synchronous, details.user || "", details.password || "");

  if (PrivateBrowsingUtils.isWindowPrivate(this.wrappedContentWin)) {
    var channel = req.channel.QueryInterface(Components.interfaces.nsIPrivateBrowsingChannel);
    channel.setPrivate(true);
  }

  if (details.overrideMimeType) {
    req.overrideMimeType(details.overrideMimeType);
  }
  if (details.responseType) {
    req.responseType = details.responseType;
  }

  if (details.timeout) {
    req.timeout = details.timeout;
  }

  if ('redirectionLimit' in details) {
    try {
      var httpChannel = req.channel.QueryInterface(
          Components.interfaces.nsIHttpChannel);
      httpChannel.redirectionLimit = details.redirectionLimit;
    } catch (e) {
      // Ignore.
    }
  }

  if (details.headers) {
    var headers = details.headers;

    for (var prop in headers) {
      if (Object.prototype.hasOwnProperty.call(headers, prop)) {
        req.setRequestHeader(prop, headers[prop]);
      }
    }
  }

  var body = details.data ? details.data : null;
  if (details.binary) {
    req.sendAsBinary(body);
  } else {
    req.send(body);
  }
};

// sets the "Referer" HTTP header for this GM_XHR request.
// Firefox does not let chrome JS set the "Referer" HTTP heade via XHR
// directly. However, we can still set it indirectly via an
// http-on-modify-request observer.
GM_xmlhttpRequester.prototype.setupReferer =
function(details, req) {
  if (!details.headers || !details.headers.Referer) return;

  var observerService = Components.classes["@mozilla.org/observer-service;1"]
      .getService(Components.interfaces.nsIObserverService);
  var requestObserver = {};
  requestObserver.observe = function(subject, topic, data) {
      observerService.removeObserver(requestObserver, "http-on-modify-request");

      var channel = subject.QueryInterface(Components.interfaces.nsIChannel);
      if (channel == req.channel) {
        var httpChannel = subject.QueryInterface(
            Components.interfaces.nsIHttpChannel);
        httpChannel.setRequestHeader("Referer", details.headers.Referer, false);
      }
    };
  observerService.addObserver(requestObserver, "http-on-modify-request", false);
};

// arranges for the specified 'event' on xmlhttprequest 'req' to call the
// method by the same name which is a property of 'details' in the content
// window's security context.
GM_xmlhttpRequester.prototype.setupRequestEvent =
function(wrappedContentWin, req, event, details) {
  // Waive Xrays so that we can read callback function properties ...
  details = Components.utils.waiveXrays(details);
  var eventCallback = details["on" + event];
  if (!eventCallback) return;

  // ... but ensure that the callback came from a script, not content, by
  // checking that its principal equals that of the sandbox.
  var callbackPrincipal = Components.utils.getObjectPrincipal(eventCallback);
  if (!this.sandboxPrincipal.equals(callbackPrincipal)) return;

  req.addEventListener(event, function(evt) {
    var responseState = {
      __exposedProps__: {
          context: "r",
          finalUrl: "r",
          lengthComputable: "r",
          loaded: "r",
          readyState: "r",
          response: "r",
          responseHeaders: "r",
          responseText: "r",
          responseXML: "r",
          status: "r",
          statusText: "r",
          total: "r",
          },
      context: details.context || null,
      readyState: req.readyState,
      response: req.response,
      responseHeaders: null,
      responseText: null,
      responseXML: null,
      status: null,
      statusText: null,
      finalUrl: null
    };

    try {
      responseState.responseText = req.responseText;
    } catch (e) {
      // Some response types don't have .responseText (but do have e.g. blob
      // .response).  Ignore.
    }

    var responseXML = null;
    try {
      responseXML = req.responseXML;
    } catch (e) {
      // Ignore failure.  At least in responseType blob case, this access fails.
    }
    if (responseXML) {
      // Clone the XML object into a content-window-scoped document.
      var xmlDoc = new wrappedContentWin.Document();
      var clone = xmlDoc.importNode(responseXML.documentElement, true);
      xmlDoc.appendChild(clone);
      responseState.responseXML = xmlDoc;
    }

    switch (event) {
      case "progress":
        responseState.lengthComputable = evt.lengthComputable;
        responseState.loaded = evt.loaded;
        responseState.total = evt.total;
        break;
      case "error":
        break;
      default:
        if (4 != req.readyState) break;
        responseState.responseHeaders = req.getAllResponseHeaders();
        responseState.status = req.status;
        responseState.statusText = req.statusText;
        responseState.finalUrl = req.channel.URI.spec;
        break;
    }

    if (GM_util.windowIsClosed(wrappedContentWin)) {
      return;
    }

    // Pop back onto browser thread and call event handler.
    // Have to use nested function here instead of GM_util.hitch because
    // otherwise details[event].apply can point to window.setTimeout, which
    // can be abused to get increased privileges.
    new XPCNativeWrapper(wrappedContentWin, "setTimeout()")
      .setTimeout(function(){ eventCallback.call(details, responseState); }, 0);
  }, false);
};
<commit_msg>Update xmlhttprequester.js (fix typo)<commit_after>var EXPORTED_SYMBOLS = ['GM_xmlhttpRequester'];

Components.utils.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
Components.utils.import("resource://greasemonkey/util.js");

function GM_xmlhttpRequester(wrappedContentWin, originUrl, sandbox) {
  this.wrappedContentWin = wrappedContentWin;
  this.originUrl = originUrl;
  this.sandboxPrincipal = Components.utils.getObjectPrincipal(sandbox);
  this.stringBundle = Components
    .classes["@mozilla.org/intl/stringbundle;1"]
    .getService(Components.interfaces.nsIStringBundleService)
    .createBundle("chrome://greasemonkey/locale/greasemonkey.properties");
}

// this function gets called by user scripts in content security scope to
// start a cross-domain xmlhttp request.
//
// details should look like:
// {method,url,onload,onerror,onreadystatechange,headers,data}
// headers should be in the form {name:value,name:value,etc}
// can't support mimetype because i think it's only used for forcing
// text/xml and we can't support that
GM_xmlhttpRequester.prototype.contentStartRequest = function(details) {
  try {
    // Validate and parse the (possibly relative) given URL.
    var uri = GM_util.uriFromUrl(details.url, this.originUrl);
    var url = uri.spec;
  } catch (e) {
    // A malformed URL won't be parsed properly.
    throw new Error(
        this.stringBundle.GetStringFromName('error.invalidUrl')
            .replace('%1', name)
        );
  }

  // This is important - without it, GM_xmlhttpRequest can be used to get
  // access to things like files and chrome. Careful.
  switch (uri.scheme) {
    case "http":
    case "https":
    case "ftp":
        var req = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"]
            .createInstance(Components.interfaces.nsIXMLHttpRequest);
        GM_util.hitch(this, "chromeStartRequest", url, details, req)();
      break;
    default:
      throw new Error(
          this.stringBundle.GetStringFromName('error.disallowedScheme')
              .replace('%1', details.url)
          );
  }

  var rv = {
    __exposedProps__: {
        finalUrl: "r",
        readyState: "r",
        responseHeaders: "r",
        responseText: "r",
        status: "r",
        statusText: "r",
        abort: "r"
        },
    abort: function () { return req.abort(); }
  };
  if (!!details.synchronous) {
    rv.finalUrl = req.finalUrl;
    rv.readyState = req.readyState;
    rv.responseHeaders = req.getAllResponseHeaders();
    rv.responseText = req.responseText;
    rv.status = req.status;
    rv.statusText = req.statusText;
  }
  return rv;
};

// this function is intended to be called in chrome's security context, so
// that it can access other domains without security warning
GM_xmlhttpRequester.prototype.chromeStartRequest =
function(safeUrl, details, req) {
  this.setupReferer(details, req);

  var setupRequestEvent = GM_util.hitch(
      this, 'setupRequestEvent', this.wrappedContentWin);

  setupRequestEvent(req, "abort", details);
  setupRequestEvent(req, "error", details);
  setupRequestEvent(req, "load", details);
  setupRequestEvent(req, "progress", details);
  setupRequestEvent(req, "readystatechange", details);
  setupRequestEvent(req, "timeout", details);
  if (details.upload) {
    setupRequestEvent(req.upload, "abort", details.upload);
    setupRequestEvent(req.upload, "error", details.upload);
    setupRequestEvent(req.upload, "load", details.upload);
    setupRequestEvent(req.upload, "progress", details.upload);
  }

  req.mozBackgroundRequest = !!details.mozBackgroundRequest;

  req.open(details.method, safeUrl,
      !details.synchronous, details.user || "", details.password || "");

  if (PrivateBrowsingUtils.isWindowPrivate(this.wrappedContentWin)) {
    var channel = req.channel
        .QueryInterface(Components.interfaces.nsIPrivateBrowsingChannel);
    channel.setPrivate(true);
  }

  if (details.overrideMimeType) {
    req.overrideMimeType(details.overrideMimeType);
  }
  if (details.responseType) {
    req.responseType = details.responseType;
  }

  if (details.timeout) {
    req.timeout = details.timeout;
  }

  if ('redirectionLimit' in details) {
    try {
      var httpChannel = req.channel.QueryInterface(
          Components.interfaces.nsIHttpChannel);
      httpChannel.redirectionLimit = details.redirectionLimit;
    } catch (e) {
      // Ignore.
    }
  }

  if (details.headers) {
    var headers = details.headers;

    for (var prop in headers) {
      if (Object.prototype.hasOwnProperty.call(headers, prop)) {
        req.setRequestHeader(prop, headers[prop]);
      }
    }
  }

  var body = details.data ? details.data : null;
  if (details.binary) {
    req.sendAsBinary(body);
  } else {
    req.send(body);
  }
};

// sets the "Referer" HTTP header for this GM_XHR request.
// Firefox does not let chrome JS set the "Referer" HTTP heade via XHR
// directly. However, we can still set it indirectly via an
// http-on-modify-request observer.
GM_xmlhttpRequester.prototype.setupReferer =
function(details, req) {
  if (!details.headers || !details.headers.Referer) return;

  var observerService = Components.classes["@mozilla.org/observer-service;1"]
      .getService(Components.interfaces.nsIObserverService);
  var requestObserver = {};
  requestObserver.observe = function(subject, topic, data) {
      observerService.removeObserver(requestObserver, "http-on-modify-request");

      var channel = subject.QueryInterface(Components.interfaces.nsIChannel);
      if (channel == req.channel) {
        var httpChannel = subject.QueryInterface(
            Components.interfaces.nsIHttpChannel);
        httpChannel.setRequestHeader("Referer", details.headers.Referer, false);
      }
    };
  observerService.addObserver(requestObserver, "http-on-modify-request", false);
};

// arranges for the specified 'event' on xmlhttprequest 'req' to call the
// method by the same name which is a property of 'details' in the content
// window's security context.
GM_xmlhttpRequester.prototype.setupRequestEvent =
function(wrappedContentWin, req, event, details) {
  // Waive Xrays so that we can read callback function properties ...
  details = Components.utils.waiveXrays(details);
  var eventCallback = details["on" + event];
  if (!eventCallback) return;

  // ... but ensure that the callback came from a script, not content, by
  // checking that its principal equals that of the sandbox.
  var callbackPrincipal = Components.utils.getObjectPrincipal(eventCallback);
  if (!this.sandboxPrincipal.equals(callbackPrincipal)) return;

  req.addEventListener(event, function(evt) {
    var responseState = {
      __exposedProps__: {
          context: "r",
          finalUrl: "r",
          lengthComputable: "r",
          loaded: "r",
          readyState: "r",
          response: "r",
          responseHeaders: "r",
          responseText: "r",
          responseXML: "r",
          status: "r",
          statusText: "r",
          total: "r",
          },
      context: details.context || null,
      readyState: req.readyState,
      response: req.response,
      responseHeaders: null,
      responseText: null,
      responseXML: null,
      status: null,
      statusText: null,
      finalUrl: null
    };

    try {
      responseState.responseText = req.responseText;
    } catch (e) {
      // Some response types don't have .responseText (but do have e.g. blob
      // .response).  Ignore.
    }

    var responseXML = null;
    try {
      responseXML = req.responseXML;
    } catch (e) {
      // Ignore failure.  At least in responseType blob case, this access fails.
    }
    if (responseXML) {
      // Clone the XML object into a content-window-scoped document.
      var xmlDoc = new wrappedContentWin.Document();
      var clone = xmlDoc.importNode(responseXML.documentElement, true);
      xmlDoc.appendChild(clone);
      responseState.responseXML = xmlDoc;
    }

    switch (event) {
      case "progress":
        responseState.lengthComputable = evt.lengthComputable;
        responseState.loaded = evt.loaded;
        responseState.total = evt.total;
        break;
      case "error":
        break;
      default:
        if (4 != req.readyState) break;
        responseState.responseHeaders = req.getAllResponseHeaders();
        responseState.status = req.status;
        responseState.statusText = req.statusText;
        responseState.finalUrl = req.channel.URI.spec;
        break;
    }

    if (GM_util.windowIsClosed(wrappedContentWin)) {
      return;
    }

    // Pop back onto browser thread and call event handler.
    // Have to use nested function here instead of GM_util.hitch because
    // otherwise details[event].apply can point to window.setTimeout, which
    // can be abused to get increased privileges.
    new XPCNativeWrapper(wrappedContentWin, "setTimeout()")
      .setTimeout(function(){ eventCallback.call(details, responseState); }, 0);
  }, false);
};
<|endoftext|>


<commit_before>if (typeof(module) !== 'undefined' && typeof(exports) !== 'undefined') {
    module.exports = OAuth;
}

/**
 * Constructor
 * @param {Object} opts consumer key and secret
 */
function OAuth(opts) {
    if(!(this instanceof OAuth)) {
        return new OAuth(opts);
    }

    if(!opts) {
        opts = {};
    }

    if(!opts.consumer) {
        throw new Error('consumer option is required');
    }

    this.consumer            = opts.consumer;
    this.signature_method    = opts.signature_method || 'HMAC-SHA1';
    this.nonce_length        = opts.nonce_length || 32;
    this.version             = opts.version || '1.0';
    this.parameter_seperator = opts.parameter_seperator || ', ';

    if(typeof opts.last_ampersand === 'undefined') {
        this.last_ampersand = true;
    } else {
        this.last_ampersand = opts.last_ampersand;
    }
}

/**
 * OAuth request authorize
 * @param  {Object} request data
 * {
 *     method,
 *     url,
 *     data
 * }
 * @param  {Object} public and secret token
 * @return {Object} OAuth Authorized data
 */
OAuth.prototype.authorize = function(request, token) {
    const oauth_data = {
        oauth_consumer_key: this.consumer.public,
        oauth_nonce: this.getNonce(),
        oauth_signature_method: this.signature_method,
        oauth_timestamp: this.getTimeStamp(),
        oauth_version: this.version
    };

    if(!token) {
        token = {};
    }

    if(token.public) {
        oauth_data.oauth_token = token.public;
    }

    if(!request.data) {
        request.data = {};
    }

    return this.getSignature(request, token.secret, oauth_data).then(function(oauth_signature) {
      oauth_data.oauth_signature = oauth_signature;
      return oauth_data;
    });
};

function text2ua(s) {
    var ua = new Uint8Array(s.length);
    for (var i = 0; i < s.length; i++) {
        ua[i] = s.charCodeAt(i);
    }
    return ua;
}

function ua2text(ua) {
    var s = '';
    for (var i = 0; i < ua.length; i++) {
        s += String.fromCharCode(ua[i]);
    }
    return s;
}

/**
 * Create a OAuth Signature
 * @param  {Object} request data
 * @param  {Object} token_secret public and secret token
 * @param  {Object} oauth_data   OAuth data
 * @return {String} Signature
 */
OAuth.prototype.getSignature = function(request, token_secret, oauth_data) {
  const bs = this.getBaseString(request, oauth_data);
  const sk = this.getSigningKey(token_secret);
  return crypto.subtle.importKey(
    "raw",
    text2ua(sk).buffer,
    {
      name: "HMAC",
      hash: {name: "SHA-1"},
    },
    true,
    ["sign"]
  ).then(key =>
    crypto.subtle.sign("HMAC", key, text2ua(bs))
  ).then(
    signature => btoa(ua2text(new Uint8Array(signature)))
  ).catch(console.error);
};

/**
 * Base String = Method + Base Url + ParameterString
 * @param  {Object} request data
 * @param  {Object} OAuth data
 * @return {String} Base String
 */
OAuth.prototype.getBaseString = function(request, oauth_data) {
    return request.method.toUpperCase() + '&' + this.percentEncode(this.getBaseUrl(request.url)) + '&' + this.percentEncode(this.getParameterString(request, oauth_data));
};

/**
 * Get data from url
 * -> merge with oauth data
 * -> percent encode key & value
 * -> sort
 *
 * @param  {Object} request data
 * @param  {Object} OAuth data
 * @return {Object} Parameter string data
 */
OAuth.prototype.getParameterString = function(request, oauth_data) {
    var base_string_data = this.sortObject(this.percentEncodeData(this.mergeObject(oauth_data, this.mergeObject(request.data, this.deParamUrl(request.url)))));

    var data_str = '';

    //base_string_data to string
    for(var key in base_string_data) {
        var value = base_string_data[key];
        // check if the value is an array
        // this means that this key has multiple values
        if (value && Array.isArray(value)){
          // sort the array first
          value.sort();

          var valString = "";
          // serialize all values for this key: e.g. formkey=formvalue1&formkey=formvalue2
          value.forEach((function(item, i){
            valString += key + '=' + item;
            if (i < value.length){
              valString += "&";
            }
          }).bind(this));
          data_str += valString;
        } else {
          data_str += key + '=' + value + '&';
        }
    }

    //remove the last character
    data_str = data_str.substr(0, data_str.length - 1);
    return data_str;
};

/**
 * Create a Signing Key
 * @param  {String} token_secret Secret Token
 * @return {String} Signing Key
 */
OAuth.prototype.getSigningKey = function(token_secret) {
    token_secret = token_secret || '';

    if(!this.last_ampersand && !token_secret) {
        return this.percentEncode(this.consumer.secret);
    }

    return this.percentEncode(this.consumer.secret) + '&' + this.percentEncode(token_secret);
};

/**
 * Get base url
 * @param  {String} url
 * @return {String}
 */
OAuth.prototype.getBaseUrl = function(url) {
    return url.split('?')[0];
};

/**
 * Get data from String
 * @param  {String} string
 * @return {Object}
 */
OAuth.prototype.deParam = function(string) {
    var arr = string.split('&');
    var data = {};

    for(var i = 0; i < arr.length; i++) {
        var item = arr[i].split('=');
        data[item[0]] = decodeURIComponent(item[1]);
    }
    return data;
};

/**
 * Get data from url
 * @param  {String} url
 * @return {Object}
 */
OAuth.prototype.deParamUrl = function(url) {
    var tmp = url.split('?');

    if (tmp.length === 1)
        return {};

    return this.deParam(tmp[1]);
};

/**
 * Percent Encode
 * @param  {String} str
 * @return {String} percent encoded string
 */
OAuth.prototype.percentEncode = function(str) {
    return encodeURIComponent(str)
        .replace(/\!/g, "%21")
        .replace(/\*/g, "%2A")
        .replace(/\'/g, "%27")
        .replace(/\(/g, "%28")
        .replace(/\)/g, "%29");
};

/**
 * Percent Encode Object
 * @param  {Object} data
 * @return {Object} percent encoded data
 */
OAuth.prototype.percentEncodeData = function(data) {
    var result = {};

    for(var key in data) {
        var value = data[key];
        // check if the value is an array
        if (value && Array.isArray(value)){
          var newValue = [];
          // percentEncode every value
          value.forEach((function(val){
            newValue.push(this.percentEncode(val));
          }).bind(this));
          value = newValue;
        } else {
          value = this.percentEncode(value);
        }
        result[this.percentEncode(key)] = value;
    }

    return result;
};

/**
 * Get OAuth data as Header
 * @param  {Object} oauth_data
 * @return {String} Header data key - value
 */
OAuth.prototype.toHeader = function(oauth_data) {
    oauth_data = this.sortObject(oauth_data);

    var header_value = 'OAuth ';

    for(var key in oauth_data) {
        if (key.indexOf('oauth_') === -1)
            continue;
        header_value += this.percentEncode(key) + '="' + this.percentEncode(oauth_data[key]) + '"' + this.parameter_seperator;
    }

    return {
        Authorization: header_value.substr(0, header_value.length - this.parameter_seperator.length) //cut the last chars
    };
};

/**
 * Create a random word characters string with input length
 * @return {String} a random word characters string
 */
OAuth.prototype.getNonce = function() {
    var word_characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
    var result = '';

    for(var i = 0; i < this.nonce_length; i++) {
        result += word_characters[parseInt(Math.random() * word_characters.length, 10)];
    }

    return result;
};

/**
 * Get Current Unix TimeStamp
 * @return {Int} current unix timestamp
 */
OAuth.prototype.getTimeStamp = function() {
    return parseInt(new Date().getTime()/1000, 10);
};

////////////////////// HELPER FUNCTIONS //////////////////////

/**
 * Merge object
 * @param  {Object} obj1
 * @param  {Object} obj2
 * @return {Object}
 */
OAuth.prototype.mergeObject = function(obj1, obj2) {
    var merged_obj = obj1;
    for(var key in obj2) {
        merged_obj[key] = obj2[key];
    }
    return merged_obj;
};

/**
 * Sort object by key
 * @param  {Object} data
 * @return {Object} sorted object
 */
OAuth.prototype.sortObject = function(data) {
    var keys = Object.keys(data);
    var result = {};

    keys.sort();

    for(var i = 0; i < keys.length; i++) {
        var key = keys[i];
        result[key] = data[key];
    }

    return result;
};
<commit_msg>Re-enable other signing algorithm. Use encoder API to convert string to ArrayBuffer.<commit_after>if (typeof(module) !== 'undefined' && typeof(exports) !== 'undefined') {
    module.exports = OAuth;
}

const encoder = new TextEncoder();

function ua2text(buf) {
  return String.fromCharCode.apply(null, buf)
}

function text2ua(str) {
  return encoder.encode(str);
}

function hmac(hashFct, baseString, signingKey) {
  return crypto.subtle.importKey(
    "raw",
    text2ua(signingKey).buffer,
    {
      name: "HMAC",
      hash: {name: hashFct},
    },
    true,
    ["sign"]
  ).then(key =>
    crypto.subtle.sign("HMAC", key, text2ua(baseString))
  ).then(
    signature => btoa(ua2text(new Uint8Array(signature)))
  ).catch(console.error);
}

/**
 * Constructor
 * @param {Object} opts consumer key and secret
 */
function OAuth(opts) {
    if(!(this instanceof OAuth)) {
        return new OAuth(opts);
    }

    if(!opts) {
        opts = {};
    }

    if(!opts.consumer) {
        throw new Error('consumer option is required');
    }

    this.consumer            = opts.consumer;
    this.signature_method    = opts.signature_method || 'HMAC-SHA1';
    this.nonce_length        = opts.nonce_length || 32;
    this.version             = opts.version || '1.0';
    this.parameter_seperator = opts.parameter_seperator || ', ';

    if(typeof opts.last_ampersand === 'undefined') {
        this.last_ampersand = true;
    } else {
        this.last_ampersand = opts.last_ampersand;
    }

    switch (this.signature_method) {
        case 'HMAC-SHA1':
            this.hash = function(base_string, key) {
                return hmac("SHA-1", base_string, key);
            };
            break;

        case 'HMAC-SHA256':
            this.hash = function(base_string, key) {
                return hmac("SHA-256", base_string, key);
            };
            break;

        case 'PLAINTEXT':
            this.hash = function(base_string, key) {
                return Promise.resolve(key);
            };
            break;

        case 'RSA-SHA1':
            return Promise.reject(new Error('oauth-1.0a does not support this signature method right now. Coming Soon...'));
        default:
            return Promise.reject(new Error('The OAuth 1.0a protocol defines three signature methods: HMAC-SHA1, HMAC-SHA256, RSA-SHA1, and PLAINTEXT only'));
    }
}

/**
 * OAuth request authorize
 * @param  {Object} request data
 * {
 *     method,
 *     url,
 *     data
 * }
 * @param  {Object} public and secret token
 * @return {Object} OAuth Authorized data
 */
OAuth.prototype.authorize = function(request, token) {
    var oauth_data = {
        oauth_consumer_key: this.consumer.public,
        oauth_nonce: this.getNonce(),
        oauth_signature_method: this.signature_method,
        oauth_timestamp: this.getTimeStamp(),
        oauth_version: this.version
    };

    if(!token) {
        token = {};
    }

    if(token.public) {
        oauth_data.oauth_token = token.public;
    }

    if(!request.data) {
        request.data = {};
    }

    return this.getSignature(request, token.secret, oauth_data).then(function(oauth_signature) {
      oauth_data.oauth_signature = oauth_signature;
      return oauth_data;
    });
};

/**
 * Create a OAuth Signature
 * @param  {Object} request data
 * @param  {Object} token_secret public and secret token
 * @param  {Object} oauth_data   OAuth data
 * @return {String} Signature
 */
OAuth.prototype.getSignature = function(request, token_secret, oauth_data) {
    return this.hash(this.getBaseString(request, oauth_data), this.getSigningKey(token_secret));
};

/**
 * Base String = Method + Base Url + ParameterString
 * @param  {Object} request data
 * @param  {Object} OAuth data
 * @return {String} Base String
 */
OAuth.prototype.getBaseString = function(request, oauth_data) {
    return request.method.toUpperCase() + '&' + this.percentEncode(this.getBaseUrl(request.url)) + '&' + this.percentEncode(this.getParameterString(request, oauth_data));
};

/**
 * Get data from url
 * -> merge with oauth data
 * -> percent encode key & value
 * -> sort
 *
 * @param  {Object} request data
 * @param  {Object} OAuth data
 * @return {Object} Parameter string data
 */
OAuth.prototype.getParameterString = function(request, oauth_data) {
    var base_string_data = this.sortObject(this.percentEncodeData(this.mergeObject(oauth_data, this.mergeObject(request.data, this.deParamUrl(request.url)))));

    var data_str = '';

    //base_string_data to string
    for(var key in base_string_data) {
        var value = base_string_data[key];
        // check if the value is an array
        // this means that this key has multiple values
        if (value && Array.isArray(value)){
          // sort the array first
          value.sort();

          var valString = "";
          // serialize all values for this key: e.g. formkey=formvalue1&formkey=formvalue2
          value.forEach((function(item, i){
            valString += key + '=' + item;
            if (i < value.length){
              valString += "&";
            }
          }).bind(this));
          data_str += valString;
        } else {
          data_str += key + '=' + value + '&';
        }
    }

    //remove the last character
    data_str = data_str.substr(0, data_str.length - 1);
    return data_str;
};

/**
 * Create a Signing Key
 * @param  {String} token_secret Secret Token
 * @return {String} Signing Key
 */
OAuth.prototype.getSigningKey = function(token_secret) {
    token_secret = token_secret || '';

    if(!this.last_ampersand && !token_secret) {
        return this.percentEncode(this.consumer.secret);
    }

    return this.percentEncode(this.consumer.secret) + '&' + this.percentEncode(token_secret);
};

/**
 * Get base url
 * @param  {String} url
 * @return {String}
 */
OAuth.prototype.getBaseUrl = function(url) {
    return url.split('?')[0];
};

/**
 * Get data from String
 * @param  {String} string
 * @return {Object}
 */
OAuth.prototype.deParam = function(string) {
    var arr = string.split('&');
    var data = {};

    for(var i = 0; i < arr.length; i++) {
        var item = arr[i].split('=');
        data[item[0]] = decodeURIComponent(item[1]);
    }
    return data;
};

/**
 * Get data from url
 * @param  {String} url
 * @return {Object}
 */
OAuth.prototype.deParamUrl = function(url) {
    var tmp = url.split('?');

    if (tmp.length === 1)
        return {};

    return this.deParam(tmp[1]);
};

/**
 * Percent Encode
 * @param  {String} str
 * @return {String} percent encoded string
 */
OAuth.prototype.percentEncode = function(str) {
    return encodeURIComponent(str)
        .replace(/\!/g, "%21")
        .replace(/\*/g, "%2A")
        .replace(/\'/g, "%27")
        .replace(/\(/g, "%28")
        .replace(/\)/g, "%29");
};

/**
 * Percent Encode Object
 * @param  {Object} data
 * @return {Object} percent encoded data
 */
OAuth.prototype.percentEncodeData = function(data) {
    var result = {};

    for(var key in data) {
        var value = data[key];
        // check if the value is an array
        if (value && Array.isArray(value)){
          var newValue = [];
          // percentEncode every value
          value.forEach((function(val){
            newValue.push(this.percentEncode(val));
          }).bind(this));
          value = newValue;
        } else {
          value = this.percentEncode(value);
        }
        result[this.percentEncode(key)] = value;
    }

    return result;
};

/**
 * Get OAuth data as Header
 * @param  {Object} oauth_data
 * @return {String} Header data key - value
 */
OAuth.prototype.toHeader = function(oauth_data) {
    oauth_data = this.sortObject(oauth_data);

    var header_value = 'OAuth ';

    for(var key in oauth_data) {
        if (key.indexOf('oauth_') === -1)
            continue;
        header_value += this.percentEncode(key) + '="' + this.percentEncode(oauth_data[key]) + '"' + this.parameter_seperator;
    }

    return {
        Authorization: header_value.substr(0, header_value.length - this.parameter_seperator.length) //cut the last chars
    };
};

/**
 * Create a random word characters string with input length
 * @return {String} a random word characters string
 */
OAuth.prototype.getNonce = function() {
    var word_characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
    var result = '';

    for(var i = 0; i < this.nonce_length; i++) {
        result += word_characters[parseInt(Math.random() * word_characters.length, 10)];
    }

    return result;
};

/**
 * Get Current Unix TimeStamp
 * @return {Int} current unix timestamp
 */
OAuth.prototype.getTimeStamp = function() {
    return parseInt(new Date().getTime()/1000, 10);
};

////////////////////// HELPER FUNCTIONS //////////////////////

/**
 * Merge object
 * @param  {Object} obj1
 * @param  {Object} obj2
 * @return {Object}
 */
OAuth.prototype.mergeObject = function(obj1, obj2) {
    var merged_obj = obj1;
    for(var key in obj2) {
        merged_obj[key] = obj2[key];
    }
    return merged_obj;
};

/**
 * Sort object by key
 * @param  {Object} data
 * @return {Object} sorted object
 */
OAuth.prototype.sortObject = function(data) {
    var keys = Object.keys(data);
    var result = {};

    keys.sort();

    for(var i = 0; i < keys.length; i++) {
        var key = keys[i];
        result[key] = data[key];
    }

    return result;
};
<|endoftext|>


<commit_before>ANGULAR1_TUT = [
  {
    id: "0",
    title: 'Bootstrapping',
    seoTitle: 'Bootstrapping',
    route: "tutorials.angular1.bootstrapping",
    path: "/tutorials/angular1/bootstrapping",
    contentTemplate: 'tutorial.step_00.md',
    video: '//www.youtube.com/embed/s2RWlIrkCaE?list=PLhCf3AUOg4PgQoY_A6xWDQ70yaNtPYtZd',
    commitDiff: 'a00a945d0a503c99efaff1e9a20dfe6f9205821f'
  },
  {
    id: "1",
    title: 'Static Template',
    seoTitle: 'Static Template',
    route: "tutorials.angular1.static-template",
    path: "/tutorials/angular1/static-template",
    contentTemplate: 'tutorial.step_01.md',
    video: '//www.youtube.com/embed/xUod-yoDfEE?list=PLhCf3AUOg4PgQoY_A6xWDQ70yaNtPYtZd'
  },
  {
    id: "2",
    title: 'Dynamic Template',
    seoTitle: 'Dynamic Template',
    route: "tutorials.angular1.dynamic-template",
    path: "/tutorials/angular1/dynamic-template",
    contentTemplate: 'tutorial.step_02.md',
    video: '//www.youtube.com/embed/xUod-yoDfEE?list=PLhCf3AUOg4PgQoY_A6xWDQ70yaNtPYtZd'
  },
  {
    id: "3",
    title: '3-Way data binding',
    seoTitle: '3-Way data binding',
    route: "tutorials.angular1.3-way-data-binding",
    path: "/tutorials/angular1/3-way-data-binding",
    contentTemplate: 'tutorial.step_03.md',
    video: '//www.youtube.com/embed/xUod-yoDfEE?list=PLhCf3AUOg4PgQoY_A6xWDQ70yaNtPYtZd'
  },
  {
    id: "4",
    title: 'Adding/removing objects and Angular event handling',
    seoTitle: 'Adding/removing objects and Angular event handling',
    route: "tutorials.angular1.adding-removing-objects-and-angular-event-handling",
    path: "/tutorials/angular1/adding-removing-objects-and-angular-event-handling",
    contentTemplate: 'tutorial.step_04.md',
    video: '//www.youtube.com/embed/ijKsWglJI0k?list=PLhCf3AUOg4PgQoY_A6xWDQ70yaNtPYtZd'
  },
  {
    id: "5",
    title: 'Routing & Multiple Views',
    seoTitle: 'Routing and Multiple Views',
    route: "tutorials.angular1.routing-and-multiple-views",
    path: "/tutorials/angular1/routing-and-multiple-views",
    contentTemplate: 'tutorial.step_05.md',
    video: '//www.youtube.com/embed/oScHP7Vd7as?list=PLhCf3AUOg4PgQoY_A6xWDQ70yaNtPYtZd'
  },
  {
    id: "6",
    title: 'Bind one object',
    seoTitle: 'Bind one object',
    route: "tutorials.angular1.bind-one-object",
    path: "/tutorials/angular1/bind-one-object",
    contentTemplate: 'tutorial.step_06.md',
    video: '//www.youtube.com/embed/kRen9GlR3K8?list=PLhCf3AUOg4PgQoY_A6xWDQ70yaNtPYtZd'
  },
  {
    id: "7",
    title: 'Folder structure',
    seoTitle: 'Folder structure',
    route: "tutorials.angular1.folder-structure",
    path: "/tutorials/angular1/folder-structure",
    contentTemplate: 'tutorial.step_07.md',
    video: '//www.youtube.com/embed/l3nTv4GuJrY?list=PLhCf3AUOg4PgQoY_A6xWDQ70yaNtPYtZd'
  },
  {
    id: "8",
    title: 'User accounts, authentication and permissions',
    seoTitle: 'User accounts, authentication and permissions',
    route: "tutorials.angular1.user-accounts-authentication-and-permissions",
    path: "/tutorials/angular1/user-accounts-authentication-and-permissions",
    contentTemplate: 'tutorial.step_08.md',
    video: '//www.youtube.com/embed/PgS-IAMn9Ig?list=PLhCf3AUOg4PgQoY_A6xWDQ70yaNtPYtZd'
  },
  {
    id: "9",
    title: 'Privacy and publish-subscribe functions',
    seoTitle: 'Privacy and publish-subscribe functions',
    route: "tutorials.angular1.privacy-and-publish-subscribe-functions",
    path: "/tutorials/angular1/privacy-and-publish-subscribe-functions",
    contentTemplate: 'tutorial.step_09.md',
    video: '//www.youtube.com/embed/wAHi7ilDHko?list=PLhCf3AUOg4PgQoY_A6xWDQ70yaNtPYtZd'
  },
  {
    id: "10",
    title: 'Deploying your app',
    seoTitle: 'Deploying your app',
    route: "tutorials.angular1.deploying-your-app",
    path: "/tutorials/angular1/deploying-your-app",
    contentTemplate: 'tutorial.step_10.md',
    video: '//www.youtube.com/embed/2WnZBKv9H9o?list=PLhCf3AUOg4PgQoY_A6xWDQ70yaNtPYtZd'
  },
  {
    id: "11",
    title: 'Running your app on Android or iOS with PhoneGap',
    seoTitle: 'Running your app on Android or iOS with PhoneGap',
    route: "tutorials.angular1.running-your-app-on-android-or-ios-with-phoneGap",
    path: "/tutorials/angular1/running-your-app-on-android-or-ios-with-phoneGap",
    contentTemplate: 'tutorial.step_11.md',
    video: '//www.youtube.com/embed/5vZOI2fi13U?list=PLhCf3AUOg4PgQoY_A6xWDQ70yaNtPYtZd'
  },
  {
    id: "12",
    title: 'Search, sort, pagination and reactive vars',
    seoTitle: 'Search, sort, pagination and reactive vars',
    route: "tutorials.angular1.search-sort-pagination-and-reactive-vars",
    path: "/tutorials/angular1/search-sort-pagination-and-reactive-vars",
    contentTemplate: 'tutorial.step_12.md',
    video: '//www.youtube.com/embed/8XQI2XpyH18?list=PLhCf3AUOg4PgQoY_A6xWDQ70yaNtPYtZd'
  },
  {
    id: "13",
    title: 'Using and creating AngularJS filters',
    seoTitle: 'Using and creating AngularJS filters',
    route: "tutorials.angular1.using-and-creating-angularjs-filters",
    path: "/tutorials/angular1/using-and-creating-angularjs-filters",
    contentTemplate: 'tutorial.step_13.md',
    video: '//www.youtube.com/embed/S049FI8TP4A?list=PLhCf3AUOg4PgQoY_A6xWDQ70yaNtPYtZd'
  },
  {
    id: "14",
    title: 'Meteor methods with promises',
    seoTitle: 'Meteor methods with promises',
    route: "tutorials.angular1.meteor-methods-with-promises",
    path: "/tutorials/angular1/meteor-methods-with-promises",
    contentTemplate: 'tutorial.step_14.md',
    video: '//www.youtube.com/embed/qNUjZjfaYt8?list=PLhCf3AUOg4PgQoY_A6xWDQ70yaNtPYtZd'
  },
  {
    id: "15",
    title: 'Conditional template directives with AngularJS',
    seoTitle: 'Conditional template directives with AngularJS',
    route: "tutorials.angular1.conditional-template-directives-with-angularjs",
    path: "/tutorials/angular1/conditional-template-directives-with-angularjs",
    contentTemplate: 'tutorial.step_15.md',
    video: '//www.youtube.com/embed/KSlVThsNCss?list=PLhCf3AUOg4PgQoY_A6xWDQ70yaNtPYtZd'
  },
  {
    id: "16",
    title: 'Google Maps',
    seoTitle: 'Google Maps',
    route: "tutorials.angular1.google-maps",
    path: "/tutorials/angular1/google-maps",
    contentTemplate: 'tutorial.step_16.md',
    video: '//www.youtube.com/embed/A6qsm_RDc9Y?list=PLhCf3AUOg4PgQoY_A6xWDQ70yaNtPYtZd'
  },
  {
    id: "17",
    title: 'CSS, LESS and Bootstrap',
    seoTitle: 'CSS, LESS and Bootstrap',
    route: "tutorials.angular1.css-less-and-bootstrap",
    path: "/tutorials/angular1/css-less-and-bootstrap",
    contentTemplate: 'tutorial.step_17.md',
    video: '//www.youtube.com/embed/A6qsm_RDc9Y?list=PLhCf3AUOg4PgQoY_A6xWDQ70yaNtPYtZd'
  },
  {
    id: "18",
    title: 'angular-material and custom Angular auth forms',
    seoTitle: 'angular-material and custom Angular auth forms',
    route: "tutorials.angular1.angular-material-and-custom-angular-auth-forms",
    path: "/tutorials/angular1/angular-material-and-custom-angular-auth-forms",
    contentTemplate: 'tutorial.step_18.md',
    video: '//www.youtube.com/embed/A6qsm_RDc9Y?list=PLhCf3AUOg4PgQoY_A6xWDQ70yaNtPYtZd'
  },
  {
    id: "19",
    title: '3rdParty Libraries',
    seoTitle: '3rdParty Libraries',
    route: "tutorials.angular1.3rd-party-libraries",
    path: "/tutorials/angular1/3rd-party-libraries",
    contentTemplate: 'tutorial.step_19.md',
    video: '//www.youtube.com/embed/A6qsm_RDc9Y?list=PLhCf3AUOg4PgQoY_A6xWDQ70yaNtPYtZd'
  },
  {
    id: "20",
    title: 'Handling Files with CollectionFS',
    seoTitle: 'Handling Files with CollectionFS',
    route: "tutorials.angular1.handling-files-with-collectionfs",
    path: "/tutorials/angular1/handling-files-with-collectionfs",
    contentTemplate: 'tutorial.step_20.md',
    video: '//www.youtube.com/embed/A6qsm_RDc9Y?list=PLhCf3AUOg4PgQoY_A6xWDQ70yaNtPYtZd',
    previousCodeStep: '18'
  },
  {
    id: "21",
    title: 'Next Steps',
    seoTitle: 'Next Steps',
    route: "tutorials.angular1.next-steps",
    path: "/tutorials/angular1/next-steps",
    contentTemplate: 'tutorial.next_steps.html'
  }
];<commit_msg>Add step 21<commit_after>ANGULAR1_TUT = [
  {
    id: "0",
    title: 'Bootstrapping',
    seoTitle: 'Bootstrapping',
    route: "tutorials.angular1.bootstrapping",
    path: "/tutorials/angular1/bootstrapping",
    contentTemplate: 'tutorial.step_00.md',
    video: '//www.youtube.com/embed/s2RWlIrkCaE?list=PLhCf3AUOg4PgQoY_A6xWDQ70yaNtPYtZd',
    commitDiff: 'a00a945d0a503c99efaff1e9a20dfe6f9205821f'
  },
  {
    id: "1",
    title: 'Static Template',
    seoTitle: 'Static Template',
    route: "tutorials.angular1.static-template",
    path: "/tutorials/angular1/static-template",
    contentTemplate: 'tutorial.step_01.md',
    video: '//www.youtube.com/embed/xUod-yoDfEE?list=PLhCf3AUOg4PgQoY_A6xWDQ70yaNtPYtZd'
  },
  {
    id: "2",
    title: 'Dynamic Template',
    seoTitle: 'Dynamic Template',
    route: "tutorials.angular1.dynamic-template",
    path: "/tutorials/angular1/dynamic-template",
    contentTemplate: 'tutorial.step_02.md',
    video: '//www.youtube.com/embed/xUod-yoDfEE?list=PLhCf3AUOg4PgQoY_A6xWDQ70yaNtPYtZd'
  },
  {
    id: "3",
    title: '3-Way data binding',
    seoTitle: '3-Way data binding',
    route: "tutorials.angular1.3-way-data-binding",
    path: "/tutorials/angular1/3-way-data-binding",
    contentTemplate: 'tutorial.step_03.md',
    video: '//www.youtube.com/embed/xUod-yoDfEE?list=PLhCf3AUOg4PgQoY_A6xWDQ70yaNtPYtZd'
  },
  {
    id: "4",
    title: 'Adding/removing objects and Angular event handling',
    seoTitle: 'Adding/removing objects and Angular event handling',
    route: "tutorials.angular1.adding-removing-objects-and-angular-event-handling",
    path: "/tutorials/angular1/adding-removing-objects-and-angular-event-handling",
    contentTemplate: 'tutorial.step_04.md',
    video: '//www.youtube.com/embed/ijKsWglJI0k?list=PLhCf3AUOg4PgQoY_A6xWDQ70yaNtPYtZd'
  },
  {
    id: "5",
    title: 'Routing & Multiple Views',
    seoTitle: 'Routing and Multiple Views',
    route: "tutorials.angular1.routing-and-multiple-views",
    path: "/tutorials/angular1/routing-and-multiple-views",
    contentTemplate: 'tutorial.step_05.md',
    video: '//www.youtube.com/embed/oScHP7Vd7as?list=PLhCf3AUOg4PgQoY_A6xWDQ70yaNtPYtZd'
  },
  {
    id: "6",
    title: 'Bind one object',
    seoTitle: 'Bind one object',
    route: "tutorials.angular1.bind-one-object",
    path: "/tutorials/angular1/bind-one-object",
    contentTemplate: 'tutorial.step_06.md',
    video: '//www.youtube.com/embed/kRen9GlR3K8?list=PLhCf3AUOg4PgQoY_A6xWDQ70yaNtPYtZd'
  },
  {
    id: "7",
    title: 'Folder structure',
    seoTitle: 'Folder structure',
    route: "tutorials.angular1.folder-structure",
    path: "/tutorials/angular1/folder-structure",
    contentTemplate: 'tutorial.step_07.md',
    video: '//www.youtube.com/embed/l3nTv4GuJrY?list=PLhCf3AUOg4PgQoY_A6xWDQ70yaNtPYtZd'
  },
  {
    id: "8",
    title: 'User accounts, authentication and permissions',
    seoTitle: 'User accounts, authentication and permissions',
    route: "tutorials.angular1.user-accounts-authentication-and-permissions",
    path: "/tutorials/angular1/user-accounts-authentication-and-permissions",
    contentTemplate: 'tutorial.step_08.md',
    video: '//www.youtube.com/embed/PgS-IAMn9Ig?list=PLhCf3AUOg4PgQoY_A6xWDQ70yaNtPYtZd'
  },
  {
    id: "9",
    title: 'Privacy and publish-subscribe functions',
    seoTitle: 'Privacy and publish-subscribe functions',
    route: "tutorials.angular1.privacy-and-publish-subscribe-functions",
    path: "/tutorials/angular1/privacy-and-publish-subscribe-functions",
    contentTemplate: 'tutorial.step_09.md',
    video: '//www.youtube.com/embed/wAHi7ilDHko?list=PLhCf3AUOg4PgQoY_A6xWDQ70yaNtPYtZd'
  },
  {
    id: "10",
    title: 'Deploying your app',
    seoTitle: 'Deploying your app',
    route: "tutorials.angular1.deploying-your-app",
    path: "/tutorials/angular1/deploying-your-app",
    contentTemplate: 'tutorial.step_10.md',
    video: '//www.youtube.com/embed/2WnZBKv9H9o?list=PLhCf3AUOg4PgQoY_A6xWDQ70yaNtPYtZd'
  },
  {
    id: "11",
    title: 'Running your app on Android or iOS with PhoneGap',
    seoTitle: 'Running your app on Android or iOS with PhoneGap',
    route: "tutorials.angular1.running-your-app-on-android-or-ios-with-phoneGap",
    path: "/tutorials/angular1/running-your-app-on-android-or-ios-with-phoneGap",
    contentTemplate: 'tutorial.step_11.md',
    video: '//www.youtube.com/embed/5vZOI2fi13U?list=PLhCf3AUOg4PgQoY_A6xWDQ70yaNtPYtZd'
  },
  {
    id: "12",
    title: 'Search, sort, pagination and reactive vars',
    seoTitle: 'Search, sort, pagination and reactive vars',
    route: "tutorials.angular1.search-sort-pagination-and-reactive-vars",
    path: "/tutorials/angular1/search-sort-pagination-and-reactive-vars",
    contentTemplate: 'tutorial.step_12.md',
    video: '//www.youtube.com/embed/8XQI2XpyH18?list=PLhCf3AUOg4PgQoY_A6xWDQ70yaNtPYtZd'
  },
  {
    id: "13",
    title: 'Using and creating AngularJS filters',
    seoTitle: 'Using and creating AngularJS filters',
    route: "tutorials.angular1.using-and-creating-angularjs-filters",
    path: "/tutorials/angular1/using-and-creating-angularjs-filters",
    contentTemplate: 'tutorial.step_13.md',
    video: '//www.youtube.com/embed/S049FI8TP4A?list=PLhCf3AUOg4PgQoY_A6xWDQ70yaNtPYtZd'
  },
  {
    id: "14",
    title: 'Meteor methods with promises',
    seoTitle: 'Meteor methods with promises',
    route: "tutorials.angular1.meteor-methods-with-promises",
    path: "/tutorials/angular1/meteor-methods-with-promises",
    contentTemplate: 'tutorial.step_14.md',
    video: '//www.youtube.com/embed/qNUjZjfaYt8?list=PLhCf3AUOg4PgQoY_A6xWDQ70yaNtPYtZd'
  },
  {
    id: "15",
    title: 'Conditional template directives with AngularJS',
    seoTitle: 'Conditional template directives with AngularJS',
    route: "tutorials.angular1.conditional-template-directives-with-angularjs",
    path: "/tutorials/angular1/conditional-template-directives-with-angularjs",
    contentTemplate: 'tutorial.step_15.md',
    video: '//www.youtube.com/embed/KSlVThsNCss?list=PLhCf3AUOg4PgQoY_A6xWDQ70yaNtPYtZd'
  },
  {
    id: "16",
    title: 'Google Maps',
    seoTitle: 'Google Maps',
    route: "tutorials.angular1.google-maps",
    path: "/tutorials/angular1/google-maps",
    contentTemplate: 'tutorial.step_16.md',
    video: '//www.youtube.com/embed/A6qsm_RDc9Y?list=PLhCf3AUOg4PgQoY_A6xWDQ70yaNtPYtZd'
  },
  {
    id: "17",
    title: 'CSS, LESS and Bootstrap',
    seoTitle: 'CSS, LESS and Bootstrap',
    route: "tutorials.angular1.css-less-and-bootstrap",
    path: "/tutorials/angular1/css-less-and-bootstrap",
    contentTemplate: 'tutorial.step_17.md',
    video: '//www.youtube.com/embed/A6qsm_RDc9Y?list=PLhCf3AUOg4PgQoY_A6xWDQ70yaNtPYtZd'
  },
  {
    id: "18",
    title: 'angular-material and custom Angular auth forms',
    seoTitle: 'angular-material and custom Angular auth forms',
    route: "tutorials.angular1.angular-material-and-custom-angular-auth-forms",
    path: "/tutorials/angular1/angular-material-and-custom-angular-auth-forms",
    contentTemplate: 'tutorial.step_18.md',
    video: '//www.youtube.com/embed/A6qsm_RDc9Y?list=PLhCf3AUOg4PgQoY_A6xWDQ70yaNtPYtZd'
  },
  {
    id: "19",
    title: '3rdParty Libraries',
    seoTitle: '3rdParty Libraries',
    route: "tutorials.angular1.3rd-party-libraries",
    path: "/tutorials/angular1/3rd-party-libraries",
    contentTemplate: 'tutorial.step_19.md',
    video: '//www.youtube.com/embed/A6qsm_RDc9Y?list=PLhCf3AUOg4PgQoY_A6xWDQ70yaNtPYtZd'
  },
  {
    id: "20",
    title: 'Handling Files with CollectionFS',
    seoTitle: 'Handling Files with CollectionFS',
    route: "tutorials.angular1.handling-files-with-collectionfs",
    path: "/tutorials/angular1/handling-files-with-collectionfs",
    contentTemplate: 'tutorial.step_20.md',
    video: '//www.youtube.com/embed/A6qsm_RDc9Y?list=PLhCf3AUOg4PgQoY_A6xWDQ70yaNtPYtZd',
    previousCodeStep: '18'
  },
  {
    id: "21",
    title: 'Mobile & Packages Isolation',
    seoTitle: 'Mobile support and AngularJS modules isolation using Meteor packages',
    route: "tutorials.angular1.mobile-support",
    path: "/tutorials/angular1/mobile-support",
    contentTemplate: 'tutorial.step_21.md',
    previousCodeStep: '20'
  },
  {
    id: "22",
    title: 'Next Steps',
    seoTitle: 'Next Steps',
    route: "tutorials.angular1.next-steps",
    path: "/tutorials/angular1/next-steps",
    contentTemplate: 'tutorial.next_steps.html'
  }
];<|endoftext|>


<commit_before>jQuery(document).ready(function($) {

    $("#submit_status").click(function(event) {
        event.preventDefault()

        var newstatus = $("select[name=application_status]").val()
        var suggestion_id = $("input[name=suggestion_id]").val()
        var original = $("input[name=current_status]").val()
        var obj = {"status" : newstatus}

        $.ajax({
            type: "POST",
            url: "/admin/suggestion/" + suggestion_id,
            contentType: "application/json",
            dataType: "json",
            data: JSON.stringify(obj),
            success: function() {
                // alert("status updated")
                // update the hidden field
                $("input[name=current_status]").val(newstatus)
                $("#submit_status").attr("disabled", "disabled")
                $("#submit_status").attr("class", "btn")
            },
            error: function() {
                alert("ERROR: unable to update status.  If the problem persists, please contact your sysadmin.")
                // reset the pull-down
                $("select[name=application_status] option").filter(function() {return $(this).val() === original}).prop("selected", true)
                $("#submit_status").attr("disabled", "disabled")
                $("#submit_status").attr("class", "btn")
            },
            complete: function(req, status) {
                // alert(status)
            }
        })

    });

    $("select[name=application_status]").change(function() {
        var original = $("input[name=current_status]").val()
        var newstatus = $("select[name=application_status]").val()

        if (newstatus !== original) {
            $("#submit_status").removeAttr("disabled")
            $("#submit_status").attr("class", "btn btn-info")
        } else {
            $("#submit_status").attr("disabled", "disabled")
            $("#submit_status").attr("class", "btn")
        }
    });

    toggle_optional_field('waiver_policy', ['#waiver_policy_url']);
    toggle_optional_field('download_statistics', ['#download_statistics_url']);
    toggle_optional_field('plagiarism_screening', ['#plagiarism_screening_url']);
    toggle_optional_field('publishing_rights', ['#publishing_rights_url'], ["True", "Other"]);
    toggle_optional_field('copyright', ['#copyright_url'], ["True", "Other"]);
    toggle_optional_field('license_embedded', ['#license_embedded_url']);
    toggle_optional_field('processing_charges', ['#processing_charges_amount', '#processing_charges_currency']);
    toggle_optional_field('submission_charges', ['#submission_charges_amount', '#submission_charges_currency']);
    toggle_optional_field('license', ['#license_checkbox'], ["other"]);

    $('#country').select2();
    $('#processing_charges_currency').select2();
    $('#submission_charges_currency').select2();

    $("#keywords").select2({
        minimumInputLength: 1,
        tags: [],
        tokenSeparators: [","],
        maximumSelectionSize: 6
    });

    $("#languages").select2();

    autocomplete('#publisher', 'bibjson.publisher');
    autocomplete('#society_institution', 'bibjson.institution');
    autocomplete('#platform', 'bibjson.provider');

});

function toggle_optional_field(field_name, optional_field_selectors, values_to_show_for) {
    var values_to_show_for = values_to_show_for || ["True"];
    var main_field_selector = 'input[name=' + field_name + '][type="radio"]';

    // hide all optional fields first
    for (var i = 0; i < optional_field_selectors.length; i++) {
        $(optional_field_selectors[i]).parents('.control-group').hide();
    }

    // show them again if the correct radio button is chosen
    $(main_field_selector).each( function () {
        __init_optional_field(this, optional_field_selectors, values_to_show_for);
    });

    $(main_field_selector).change( function () {
        if ($.inArray(this.value, values_to_show_for) >= 0) {
            for (var i = 0; i < optional_field_selectors.length; i++) {
                $(optional_field_selectors[i]).parents('.control-group').show();
            }
        } else {
            for (var i = 0; i < optional_field_selectors.length; i++) {
                $(optional_field_selectors[i]).parents('.control-group').hide();
            }
        }
    });
}

function __init_optional_field(elem, optional_field_selectors, values_to_show_for) {
    var values_to_show_for = values_to_show_for || ["True"];
    var main_field = $(elem);

    if (main_field.is(':checked') && $.inArray(main_field.val(), values_to_show_for) >= 0) {
        for (var i = 0; i < optional_field_selectors.length; i++) {
            $(optional_field_selectors[i]).parents('.control-group').show();
        }
    }
}

function autocomplete(selector, doc_field, doc_type) {
    var doc_type = doc_type || "journal";
    $(selector).select2({
        minimumInputLength: 3,
        ajax: {
            url: "../autocomplete/" + doc_type + "/" + doc_field,
            dataType: 'json',
            data: function (term, page) {
                return {
                    q: term
                };
            },
            results: function (data, page) {
                return { results: data["suggestions"] };
            }
        },
        createSearchChoice: function(term) {return {"id":term, "text": term};}
    });
}
<commit_msg>application form: hide optional license_url field appropriately<commit_after>jQuery(document).ready(function($) {

    $("#submit_status").click(function(event) {
        event.preventDefault()

        var newstatus = $("select[name=application_status]").val()
        var suggestion_id = $("input[name=suggestion_id]").val()
        var original = $("input[name=current_status]").val()
        var obj = {"status" : newstatus}

        $.ajax({
            type: "POST",
            url: "/admin/suggestion/" + suggestion_id,
            contentType: "application/json",
            dataType: "json",
            data: JSON.stringify(obj),
            success: function() {
                // alert("status updated")
                // update the hidden field
                $("input[name=current_status]").val(newstatus)
                $("#submit_status").attr("disabled", "disabled")
                $("#submit_status").attr("class", "btn")
            },
            error: function() {
                alert("ERROR: unable to update status.  If the problem persists, please contact your sysadmin.")
                // reset the pull-down
                $("select[name=application_status] option").filter(function() {return $(this).val() === original}).prop("selected", true)
                $("#submit_status").attr("disabled", "disabled")
                $("#submit_status").attr("class", "btn")
            },
            complete: function(req, status) {
                // alert(status)
            }
        })

    });

    $("select[name=application_status]").change(function() {
        var original = $("input[name=current_status]").val()
        var newstatus = $("select[name=application_status]").val()

        if (newstatus !== original) {
            $("#submit_status").removeAttr("disabled")
            $("#submit_status").attr("class", "btn btn-info")
        } else {
            $("#submit_status").attr("disabled", "disabled")
            $("#submit_status").attr("class", "btn")
        }
    });

    toggle_optional_field('waiver_policy', ['#waiver_policy_url']);
    toggle_optional_field('download_statistics', ['#download_statistics_url']);
    toggle_optional_field('plagiarism_screening', ['#plagiarism_screening_url']);
    toggle_optional_field('publishing_rights', ['#publishing_rights_url'], ["True", "Other"]);
    toggle_optional_field('copyright', ['#copyright_url'], ["True", "Other"]);
    toggle_optional_field('license_embedded', ['#license_embedded_url']);
    toggle_optional_field('processing_charges', ['#processing_charges_amount', '#processing_charges_currency']);
    toggle_optional_field('submission_charges', ['#submission_charges_amount', '#submission_charges_currency']);
    toggle_optional_field('license', ['#license_checkbox'], ["other"]);
    toggle_optional_field('license', ['#license_url'], ['cc-by', 'cc-by-sa', 'cc-by-nc', 'cc-by-nd', 'cc-by-nc-nd', "other"]);

    $('#country').select2();
    $('#processing_charges_currency').select2();
    $('#submission_charges_currency').select2();

    $("#keywords").select2({
        minimumInputLength: 1,
        tags: [],
        tokenSeparators: [","],
        maximumSelectionSize: 6
    });

    $("#languages").select2();

    autocomplete('#publisher', 'bibjson.publisher');
    autocomplete('#society_institution', 'bibjson.institution');
    autocomplete('#platform', 'bibjson.provider');

});

function toggle_optional_field(field_name, optional_field_selectors, values_to_show_for) {
    var values_to_show_for = values_to_show_for || ["True"];
    var main_field_selector = 'input[name=' + field_name + '][type="radio"]';

    // hide all optional fields first
    for (var i = 0; i < optional_field_selectors.length; i++) {
        $(optional_field_selectors[i]).parents('.control-group').hide();
    }

    // show them again if the correct radio button is chosen
    $(main_field_selector).each( function () {
        __init_optional_field(this, optional_field_selectors, values_to_show_for);
    });

    $(main_field_selector).change( function () {
        if ($.inArray(this.value, values_to_show_for) >= 0) {
            for (var i = 0; i < optional_field_selectors.length; i++) {
                $(optional_field_selectors[i]).parents('.control-group').show();
            }
        } else {
            for (var i = 0; i < optional_field_selectors.length; i++) {
                $(optional_field_selectors[i]).parents('.control-group').hide();
            }
        }
    });
}

function __init_optional_field(elem, optional_field_selectors, values_to_show_for) {
    var values_to_show_for = values_to_show_for || ["True"];
    var main_field = $(elem);

    if (main_field.is(':checked') && $.inArray(main_field.val(), values_to_show_for) >= 0) {
        for (var i = 0; i < optional_field_selectors.length; i++) {
            $(optional_field_selectors[i]).parents('.control-group').show();
        }
    }
}

function autocomplete(selector, doc_field, doc_type) {
    var doc_type = doc_type || "journal";
    $(selector).select2({
        minimumInputLength: 3,
        ajax: {
            url: "../autocomplete/" + doc_type + "/" + doc_field,
            dataType: 'json',
            data: function (term, page) {
                return {
                    q: term
                };
            },
            results: function (data, page) {
                return { results: data["suggestions"] };
            }
        },
        createSearchChoice: function(term) {return {"id":term, "text": term};}
    });
}
<|endoftext|>


<commit_before>define(['app', '/app/views/forms/edit/edit.js', '/app/js/common.js',
        '../view/view-abs-permit.directive.js'], function (app) {

  app.controller("editAbsPermit", ["$scope", "$http", "Thesaurus", "guid", "$filter", "$q", "Enumerable",
                                    "editFormUtility", "$controller","IStorage","$location", "commonjs",
   function ($scope, $http, Thesaurus, guid, $filter, $q, Enumerable, editFormUtility, $controller, storage, $location, commonjs) {
    $controller('editController', {$scope: $scope});

    $scope.path= $location.path();

    _.extend($scope.options, {
      usages		: function () { return $http.get("/api/v2013/thesaurus/domains/A7B77788-8C90-4849-9327-E181E9522F3A/terms",	{ cache: true }).then(function (o) { return o.data; }); },
      keywords  : function () { return $q.all([$http.get("/api/v2013/thesaurus/domains/1A22EAAB-9BBC-4543-890E-DEF913F59E98/terms", { cache: true }),
                               $http.get("/api/v2013/thesaurus/terms/5B6177DD-5E5E-434E-8CB7-D63D67D5EBED",   { cache: true })])
                           .then(function (o) {
                                      var data = o[0].data;
                                      data.push(o[1].data)
                                      return Thesaurus.buildTree(data);
                                    }); },
     permits    : function () {return commonjs.loadSchemaDocumentsForDropdown('absPermit', $scope.document.header.identifier);},
    });

    //==================================
    //
    //==================================
    $scope.$watch("document.gisFiles", function () {

      var qLinks = [];
      var qGis = [];

      if ($scope.document)
        qLinks = $scope.document.gisFiles || [];

      /* global L: true */ // JSHint for leaflet

      angular.forEach(qLinks, function (link) {
        qGis.push($http.get(link.url).then(function (res) {
          return L.geoJson(res.data);
        }));
      });

      $q.all(qGis).then(function (layers) {
        $scope.gisLayer = layers;
      });
    });

    //==================================
    //
    //==================================
    $scope.$on("loadDocument", function(evt, info) {

      if(info.schema!="absPermit")
        return;

      if(info.identifier)//$scope.status=="loading"
          $q.when(editFormUtility.documentExists(info.identifier),function(exists){
              $scope.documentExists = exists;

              //amendment intent should be entered by users for every edit/ clear it on load if any from previous update
              if($scope.document.amendmentIntent != undefined){
                if($scope.document.amendmentIntent == 1)
                  $scope.isIRCCRevoked = true;

                if(!$scope.isIRCCRevoked)
                  $scope.document.amendmentIntent = undefined;
              }

              if(!$scope.isIRCCRevoked && $scope.document.amendmentDescription)
                $scope.document.amendmentDescription = undefined;

          });
    });

    //==================================
    //
    //==================================
    $scope.getCleanDocument = function(document) {

      document = document || $scope.document;

      if (!document)
        return undefined;

      document = angular.fromJson(angular.toJson(document));

      if (!document.picGranted) {
        document.picInformation = undefined;
        document.picDocuments = undefined;
      }

      if (!document.matEstablished) {
        document.matInformation = undefined;
        document.matDocuments = undefined;
      }

      if (document.gisFiles && document.gisFiles.length===0) {
        document.gisFiles = undefined;
      }

      if (document.amendedPermits && document.amendedPermits.length===0) {
        document.amendedPermits = undefined;
      }

      if (!document.amendedPermits) {
        document.consentedAmendment = undefined;
        //document.amendmentDescription = undefined;
      }
      if (document.providersConfidential) {
        document.providers = undefined;
      }
      if (document.entitiesToWhomPICGrantedConfidential) {
        document.entitiesToWhomPICGranted = undefined;
      }
      if(document.usagesConfidential){
          document.usages = undefined;
      }
      if (document.dateOfExpiry=="") {
        document.dateOfExpiry = undefined;
      }
      if (document.subjectMatterConfidential) {
        document.subjectMatter = undefined;
      }
      if(!$scope.documentExists){
        document.amendmentIntent = undefined;
        document.amendmentDescription = undefined;
      }
      else
        document.amendmentIntent = 1;

      if(!$scope.isOthers()){
        document.keywordOther = undefined;
      }
      if (/^\s*$/g.test(document.notes))
        document.notes = undefined;

      return document;
    };

    //==================================
    //
    //==================================
    $scope.isCommercial = function (document) {
      document = document || $scope.document;

      if (!document || !document.usages)
        return false;

      var qLibraries = Enumerable.from(document.usages);

      return qLibraries.Any(function (o) { return o.identifier == "5E833A3F-87D1-4ADD-8701-9F1B76383017"; });
    };

    //This edit contact stuff should NOT be here... it should probably be in a directive or something.
    //==================================
    //
    //==================================
    $scope.editContact = function(property) {
      $scope.editedProperty = property;
      $scope.editedContact  = clone($scope.document[property] || { source : guid(),  type: "organization" });
    };

    //==================================
    //
    //==================================
    $scope.isOthers = function(document) {

      document = document || $scope.document;
      if (!document || !document.keywords)
        return false;

      var qLibraries = Enumerable.from(document.keywords);

      return qLibraries.any(function (o) { return o.identifier == "5B6177DD-5E5E-434E-8CB7-D63D67D5EBED"; });

    };

    //==================================
    //
    //==================================
    $scope.saveContact = function() {

      if(!$scope.editedProperty)
        return;

      $scope.document[$scope.editedProperty] = clone($scope.editedContact);
    };

    $scope.setDocument();
  }]);
});
<commit_msg>fix irrcc amendment intent issue<commit_after>define(['app', '/app/views/forms/edit/edit.js', '/app/js/common.js',
        '../view/view-abs-permit.directive.js'], function (app) {

  app.controller("editAbsPermit", ["$scope", "$http", "Thesaurus", "guid", "$filter", "$q", "Enumerable",
                                    "editFormUtility", "$controller","IStorage","$location", "commonjs",
   function ($scope, $http, Thesaurus, guid, $filter, $q, Enumerable, editFormUtility, $controller, storage, $location, commonjs) {
    $controller('editController', {$scope: $scope});

    $scope.path= $location.path();

    _.extend($scope.options, {
      usages		: function () { return $http.get("/api/v2013/thesaurus/domains/A7B77788-8C90-4849-9327-E181E9522F3A/terms",	{ cache: true }).then(function (o) { return o.data; }); },
      keywords  : function () { return $q.all([$http.get("/api/v2013/thesaurus/domains/1A22EAAB-9BBC-4543-890E-DEF913F59E98/terms", { cache: true }),
                               $http.get("/api/v2013/thesaurus/terms/5B6177DD-5E5E-434E-8CB7-D63D67D5EBED",   { cache: true })])
                           .then(function (o) {
                                      var data = o[0].data;
                                      data.push(o[1].data)
                                      return Thesaurus.buildTree(data);
                                    }); },
     permits    : function () {return commonjs.loadSchemaDocumentsForDropdown('absPermit', $scope.document.header.identifier);},
    });

    //==================================
    //
    //==================================
    $scope.$watch("document.gisFiles", function () {

      var qLinks = [];
      var qGis = [];

      if ($scope.document)
        qLinks = $scope.document.gisFiles || [];

      /* global L: true */ // JSHint for leaflet

      angular.forEach(qLinks, function (link) {
        qGis.push($http.get(link.url).then(function (res) {
          return L.geoJson(res.data);
        }));
      });

      $q.all(qGis).then(function (layers) {
        $scope.gisLayer = layers;
      });
    });

    //==================================
    //
    //==================================
    $scope.$on("loadDocument", function(evt, info) {

      if(info.schema!="absPermit")
        return;

      if(info.identifier)//$scope.status=="loading"
          $q.when(editFormUtility.documentExists(info.identifier),function(exists){
              $scope.documentExists = exists;

              //amendment intent should be entered by users for every edit/ clear it on load if any from previous update
              if($scope.document.amendmentIntent != undefined){
                if($scope.document.amendmentIntent == 1)
                  $scope.isIRCCRevoked = true;

                if(!$scope.isIRCCRevoked)
                  $scope.document.amendmentIntent = undefined;
              }

              if(!$scope.isIRCCRevoked && $scope.document.amendmentDescription)
                $scope.document.amendmentDescription = undefined;

          });
    });

    //==================================
    //
    //==================================
    $scope.getCleanDocument = function(document) {

      document = document || $scope.document;

      if (!document)
        return undefined;

      document = angular.fromJson(angular.toJson(document));

      if (!document.picGranted) {
        document.picInformation = undefined;
        document.picDocuments = undefined;
      }

      if (!document.matEstablished) {
        document.matInformation = undefined;
        document.matDocuments = undefined;
      }

      if (document.gisFiles && document.gisFiles.length===0) {
        document.gisFiles = undefined;
      }

      if (document.amendedPermits && document.amendedPermits.length===0) {
        document.amendedPermits = undefined;
      }

      if (!document.amendedPermits) {
        document.consentedAmendment = undefined;
        //document.amendmentDescription = undefined;
      }
      if (document.providersConfidential) {
        document.providers = undefined;
      }
      if (document.entitiesToWhomPICGrantedConfidential) {
        document.entitiesToWhomPICGranted = undefined;
      }
      if(document.usagesConfidential){
          document.usages = undefined;
      }
      if (document.dateOfExpiry=="") {
        document.dateOfExpiry = undefined;
      }
      if (document.subjectMatterConfidential) {
        document.subjectMatter = undefined;
      }
      if(!$scope.documentExists){
        document.amendmentIntent = undefined;
        document.amendmentDescription = undefined;
      }
      else
        document.amendmentIntent = 0;

      if(!$scope.isOthers()){
        document.keywordOther = undefined;
      }
      if (/^\s*$/g.test(document.notes))
        document.notes = undefined;

      return document;
    };

    //==================================
    //
    //==================================
    $scope.isCommercial = function (document) {
      document = document || $scope.document;

      if (!document || !document.usages)
        return false;

      var qLibraries = Enumerable.from(document.usages);

      return qLibraries.Any(function (o) { return o.identifier == "5E833A3F-87D1-4ADD-8701-9F1B76383017"; });
    };

    //This edit contact stuff should NOT be here... it should probably be in a directive or something.
    //==================================
    //
    //==================================
    $scope.editContact = function(property) {
      $scope.editedProperty = property;
      $scope.editedContact  = clone($scope.document[property] || { source : guid(),  type: "organization" });
    };

    //==================================
    //
    //==================================
    $scope.isOthers = function(document) {

      document = document || $scope.document;
      if (!document || !document.keywords)
        return false;

      var qLibraries = Enumerable.from(document.keywords);

      return qLibraries.any(function (o) { return o.identifier == "5B6177DD-5E5E-434E-8CB7-D63D67D5EBED"; });

    };

    //==================================
    //
    //==================================
    $scope.saveContact = function() {

      if(!$scope.editedProperty)
        return;

      $scope.document[$scope.editedProperty] = clone($scope.editedContact);
    };

    $scope.setDocument();
  }]);
});
<|endoftext|>


<commit_before>
window._ = require('lodash');

/**
 * We'll load jQuery and the Bootstrap jQuery plugin which provides support
 * for JavaScript based Bootstrap features such as modals and tabs. This
 * code may be modified to fit the specific needs of your application.
 */

window.$ = window.jQuery = require('jquery');
require('bootstrap-sass');

require('admin-lte');
window.toastr = require('toastr');
require('icheck');

/**
 * Vue is a modern JavaScript library for building interactive web interfaces
 * using reactive data binding and reusable components. Vue's API is clean
 * and simple, leaving you to focus on building your next great project.
 */

window.Vue = require('vue');
require('vue-resource');

/**
 * We'll register a HTTP interceptor to attach the "CSRF" header to each of
 * the outgoing requests issued by this application. The CSRF middleware
 * included with Laravel will automatically verify the header's value.
 */

Vue.http.interceptors.push((request, next) => {
    request.headers['X-CSRF-TOKEN'] = Laravel.csrfToken;

    next();
});

/**
 * Echo exposes an expressive API for subscribing to channels and listening
 * for events that are broadcast by Laravel. Echo and event broadcasting
 * allows your team to easily build robust real-time web applications.
 */

// import Echo from "laravel-echo"

// window.Echo = new Echo({
//     broadcaster: 'pusher',
//     key: 'your-pusher-key'
// });
<commit_msg>Changed bootstrap-sass for bootstrap-less in resources/assests/js/bootstrap.js<commit_after>
window._ = require('lodash');

/**
 * We'll load jQuery and the Bootstrap jQuery plugin which provides support
 * for JavaScript based Bootstrap features such as modals and tabs. This
 * code may be modified to fit the specific needs of your application.
 */

window.$ = window.jQuery = require('jquery');
require('bootstrap-less');

require('admin-lte');
window.toastr = require('toastr');
require('icheck');

/**
 * Vue is a modern JavaScript library for building interactive web interfaces
 * using reactive data binding and reusable components. Vue's API is clean
 * and simple, leaving you to focus on building your next great project.
 */

window.Vue = require('vue');
require('vue-resource');

/**
 * We'll register a HTTP interceptor to attach the "CSRF" header to each of
 * the outgoing requests issued by this application. The CSRF middleware
 * included with Laravel will automatically verify the header's value.
 */

Vue.http.interceptors.push((request, next) => {
    request.headers['X-CSRF-TOKEN'] = Laravel.csrfToken;

    next();
});

/**
 * Echo exposes an expressive API for subscribing to channels and listening
 * for events that are broadcast by Laravel. Echo and event broadcasting
 * allows your team to easily build robust real-time web applications.
 */

// import Echo from "laravel-echo"

// window.Echo = new Echo({
//     broadcaster: 'pusher',
//     key: 'your-pusher-key'
// });
<|endoftext|>


<commit_before>if (typeof(module) !== 'undefined' && typeof(exports) !== 'undefined') {
    module.exports = OAuth;
    var CryptoJS = require("crypto-js");
}

/**
 * Constructor
 * @param {Object} opts consumer key and secret
 */
function OAuth(opts) {
    if(!(this instanceof OAuth)) {
        return new OAuth(opts);
    }

    if(!opts) {
        opts = {};
    }

    if(!opts.consumer) {
        throw new Error('consumer option is required');
    }

    this.consumer            = opts.consumer;
    this.signature_method    = opts.signature_method || 'HMAC-SHA1';
    this.nonce_length        = opts.nonce_length || 32;
    this.version             = opts.version || '1.0';
    this.parameter_seperator = opts.parameter_seperator || ', ';

    if(typeof opts.last_ampersand === 'undefined') {
        this.last_ampersand = true;
    } else {
        this.last_ampersand = opts.last_ampersand;
    }

    switch (this.signature_method) {
        case 'HMAC-SHA1':
            this.hash = function(base_string, key) {
                return CryptoJS.HmacSHA1(base_string, key).toString(CryptoJS.enc.Base64);
            };
            break;

        case 'HMAC-SHA256':
            this.hash = function(base_string, key) {
                return CryptoJS.HmacSHA256(base_string, key).toString(CryptoJS.enc.Base64);
            };
            break;

        case 'PLAINTEXT':
            this.hash = function(base_string, key) {
                return key;
            };
            break;

        case 'RSA-SHA1':
            throw new Error('oauth-1.0a does not support this signature method right now. Coming Soon...');
        default:
            throw new Error('The OAuth 1.0a protocol defines three signature methods: HMAC-SHA1, RSA-SHA1, and PLAINTEXT only');
    }
}

/**
 * OAuth request authorize
 * @param  {Object} request data
 * {
 *     method,
 *     url,
 *     data
 * }
 * @param  {Object} public and secret token
 * @return {Object} OAuth Authorized data
 */
OAuth.prototype.authorize = function(request, token) {
    var oauth_data = {
        oauth_consumer_key: this.consumer.public,
        oauth_nonce: this.getNonce(),
        oauth_signature_method: this.signature_method,
        oauth_timestamp: this.getTimeStamp(),
        oauth_version: this.version
    };

    if(!token) {
        token = {};
    }

    if(token.public) {
        oauth_data.oauth_token = token.public;
    }

    if(!request.data) {
        request.data = {};
    }

    oauth_data.oauth_signature = this.getSignature(request, token.secret, oauth_data);

    return oauth_data;
};

/**
 * Create a OAuth Signature
 * @param  {Object} request data
 * @param  {Object} token_secret public and secret token
 * @param  {Object} oauth_data   OAuth data
 * @return {String} Signature
 */
OAuth.prototype.getSignature = function(request, token_secret, oauth_data) {
    return this.hash(this.getBaseString(request, oauth_data), this.getSigningKey(token_secret));
};

/**
 * Base String = Method + Base Url + ParameterString
 * @param  {Object} request data
 * @param  {Object} OAuth data
 * @return {String} Base String
 */
OAuth.prototype.getBaseString = function(request, oauth_data) {
    return request.method.toUpperCase() + '&' + this.percentEncode(this.getBaseUrl(request.url)) + '&' + this.percentEncode(this.getParameterString(request, oauth_data));
};

/**
 * Get data from url
 * -> merge with oauth data
 * -> percent encode key & value
 * -> sort
 *
 * @param  {Object} request data
 * @param  {Object} OAuth data
 * @return {Object} Parameter string data
 */
OAuth.prototype.getParameterString = function(request, oauth_data) {
    var base_string_data = this.sortObject(this.percentEncodeData(this.mergeObject(oauth_data, this.mergeObject(request.data, this.deParamUrl(request.url)))));

    var data_str = '';

    //base_string_data to string
    for(var key in base_string_data) {
        var value = base_string_data[key];
        // check if the value is an array
        // this means that this key has multiple values
        if (value && Array.isArray(value)){
          // sort the array first
          value.sort();

          var valString = "";
          // serialize all values for this key: e.g. formkey=formvalue1&formkey=formvalue2
          value.forEach((function(item, i){
            valString += key + '=' + item;
            if (i < value.length){
              valString += "&";
            }
          }).bind(this));
          data_str += valString;
        } else {
          data_str += key + '=' + value + '&';
        }
    }

    //remove the last character
    data_str = data_str.substr(0, data_str.length - 1);
    return data_str;
};

/**
 * Create a Signing Key
 * @param  {String} token_secret Secret Token
 * @return {String} Signing Key
 */
OAuth.prototype.getSigningKey = function(token_secret) {
    token_secret = token_secret || '';

    if(!this.last_ampersand && !token_secret) {
        return this.percentEncode(this.consumer.secret);
    }

    return this.percentEncode(this.consumer.secret) + '&' + this.percentEncode(token_secret);
};

/**
 * Get base url
 * @param  {String} url
 * @return {String}
 */
OAuth.prototype.getBaseUrl = function(url) {
    return url.split('?')[0];
};

/**
 * Get data from String
 * @param  {String} string
 * @return {Object}
 */
OAuth.prototype.deParam = function(string) {
    var arr = string.split('&');
    var data = {};

    for(var i = 0; i < arr.length; i++) {
        var item = arr[i].split('=');
        data[item[0]] = decodeURIComponent(item[1]);
    }
    return data;
};

/**
 * Get data from url
 * @param  {String} url
 * @return {Object}
 */
OAuth.prototype.deParamUrl = function(url) {
    var tmp = url.split('?');

    if (tmp.length === 1)
        return {};

    return this.deParam(tmp[1]);
};

/**
 * Percent Encode
 * @param  {String} str
 * @return {String} percent encoded string
 */
OAuth.prototype.percentEncode = function(str) {
    return encodeURIComponent(str)
        .replace(/\!/g, "%21")
        .replace(/\*/g, "%2A")
        .replace(/\'/g, "%27")
        .replace(/\(/g, "%28")
        .replace(/\)/g, "%29");
};

/**
 * Percent Encode Object
 * @param  {Object} data
 * @return {Object} percent encoded data
 */
OAuth.prototype.percentEncodeData = function(data) {
    var result = {};

    for(var key in data) {
        var value = data[key];
        // check if the value is an array
        if (value && Array.isArray(value)){
          var newValue = [];
          // percentEncode every value
          value.forEach((function(val){
            newValue.push(this.percentEncode(val));
          }).bind(this));
          value = newValue;
        } else {
          value = this.percentEncode(value);
        }
        result[this.percentEncode(key)] = value;
    }

    return result;
};

/**
 * Get OAuth data as Header
 * @param  {Object} oauth_data
 * @return {String} Header data key - value
 */
OAuth.prototype.toHeader = function(oauth_data) {
    oauth_data = this.sortObject(oauth_data);

    var header_value = 'OAuth ';

    for(var key in oauth_data) {
        if (key.indexOf('oauth_') === -1)
            continue;
        header_value += this.percentEncode(key) + '="' + this.percentEncode(oauth_data[key]) + '"' + this.parameter_seperator;
    }

    return {
        Authorization: header_value.substr(0, header_value.length - this.parameter_seperator.length) //cut the last chars
    };
};

/**
 * Create a random word characters string with input length
 * @return {String} a random word characters string
 */
OAuth.prototype.getNonce = function() {
    var word_characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
    var result = '';

    for(var i = 0; i < this.nonce_length; i++) {
        result += word_characters[parseInt(Math.random() * word_characters.length, 10)];
    }

    return result;
};

/**
 * Get Current Unix TimeStamp
 * @return {Int} current unix timestamp
 */
OAuth.prototype.getTimeStamp = function() {
    return parseInt(new Date().getTime()/1000, 10);
};

////////////////////// HELPER FUNCTIONS //////////////////////

/**
 * Merge object
 * @param  {Object} obj1
 * @param  {Object} obj2
 * @return {Object}
 */
OAuth.prototype.mergeObject = function(obj1, obj2) {
    var merged_obj = obj1;
    for(var key in obj2) {
        merged_obj[key] = obj2[key];
    }
    return merged_obj;
};

/**
 * Sort object by key
 * @param  {Object} data
 * @return {Object} sorted object
 */
OAuth.prototype.sortObject = function(data) {
    var keys = Object.keys(data);
    var result = {};

    keys.sort();

    for(var i = 0; i < keys.length; i++) {
        var key = keys[i];
        result[key] = data[key];
    }

    return result;
};
<commit_msg>sign xauth headers<commit_after>if (typeof(module) !== 'undefined' && typeof(exports) !== 'undefined') {
    module.exports = OAuth;
    var CryptoJS = require("crypto-js");
}

/**
 * Constructor
 * @param {Object} opts consumer key and secret
 */
function OAuth(opts) {
    if(!(this instanceof OAuth)) {
        return new OAuth(opts);
    }

    if(!opts) {
        opts = {};
    }

    if(!opts.consumer) {
        throw new Error('consumer option is required');
    }

    this.consumer            = opts.consumer;
    this.signature_method    = opts.signature_method || 'HMAC-SHA1';
    this.nonce_length        = opts.nonce_length || 32;
    this.version             = opts.version || '1.0';
    this.parameter_seperator = opts.parameter_seperator || ', ';

    if(typeof opts.last_ampersand === 'undefined') {
        this.last_ampersand = true;
    } else {
        this.last_ampersand = opts.last_ampersand;
    }

    switch (this.signature_method) {
        case 'HMAC-SHA1':
            this.hash = function(base_string, key) {
                return CryptoJS.HmacSHA1(base_string, key).toString(CryptoJS.enc.Base64);
            };
            break;

        case 'HMAC-SHA256':
            this.hash = function(base_string, key) {
                return CryptoJS.HmacSHA256(base_string, key).toString(CryptoJS.enc.Base64);
            };
            break;

        case 'PLAINTEXT':
            this.hash = function(base_string, key) {
                return key;
            };
            break;

        case 'RSA-SHA1':
            throw new Error('oauth-1.0a does not support this signature method right now. Coming Soon...');
        default:
            throw new Error('The OAuth 1.0a protocol defines three signature methods: HMAC-SHA1, RSA-SHA1, and PLAINTEXT only');
    }
}

/**
 * OAuth request authorize
 * @param  {Object} request data
 * {
 *     method,
 *     url,
 *     data
 * }
 * @param  {Object} public and secret token
 * @return {Object} OAuth Authorized data
 */
OAuth.prototype.authorize = function(request, token) {
    var oauth_data = {
        oauth_consumer_key: this.consumer.public,
        oauth_nonce: this.getNonce(),
        oauth_signature_method: this.signature_method,
        oauth_timestamp: this.getTimeStamp(),
        oauth_version: this.version
    };

    if(!token) {
        token = {};
    }

    if(token.public) {
        oauth_data.oauth_token = token.public;
    }

    if(!request.data) {
        request.data = {};
    }

    oauth_data.oauth_signature = this.getSignature(request, token.secret, oauth_data);

    return oauth_data;
};

/**
 * Create a OAuth Signature
 * @param  {Object} request data
 * @param  {Object} token_secret public and secret token
 * @param  {Object} oauth_data   OAuth data
 * @return {String} Signature
 */
OAuth.prototype.getSignature = function(request, token_secret, oauth_data) {
    return this.hash(this.getBaseString(request, oauth_data), this.getSigningKey(token_secret));
};

/**
 * Base String = Method + Base Url + ParameterString
 * @param  {Object} request data
 * @param  {Object} OAuth data
 * @return {String} Base String
 */
OAuth.prototype.getBaseString = function(request, oauth_data) {
    return request.method.toUpperCase() + '&' + this.percentEncode(this.getBaseUrl(request.url)) + '&' + this.percentEncode(this.getParameterString(request, oauth_data));
};

/**
 * Get data from url
 * -> merge with oauth data
 * -> percent encode key & value
 * -> sort
 *
 * @param  {Object} request data
 * @param  {Object} OAuth data
 * @return {Object} Parameter string data
 */
OAuth.prototype.getParameterString = function(request, oauth_data) {
    var base_string_data = this.sortObject(this.percentEncodeData(this.mergeObject(oauth_data, this.mergeObject(request.data, this.deParamUrl(request.url)))));

    var data_str = '';

    //base_string_data to string
    for(var key in base_string_data) {
        var value = base_string_data[key];
        // check if the value is an array
        // this means that this key has multiple values
        if (value && Array.isArray(value)){
          // sort the array first
          value.sort();

          var valString = "";
          // serialize all values for this key: e.g. formkey=formvalue1&formkey=formvalue2
          value.forEach((function(item, i){
            valString += key + '=' + item;
            if (i < value.length){
              valString += "&";
            }
          }).bind(this));
          data_str += valString;
        } else {
          data_str += key + '=' + value + '&';
        }
    }

    //remove the last character
    data_str = data_str.substr(0, data_str.length - 1);
    return data_str;
};

/**
 * Create a Signing Key
 * @param  {String} token_secret Secret Token
 * @return {String} Signing Key
 */
OAuth.prototype.getSigningKey = function(token_secret) {
    token_secret = token_secret || '';

    if(!this.last_ampersand && !token_secret) {
        return this.percentEncode(this.consumer.secret);
    }

    return this.percentEncode(this.consumer.secret) + '&' + this.percentEncode(token_secret);
};

/**
 * Get base url
 * @param  {String} url
 * @return {String}
 */
OAuth.prototype.getBaseUrl = function(url) {
    return url.split('?')[0];
};

/**
 * Get data from String
 * @param  {String} string
 * @return {Object}
 */
OAuth.prototype.deParam = function(string) {
    var arr = string.split('&');
    var data = {};

    for(var i = 0; i < arr.length; i++) {
        var item = arr[i].split('=');
        data[item[0]] = decodeURIComponent(item[1]);
    }
    return data;
};

/**
 * Get data from url
 * @param  {String} url
 * @return {Object}
 */
OAuth.prototype.deParamUrl = function(url) {
    var tmp = url.split('?');

    if (tmp.length === 1)
        return {};

    return this.deParam(tmp[1]);
};

/**
 * Percent Encode
 * @param  {String} str
 * @return {String} percent encoded string
 */
OAuth.prototype.percentEncode = function(str) {
    return encodeURIComponent(str)
        .replace(/\!/g, "%21")
        .replace(/\*/g, "%2A")
        .replace(/\'/g, "%27")
        .replace(/\(/g, "%28")
        .replace(/\)/g, "%29");
};

/**
 * Percent Encode Object
 * @param  {Object} data
 * @return {Object} percent encoded data
 */
OAuth.prototype.percentEncodeData = function(data) {
    var result = {};

    for(var key in data) {
        var value = data[key];
        // check if the value is an array
        if (value && Array.isArray(value)){
          var newValue = [];
          // percentEncode every value
          value.forEach((function(val){
            newValue.push(this.percentEncode(val));
          }).bind(this));
          value = newValue;
        } else {
          value = this.percentEncode(value);
        }
        result[this.percentEncode(key)] = value;
    }

    return result;
};

/**
 * Get OAuth data as Header
 * @param  {Object} oauth_data
 * @return {String} Header data key - value
 */
OAuth.prototype.toHeader = function(oauth_data) {
    oauth_data = this.sortObject(oauth_data);

    var header_value = 'OAuth ';

    for(var key in oauth_data) {
        if (key.indexOf('oauth_') === -1 && key.indexOf('x_auth_') === -1)
            continue;
        header_value += this.percentEncode(key) + '="' + this.percentEncode(oauth_data[key]) + '"' + this.parameter_seperator;
    }

    return {
        Authorization: header_value.substr(0, header_value.length - this.parameter_seperator.length) //cut the last chars
    };
};

/**
 * Create a random word characters string with input length
 * @return {String} a random word characters string
 */
OAuth.prototype.getNonce = function() {
    var word_characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
    var result = '';

    for(var i = 0; i < this.nonce_length; i++) {
        result += word_characters[parseInt(Math.random() * word_characters.length, 10)];
    }

    return result;
};

/**
 * Get Current Unix TimeStamp
 * @return {Int} current unix timestamp
 */
OAuth.prototype.getTimeStamp = function() {
    return parseInt(new Date().getTime()/1000, 10);
};

////////////////////// HELPER FUNCTIONS //////////////////////

/**
 * Merge object
 * @param  {Object} obj1
 * @param  {Object} obj2
 * @return {Object}
 */
OAuth.prototype.mergeObject = function(obj1, obj2) {
    var merged_obj = obj1;
    for(var key in obj2) {
        merged_obj[key] = obj2[key];
    }
    return merged_obj;
};

/**
 * Sort object by key
 * @param  {Object} data
 * @return {Object} sorted object
 */
OAuth.prototype.sortObject = function(data) {
    var keys = Object.keys(data);
    var result = {};

    keys.sort();

    for(var i = 0; i < keys.length; i++) {
        var key = keys[i];
        result[key] = data[key];
    }

    return result;
};
<|endoftext|>