<commit_before>define([
    'require',
    './core/store',
    './lib/dist/underscore',
    './lib/jquery',
    './logging'
], function(require) {
    // XXX: not nice
    var mapal = require('./core/store'),
        getLogger = require('./logging').getLogger;

    var extractParameters = function(params, sources) {
        var tmp,
            j,
            paramObjs = {};
        if (params.length > 0) {
            var p = params.slice(1).split('!');
            for (var i = p.length-1; i >= 0; i--) {
                // support injection parameters in other patterns
                if (p[i][0] == '#') {
                    var param, effect;

                    if (p[i].indexOf('.') > 0) {
                        tmp = p[i].split('.');
                        param = tmp[0];
                        effect = tmp[1];
                    } else {
                        param = p[i];
                        effect = undefined;
                    }
                    var source = [sources.pop()];
                    // XXX: $a and url were also not defined in the old context
                    // We need automated tests
                    mapal.injection.load($a, url, param.slice(1), source);
                } else if (p[i].indexOf('=') > 0) {
                    j = p[i].indexOf('=');
                    paramObjs[p[i].slice(0, j)] = p[i].slice(j+1);
                } else {
                    paramObjs[p[i]] = true;
                }
            }
        }
        return paramObjs;
    };

    // input = "a!b=1 2!c=x"
    // --> options = {a: true, b: '1 2', c: 'x'}
    var parseOptions = function(input) {
        var params = input.split("!"),
            options = {}, name, value, index;

        for (var i=0; i<params.length; i++) {
            index = params[i].indexOf("=");
            if (index === -1) {
                name = params[i];
                value = true;
            } else {
                name = params[i].slice(0, index);
                value = params[i].slice(index+1);
            }
            options[name] = value;
        }
        return options;
    };

    var log_init = function(name, method) {
        var log_wrapper = function($el) {
            getLogger(name).debug('Initialising:', $el);
            var ret = method.apply(this, arguments);
            getLogger(name).debug('initialised:', $el);
        };
        return log_wrapper;
    };

    var set_initialised_class = function(method, pattern) {
        var cls = pattern.initialised_class;
        if (!cls) return method;
        var initialised_class_wrapper = function($el) {
            var ret = method.apply(this, arguments);
            $el.addClass(cls);
            return ret;
        };
        return initialised_class_wrapper;
    };

    // run a method on an element only once
    var once = function(key, method) {
        var once_wrapper = function($el) {
            var initialised = $el.data(key);
            if (initialised) return undefined;
            var ret = method.apply(this, arguments);
            $el.data(key, true);
            return ret;
        };
        return once_wrapper;
    };

    // work on elements single file
    var turnstiled = function(method) {
        var turnstile = function($el) {
            var rest = Array.prototype.slice.call(arguments, 1);
            $el.each(function() {
                var $this = $(this);
                return method.apply($this, [$this].concat(rest));
            });
        };
        return turnstile;
    };

    // return a pimpéd pattern
    // - init only once
    // - put multiple elements through a turnstile
    var pimp_pattern = function(pname, pattern) {
        var pimped = {};
        for (var mname in pattern) {
            var method = pattern[mname];
            if (mname === "init") {
                method = log_init(pname, method);
                method = set_initialised_class(method, pattern);
                method = once(pname + '-' + mname, method);
            }
            if (typeof method === "function") method = turnstiled(method);
            pimped[mname] = method;
        }
        return pimped;
    };

    var jquery_plugin = function(name, pattern) {
        var plugin = function(method) {
            if (!method || typeof method === "object") {
                pattern.init.apply(this, [this].concat(arguments));
            } else if (pattern[method]) {
                pattern[method].apply(
                    this,
                    [this].concat(Array.prototype.slice.call(arguments, 1))
                );
            } else {
                $.error('Method ' + method + ' does not exist on jQuery.' + name);
            }
            return this;
        };
        return plugin;
    };

    // XXX: need to understand require magic to make this work here
    // require paths are local to where it is called, we would need to
    // pass the path of the module that called load_modules
    var load_modules = function(prefix, names, suffix) {
        prefix = prefix || '';
        suffix = suffix || '';
        var modules = _.reduce(names, function(acc, name) {
            acc[name] = require(prefix + name + suffix);
            return acc;
        }, {});
        return modules;
    };

    var utils = {
        extractParameters: extractParameters,
        parseOptions: parseOptions,
        //load_modules: load_modules,
        // pattern pimping - own module?
        set_initialised_class: set_initialised_class,
        once: once,
        turnstiled: turnstiled,
        pimp_pattern: pimp_pattern,
        jquery_plugin: jquery_plugin
    };

    return utils;
});
<commit_msg>once, log_init and initialised_class_wrapper are aware of skipped elements<commit_after>define([
    'require',
    './core/store',
    './lib/dist/underscore',
    './lib/jquery',
    './logging'
], function(require) {
    // XXX: not nice
    var mapal = require('./core/store'),
        getLogger = require('./logging').getLogger;

    var extractParameters = function(params, sources) {
        var tmp,
            j,
            paramObjs = {};
        if (params.length > 0) {
            var p = params.slice(1).split('!');
            for (var i = p.length-1; i >= 0; i--) {
                // support injection parameters in other patterns
                if (p[i][0] == '#') {
                    var param, effect;

                    if (p[i].indexOf('.') > 0) {
                        tmp = p[i].split('.');
                        param = tmp[0];
                        effect = tmp[1];
                    } else {
                        param = p[i];
                        effect = undefined;
                    }
                    var source = [sources.pop()];
                    // XXX: $a and url were also not defined in the old context
                    // We need automated tests
                    mapal.injection.load($a, url, param.slice(1), source);
                } else if (p[i].indexOf('=') > 0) {
                    j = p[i].indexOf('=');
                    paramObjs[p[i].slice(0, j)] = p[i].slice(j+1);
                } else {
                    paramObjs[p[i]] = true;
                }
            }
        }
        return paramObjs;
    };

    // input = "a!b=1 2!c=x"
    // --> options = {a: true, b: '1 2', c: 'x'}
    var parseOptions = function(input) {
        var params = input.split("!"),
            options = {}, name, value, index;

        for (var i=0; i<params.length; i++) {
            index = params[i].indexOf("=");
            if (index === -1) {
                name = params[i];
                value = true;
            } else {
                name = params[i].slice(0, index);
                value = params[i].slice(index+1);
            }
            options[name] = value;
        }
        return options;
    };

    var log_init = function(name, method) {
        var log_wrapper = function($el) {
            var log = getLogger(name);
            log.debug('Initialising:', $el);
            var ret = method.apply(this, arguments);
            if (ret === false) {
                log.debug('skipped', $el);
            } else {
                log.debug('initialised:', $el);
            }
            return ret;
        };
        return log_wrapper;
    };

    var set_initialised_class = function(method, pattern) {
        var cls = pattern.initialised_class;
        if (!cls) return method;
        var initialised_class_wrapper = function($el) {
            var ret = method.apply(this, arguments);
            if (ret !== false) $el.addClass(cls);
            return ret;
        };
        return initialised_class_wrapper;
    };

    // run a method on an element only once
    var once = function(key, method) {
        var once_wrapper = function($el) {
            var initialised = $el.data(key);
            if (initialised) return undefined;
            var ret = method.apply(this, arguments);
            if (ret !== false) $el.data(key, true);
            return ret;
        };
        return once_wrapper;
    };

    // work on elements single file
    var turnstiled = function(method) {
        var turnstile = function($el) {
            var rest = Array.prototype.slice.call(arguments, 1);
            $el.each(function() {
                var $this = $(this);
                return method.apply($this, [$this].concat(rest));
            });
        };
        return turnstile;
    };

    // return a pimpéd pattern
    // - init only once
    // - put multiple elements through a turnstile
    var pimp_pattern = function(pname, pattern) {
        var pimped = {};
        for (var mname in pattern) {
            var method = pattern[mname];
            if (mname === "init") {
                method = log_init(pname, method);
                method = set_initialised_class(method, pattern);
                method = once(pname + '-' + mname, method);
            }
            if (typeof method === "function") method = turnstiled(method);
            pimped[mname] = method;
        }
        return pimped;
    };

    var jquery_plugin = function(name, pattern) {
        var plugin = function(method) {
            if (!method || typeof method === "object") {
                pattern.init.apply(this, [this].concat(arguments));
            } else if (pattern[method]) {
                pattern[method].apply(
                    this,
                    [this].concat(Array.prototype.slice.call(arguments, 1))
                );
            } else {
                $.error('Method ' + method + ' does not exist on jQuery.' + name);
            }
            return this;
        };
        return plugin;
    };

    // XXX: need to understand require magic to make this work here
    // require paths are local to where it is called, we would need to
    // pass the path of the module that called load_modules
    var load_modules = function(prefix, names, suffix) {
        prefix = prefix || '';
        suffix = suffix || '';
        var modules = _.reduce(names, function(acc, name) {
            acc[name] = require(prefix + name + suffix);
            return acc;
        }, {});
        return modules;
    };

    var utils = {
        extractParameters: extractParameters,
        parseOptions: parseOptions,
        //load_modules: load_modules,
        // pattern pimping - own module?
        set_initialised_class: set_initialised_class,
        once: once,
        turnstiled: turnstiled,
        pimp_pattern: pimp_pattern,
        jquery_plugin: jquery_plugin
    };

    return utils;
});
<|endoftext|>


<commit_before> /*
    MagJS v0.20
    http://github.com/magnumjs/mag.js
    (c) Michael Glazer
    License: MIT
    */

 ;
 (function(mag) {

   'use strict';

   var utils = {};

   utils.callHook = function(hookins, key, name, i, data, before) {
     data.change = false
     if (hookins[name][i].key == key) {
       before = JSON.stringify({
         v: data.value,
         k: data.key
       })
       hookins[name][i].handler.call(hookins[name][i].context, data)
         //if any change
       if (before !== JSON.stringify({
           v: data.value,
           k: data.key
         })) {
         data.change = true
       }
     }
   }

   utils.unloaders = []
   utils.callLCEvent = function(eventName, controller, node, index, once) {
     var isPrevented = false,
       event = {
         preventDefault: function() {
           isPrevented = true
         }
       }
     if (controller && controller[eventName]) {
       controller[eventName].call({}, event, node)
       if (once) controller[eventName] = 0
     }

     if (isPrevented) {
       // unloading

       for (var i = 0, unloader; unloader = utils.unloaders[index][i]; i++) {

         if (unloader.controller.onunload) {
           unloader.handler.call(unloader.controller, node)
           unloader.controller.onunload = 0
         }
       }
     }

     return isPrevented
   }

   //UTILITY
   utils.copy = function(o) {
     var out, v, key;
     out = Array.isArray(o) ? [] : {};
     for (key in o) {
       v = o[key];
       out[key] = (typeof v === "object") ? utils.copy(v) : typeof v == 'function' && v.type == 'fun' ? mag.prop(v()) : v;
     }
     return out;
   }

   utils.merge = function(destination, source) {
     for (var p in source) {
       if (source[p] && source[p].constructor == Object) {
         if (destination[p]) {
           utils.merge(destination[p], source[p]);
           continue;
         }
       }
       destination[p] = source[p];
     }
     return destination
   }

   // utils.merge = function(source, destination) {
   //   for (var k in source) destination[k] = source[k]
   //   return destination;
   // }

   var a = {
     i: [],
     isItem: function(id) {
       return a.i.indexOf(id) > -1
     },
     setItem: function(id) {
       a.i[a.i.length] = id
     },
     getItem: function(id) {
       return a.i.indexOf(id)
     },
     getItemVal: function(index) {
       return a.i[index]
     },
     removeItem: function(id) {
       a.i.splice(a.i.indexOf(id), 1)
     }
   }

   utils.items = a

   utils.getItemInstanceIdAll = function() {
     return a.i
   }

   utils.getItemInstanceId = function(id) {
     if (a.isItem(id)) {
       return a.getItem(id)
     } else {
       a.setItem(id)
       return a.getItem(id)
     }
   }

   mag.utils = utils

 }(window.mag || {}));

 var $cancelAnimationFrame = window.cancelAnimationFrame || window.clearTimeout;

 var $requestAnimationFrame = window.requestAnimationFrame || window.setTimeout;

