ANTON072
5/21/2015 - 3:08 PM

My useful curried functions returns Promises

My useful curried functions returns Promises

'use strict';

import 'babel/polyfill';

import _ from 'underscore';

let w = global, d = w.document, n = w.navigator, Promise = w.Promise;
let GM = w.google.maps;


/**
 * serialize an object
 */
export let serialize = (data={}) => {
  return _.chain(data)
  .pairs()
  .map((pair) => {
    if (_.isArray(pair[1])) return _.map(pair[1], (val) => `${pair[0]}[]=${val}`);
    return `${pair[0]}=${pair[1]}`;
  })
  .flatten()
  .value()
  .join('&')
  .replace(/%20/g, '+');
};


/**
 * Function returns curried function
 * @param {Function} func A function being curried
 * @returns {Function} A curried function
 */
export let curry = (func) => {
  let partial = (...args) => {
    if (args.length >= func.length) return func.apply(undefined, args);
    return (..._args) => partial.apply(undefined, args.concat(_args));
  };
  return partial;
};


/**
 * Promise returns a value
 * @returns {Object} A Promise object
 */
export let promisify = (val) => {
  return new Promise((resolve, reject) => {
    if (_.isError(val)) return reject(val);
    resolve(val);
  });
};


/**
 * Function returns curried function and immediate function as a Promise
 * @param {Function} func An immediate function being curried
 * @returns {Function|Object} A curried function or a Promise object
 */
export let immediatify = (func) => {
  let partial = (...args) => {
    if (args.length < func.length) return (..._args) => partial.apply(undefined, args.concat(_args));
    let val = func.apply(undefined, args); // Immediate evaltuation
    return new Promise((resolve, reject) => {
      if (_.isError(val)) return reject(val);
      resolve(val);
    });
  };
  return partial;
};


/**
 * Function returns curried function and lazy function as a Promise
 * @param {Function} func An immediate function being curried and lazy evaluated
 * @returns {Function|Object} A curried function or a Promise object
 */
export let lazify = (func) => {
  let partial = (...args) => {
    if (args.length < func.length) return (..._args) => partial.apply(undefined, args.concat(_args));
    return new Promise((resolve, reject) => {
      let id = w.setTimeout(() => {
        w.clearTimeout(id);
        let val = func.apply(undefined, args);  // Lazy evaluation
        if (_.isError(val)) return reject(val);
        resolve(val);
      }, 1);
    });
  };
  return partial;
};


/**
 * Promise concatenates array values as Promises
 * @param {Array} items Array values will be tranformed to Promises
 * @param {Function} func A function transforms `items` to Promises
 * @returns {Object} A Promise object
 */
export let promisedArray = (items, func) => {
  return new Promise((resolve, reject) => {
    let promises = _.map(items, func);
    Promise.all(promises).then(resolve, reject);
  });
};


/**
 * Promise returns timer sleep
 * @param {Number} millisec Milliseconds to wait
 * @returns {Object} A Promise object
 */
export let sleep = (millisec=80) => {
  return new Promise((resolve, reject) => {
    let id = w.setTimeout(() => {
      w.clearTimeout(id);
      resolve(millisec);
    }, millisec);
  });
};


/**
 * Promise returns DOMContentLoaded event
 * @returns {Object} A Promise object
 */
export let contentLoaded = () => {
  return new Promise((resolve, reject) => {
    let loaded = (ev) => {
      ev.target.removeEventListener(ev.type, loaded);
      resolve(ev.target);
    };
    d.addEventListener('DOMContentLoaded', loaded, false);
  });
};


/**
 * Promise returns DOM Image element
 * @param {String} uri An image URI to load
 * @returns {Object} A Promise object
 */
export let imageLoad = (uri) => {
  return new Promise((resolve, reject) => {
    let img = d.createElement('img');
    let error = (ev) => {
      ev.target.removeEventListener(ev.type, error);
      ev.target.parentNode.removeChild(ev.target);
      reject(new w.Error(uri));
    };
    let load = (ev) => {
      ev.target.removeEventListener(ev.type, load);
      resolve(ev.target);
    };
    img.addEventListener('error', error, false);
    img.addEventListener('load', load, false);
    img.setAttribute('src', uri);
  });
};


/**
 * Promise chainable curried function returns XHR as a promise
 * @param {String} method A method to request XHR process
 * @param {String} uri A URI to request to
 * @param {Object} data A data will be sent
 * @returns {Function|Object} A curried function or a Promise object
 */
