Source: sputils.helpers.js

(function () {
  'use strict';

  /**
   * @ignore
   * @summary
   * Uses SP builtin dependency management to load a specific dependency.
   * @function sputils.helpers.resolveDependency
   * @param {object} dep
   * <pre>
   * {
   *   file: {string},
   *   namespace: {string}
   * }
   * - `file` is the filename of the module, e.g. sp.taxonomy.js
   * - `namespace` is the namespace provided by the SP module, e.g. SP.Taxonomy
   * </pre>
   * @returns {Promise<Void>} - resolved when the SP dependency is loaded.
   */
  var resolveDependency = function (dep) {
    var file = dep.file,
        namespace = dep.namespace;

    return new Promise(function (resolve, reject) {
      SP.SOD.registerSod(file, SP.Utilities.Utility.getLayoutsPageUrl(file));
      SP.SOD.executeFunc(file, namespace, resolve);
    });
  };


  /**
   * Returns a promise which resolves when
   * all the specified dependencies are loaded.
   *
   * Takes a list of strings which correspond
   * to SP JS dependencies. Each dependency is
   * registered and loaded.
   *
   * Uses SP builtin dependency management to load a specific dependency.
   *
   * @function sputils.helpers.withSharePointDependencies
   * @param {Array<dep>} deps
   * An array containing hashes with the following keys: {file, namespace}
   * <pre>
   * [ { file: string, namespace: string } ]
   * </pre>
   * @returns {Promise<Void>} - resolved when all deps are loaded.
   */
  var withSharePointDependencies = function (deps) {
    return new Promise(function (resolve, reject) {
      // sp.js is a dependency for our resolveDependency
      // function, so we load it separately before
      // the rest.
      SP.SOD.executeFunc('sp.js', 'SP.ClientContext', function () {
        var promises = deps.map(resolveDependency);
        Promise.all(promises).then(resolve);
      });
    });
  };


  /**
  * Get the relative (to the root) portion of an absolute url.
  *
  * @function sputils.helpers.abs2rel
  * @param {string} - the absolute url
  * @returns {string} - the relative url
  * @example
  * var aurl = 'http://example.com/a/path/to.html';
  * var rurl = sputils.helpers.abs2rel(aurl);
  * rurl === '/a/path/to.html';
  **/
  var abs2rel = function (absUrl) {
    var hostname = sputils.lib.getval('location.hostname');

    // splitting on the hostname should yield an array with 2 elements
    // the 1st element should be empty string and the second the rel path.
    // coalesce into an array with the result being a '/'
    var parts = (absUrl || '').split(hostname);

    return parts[1] || '/';
  };

  /**
   * @function sputils.helpers.parseQueryString
   * @param {Optional<string>} optArg
   * a query string, as in the form of "?k1=v1&k2=v2"
   * @returns {Object}
   * an object representing the dictionary of the query string.
   * @example
   * console.log(location.search); // => '?a=1&b=some value'
   * var qsHash = parseQueryString(); // no argument, will use the current browsing context's location.search
   * // qsHash ~=~ {a:1, b: 'some value'};
   * parseQueryString('?a=1&b=some value'); // ~=~ qsHash
   **/
  var parseQueryString = function (optArg) {
    var result = {};
    var qs = (optArg || sputils.lib.getval('location.search')).replace('?', '');
    var parts = qs.split('&');
    parts = fjs.filter(function (a) {
      return a !== '';
    }, parts);
    fjs.each(function (part) {
      var kvp = part.split('=');
      result[kvp[0]] = kvp[1];
    }, parts);
    return result;
  };

  /**
  * Get a promise for a client context that could be on another
  * site/web.
  *
  * This client context is augmented to contain extra info in the
  * `_info` property.
  *
  * @function sputils.helpers.clientContextForWeb
  * @param {string} absoluteUrl the absolute url of the listitem, file or other asset.
  * @returns {Promise<SP.ClientContext>} - the promise of a client context
  * @example
  * console.log(location);// => http://contoso.com/sub1
  * var fileUrl = 'http://contoso.com/sub21231/Shared Documents/file1.docx';
  * var cctxPromise = sputils.helpers.clientContext(fileUrl);
  * cctxPromise.then(function (cctx) {
  *   var webUrl = cctx._info.WebFullUrl;
  *   var web = cctx.get_web();
  *   var file = web.getFileByServerRelativeUrl(sputils.helpers.abs2rel(fileUrl));
  *   // ...
  * });
  **/
  var clientContext = function (absoluteUrl) {
    // TODO: handle URLs to sites/webs
    var url = absoluteUrl.substring(
      0, absoluteUrl.lastIndexOf('/')) ;

    return sputils.rest.contextInfo(url).then(function (info) {
      // create the context and set the extra prop.
      var cctx = new SP.ClientContext(info.WebFullUrl);
      cctx._info = info;

      return cctx;
    });
  };
  /**
  * Download a file using javascript
  * Useful to implement Export functionality in Search.
  *
  * You can download as csv or text. The function triggers the download
  * not the actual csv creation.
  *
  * Works with utf-8
  *
  * @function sputils.helpers.downloadContent
  * @param {Array<string>} options.
  * @returns {nil}
  * @example
  * var separator = ';';
  * downloadContent({
  *   type: 'text/csv;charset=utf-8',
  *   filename: 'results.csv',
  *   content: ['ASCII', separator,
  *      'Åbäcka sig', separator,
  *      'to się podoba: żźćąęłć',
  *      separator, 'Яшлӑхӑма туйаймарӑм'].join('')
  * });
  **/
  var downloadContent = function (options) {
    if (!options || !options.content) {
      throw new Error('You have at least to provide content to download');
    }
    options.filename = options.filename || 'download.txt';
    options.type = options.type || 'text/plain;charset=utf-8';
    options.bom = options.bom || decodeURIComponent('%ef%bb%bf');
    if (window.navigator.msSaveBlob) {
      var blob = new Blob([options.bom + options.content], {type: options.type});
      window.navigator.msSaveBlob(blob, options.filename);
    } else {
      var link = document.createElement('a');
      var content = options.bom + options.content;
      var uriScheme = ['data:', options.type, ','].join('');
      link.href = uriScheme + content;
      link.download = options.filename;
      //FF requires appending it to the DOM to make it clickable
      document.body.appendChild(link);
      link.click();
      document.body.removeChild(link);
    }
  };

  /** @namespace */
  sputils.helpers = {
    withSharePointDependencies: withSharePointDependencies,
    abs2rel: abs2rel,
    clientContext: clientContext,
    parseQueryString: parseQueryString,
    downloadContent: downloadContent
  };
})();