mag.throttle = function (fn, threshhold, scope) {
  threshhold || (threshhold = 16);
  var last,
      deferTimer;
  return function () {
    var context = scope || this;
    var now = +new Date,
        args = arguments;
    if (last && now < last + threshhold) {
      // hold on to it
      $cancelAnimationFrame(deferTimer);
      deferTimer = $requestAnimationFrame(function () {
        last = now;
        fn.apply(context, args);
      }, threshhold);
    } else {
      last = now;
      fn.apply(context, args);
    }
  };
}

 mag.debounce = function(func, wait, immediate) {
   wait || (wait = 16)
 var timeout;
	return function() {
		var context = this, args = arguments;
		var later = function() {
			timeout = null;
			if (!immediate) func.apply(context, args);
		};
		var callNow = immediate && !timeout;
		clearTimeout(timeout);
		timeout = setTimeout(later, wait);
		if (callNow) func.apply(context, args);
	};
 };

 mag.prop = function(store) {
   var prop = function() {
     if (arguments.length) store = arguments[0];
     return store;
   };

   prop.toJSON = function() {
     return store;
   };
   prop.type = 'fun'
   return prop;
 };

 mag.withProp = function(prop, withAttrCallback) {
   return function(e) {
     e = e || event;
     var currentTarget = e.currentTarget || this;
     withAttrCallback(prop in currentTarget ? currentTarget[prop] : currentTarget.getAttribute(prop))
   }
 }
<commit_msg>Update utils.js<commit_after> /*
     MagJS v0.20
     http://github.com/magnumjs/mag.js
     (c) Michael Glazer
     License: MIT
     */

 ;
 (function(mag) {

   'use strict';

   var utils = {};

   utils.callHook = function(hookins, key, name, i, data, before) {
     data.change = false
     if (hookins[name][i].key == key) {
       before = JSON.stringify({
         v: data.value,
         k: data.key
       })
       hookins[name][i].handler.call(hookins[name][i].context, data)
         //if any change
       if (before !== JSON.stringify({
           v: data.value,
           k: data.key
         })) {
         data.change = true
       }
     }
   }

   utils.unloaders = []
   utils.callLCEvent = function(eventName, controller, node, index, once) {
     var isPrevented = false,
       event = {
         preventDefault: function() {
           isPrevented = true
         }
       }
     if (controller && controller[eventName]) {
       controller[eventName].call({}, event, node)
       if (once) controller[eventName] = 0
     }

     if (isPrevented) {
       // unloading

       for (var i = 0, unloader; unloader = utils.unloaders[index][i]; i++) {

         if (unloader.controller.onunload) {
           unloader.handler.call(unloader.controller, node)
           unloader.controller.onunload = 0
         }
       }
     }

     return isPrevented
   }

   //UTILITY
   utils.copy = function(o) {
     var out, v, key;
     out = Array.isArray(o) ? [] : {};
     for (key in o) {
       v = o[key];
       out[key] = (typeof v === "object") ? utils.copy(v) : typeof v == 'function' && v.type == 'fun' ? mag.prop(v()) : v;
     }
     return out;
   }

   utils.merge = function(destination, source) {
     for (var p in source) {
       if (source[p] && source[p].constructor == Object) {
         if (destination[p]) {
           utils.merge(destination[p], source[p]);
           continue;
         }
       }
       destination[p] = source[p];
     }
     return destination
   }

   // utils.merge = function(source, destination) {
   //   for (var k in source) destination[k] = source[k]
   //   return destination;
   // }

   var a = {
     i: [],
     isItem: function(id) {
       return a.i.indexOf(id) > -1
     },
     setItem: function(id) {
       a.i[a.i.length] = id
     },
     getItem: function(id) {
       return a.i.indexOf(id)
     },
     getItemVal: function(index) {
       return a.i[index]
     },
     removeItem: function(id) {
       a.i.splice(a.i.indexOf(id), 1)
     }
   }

   utils.items = a

   utils.getItemInstanceIdAll = function() {
     return a.i
   }

   utils.getItemInstanceId = function(id) {
     if (a.isItem(id)) {
       return a.getItem(id)
     } else {
       a.setItem(id)
       return a.getItem(id)
     }
   }

   mag.utils = utils

   mag.debounce = function(func, wait, immediate) {
     wait || (wait = 16)
     var timeout;
     return function() {
       var context = this,
         args = arguments;
       var later = function() {
         timeout = null;
         if (!immediate) func.apply(context, args);
       };
       var callNow = immediate && !timeout;
       clearTimeout(timeout);
       timeout = setTimeout(later, wait);
       if (callNow) func.apply(context, args);
     };
   };

   mag.prop = function(store) {
     var prop = function() {
       if (arguments.length) store = arguments[0];
       return store;
     };

     prop.toJSON = function() {
       return store;
     };
     prop.type = 'fun'
     return prop;
   };

   mag.withProp = function(prop, withAttrCallback) {
     return function(e) {
       e = e || event;
       var currentTarget = e.currentTarget || this;
       withAttrCallback(prop in currentTarget ? currentTarget[prop] : currentTarget.getAttribute(prop))
     }
   }

 }(window.mag || {}));
<|endoftext|>


<commit_before>// These are statuses for in-flight requests. If a request has no status
// associated with it, then it would have a status of `NULL`.
export const xhrStatuses = {
  PENDING: 'PENDING',
  SUCCEEDED: 'SUCCEEDED',
  FAILED: 'FAILED',
  ABORTED: 'ABORTED',
  NULL: 'NULL'
};

// Updates a single resource's metadata
export function updateResourceMeta({resourceMeta, newMeta, id, replace}) {
  const existingMeta = replace ? {} : resourceMeta[id];
  return {
    // Shallow clone the current resourceMeta
    ...resourceMeta,
    // Update the key that is the "id" of the resource
    [id]: {
      // Shallow clone the existing metadata for this resource
      ...existingMeta,
      // Shallow clone the passed-in metadata
      ...newMeta
    }
  };
}

// Similar to `updateResourceMeta`, but it accepts an array of IDs instead of
// a single ID. Used for bulk updating meta.
export function updateManyResourceMetas({resourceMeta, newMeta, ids, replace}) {
  const next = replace ? {} : {...resourceMeta};

  ids.forEach((id) => {
    const current = next[id];
    next[id] = {
      ...current,
      ...newMeta
    };
  });

  return next;
}

// Insert a new resource or update an existing resource.
export function upsertResource({resources, resource, id, idAttribute, replace}) {
  // Attempt to find the resource by its ID. If the ID doesn't exist, or if
  // no resource by that ID exists, then we append it to the end as a new
  // resource.
  const resourceIndex = id && resources.findIndex(item => item[idAttribute] === id);
  if (!id || resourceIndex === -1) {
    return [...resources, resource];
  }

  const shallowClone = [...resources];

  let resourceToInsert;
  if (!replace) {
    const currentResource = shallowClone[resourceIndex];
    resourceToInsert = {
      ...currentResource,
      ...resource
    };
  } else {
    resourceToInsert = resource;
  }

  // Otherwise, it does exist and we add it to the list at the appropriate
  // location
  shallowClone.splice(resourceIndex, 1, resourceToInsert);
  return shallowClone;
}

// Similar to `upsertResource`, but for many resources.
export function upsertManyResources({resources, newResources, idAttribute, replace}) {
  const shallowClone = replace ? [] : [...resources];

  newResources.forEach(resource => {
    const id = resource[idAttribute];
    const resourceIndex = id && resources.findIndex(item => item[idAttribute] === id);

    if (!id || resourceIndex === -1) {
      return shallowClone.push(resource);
    }

    let resourceToInsert;
    if (!replace) {
      const currentResource = shallowClone[resourceIndex];
      resourceToInsert = {
        ...currentResource,
        ...resource
      };
    } else {
      resourceToInsert = resource;
    }

    shallowClone.splice(resourceIndex, 1, resourceToInsert);
  });

  return shallowClone;
}

export const initialResourceMetaState = {
  // The status of any existing request to update this resource
  updatingStatus: xhrStatuses.NULL,
  // The status of any existing request to fetch this resource
  retrievingStatus: xhrStatuses.NULL,
  // The status of an any existing request to delete this resource. Note that
  // this will never be "SUCCEEDED," as a successful delete removes the
  // resource from the store.
  deletingStatus: xhrStatuses.NULL
};

export function snakeCase(str) {
  return str
    .replace(/([a-z])([A-Z])/g, '$1_$2')
    .replace(/\b([A-Z]+)([A-Z])([a-z])/, '$1_$2$3')
    .toLowerCase();
}

export function generateDefaultInitialState() {
  return {
    // These are the actual resources that the server sends back.
    resources: [],
    // This is metadata about _specific_ resources. For instance, if a DELETE
    // is in flight for a book with ID 24, then you could find that here.
    resourceMeta: {},
    // This is metadata about the entire collection of resources. For instance,
    // on page load, you might fetch all of the resources. The XHR status for
    // that request would live here.
    resourceListMeta: {
      retrievingStatus: xhrStatuses.NULL,
      creatingStatus: xhrStatuses.NULL
    }
  };
}
<commit_msg>Remove use of spread<commit_after>// These are statuses for in-flight requests. If a request has no status
// associated with it, then it would have a status of `NULL`.
export const xhrStatuses = {
  PENDING: 'PENDING',
  SUCCEEDED: 'SUCCEEDED',
  FAILED: 'FAILED',
  ABORTED: 'ABORTED',
  NULL: 'NULL'
};