export let xhr = curry((method, uri, data={}) => {
  return new Promise((resolve, reject) => {
    let req = new w.XMLHttpRequest();

    let readystatechange = (ev) => {
      if (ev.target.DONE !== ev.target.readyState) return;

      ev.removeEventListener(ev.type, readystatechange);

      if (200 === ev.target.status) {
        let response, ctype = ev.target.getResponseHeader('Content-Type');
        switch (ctype) {
          case 'application/json':
          case 'text/json':
            response = w.JSON.parse(ev.target.responseText);
            break;
          case 'application/xml':
          case 'text/xml':
            response = ev.target.responseXML;
            break;
          default:
            response = ev.target.responseText;
        }
        resolve({ 'response': response });
      } else {
        reject(new w.Error(`${ev.target.status} ${ev.target.responseText}`));
      }
    };

    req.addEventListener('readystatechange', readystatechange, false);

    switch (method) {
      case 'GET':
        req.open(method, `${uri}?${serialize(data)}`, true);
        req.send();
        break;
      case 'POST':
        req.open(method, uri, true);
        req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
        req.send(serialize(data));
        break;
    }
  });
});


/**
 * Promise returns current position as Google Maps API LatLng instance from Geolocation API
 * @see https://developer.mozilla.org/en-US/docs/Web/API/Geolocation/getCurrentPosition
 * @see https://developers.google.com/maps/documentation/javascript/reference#LatLng
 * @param {Object} options A PositionOptions object of Geolocation API
 * @returns {Object} a jQuery Deferred's Promise object
 */
export let currentPosition = (options={ enableHighAccuracy: true, timeout: 10000 }) => {
  return new Promise((resolve, reject) => {
    let success = (pos) => {
      let latLng = new GM.LatLng(pos.coords.latitude, pos.coords.longitude);
      resolve(latLng);
    };
    let error = reject;
    n.geolocation.getCurrentPosition(success, error, options);
  });
};


/**
 * A localStorage wrapper returns a Promise
 */
export let localStore = {
  set: lazify((key, val) => {
    // Avoid Exception on Safari private mode
    try {
      return w.localStorage.setItem(key, val);
    } catch (err) {
      return err;
    }
  }),
  get: lazify((key) => w.localStorage.getItem(key)),
  clear: lazify(() => w.localStorage.clear())
};


/**
 * A sessionStorage wrapper returns a Promise
 */
export let sessionStore = {
  set: lazify((key, val) => {
    // Avoid Exception on Safari private mode
    try {
      return w.sessionStorage.setItem(key, val);
    } catch (err) {
      return err;
    }
  }),
  get: lazify((key) => w.sessionStorage.getItem(key)),
  clear: lazify(() => w.sessionStorage.clear())
};
'use strict';

var _ = require('underscore');
var $ = require('jquery');

var global = global || window, doc = global.document, nav = global.navigator;


/**
 * Function returns curried function
 * @param {Function} func A function being curried
 * @returns {Function} A curried function
 */
exports.curry = function (func) {
  return function partial () {
    var argsA = _.toArray(arguments);
    if (argsA.length >= func.length) return func.apply(undefined, argsA);
    return function () {
      var argsB = _.toArray(arguments);
      return partial.apply(undefined, argsA.concat(argsB));
    };
  };
};


/**
 * Promise returns a value
 * @returns {Object} a jQuery Deferred's Promise object
 */
exports.promisify = function (val) {
  var id, dfr = new $.Deferred();
  id = global.setTimeout(function () {
    global.clearTimeout(id);
    if (_.isError(val)) return dfr.reject(val);
    dfr.resolve(val);
  }, 1);
  return dfr.promise();
};


/**
 * Function returns curried function and immediate function as a Promise
 * @param {Function} func An immediate function being curried
 * @returns {Function|Object} A curried function or a jQuery Deferred's Promise object
 */
exports.immediatify = function (func) {
  return function partial () {
    var argsA = _.toArray(arguments);
    if (argsA.length < func.length) {
      return function () {
        var argsB = _.toArray(arguments);
        return partial.apply(undefined, argsA.concat(argsB));
      };
    }
    var id, dfr = new $.Deferred();
    var val = func.apply(undefined, argsA); // Immediate evaltuation
    id = global.setTimeout(function () {
      global.clearTimeout(id);
      if (_.isError(val)) return dfr.reject(val);
      dfr.resolve(val);
    }, 1);
    return dfr.promise();
  };
};


/**
 * Function returns curried function and lazy function as a Promise
 * @param {Function} func An immediate function being curried and lazy evaluated
 * @returns {Function|Object} A curried function or a jQuery Deferred's Promise object
 */
exports.lazify = function (func) {
  return function partial () {
    var argsA = _.toArray(arguments);
    if (argsA.length < func.length) {
      return function () {
        var argsB = _.toArray(arguments);
        return partial.apply(undefined, argsA.concat(argsB));
      };
    }
    var id, dfr = new $.Deferred();
    id = global.setTimeout(function () {
      global.clearTimeout(id);
      var val = func.apply(undefined, argsA);  // Lazy evaluation
      if (_.isError(val)) return dfr.reject(val);
      dfr.resolve(val);
    }, 1);
    return dfr.promise();
  };
};