// Updates a single resource's metadata
export function updateResourceMeta({resourceMeta, newMeta, id, replace}) {
  const existingMeta = replace ? {} : resourceMeta[id];
  return {
    // Shallow clone the current resourceMeta
    ...resourceMeta,
    // Update the key that is the "id" of the resource
    [id]: {
      // Shallow clone the existing metadata for this resource
      ...existingMeta,
      // Shallow clone the passed-in metadata
      ...newMeta
    }
  };
}

// Similar to `updateResourceMeta`, but it accepts an array of IDs instead of
// a single ID. Used for bulk updating meta.
export function updateManyResourceMetas({resourceMeta, newMeta, ids, replace}) {
  const next = replace ? {} : {...resourceMeta};

  ids.forEach((id) => {
    const current = next[id];
    next[id] = {
      ...current,
      ...newMeta
    };
  });

  return next;
}

// Insert a new resource or update an existing resource.
export function upsertResource({resources, resource, id, idAttribute, replace}) {
  // Attempt to find the resource by its ID. If the ID doesn't exist, or if
  // no resource by that ID exists, then we append it to the end as a new
  // resource.
  const resourceIndex = id && resources.findIndex(item => item[idAttribute] === id);
  const shallowClone = Array.prototype.slice.call(resources);
  if (!id || resourceIndex === -1) {
    shallowClone.push(resource);
    return shallowClone;
  }

  let resourceToInsert;
  if (!replace) {
    const currentResource = shallowClone[resourceIndex];
    resourceToInsert = {
      ...currentResource,
      ...resource
    };
  } else {
    resourceToInsert = resource;
  }

  // Otherwise, it does exist and we add it to the list at the appropriate
  // location
  shallowClone.splice(resourceIndex, 1, resourceToInsert);
  return shallowClone;
}

// Similar to `upsertResource`, but for many resources.
export function upsertManyResources({resources, newResources, idAttribute, replace}) {
  const shallowClone = replace ? [] : Array.prototype.slice.call(resources);

  newResources.forEach(resource => {
    const id = resource[idAttribute];
    const resourceIndex = id && resources.findIndex(item => item[idAttribute] === id);

    if (!id || resourceIndex === -1) {
      return shallowClone.push(resource);
    }

    let resourceToInsert;
    if (!replace) {
      const currentResource = shallowClone[resourceIndex];
      resourceToInsert = {
        ...currentResource,
        ...resource
      };
    } else {
      resourceToInsert = resource;
    }

    shallowClone.splice(resourceIndex, 1, resourceToInsert);
  });

  return shallowClone;
}

export const initialResourceMetaState = {
  // The status of any existing request to update this resource
  updatingStatus: xhrStatuses.NULL,
  // The status of any existing request to fetch this resource
  retrievingStatus: xhrStatuses.NULL,
  // The status of an any existing request to delete this resource. Note that
  // this will never be "SUCCEEDED," as a successful delete removes the
  // resource from the store.
  deletingStatus: xhrStatuses.NULL
};

export function snakeCase(str) {
  return str
    .replace(/([a-z])([A-Z])/g, '$1_$2')
    .replace(/\b([A-Z]+)([A-Z])([a-z])/, '$1_$2$3')
    .toLowerCase();
}

export function generateDefaultInitialState() {
  return {
    // These are the actual resources that the server sends back.
    resources: [],
    // This is metadata about _specific_ resources. For instance, if a DELETE
    // is in flight for a book with ID 24, then you could find that here.
    resourceMeta: {},
    // This is metadata about the entire collection of resources. For instance,
    // on page load, you might fetch all of the resources. The XHR status for
    // that request would live here.
    resourceListMeta: {
      retrievingStatus: xhrStatuses.NULL,
      creatingStatus: xhrStatuses.NULL
    }
  };
}
<|endoftext|>


<commit_before>import uuid from 'uuid/v1';
import flow from 'lodash/flow';

export const createOneTimeCallable = (fn, errorMessage) => {
  let called = false;
  return (...args) => {
    if (called) {
      throw new Error(errorMessage);
    }
    called = true;

    return fn(...args);
  };
};

export const generateUId = uuid;

export const getErrorStack = () => (new Error()).stack.toString().split(/\r\n|\n/);

export const runHandler = (handler, args) => {
  const { onError, ...restArgs } = args;
  let resolve;
  let reject;

  const promise = new Promise((_resolve, _reject) => {
    resolve = _resolve;
    reject = _reject;
  });

  const handleResult = createOneTimeCallable((err, result) => {
    if (err) {
      onError(err);
      reject(err);
    } else {
      resolve(result);
    }
  }, 'The result was already handled!');

  const reply = (value) => {
    const err = value instanceof Error ? value : null;
    handleResult(err, value);
  };

  try {
    const result = handler({
      ...restArgs,
      reply,
    }, handleResult);

    if (result !== undefined) {
      handleResult(null, result);
    }
  } catch (err) {
    handleResult(err);
  }

  return promise;
};

export const applyDecorators = (decorators, handler) => flow(decorators.reverse())(handler);

export const getTime = () => new Date().getTime();
<commit_msg>cleanup<commit_after><|endoftext|>


<commit_before>const childProcess = require('child_process')
const fs = require('fs')
// const _        = require('lodash')
const path = require('path')
const os = require('os')
// const _ = require('lodash')
const yaml = require('js-yaml')
// const spawnSync   = require('spawn-sync')
// const oneLine     = require('common-tags/lib/oneLine')
// const stripIndent = require('common-tags/lib/stripIndent')
const scrolex = require('scrolex').persistOpts({
  announce             : true,
  addCommandAsComponent: true,
})
const utils = this
const oneLine = require('common-tags/lib/oneLine')
// const pad = require('pad')

if (require.main === module) {
  scrolex.failure(`Please only used this module via require`)
  process.exit(1)
}

module.exports.formatCmd = function formatCmd (cmd, { runtime, cmdName }) {
  // Replace dirs
  cmd = cmd.replace(/\[lanyonDir]/g, runtime.lanyonDir)
  cmd = cmd.replace(/\[contentBuildDir]/g, runtime.contentBuildDir)
  cmd = cmd.replace(/\[projectDir]/g, runtime.projectDir)
  cmd = cmd.replace(/\[cacheDir]/g, runtime.cacheDir)

  // Replace all npms with their first-found full-path executables
  const npmBins = {
    'browser-sync': 'node_modules/browser-sync/bin/browser-sync.js',
    'nodemon'     : 'node_modules/nodemon/bin/nodemon.js',
    'webpack'     : 'node_modules/webpack/bin/webpack.js',
    // 'imagemin'     : 'node_modules/imagemin-cli/cli.js',
  }
  for (const name in npmBins) {
    const tests = [
      `/srv/lanyon/${npmBins[name]}`,
      `${runtime.npmRoot}/${npmBins[name]}`,
      `${runtime.projectDir}/${npmBins[name]}`,
      `${runtime.lanyonDir}/${npmBins[name]}`,
      `${runtime.gitRoot}/${npmBins[name]}`,
    ]

    let found = false
    tests.forEach(test => {
      if (fs.existsSync(test)) {
        npmBins[name] = test
        found = true
        return false // Stop looking on first hit
      }
    })

    if (!found) {
      throw new Error(`Cannot find dependency "${name}" in "${tests.join('", "')}"`)
    }
    const pat = new RegExp(`(\\s|^)\\[${name}\\](\\s|$)`)
    cmd = cmd.replace(pat, `$1node ${npmBins[name]}$2`)

    // let nodeBin = utils.dockerString('node', { extraArgs: extraVolumes, runtime })
    // cmd = cmd.replace(pat, `$1${nodeBin} ${npmBins[name]}$2`)
  }

  // cp -f ${runtime.projectDir}/Gemfile ${runtime.cacheDir}/Gemfile &&
  let jekyllBin = utils.dockerString('jekyll', { runtime })

  // Replace shims
  cmd = cmd.replace(/(\s|^)\[jekyll\](\s|$)/, `$1${jekyllBin}$2`)

  return cmd
}

module.exports.splitDir = function splitDir (dir) {
  let dirList = []
  let parts = `${dir}`.split('/')
  let cont = ''
  for (let i in parts) {
    let part = parts[i]
    if (!part) continue
    cont = `${cont}/${part}`
    dirList.push(cont)
  }
  return dirList
}

module.exports.trapCleanup = function trapCleanup ({ runtime, code = 0, signal = '' }) {
  if (runtime.dying === true) {
    return
  }
  runtime.dying = true

  let cleanupCmds = [
    'pkill -f nodemon',
    'pkill -f browser-sync',
  ]

  if (runtime.dockerSync && runtime.dockerSync.enabled === true) {
    cleanupCmds = cleanupCmds.concat([
      `docker-sync stop`,
      // `docker-compose stop`,
    ])
  }

  console.log(`>>> About to exit. code=${code}, signal=${signal}. Cleaning up... `)
  for (let i in cleanupCmds) {
    let c = cleanupCmds[i]
    console.error(`Cleanup command: '${c}' ...`)
    try {
      childProcess.execSync(`${c}`, {
        cwd: `${runtime.cacheDir}`,
      })
    } catch (err) {} // eslint-disable-line
  }

  if (signal === 'SIGINT') {
    console.log(`>>> Process aborted`)
    process.exit(1)
  }
}

module.exports.dockerString = function dockerString (cmd, { opts = {}, runtime } = {}) {
  let volumePaths = utils.volumePaths({ runtime })
  let listVolumes = []
  for (let key in volumePaths) {
    listVolumes.push(`--volume ${key}:${volumePaths[key]}`)
  }

  if (runtime.dockerSync && runtime.dockerSync.enabled === true) {
    // -i
    // --rm
    // ${cmd}
    // --workdir=${runtime.cacheDir}
    // -e "JEKYLL_ENV=${runtime.lanyonEnv}"
    // docker-sync start && docker-compose up && docker-compose exec
    return oneLine`
      docker-compose exec -T
        lanyon-container
      ${cmd}
    `
  }

  if (!opts || !('priviliged' in opts)) {
    opts.priviliged = false
  }

  // https://github.com/envygeeks/jekyll-docker/issues/223
  // --env "JEKYLL_GID=${os.userInfo().gid}" // <-- runs the risk of: groupmod: GID '20' already exists
  let userstr = ''
  if (opts.priviliged === false) {
    userstr = oneLine`
      --env "JEKYLL_UID=${os.userInfo().uid}"
    `
  }

  return oneLine`
    docker run
      --rm
      --interactive
      ${userstr}
      --env "JEKYLL_ENV=${runtime.lanyonEnv}"
      --workdir ${runtime.cacheDir}
      ${listVolumes.join('\n')}
      ${runtime.dockerImage}
    ${cmd}
  `
}

module.exports.volumePaths = function volumePaths ({ runtime }) {
  let volumePaths = {}

  if (runtime.contentBuildDir.indexOf(runtime.projectDir) === -1) {
    volumePaths[runtime.contentBuildDir] = runtime.contentBuildDir
  }
  if (runtime.cacheDir.indexOf(runtime.projectDir) === -1) {
    volumePaths[runtime.cacheDir] = runtime.cacheDir
  }

  volumePaths[runtime.projectDir] = runtime.projectDir

  return volumePaths
}

module.exports.runString = async function runString (cmd, { runtime, cmdName, origCmd, hookName }) {
  const scrolexOpts = {
    cwd                  : runtime.cacheDir,
    fatal                : true,
    mode                 : 'passthru',
    components           : `lanyon>${cmdName}>${origCmd.split('--')[0].trim().replace('[', '').replace(']', '')}`,
    addCommandAsComponent: false,
  }

  await utils.runhooks('pre', hookName, runtime)
  await scrolex.exe(cmd, scrolexOpts)
  await utils.runhooks('post', hookName, runtime)
}

module.exports.runhooks = async (order, cmdName, runtime) => {
  let squashedHooks = utils.gethooks(order, cmdName, runtime)

  scrolex.stick(`Running ${squashedHooks.length} ${order}${cmdName} hooks`)
  if (!squashedHooks.length) {
    return
  }

  return scrolex.exe(squashedHooks.join(' && '), {
    cwd       : runtime.projectDir,
    fatal     : true,
    mode      : 'passthru',
    components: `lanyon>hooks>${order}${cmdName}`,
  })
}

module.exports.gethooks = (order, cmdName, runtime) => {
  let arr = []

  arr = [
    `${order}${cmdName}`,
    `${order}${cmdName}:production`,
    `${order}${cmdName}:development`,
    `${order}${cmdName}:content`,
    `${order}${cmdName}:content:production`,
    `${order}${cmdName}:content:development`,
    `${order}${cmdName}:assets`,
    `${order}${cmdName}:assets:production`,
    `${order}${cmdName}:assets:development`,
  ]

  let squashedHooks = []
  for (let i in arr) {
    let hook = arr[i]
    if (runtime[hook]) {
      const lastPart = hook.split(':').pop()
      let needEnv = 'both'

      if (lastPart === 'production') {
        needEnv = lastPart
      }
      if (lastPart === 'development') {
        needEnv = lastPart
      }

      if (needEnv === 'both' || runtime.lanyonEnv === needEnv) {
        squashedHooks = squashedHooks.concat(runtime[hook])
      }
    }
  }

  return squashedHooks
}

module.exports.upwardDirContaining = (find, cwd, not) => {
  if (!cwd) {
    cwd = process.env.PWD || process.cwd()
  }
  const parts = cwd.split('/')
  while (parts.length) {
    const newParts = parts
    const ppath = `${newParts.join('/')}/${find}`
    if (fs.existsSync(ppath)) {
      if (not === undefined || not !== path.basename(path.dirname(ppath))) {
        return path.dirname(ppath)
      }
    }
    parts.pop()
  }
  return false
}

module.exports.initProject = async ({ assetsBuildDir, gitRoot, cacheDir }) => {
  const scrolexOpts = {
    cwd  : gitRoot,
    fatal: false,
    mode : 'passthru',
  }

  const dirs = [
    assetsBuildDir,
    cacheDir,
  ]

  for (let dir of dirs) {
    if (!fs.existsSync(dir)) {
      let rel = path.relative(gitRoot, dir)
      try {
        await scrolex.exe(`mkdir -p '${rel}' && echo '${rel}' >> .gitignore`, scrolexOpts)
      } catch (err) {
        console.error(`Could not create dir. rel=${rel} gitRoot=${gitRoot} dir=${dir} err=${err}`)
      }
    }
  }
}

module.exports.fsCopySync = (src, dst, { mode = '644', encoding = 'utf-8' } = {}) => {
  fs.writeFileSync(`${dst}`, fs.readFileSync(`${src}`, 'utf-8'), { mode, encoding })
}

module.exports.writeConfig = (cfg) => {
  if (!fs.existsSync(`${cfg.runtime.cacheDir}/jekyll.lanyon_assets.yml`)) {
    fs.writeFileSync(`${cfg.runtime.cacheDir}/jekyll.lanyon_assets.yml`, '# this file should be overwritten by the Webpack AssetsPlugin', 'utf-8')
  }
  try {
    fs.writeFileSync(`${cfg.runtime.cacheDir}/jekyll.config.yml`, yaml.safeDump(cfg.jekyll), 'utf-8')
  } catch (e) {
    console.error({ jekyll: cfg.jekyll })
    throw new Error(`Unable to write above config to ${cfg.runtime.cacheDir}/jekyll.config.yml. ${e.message}`)
  }

  if (cfg.runtime.dockerSync && cfg.runtime.dockerSync.enabled === true) {
    try {
      fs.writeFileSync(`${cfg.runtime.cacheDir}/docker-sync.yml`, yaml.safeDump(cfg.dockerSync), 'utf-8')
    } catch (e) {
      console.error({ dockerSync: cfg.dockerSync })
      throw new Error(`Unable to write above config to ${cfg.runtime.cacheDir}/docker-sync.yml. ${e.message}`)
    }
    try {
      fs.writeFileSync(`${cfg.runtime.cacheDir}/docker-compose.yml`, yaml.safeDump(cfg.dockerCompose), 'utf-8')
    } catch (e) {
      console.error({ dockerCompose: cfg.dockerCompose })
      throw new Error(`Unable to write above config to ${cfg.runtime.cacheDir}/docker-compose.yml. ${e.message}`)
    }
    try {
      fs.writeFileSync(`${cfg.runtime.cacheDir}/docker-compose-dev.yml`, yaml.safeDump(cfg.dockerComposeDev), 'utf-8')
    } catch (e) {
      console.error({ dockerComposeDev: cfg.dockerComposeDev })
      throw new Error(`Unable to write above config to ${cfg.runtime.cacheDir}/docker-compose-dev.yml. ${e.message}`)
    }
  }

  fs.writeFileSync(`${cfg.runtime.cacheDir}/nodemon.config.json`, JSON.stringify(cfg.nodemon, null, '  '), 'utf-8')
  fs.writeFileSync(`${cfg.runtime.cacheDir}/full-config-dump.json`, JSON.stringify(cfg, null, '  '), 'utf-8')
  fs.writeFileSync(`${cfg.runtime.cacheDir}/browsersync.config.js`, `module.exports = require("${cfg.runtime.lanyonDir}/src/config.js").browsersync`, 'utf-8')
  fs.writeFileSync(`${cfg.runtime.cacheDir}/webpack.config.js`, `module.exports = require("${cfg.runtime.lanyonDir}/src/config.js").webpack`, 'utf-8')
  fs.writeFileSync(cfg.runtime.recordsPath, JSON.stringify({}, null, '  '), 'utf-8')
}
<commit_msg>Fix reference to browsersync<commit_after>const childProcess = require('child_process')
const fs = require('fs')
// const _        = require('lodash')
const path = require('path')
const os = require('os')
// const _ = require('lodash')
const yaml = require('js-yaml')
// const spawnSync   = require('spawn-sync')
// const oneLine     = require('common-tags/lib/oneLine')
// const stripIndent = require('common-tags/lib/stripIndent')
const scrolex = require('scrolex').persistOpts({
  announce             : true,
  addCommandAsComponent: true,
})
const utils = this
const oneLine = require('common-tags/lib/oneLine')
// const pad = require('pad')

if (require.main === module) {
  scrolex.failure(`Please only used this module via require`)
  process.exit(1)
}

module.exports.formatCmd = function formatCmd (cmd, { runtime, cmdName }) {
  // Replace dirs
  cmd = cmd.replace(/\[lanyonDir]/g, runtime.lanyonDir)
  cmd = cmd.replace(/\[contentBuildDir]/g, runtime.contentBuildDir)
  cmd = cmd.replace(/\[projectDir]/g, runtime.projectDir)
  cmd = cmd.replace(/\[cacheDir]/g, runtime.cacheDir)

  // Replace all npms with their first-found full-path executables
  const npmBins = {
    'browser-sync': 'node_modules/browser-sync/dist/bin.js',
    'nodemon'     : 'node_modules/nodemon/bin/nodemon.js',
    'webpack'     : 'node_modules/webpack/bin/webpack.js',
    // 'imagemin'     : 'node_modules/imagemin-cli/cli.js',
  }
  for (const name in npmBins) {
    const tests = [
      `/srv/lanyon/${npmBins[name]}`,
      `${runtime.npmRoot}/${npmBins[name]}`,
      `${runtime.projectDir}/${npmBins[name]}`,
      `${runtime.lanyonDir}/${npmBins[name]}`,
      `${runtime.gitRoot}/${npmBins[name]}`,
    ]

    let found = false
    tests.forEach(test => {
      if (fs.existsSync(test)) {
        npmBins[name] = test
        found = true
        return false // Stop looking on first hit
      }
    })

    if (!found) {
      throw new Error(`Cannot find dependency "${name}" in "${tests.join('", "')}"`)
    }
    const pat = new RegExp(`(\\s|^)\\[${name}\\](\\s|$)`)
    cmd = cmd.replace(pat, `$1node ${npmBins[name]}$2`)

    // let nodeBin = utils.dockerString('node', { extraArgs: extraVolumes, runtime })
    // cmd = cmd.replace(pat, `$1${nodeBin} ${npmBins[name]}$2`)
  }

  // cp -f ${runtime.projectDir}/Gemfile ${runtime.cacheDir}/Gemfile &&
  let jekyllBin = utils.dockerString('jekyll', { runtime })

  // Replace shims
  cmd = cmd.replace(/(\s|^)\[jekyll\](\s|$)/, `$1${jekyllBin}$2`)

  return cmd
}

module.exports.splitDir = function splitDir (dir) {
  let dirList = []
  let parts = `${dir}`.split('/')
  let cont = ''
  for (let i in parts) {
    let part = parts[i]
    if (!part) continue
    cont = `${cont}/${part}`
    dirList.push(cont)
  }
  return dirList
}

module.exports.trapCleanup = function trapCleanup ({ runtime, code = 0, signal = '' }) {
  if (runtime.dying === true) {
    return
  }
  runtime.dying = true

  let cleanupCmds = [
    'pkill -f nodemon',
    'pkill -f browser-sync',
  ]

  if (runtime.dockerSync && runtime.dockerSync.enabled === true) {
    cleanupCmds = cleanupCmds.concat([
      `docker-sync stop`,
      // `docker-compose stop`,
    ])
  }

  console.log(`>>> About to exit. code=${code}, signal=${signal}. Cleaning up... `)
  for (let i in cleanupCmds) {
    let c = cleanupCmds[i]
    console.error(`Cleanup command: '${c}' ...`)
    try {
      childProcess.execSync(`${c}`, {
        cwd: `${runtime.cacheDir}`,
      })
    } catch (err) {} // eslint-disable-line
  }

  if (signal === 'SIGINT') {
    console.log(`>>> Process aborted`)
    process.exit(1)
  }
}