/**
 * Function returns curried function and chain pre and post processes as Promise
 * @param {Object} pre A pre-process defined as a jQuery Deferred's Promise object
 * @param {Function} main A main process function evaluated as a jQuery Deferred's Promise object
 * @param {Function} post A post-process function evaluated as a jQuery Deferred's Promise object
 * @param {Function} error An error process function evaluated as a jQuery Deferred's Promise object
 * @returns {Function|Object} A curried function or a jQuery Deferred's Promise object
 */
exports.decoratify = exports.curry(function (pre, main, post, error) {
  return function () {
    var argsA = _.toArray(arguments);
    return pre.then(function () {
      var argsB = _.toArray(arguments);
      return main.apply(undefined, argsA.concat(argsB));
    }).then(post, error);
  };
});


/**
 * Promise concatenates array values as promises
 * @param {Array} items Array values will be tranformed to promises
 * @param {Function} promisify A function transforms `items` to promises
 * @returns {Object} a jQuery Deferred's Promise object
 */
exports.promisedMap = function (items, promisify) {
  var dfr = new $.Deferred(), promises = _.map(items, promisify);
  $.when.apply(undefined, promises).then(dfr.resolve, dfr.reject);
  return dfr.promise();
};


/**
 * Promise returns timer sleep
 * @param {Number} millisec Milliseconds to wait
 * @returns {Object} a jQuery Deferred's Promise object
 */
exports.sleep = function (millisec) {
  if (!millisec) millisec = 80;
  var id, dfr = new $.Deferred();
  id = global.setTimeout(function () {
    global.clearTimeout(id);
    dfr.resolve(millisec);
  }, millisec);
  return dfr.promise();
};


/**
 * Promise returns DOMContentLoaded event
 * @see https://api.jquery.com/ready/
 * @returns {Object} a jQuery Deferred's Promise object
 */
exports.contentLoaded = function () {
  var dfr = new $.Deferred();
  $(doc).ready(dfr.resolve);
  return dfr.promise();
};


/**
 * Promise returns DOM Image element
 * @param {String} uri An image URI to load
 * @returns {Object} a jQuery Deferred's Promise object
 */
exports.imageLoad = function (uri) {
  var dfr = new $.Deferred(), $img = $(doc.createElement('img'));
  $img.one('error', function (ev) {
    $(ev.target).remove();
    dfr.reject(new global.Error(uri));
  });
  $img.one('load', function (ev) {
    dfr.resolve(ev.target);
  });
  $img.attr('src', uri);
  return dfr.promise();
};


/**
 * Promise chainable curried function returns XHR as a Promise
 * @param {String} method A method to request XHR process
 * @param {String} uri A URI to request to
 * @param {String|Object|Array} data A data will be sent
 * @returns {Function|Object} A curried function or a jQuery Deferred's Promise object
 */
exports.xhr = exports.curry(function (method, uri, data) {
  return $.ajax({
    data: data,
    method: method,
    url: uri
  });
});


/**
 * Promise returns current position as Google Maps API LatLng instance from Geolocation API
 * @see https://developer.mozilla.org/en-US/docs/Web/API/Geolocation/getCurrentPosition
 * @see https://developers.google.com/maps/documentation/javascript/reference#LatLng
 * @param {Object} options A PositionOptions object of Geolocation API
 * @returns {Object} a jQuery Deferred's Promise object
 */
exports.currentPosition = function (options) {
  if (null == options) options = { enableHighAccuracy: true, timeout: 10000 };
  var dfr = new $.Deferred();
  var success = function (pos) {
    var latLng = [pos.coords.latitude, pos.coords.longitude];
    dfr.resolve(latLng);
  };
  var error = dfr.reject;
  nav.geolocation.getCurrentPosition(success, error, options);
  return dfr.promise();
};


/**
 * A localStorage wrapper returns a Promise
 */
exports.localStore = {
  set: exports.lazify(function (key, val) {
    // Avoid Exception on Safari private mode
    try {
      return global.localStorage.setItem(key, val);
    } catch (err) {
      return err;
    }
  }),
  get: exports.lazify(function (key) { return global.localStorage.getItem(key); }),
  clear: exports.lazify(function () { return global.localStorage.clear(); })
};


/**
 * A sessionStorage wrapper returns a Promise
 */
exports.sessionStore = {
  set: exports.lazify(function (key, val) {
    // Avoid Exception on Safari private mode
    try {
      return global.sessionStorage.setItem(key, val);
    } catch (err) {
      return err;
    }
  }),
  get: exports.lazify(function (key) { return global.sessionStorage.getItem(key); }),
  clear: exports.lazify(function () { return global.sessionStorage.clear(); })
};