module.exports.dockerString = function dockerString (cmd, { opts = {}, runtime } = {}) {
  let volumePaths = utils.volumePaths({ runtime })
  let listVolumes = []
  for (let key in volumePaths) {
    listVolumes.push(`--volume ${key}:${volumePaths[key]}`)
  }

  if (runtime.dockerSync && runtime.dockerSync.enabled === true) {
    // -i
    // --rm
    // ${cmd}
    // --workdir=${runtime.cacheDir}
    // -e "JEKYLL_ENV=${runtime.lanyonEnv}"
    // docker-sync start && docker-compose up && docker-compose exec
    return oneLine`
      docker-compose exec -T
        lanyon-container
      ${cmd}
    `
  }

  if (!opts || !('priviliged' in opts)) {
    opts.priviliged = false
  }

  // https://github.com/envygeeks/jekyll-docker/issues/223
  // --env "JEKYLL_GID=${os.userInfo().gid}" // <-- runs the risk of: groupmod: GID '20' already exists
  let userstr = ''
  if (opts.priviliged === false) {
    userstr = oneLine`
      --env "JEKYLL_UID=${os.userInfo().uid}"
    `
  }

  return oneLine`
    docker run
      --rm
      --interactive
      ${userstr}
      --env "JEKYLL_ENV=${runtime.lanyonEnv}"
      --workdir ${runtime.cacheDir}
      ${listVolumes.join('\n')}
      ${runtime.dockerImage}
    ${cmd}
  `
}

module.exports.volumePaths = function volumePaths ({ runtime }) {
  let volumePaths = {}

  if (runtime.contentBuildDir.indexOf(runtime.projectDir) === -1) {
    volumePaths[runtime.contentBuildDir] = runtime.contentBuildDir
  }
  if (runtime.cacheDir.indexOf(runtime.projectDir) === -1) {
    volumePaths[runtime.cacheDir] = runtime.cacheDir
  }

  volumePaths[runtime.projectDir] = runtime.projectDir

  return volumePaths
}

module.exports.runString = async function runString (cmd, { runtime, cmdName, origCmd, hookName }) {
  const scrolexOpts = {
    cwd                  : runtime.cacheDir,
    fatal                : true,
    mode                 : 'passthru',
    components           : `lanyon>${cmdName}>${origCmd.split('--')[0].trim().replace('[', '').replace(']', '')}`,
    addCommandAsComponent: false,
  }

  await utils.runhooks('pre', hookName, runtime)
  await scrolex.exe(cmd, scrolexOpts)
  await utils.runhooks('post', hookName, runtime)
}

module.exports.runhooks = async (order, cmdName, runtime) => {
  let squashedHooks = utils.gethooks(order, cmdName, runtime)

  scrolex.stick(`Running ${squashedHooks.length} ${order}${cmdName} hooks`)
  if (!squashedHooks.length) {
    return
  }

  return scrolex.exe(squashedHooks.join(' && '), {
    cwd       : runtime.projectDir,
    fatal     : true,
    mode      : 'passthru',
    components: `lanyon>hooks>${order}${cmdName}`,
  })
}

module.exports.gethooks = (order, cmdName, runtime) => {
  let arr = []

  arr = [
    `${order}${cmdName}`,
    `${order}${cmdName}:production`,
    `${order}${cmdName}:development`,
    `${order}${cmdName}:content`,
    `${order}${cmdName}:content:production`,
    `${order}${cmdName}:content:development`,
    `${order}${cmdName}:assets`,
    `${order}${cmdName}:assets:production`,
    `${order}${cmdName}:assets:development`,
  ]

  let squashedHooks = []
  for (let i in arr) {
    let hook = arr[i]
    if (runtime[hook]) {
      const lastPart = hook.split(':').pop()
      let needEnv = 'both'

      if (lastPart === 'production') {
        needEnv = lastPart
      }
      if (lastPart === 'development') {
        needEnv = lastPart
      }

      if (needEnv === 'both' || runtime.lanyonEnv === needEnv) {
        squashedHooks = squashedHooks.concat(runtime[hook])
      }
    }
  }

  return squashedHooks
}

module.exports.upwardDirContaining = (find, cwd, not) => {
  if (!cwd) {
    cwd = process.env.PWD || process.cwd()
  }
  const parts = cwd.split('/')
  while (parts.length) {
    const newParts = parts
    const ppath = `${newParts.join('/')}/${find}`
    if (fs.existsSync(ppath)) {
      if (not === undefined || not !== path.basename(path.dirname(ppath))) {
        return path.dirname(ppath)
      }
    }
    parts.pop()
  }
  return false
}

module.exports.initProject = async ({ assetsBuildDir, gitRoot, cacheDir }) => {
  const scrolexOpts = {
    cwd  : gitRoot,
    fatal: false,
    mode : 'passthru',
  }

  const dirs = [
    assetsBuildDir,
    cacheDir,
  ]

  for (let dir of dirs) {
    if (!fs.existsSync(dir)) {
      let rel = path.relative(gitRoot, dir)
      try {
        await scrolex.exe(`mkdir -p '${rel}' && echo '${rel}' >> .gitignore`, scrolexOpts)
      } catch (err) {
        console.error(`Could not create dir. rel=${rel} gitRoot=${gitRoot} dir=${dir} err=${err}`)
      }
    }
  }
}

module.exports.fsCopySync = (src, dst, { mode = '644', encoding = 'utf-8' } = {}) => {
  fs.writeFileSync(`${dst}`, fs.readFileSync(`${src}`, 'utf-8'), { mode, encoding })
}

module.exports.writeConfig = (cfg) => {
  if (!fs.existsSync(`${cfg.runtime.cacheDir}/jekyll.lanyon_assets.yml`)) {
    fs.writeFileSync(`${cfg.runtime.cacheDir}/jekyll.lanyon_assets.yml`, '# this file should be overwritten by the Webpack AssetsPlugin', 'utf-8')
  }
  try {
    fs.writeFileSync(`${cfg.runtime.cacheDir}/jekyll.config.yml`, yaml.safeDump(cfg.jekyll), 'utf-8')
  } catch (e) {
    console.error({ jekyll: cfg.jekyll })
    throw new Error(`Unable to write above config to ${cfg.runtime.cacheDir}/jekyll.config.yml. ${e.message}`)
  }

  if (cfg.runtime.dockerSync && cfg.runtime.dockerSync.enabled === true) {
    try {
      fs.writeFileSync(`${cfg.runtime.cacheDir}/docker-sync.yml`, yaml.safeDump(cfg.dockerSync), 'utf-8')
    } catch (e) {
      console.error({ dockerSync: cfg.dockerSync })
      throw new Error(`Unable to write above config to ${cfg.runtime.cacheDir}/docker-sync.yml. ${e.message}`)
    }
    try {
      fs.writeFileSync(`${cfg.runtime.cacheDir}/docker-compose.yml`, yaml.safeDump(cfg.dockerCompose), 'utf-8')
    } catch (e) {
      console.error({ dockerCompose: cfg.dockerCompose })
      throw new Error(`Unable to write above config to ${cfg.runtime.cacheDir}/docker-compose.yml. ${e.message}`)
    }
    try {
      fs.writeFileSync(`${cfg.runtime.cacheDir}/docker-compose-dev.yml`, yaml.safeDump(cfg.dockerComposeDev), 'utf-8')
    } catch (e) {
      console.error({ dockerComposeDev: cfg.dockerComposeDev })
      throw new Error(`Unable to write above config to ${cfg.runtime.cacheDir}/docker-compose-dev.yml. ${e.message}`)
    }
  }

  fs.writeFileSync(`${cfg.runtime.cacheDir}/nodemon.config.json`, JSON.stringify(cfg.nodemon, null, '  '), 'utf-8')
  fs.writeFileSync(`${cfg.runtime.cacheDir}/full-config-dump.json`, JSON.stringify(cfg, null, '  '), 'utf-8')
  fs.writeFileSync(`${cfg.runtime.cacheDir}/browsersync.config.js`, `module.exports = require("${cfg.runtime.lanyonDir}/src/config.js").browsersync`, 'utf-8')
  fs.writeFileSync(`${cfg.runtime.cacheDir}/webpack.config.js`, `module.exports = require("${cfg.runtime.lanyonDir}/src/config.js").webpack`, 'utf-8')
  fs.writeFileSync(cfg.runtime.recordsPath, JSON.stringify({}, null, '  '), 'utf-8')
}
<|endoftext|>


<commit_before>define([
    "jquery"
], function($) {
    var extractParameters = function(params, sources) {
        var tmp,
            j,
            paramObjs = {};
        if (params.length > 0) {
            var p = params.slice(1).split('!');
            for (var i = p.length-1; i >= 0; i--) {
                // support injection parameters in other patterns
                if (p[i][0] == '#') {
                    var param, effect;

                    if (p[i].indexOf('.') > 0) {
                        tmp = p[i].split('.');
                        param = tmp[0];
                        effect = tmp[1];
                    } else {
                        param = p[i];
                        effect = undefined;
                    }
                    var source = [sources.pop()];
                    // XXX: $a and url were also not defined in the old context
                    // We need automated tests
                    mapal.injection.load($a, url, param.slice(1), source);
                } else if (p[i].indexOf('=') > 0) {
                    j = p[i].indexOf('=');
                    paramObjs[p[i].slice(0, j)] = p[i].slice(j+1);
                } else {
                    paramObjs[p[i]] = true;
                }
            }
        }
        return paramObjs;
    };

    // input = "a!b=1 2!c=x"
    // --> options = {a: true, b: '1 2', c: 'x'}
    var parseOptions = function(input) {
        var params = input.split("!"),
            options = {}, name, value, index;

        for (var i=0; i<params.length; i++) {
            index = params[i].indexOf("=");
            if (index === -1) {
                name = params[i];
                value = true;
            } else {
                name = params[i].slice(0, index);
                value = params[i].slice(index+1);
            }
            options[name] = value;
        }
        return options;
    };

    var jquery_plugin = function(pattern) {
        var plugin = function(method) {
            if (!method || typeof method === "object") {
                pattern.init.apply(this, [this].concat(arguments));
            } else if (pattern[method]) {
                pattern[method].apply(
                    this,
                    [this].concat(Array.prototype.slice.call(arguments, 1))
                );
            } else {
                $.error('Method ' + method +
                        ' does not exist on jQuery.' + pattern.name);
            }
            return this;
        };
        return plugin;
    };

    function _renumberAttribute(el, attr, i) {
        var $el = $(el),
            buf = $el.attr(attr);
        if (buf) {
            $el.attr(attr, buf.replace(/[0-9]+/, i));
        }
    }

    function _renumberEl() {
	_renumberAttribute(this, "for", i);
	_renumberAttribute(this, "id", i);
	_renumberAttribute(this, "name", i);
    }

    function renumber($container, selector) {
        var $entries = $container.find(selector ? selector : "fieldset,tr,dd"),
            entry, i;

        for (i=0; i<$entries.length; i++) {
            entry = $entries.get(i);
            _renumberAttribute(entry, "id", i);
            $("label, :input", entry).each(_renumberEl);
        }
    }

    function hasContent($el) {
        if ($el.is(":input")) {
            return $el.val();
        } else {
            return $el.text().replace(/\s*/g, "") || $el.find("img,object,video,audio").length;
        }
    }

    //     Underscore.js 1.3.1
    //     (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc.
    //     Underscore is freely distributable under the MIT license.
    //     Portions of Underscore are inspired or borrowed from Prototype,
    //     Oliver Steele's Functional, and John Resig's Micro-Templating.
    //     For all details and documentation:
    //     http://documentcloud.github.com/underscore
    //
    // Returns a function, that, as long as it continues to be invoked, will not
    // be triggered. The function will be called after it stops being called for
    // N milliseconds.
    var debounce = function(func, wait) {
        var timeout;
        return function() {
            var context = this, args = arguments;
            var later = function() {
                timeout = null;
                func.apply(context, args);
            };
            clearTimeout(timeout);
            timeout = setTimeout(later, wait);
        };
    };

    var utils = {
        extractParameters: extractParameters,
        parseOptions: parseOptions,
        //load_modules: load_modules,
        // pattern pimping - own module?
        jquery_plugin: jquery_plugin,
        debounce: debounce,
	renumber: renumber,
	hasContent: hasContent
    };

    return utils;
});
<commit_msg>Fix arguments for jQuery plugin init calls<commit_after>define([
    "jquery"
], function($) {
    var extractParameters = function(params, sources) {
        var tmp,
            j,
            paramObjs = {};
        if (params.length > 0) {
            var p = params.slice(1).split('!');
            for (var i = p.length-1; i >= 0; i--) {
                // support injection parameters in other patterns
                if (p[i][0] == '#') {
                    var param, effect;

                    if (p[i].indexOf('.') > 0) {
                        tmp = p[i].split('.');
                        param = tmp[0];
                        effect = tmp[1];
                    } else {
                        param = p[i];
                        effect = undefined;
                    }
                    var source = [sources.pop()];
                    // XXX: $a and url were also not defined in the old context
                    // We need automated tests
                    mapal.injection.load($a, url, param.slice(1), source);
                } else if (p[i].indexOf('=') > 0) {
                    j = p[i].indexOf('=');
                    paramObjs[p[i].slice(0, j)] = p[i].slice(j+1);
                } else {
                    paramObjs[p[i]] = true;
                }
            }
        }
        return paramObjs;
    };

    // input = "a!b=1 2!c=x"
    // --> options = {a: true, b: '1 2', c: 'x'}
    var parseOptions = function(input) {
        var params = input.split("!"),
            options = {}, name, value, index;

        for (var i=0; i<params.length; i++) {
            index = params[i].indexOf("=");
            if (index === -1) {
                name = params[i];
                value = true;
            } else {
                name = params[i].slice(0, index);
                value = params[i].slice(index+1);
            }
            options[name] = value;
        }
        return options;
    };

    var jquery_plugin = function(pattern) {
        var plugin = function(method) {
            if (!method || typeof method === "object") {
                pattern.init.apply(
                        this,
                        [this].concat(Array.prototype.slice.call(arguments)));
            } else if (pattern[method]) {
                pattern[method].apply(
                    this,
                    [this].concat(Array.prototype.slice.call(arguments, 1))
                );
            } else {
                $.error('Method ' + method +
                        ' does not exist on jQuery.' + pattern.name);
            }
            return this;
        };
        return plugin;
    };

    function _renumberAttribute(el, attr, i) {
        var $el = $(el),
            buf = $el.attr(attr);
        if (buf) {
            $el.attr(attr, buf.replace(/[0-9]+/, i));
        }
    }

    function _renumberEl() {
	_renumberAttribute(this, "for", i);
	_renumberAttribute(this, "id", i);
	_renumberAttribute(this, "name", i);
    }

    function renumber($container, selector) {
        var $entries = $container.find(selector ? selector : "fieldset,tr,dd"),
            entry, i;

        for (i=0; i<$entries.length; i++) {
            entry = $entries.get(i);
            _renumberAttribute(entry, "id", i);
            $("label, :input", entry).each(_renumberEl);
        }
    }

    function hasContent($el) {
        if ($el.is(":input")) {
            return $el.val();
        } else {
            return $el.text().replace(/\s*/g, "") || $el.find("img,object,video,audio").length;
        }
    }

    //     Underscore.js 1.3.1
    //     (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc.
    //     Underscore is freely distributable under the MIT license.
    //     Portions of Underscore are inspired or borrowed from Prototype,
    //     Oliver Steele's Functional, and John Resig's Micro-Templating.
    //     For all details and documentation:
    //     http://documentcloud.github.com/underscore
    //
    // Returns a function, that, as long as it continues to be invoked, will not
    // be triggered. The function will be called after it stops being called for
    // N milliseconds.
    var debounce = function(func, wait) {
        var timeout;
        return function() {
            var context = this, args = arguments;
            var later = function() {
                timeout = null;
                func.apply(context, args);
            };
            clearTimeout(timeout);
            timeout = setTimeout(later, wait);
        };
    };

    var utils = {
        extractParameters: extractParameters,
        parseOptions: parseOptions,
        //load_modules: load_modules,
        // pattern pimping - own module?
        jquery_plugin: jquery_plugin,
        debounce: debounce,
	renumber: renumber,
	hasContent: hasContent
    };

    return utils;
});
<|endoftext|>


<commit_before>'use strict';

const Promise  = require('bluebird');
const Path     = require('path');
const fs       = require('fs');
const crypto   = require('crypto');
const minimist = require('minimist');
const fang     = require('@allmarkedup/fang');
const _        = require('lodash');

module.exports = {

    lang(filePath) {
        return fang(filePath) || {
            name:  Path.parse(filePath).ext.replace('.', '').toUpperCase(),
            mode:  'text',
            scope: null,
            color: null
        };
    },

    titlize: function (str) {
        return _.startCase(str);
    },

    slugify: str => _.kebabCase(_.deburr(str)).toLowerCase(),

    toJSON(item) {
        const obj = {};
        _.forOwn(item, (value, key) => {
            if (!key.startsWith('_')) {
                if (value instanceof Buffer) {
                    obj[key] = '<Buffer>';
                } else if (value && typeof value.toJSON === 'function') {
                    obj[key] = value.toJSON();
                } else {
                    obj[key] = value;
                }
            }
        });
        return obj;
    },

    escapeForRegexp(str) {
        return str.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
    },

    parseArgv() {
       const argv = minimist(process.argv.slice(2));
       const args = argv._;
       const command = args.length ? args.shift() : null;
       const opts = argv;
       delete opts._;
       delete opts.$0;
       return {
           command: command,
           args: args,
           opts: opts
       };
   },

    stringify(data, indent) {
       return JSON.stringify(data, function (key, val) {
           if (this[key] instanceof Buffer) {
               return '<Buffer>';
           }
           if (this[key] instanceof Function) {
               return '<Function>';
           }
           if (_.isPlainObject(this[key]) && !_.size(this[key])) {
               return '{}';
           }
           return val;
       }, indent || 4);
   },

    fileExistsSync(path) {
       try {
           fs.accessSync(path, fs.F_OK);
           return true;
       } catch (e) {
           return false;
       }
   },

    isPromise(value) {
       return (value && _.isFunction(value.then));
   },

    md5(str) {
       return crypto.createHash('md5').update(str).digest('hex');
   },

    mergeProp(prop, upstream) {
       if (_.isArray(upstream)) {
           return _.uniq(_.concat(upstream, _.castArray(prop)));
       } else if (_.isObject(upstream)) {
           return _.defaultsDeep(_.clone(prop || {}), _.clone(upstream));
       }
       if (_.isUndefined(prop)) {
           return upstream;
       }
       return prop;
   }

};
<commit_msg>fix(components): prevent underscores in component names getting converted to dashes<commit_after>'use strict';

const Promise  = require('bluebird');
const Path     = require('path');
const fs       = require('fs');
const crypto   = require('crypto');
const minimist = require('minimist');
const fang     = require('@allmarkedup/fang');
const _        = require('lodash');

module.exports = {

    lang(filePath) {
        return fang(filePath) || {
            name:  Path.parse(filePath).ext.replace('.', '').toUpperCase(),
            mode:  'text',
            scope: null,
            color: null
        };
    },

    titlize: function (str) {
        return _.startCase(str);
    },

    slugify: str => _.deburr(str).replace(/\s+/g, '-').toLowerCase(),

    toJSON(item) {
        const obj = {};
        _.forOwn(item, (value, key) => {
            if (!key.startsWith('_')) {
                if (value instanceof Buffer) {
                    obj[key] = '<Buffer>';
                } else if (value && typeof value.toJSON === 'function') {
                    obj[key] = value.toJSON();
                } else {
                    obj[key] = value;
                }
            }
        });
        return obj;
    },

    escapeForRegexp(str) {
        return str.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
    },

    parseArgv() {
       const argv = minimist(process.argv.slice(2));
       const args = argv._;
       const command = args.length ? args.shift() : null;
       const opts = argv;
       delete opts._;
       delete opts.$0;
       return {
           command: command,
           args: args,
           opts: opts
       };
   },

    stringify(data, indent) {
       return JSON.stringify(data, function (key, val) {
           if (this[key] instanceof Buffer) {
               return '<Buffer>';
           }
           if (this[key] instanceof Function) {
               return '<Function>';
           }
           if (_.isPlainObject(this[key]) && !_.size(this[key])) {
               return '{}';
           }
           return val;
       }, indent || 4);
   },

    fileExistsSync(path) {
       try {
           fs.accessSync(path, fs.F_OK);
           return true;
       } catch (e) {
           return false;
       }
   },

    isPromise(value) {
       return (value && _.isFunction(value.then));
   },

    md5(str) {
       return crypto.createHash('md5').update(str).digest('hex');
   },

    mergeProp(prop, upstream) {
       if (_.isArray(upstream)) {
           return _.uniq(_.concat(upstream, _.castArray(prop)));
       } else if (_.isObject(upstream)) {
           return _.defaultsDeep(_.clone(prop || {}), _.clone(upstream));
       }
       if (_.isUndefined(prop)) {
           return upstream;
       }
       return prop;
   }

};
<|endoftext|>


<commit_before>/**
 * Extends an object, copying properties from right to left
 * @param  {Object} objectsN N objects to copy
 * @return {Object}          conglomeration of all object properties
 */
var extend = function( objectsN ) {
  var destination = {};
  var i = 0;
  var length = arguments.length;
  var prop;
  var src;

  for ( i = 0; i < length; i++ ) {
    src = arguments[i];
    for( prop in src ) {

      if (src[prop] !== undefined) {
        destination[prop] = src[prop];
      }
    }
  }

  return destination;
};

/**
 * Export a symbol to the namespace property
 * @param  {string} name  the name of the symbol
 * @param  {Object} value the value of the symbol
 */
var exportSymbol = function( name, value ) {
  if ( ! LM[name] ) {
    LM[name] = value;
  }
};

/**
 * Export a symbol only in test environments
 * @param  {string} name  symbol name
 * @param  {Object} value symbol value
 */
var exportTestSymbol = function( name, value ) {
  if( ! LM[name] && module && module.exports ) {
    LM[name] = value;
  }
};

exportTestSymbol('extend', extend);
<commit_msg>fix undefined module reference<commit_after>/**
 * Extends an object, copying properties from right to left
 * @param  {Object} objectsN N objects to copy
 * @return {Object}          conglomeration of all object properties
 */
var extend = function( objectsN ) {
  var destination = {};
  var i = 0;
  var length = arguments.length;
  var prop;
  var src;

  for ( i = 0; i < length; i++ ) {
    src = arguments[i];
    for( prop in src ) {

      if (src[prop] !== undefined) {
        destination[prop] = src[prop];
      }
    }
  }

  return destination;
};

/**
 * Export a symbol to the namespace property
 * @param  {string} name  the name of the symbol
 * @param  {Object} value the value of the symbol
 */
var exportSymbol = function( name, value ) {
  if ( ! LM[name] ) {
    LM[name] = value;
  }
};

/**
 * Export a symbol only in test environments
 * @param  {string} name  symbol name
 * @param  {Object} value symbol value
 */
var exportTestSymbol = function( name, value ) {
  if( ! LM[name] && typeof module !== 'undefined' && module.exports ) {
    LM[name] = value;
  }
};

exportTestSymbol('extend', extend);
<|endoftext|>


<commit_before>import database from 'sqlite';
import Promise from 'bluebird';
import {find, get} from 'lodash';
import moment from 'moment';
import {AllHtmlEntities} from 'html-entities';

import {getTodaysMessages, processMessages} from './slack';
import {slack, logger} from './resources';
import config from '../config';

/**
 * Returns string with pretty printed json object
 *
 * @param {Object} json - json object
 * @returns {string} - pretty printed json string
 */

export function prettyPrint(json) {
  return JSON.stringify(json, null, 2);
}

/**
 * Strips the @obedbot part of the message
 *
 * @param {string} order - message with the order
 * @returns {string} - order message without the @obedbot mention
 */

export function stripMention(order) {
  //check if user used full colon after @obedbot
  const orderStart = (order.charAt(12) === ':') ? 14 : 13;

  return order.substring(orderStart);
}

export function isObedbotMentioned(order) {
  return new RegExp(`<@${config.slack.botId}>:?`).test(order);
}

export function isChannelPublic(channel) {
  return channel === config.slack.lunchChannelId;
}

export function alreadyReacted(reactions) {
  return !!find(
    reactions,
    ({name, users}) => name === config.orderReaction && users.includes(config.slack.botId)
  );
}

/**
 * Checks if the given message is an order
 *
 * @param {string} order - order message
 * @returns {bool} - true if order matches, false if not identified
 */
export function isOrder(order) {
  const regexes = config.orderRegex;
  if (isObedbotMentioned(order)) {
    order = stripMention(order);
  }
  order = order.toLowerCase().trim();

  logger.devLog('Checking order: ' + order);

  for (let regexKey in regexes) {
    if (regexes.hasOwnProperty(regexKey)) {
      if (regexes[regexKey].test(order)) {
        logger.devLog(`Order type is ${regexKey}`);
        return true;
      }
    }
  }

  logger.devLog('Message is not an order');
  return false;
}

/**
 * Loads the orders since the last noon
 */
export function loadTodayOrders() {
  logger.devLog('Loading today\'s orders');

  getTodaysMessages().then(processMessages);
}

/**
 * Returns the name of the restaurant to which the order belongs to
 *
 * @param {string} order - message with the order
 * @returns {string} - name of the restaurant
 */

export const restaurants = {
  presto: 'presto',
  pizza: 'pizza',
  veglife: 'veglife',
  spaghetti: 'spaghetti',
  shop: 'shop',
};

export function identifyRestaurant(order) {
  const regexes = config.orderRegex;
  const values = [
    {regex: regexes.presto, name: restaurants.presto},
    {regex: regexes.pizza, name: restaurants.pizza},
    {regex: regexes.veglife, name: restaurants.veglife},
    {regex: regexes.spaghetti, name: restaurants.spaghetti},
    {regex: regexes.shop, name: restaurants.shop},
  ];
  let ans;

  values.forEach((restaurant) => {
    if (restaurant.regex.test(order)) {
      ans = restaurant.name;
    }
  });
  return ans;
}

export function getOrderFromMessage(msg, restaurant) {
  const regex = config.orderRegex[restaurant];
  return msg.match(regex)[0];
}

export function saveUser(userId) {
  logger.devLog('Saving user ' + userId);

  slack.web.im.open(userId)
    .then(({channel: {id: channelId}}) => {
      if (!config.dev) {
        slack.web.chat.postMessage(
          channelId,
          'Ahoj, volám sa obedbot, našiel som ťa v channely #obedy ' +
          'a nemal som ťa ešte v mojom zápisníčku, tak si ťa poznamenávam, ' +
          'budem ti odteraz posielať last cally, pokiaľ v daný deň nemáš nič objednané :)',
        );
      }

      slack.web.users.info(userId)
        .then((userInfo) => {
          const realname = userInfo.user.profile.real_name;
          database.run(
              'INSERT INTO users(user_id, channel_id, username) VALUES($userId, $channelId, $username)',
              {$userId: userId, $channelId: channelId, $username: realname}
            )
            .then(() => {
              logger.devLog(`User ${realname} has been added to database`);
              if (!config.dev) {
                slack.web.chat.postMessage(
                  channelId,
                  'Dobre, už som si ťa zapísal :) Môžeš si teraz objednávať v channely ' +
                  '#obedy tak, že napíšeš `@obedbot [tvoja objednávka]`',
                );
              }
            }).catch((err) => logger.error(`User ${realname} is already in the database`, err));
        });
    }).catch(
      () => logger.error('Trying to save bot or disabled user')
    );
}

export async function userExists(userId) {
  return database
    .get(
      'SELECT * FROM users WHERE user_id=$userId',
      {$userId: userId}
    ).then((result) => !!result);
}

export function parseOrders() {
  let presto = {
    soups: {},
    meals: Array(7).fill(0),
  };
  let pizza = {};
  let spaghetti = {};
  let veglife = {
    meals: Array(4).fill(0),
    soups: 0,
    salads: 0,
  };
  let shop = [];

  logger.devLog('Parsing orders for webpage display');

  return getTodaysMessages()
    .then((messages) => {
      for (let message of messages) {
        if (!(isObedbotMentioned(message.text) && isOrder(message.text))) {
          continue;
        }
        const text = stripMention(message.text).toLowerCase().trim();

        const restaurant = identifyRestaurant(text);
        const order = getOrderFromMessage(text, restaurant);

        logger.devLog(`Message ${text} is from ${restaurant}, order ${order}`);

        if (restaurant === restaurants.presto) {
          const mainMealNum = parseInt(order.charAt(6), 10) - 1;
          const soup = order.substring(8);

          presto.meals[mainMealNum]++;
          if (soup) {
            presto.soups[soup] = get(presto.soups, soup, 0) + 1;
          }
        } else if (restaurant === restaurants.pizza) {
          const pizzaNum = order.match(/\d+/g)[0];
          const pizzaSize = order.match(/\d+/g)[1];

          if (!pizzaSize || pizzaSize === '33') {
            pizza[pizzaNum] = get(pizza, pizzaNum, 0) + 1;
          } else {
            pizza[`${pizzaNum} veľkosti ${pizzaSize}`] = get(pizza, `${pizzaNum} veľkosti ${pizzaSize}`, 0) + 1;
          }
        } else if (restaurant === restaurants.veglife) {
          const mainMealNum = parseInt(order.charAt(3), 10) - 1;
          const saladOrSoup = order.charAt(order.length - 1);

          veglife.meals[mainMealNum]++;
          if (saladOrSoup === 's') {
            veglife.salads++;
          } else {
            veglife.soups++;
          }
        } else if (restaurant === restaurants.spaghetti) {
          spaghetti[order] = get(spaghetti, order, 0) + 1;
        } else if (restaurant === restaurants.shop) {
          shop.push(order.substring(6));
        }
      }

      return Promise.resolve({presto, pizza, spaghetti, veglife, shop});
    });
}

export function parseOrdersNamed() {
  const orders = {
    presto: [],
    pizza: [],
    veglife: [],
    spaghetti: [],
    shop: [],
  };
  let messages;

  return getTodaysMessages()
    .then((history) => {
      messages = history;
      return database.all('SELECT * FROM users');
    }).then((users) => {
      for (let message of messages) {
        if (!(isObedbotMentioned(message.text) && isOrder(message.text))) {
          continue;
        }
        const text = stripMention(message.text).toLowerCase().trim();

        const restaurant = identifyRestaurant(text);
        const order = {
          user: find(users, {user_id: message.user}).username,
          order: getOrderFromMessage(text, restaurant),
        };

        if (restaurant === restaurants.presto) {
          orders.presto.push(order);
        } else if (restaurant === restaurants.pizza) {
          orders.pizza.push(order);
        } else if (restaurant === restaurants.veglife) {
          orders.veglife.push(order);
        } else if (restaurant === restaurants.spaghetti) {
          orders.spaghetti.push(order);
        } else if (restaurant === restaurants.shop) {
          order.order = order.order.substring(6);
          orders.shop.push(order);
        }
      }
      logger.devLog(`Orders for named display on webpage: ${orders}`);
      return Promise.resolve(orders);
    });
}

export function getTodaysPrestoMenu(menu) {
  const entities = new AllHtmlEntities();
  // CENA is there as a delimiter because the menu continues on with different things
  const slovakDays = ['', 'PONDELOK', 'UTOROK', 'STREDA', 'ŠTVRTOK', 'PIATOK', 'CENA'];
  const now = moment();
  let today = now.day();

  // if it is Saturday, Sunday or Friday afternoon, set day to Monday
  if (today === 0 || today === 6 || (today === 5 && now.hours() > 13)) {
    today = 1;
  } else if (now.hours() > 13) {
    today++;
  }

  // delete all HTML tags
  menu = menu.replace(/<[^>]*>/g, '');
  menu = entities.decode(menu);
  menu = menu
    // presto has the whole menu on single page, cut out only today
    .substring(menu.indexOf(slovakDays[today]), menu.indexOf(slovakDays[today + 1]))
    .split('\n')
    .map((row) => row.trim())
    .filter((row) => row.length)
    .join('\n')
    // replace all multiple whitespaces with single space
    .replace(/\s\s+/g, ' ');

  return menu;
}

export function getTodaysVeglifeMenu(menu) {
  const slovakDays = ['', 'PONDELOK', 'UTOROK', 'STREDA', 'ŠTVRTOK', 'PIATOK', 'SOBOTA'];
  const now = moment();
  let today = now.day();

  // if it is Saturday or Sunday, set day to Monday
  if (today === 0 || today === 6 || (today === 5 && now.hours() > 13)) {
    today = 1;
  } else if (now.hours() > 13) {
    today++;
  }

  return menu
    .substring(menu.indexOf(slovakDays[today]), menu.indexOf(slovakDays[today + 1]))
    // delete all HTML tags
    .replace(/<[^>]*>/g, '')
    .split('\n')
    .map((row) => row.trim())
    .filter((row) => row.length)
    .join('\n');
}
<commit_msg>Fix as_user bug in greeting messages<commit_after>import database from 'sqlite';
import Promise from 'bluebird';
import {find, get} from 'lodash';
import moment from 'moment';
import {AllHtmlEntities} from 'html-entities';

import {getTodaysMessages, processMessages} from './slack';
import {slack, logger} from './resources';
import config from '../config';

/**
 * Returns string with pretty printed json object
 *
 * @param {Object} json - json object
 * @returns {string} - pretty printed json string
 */

export function prettyPrint(json) {
  return JSON.stringify(json, null, 2);
}

/**
 * Strips the @obedbot part of the message
 *
 * @param {string} order - message with the order
 * @returns {string} - order message without the @obedbot mention
 */

export function stripMention(order) {
  //check if user used full colon after @obedbot
  const orderStart = (order.charAt(12) === ':') ? 14 : 13;

  return order.substring(orderStart);
}

export function isObedbotMentioned(order) {
  return new RegExp(`<@${config.slack.botId}>:?`).test(order);
}

export function isChannelPublic(channel) {
  return channel === config.slack.lunchChannelId;
}

export function alreadyReacted(reactions) {
  return !!find(
    reactions,
    ({name, users}) => name === config.orderReaction && users.includes(config.slack.botId)
  );
}

/**
 * Checks if the given message is an order
 *
 * @param {string} order - order message
 * @returns {bool} - true if order matches, false if not identified
 */
export function isOrder(order) {
  const regexes = config.orderRegex;
  if (isObedbotMentioned(order)) {
    order = stripMention(order);
  }
  order = order.toLowerCase().trim();

  logger.devLog('Checking order: ' + order);

  for (let regexKey in regexes) {
    if (regexes.hasOwnProperty(regexKey)) {
      if (regexes[regexKey].test(order)) {
        logger.devLog(`Order type is ${regexKey}`);
        return true;
      }
    }
  }

  logger.devLog('Message is not an order');
  return false;
}

/**
 * Loads the orders since the last noon
 */
export function loadTodayOrders() {
  logger.devLog('Loading today\'s orders');

  getTodaysMessages().then(processMessages);
}

/**
 * Returns the name of the restaurant to which the order belongs to
 *
 * @param {string} order - message with the order
 * @returns {string} - name of the restaurant
 */

export const restaurants = {
  presto: 'presto',
  pizza: 'pizza',
  veglife: 'veglife',
  spaghetti: 'spaghetti',
  shop: 'shop',
};

export function identifyRestaurant(order) {
  const regexes = config.orderRegex;
  const values = [
    {regex: regexes.presto, name: restaurants.presto},
    {regex: regexes.pizza, name: restaurants.pizza},
    {regex: regexes.veglife, name: restaurants.veglife},
    {regex: regexes.spaghetti, name: restaurants.spaghetti},
    {regex: regexes.shop, name: restaurants.shop},
  ];
  let ans;

  values.forEach((restaurant) => {
    if (restaurant.regex.test(order)) {
      ans = restaurant.name;
    }
  });
  return ans;
}

export function getOrderFromMessage(msg, restaurant) {
  const regex = config.orderRegex[restaurant];
  return msg.match(regex)[0];
}

export function saveUser(userId) {
  logger.devLog('Saving user ' + userId);

  slack.web.im.open(userId)
    .then(({channel: {id: channelId}}) => {
      if (!config.dev) {
        slack.web.chat.postMessage(
          channelId,
          'Ahoj, volám sa obedbot, našiel som ťa v channely #obedy ' +
          'a nemal som ťa ešte v mojom zápisníčku, tak si ťa poznamenávam, ' +
          'budem ti odteraz posielať last cally, pokiaľ v daný deň nemáš nič objednané :)',
          {as_user: true}
        );
      }

      slack.web.users.info(userId)
        .then((userInfo) => {
          const realname = userInfo.user.profile.real_name;
          database.run(
              'INSERT INTO users(user_id, channel_id, username) VALUES($userId, $channelId, $username)',
              {$userId: userId, $channelId: channelId, $username: realname}
            )
            .then(() => {
              logger.devLog(`User ${realname} has been added to database`);
              if (!config.dev) {
                slack.web.chat.postMessage(
                  channelId,
                  'Dobre, už som si ťa zapísal :) Môžeš si teraz objednávať v channely ' +
                  '#obedy tak, že napíšeš `@obedbot [tvoja objednávka]`',
                  {as_user: true}
                );
              }
            }).catch((err) => logger.error(`User ${realname} is already in the database`, err));
        });
    }).catch(
      () => logger.error('Trying to save bot or disabled user')
    );
}

export async function userExists(userId) {
  return database
    .get(
      'SELECT * FROM users WHERE user_id=$userId',
      {$userId: userId}
    ).then((result) => !!result);
}

export function parseOrders() {
  let presto = {
    soups: {},
    meals: Array(7).fill(0),
  };
  let pizza = {};
  let spaghetti = {};
  let veglife = {
    meals: Array(4).fill(0),
    soups: 0,
    salads: 0,
  };
  let shop = [];

  logger.devLog('Parsing orders for webpage display');

  return getTodaysMessages()
    .then((messages) => {
      for (let message of messages) {
        if (!(isObedbotMentioned(message.text) && isOrder(message.text))) {
          continue;
        }
        const text = stripMention(message.text).toLowerCase().trim();

        const restaurant = identifyRestaurant(text);
        const order = getOrderFromMessage(text, restaurant);

        logger.devLog(`Message ${text} is from ${restaurant}, order ${order}`);

        if (restaurant === restaurants.presto) {
          const mainMealNum = parseInt(order.charAt(6), 10) - 1;
          const soup = order.substring(8);

          presto.meals[mainMealNum]++;
          if (soup) {
            presto.soups[soup] = get(presto.soups, soup, 0) + 1;
          }
        } else if (restaurant === restaurants.pizza) {
          const pizzaNum = order.match(/\d+/g)[0];
          const pizzaSize = order.match(/\d+/g)[1];

          if (!pizzaSize || pizzaSize === '33') {
            pizza[pizzaNum] = get(pizza, pizzaNum, 0) + 1;
          } else {
            pizza[`${pizzaNum} veľkosti ${pizzaSize}`] = get(pizza, `${pizzaNum} veľkosti ${pizzaSize}`, 0) + 1;
          }
        } else if (restaurant === restaurants.veglife) {
          const mainMealNum = parseInt(order.charAt(3), 10) - 1;
          const saladOrSoup = order.charAt(order.length - 1);

          veglife.meals[mainMealNum]++;
          if (saladOrSoup === 's') {
            veglife.salads++;
          } else {
            veglife.soups++;
          }
        } else if (restaurant === restaurants.spaghetti) {
          spaghetti[order] = get(spaghetti, order, 0) + 1;
        } else if (restaurant === restaurants.shop) {
          shop.push(order.substring(6));
        }
      }

      return Promise.resolve({presto, pizza, spaghetti, veglife, shop});
    });
}

export function parseOrdersNamed() {
  const orders = {
    presto: [],
    pizza: [],
    veglife: [],
    spaghetti: [],
    shop: [],
  };
  let messages;

  return getTodaysMessages()
    .then((history) => {
      messages = history;
      return database.all('SELECT * FROM users');
    }).then((users) => {
      for (let message of messages) {
        if (!(isObedbotMentioned(message.text) && isOrder(message.text))) {
          continue;
        }
        const text = stripMention(message.text).toLowerCase().trim();

        const restaurant = identifyRestaurant(text);
        const order = {
          user: find(users, {user_id: message.user}).username,
          order: getOrderFromMessage(text, restaurant),
        };

        if (restaurant === restaurants.presto) {
          orders.presto.push(order);
        } else if (restaurant === restaurants.pizza) {
          orders.pizza.push(order);
        } else if (restaurant === restaurants.veglife) {
          orders.veglife.push(order);
        } else if (restaurant === restaurants.spaghetti) {
          orders.spaghetti.push(order);
        } else if (restaurant === restaurants.shop) {
          order.order = order.order.substring(6);
          orders.shop.push(order);
        }
      }
      logger.devLog(`Orders for named display on webpage: ${orders}`);
      return Promise.resolve(orders);
    });
}

export function getTodaysPrestoMenu(menu) {
  const entities = new AllHtmlEntities();
  // CENA is there as a delimiter because the menu continues on with different things
  const slovakDays = ['', 'PONDELOK', 'UTOROK', 'STREDA', 'ŠTVRTOK', 'PIATOK', 'CENA'];
  const now = moment();
  let today = now.day();

  // if it is Saturday, Sunday or Friday afternoon, set day to Monday
  if (today === 0 || today === 6 || (today === 5 && now.hours() > 13)) {
    today = 1;
  } else if (now.hours() > 13) {
    today++;
  }

  // delete all HTML tags
  menu = menu.replace(/<[^>]*>/g, '');
  menu = entities.decode(menu);
  menu = menu
    // presto has the whole menu on single page, cut out only today
    .substring(menu.indexOf(slovakDays[today]), menu.indexOf(slovakDays[today + 1]))
    .split('\n')
    .map((row) => row.trim())
    .filter((row) => row.length)
    .join('\n')
    // replace all multiple whitespaces with single space
    .replace(/\s\s+/g, ' ');

  return menu;
}

export function getTodaysVeglifeMenu(menu) {
  const slovakDays = ['', 'PONDELOK', 'UTOROK', 'STREDA', 'ŠTVRTOK', 'PIATOK', 'SOBOTA'];
  const now = moment();
  let today = now.day();

  // if it is Saturday or Sunday, set day to Monday
  if (today === 0 || today === 6 || (today === 5 && now.hours() > 13)) {
    today = 1;
  } else if (now.hours() > 13) {
    today++;
  }

  return menu
    .substring(menu.indexOf(slovakDays[today]), menu.indexOf(slovakDays[today + 1]))
    // delete all HTML tags
    .replace(/<[^>]*>/g, '')
    .split('\n')
    .map((row) => row.trim())
    .filter((row) => row.length)
    .join('\n');
}
<|endoftext|>