<commit_before>
import _ from 'lodash';
import express from 'express';
import WebSocket from 'ws';
import bodyParser from 'body-parser';
import mongodb from 'mongodb';
import core from './forest-core';

let serverHost=null;
let serverPort=0;

// --------------------------------

const logRequest = (req, res, next) => {
  console.log('---------------------------->');
  if(req.method==='POST') console.log(req.method, req.originalUrl, req.headers.remote||'', '\n', req.body);
  else                    console.log(req.method, req.originalUrl, req.headers.remote||'');
  next();
};

const logResponse = (req, res, next) => {
  console.log(res.statusCode)
  console.log('<----------------------------');
  next();
};

const CORS = (req, res, next) => {
  res.header('Access-Control-Allow-Origin','*');
  res.header('Access-Control-Allow-Methods', 'GET,POST,OPTIONS');
  res.header('Access-Control-Allow-Headers', 'Content-Type, X-Requested-With, Remote'); // Accept, X-Requested-By, Origin, Cache-Control
  next();
};

const app = express();

app.use(bodyParser.json());

app.options("/*",
  logRequest,
  CORS,
  (req, res, next) => {
    res.sendStatus(200);
  },
  logResponse,
);

function prefixUIDs(o){
  const s = JSON.stringify(_.omit(o, core.localProps), null, 2);
  return s.replace(/"uid-/g, `"http://${serverHost}:${serverPort}/uid-`)
}

app.get('/*',
  logRequest,
  CORS,
  (req, res, next) => {
    const Remote = req.headers.remote;
    const uid = req.originalUrl.substring(1);
    core.getObject(uid)
      .then(o => { if(o){ res.json(JSON.parse(prefixUIDs(o))); if(Remote) core.setNotify(o,Remote)} else res.status(404).send('Not found')})
      .then(()=>next())
      .catch(e => console.error(e));
  },
  logResponse,
);

const remote2ws = {};

app.post('/*',
  logRequest,
  CORS,
  (req, res, next) => {
    const json=req.body;
    if(!json || !json.UID) next();
    const Remote = req.headers.remote;
    const path = req.originalUrl.substring(1);
    core.incomingObject(Object.assign(json, Remote && { Remote }), path!=='notify' && path)
    res.json({ });
    next();
  },
  logResponse,
);

function doGet(url){
  return fetch(url)
    .then(res => res.json());
}

const pendingWSpackets = {};

let serverRemote = null;

function doPost(o, u){
  if(core.isURL(u)){
    console.log('not posting to peer yet:', u);
  }
  else{
    const Remote = core.isNotify(u)? u: o.Remote;
    if(!pendingWSpackets[Remote]) pendingWSpackets[Remote] = [];
    pendingWSpackets[Remote].push(prefixUIDs(o));
    wsFlush(Remote);
  }
}

function wsInit(config){
  const wss = new WebSocket.Server(config);
  wss.on('connection', (ws) => {
    ws.on('message', (data) => {
      const json = JSON.parse(data);
      if(json.Remote){
        console.log('ws init:', json);
        remote2ws[json.Remote]=ws;
        if(serverRemote) ws.send(JSON.stringify({ Remote: serverRemote }));
        wsFlush(json.Remote);
      }
      else{
        console.log('ws incoming json:', json);
      }
    });
  });
}

function wsFlush(Remote){
  const ws = remote2ws[Remote];
  if(!ws){
    console.log('websocket not found', Remote, 'sending:', pendingWSpackets[Remote]);
    return;
  }
  let packet;
  while((packet=(pendingWSpackets[Remote]||[]).shift())){
    try{
      console.log('<<----------ws---------------\n', Remote, '\n', packet, '\n<<---------------------------');
      if(ws.readyState === ws.OPEN) ws.send(packet);
      else console.log('WebSocket closed sending\n', packet, '\nto', Remote)
    }
    catch(e){
      console.error('error sending\n', packet, '\nto', Remote, '\n', e)
    }
  }
}

core.setNetwork({ doGet, doPost });

// --------------------------------

let forestdb;

function persistenceInit(mongoHostPort){
  return mongodb.MongoClient.connect(mongoHostPort)
    .then((client) => {
      forestdb = client.db('forest');
    });
}

function persist(o){
  if(!o.is) return Promise.resolve();
  const collectionName = (o.is.constructor===String)? o.is:
                        ((o.is.constructor===Array)? o.is.join('-'): null);
  if(!collectionName) return Promise.resolve();
  return forestdb.collection(collectionName)
    .update({ UID: o.UID }, o, { upsert: true })
    .then((result) => { console.log(`updated ${o.UID} in ${collectionName}`); return result.result })
    .catch((err) => { console.log(err, `Failed to insert ${o.UID} in ${collectionName}`); return err });
}

function fetch(uid){
  return forestdb.collections().then(colls=>findOneFirst(colls, uid))
}

function findOneFirst(colls, uid){
  if(!(colls && colls.length)) return Promise.resolve(null);
  return colls[0].findOne({ UID: uid }, { projection: { _id: 0 }}).then(o => o || findOneFirst(colls.slice(1), uid));
}

function recache(){
  return forestdb.collections()
    .then(colls=>Promise.all(colls.map(coll=>coll.find({ Cache: 'keep-active' }, { projection: { _id: 0 }}).toArray()))
                  .then(actives => [].concat(...actives)))
}

function query(is, scope, query){
  return forestdb.collection(is.join('-'))
    .find(toMongo(scope, query.match), { projection: { _id: 0 }})
    .toArray()
    .then(r => r.map(o => query.inline? getInlineVals(o, query.inline): o.UID))
    .catch(e => console.error(e));
}

function toMongo(scope, match){
  return Object.assign({}, ...Object.keys(match).map(k => ({[k]: toMongoProp(k,match[k])})));
}

function toMongoProp(key, val){
  if(val.length===3 && val[1]==='..'){
    return { $gt: val[0], $lt: val[2] };
  }
  return val;
}

function getInlineVals(o, inline){
  return Object.assign({}, ...inline.map(k => o[k] && { [k]: o[k] }), { More: o.UID })
}

core.setPersistence({ persist, fetch, recache, query });

// --------------------------------

function init({httpHost, httpPort, wsPort, mongoHostPort}){
  serverHost=httpHost; serverPort=httpPort;
  return new Promise((resolve, reject) => {
    persistenceInit(mongoHostPort)
      .then(() => {
        app.listen(httpPort, ()=>{
          console.log(`Server started on port ${httpPort}`);
          wsInit({ port: wsPort });
          resolve();
        }).on('error', (err) => reject(err));
      })
      .catch((err) => reject(err));
  });
}

function setRemote(Remote){ serverRemote = Remote; }

export default {
  init,
  setRemote,
  cacheObjects:   core.cacheObjects,
  reCacheObjects: core.reCacheObjects,
  setEvaluator:   core.setEvaluator,
  getObject:      core.getObject,
  spawnObject:    core.spawnObject,
  makeUID:        core.makeUID,
}

<commit_msg>reduce logging<commit_after>
import _ from 'lodash';
import express from 'express';
import WebSocket from 'ws';
import bodyParser from 'body-parser';
import mongodb from 'mongodb';
import core from './forest-core';

let serverHost=null;
let serverPort=0;

// --------------------------------

const logRequest = (req, res, next) => {
  console.log('---------------------------->');
  if(req.method==='POST') console.log(req.method, req.originalUrl, req.headers.remote||'', '\n', req.body);
  else                    console.log(req.method, req.originalUrl, req.headers.remote||'');
  next();
};

const logResponse = (req, res, next) => {
  console.log(res.statusCode)
  console.log('<----------------------------');
  next();
};

const CORS = (req, res, next) => {
  res.header('Access-Control-Allow-Origin','*');
  res.header('Access-Control-Allow-Methods', 'GET,POST,OPTIONS');
  res.header('Access-Control-Allow-Headers', 'Content-Type, X-Requested-With, Remote'); // Accept, X-Requested-By, Origin, Cache-Control
  next();
};

const app = express();

app.use(bodyParser.json());

app.options("/*",
  logRequest,
  CORS,
  (req, res, next) => {
    res.sendStatus(200);
  },
  logResponse,
);

function prefixUIDs(o){
  const s = JSON.stringify(_.omit(o, core.localProps), null, 2);
  return s.replace(/"uid-/g, `"http://${serverHost}:${serverPort}/uid-`)
}

app.get('/*',
  logRequest,
  CORS,
  (req, res, next) => {
    const Remote = req.headers.remote;
    const uid = req.originalUrl.substring(1);
    core.getObject(uid)
      .then(o => { if(o){ res.json(JSON.parse(prefixUIDs(o))); if(Remote) core.setNotify(o,Remote)} else res.status(404).send('Not found')})
      .then(()=>next())
      .catch(e => console.error(e));
  },
  logResponse,
);

const remote2ws = {};

app.post('/*',
  logRequest,
  CORS,
  (req, res, next) => {
    const json=req.body;
    if(!json || !json.UID) next();
    const Remote = req.headers.remote;
    const path = req.originalUrl.substring(1);
    core.incomingObject(Object.assign(json, Remote && { Remote }), path!=='notify' && path)
    res.json({ });
    next();
  },
  logResponse,
);

function doGet(url){
  return fetch(url)
    .then(res => res.json());
}

const pendingWSpackets = {};

let serverRemote = null;

function doPost(o, u){
  if(core.isURL(u)){
    console.log('not posting to peer yet:', u);
  }
  else{
    const Remote = core.isNotify(u)? u: o.Remote;
    if(!pendingWSpackets[Remote]) pendingWSpackets[Remote] = [];
    pendingWSpackets[Remote].push(prefixUIDs(o));
    wsFlush(Remote);
  }
}

function wsInit(config){
  const wss = new WebSocket.Server(config);
  wss.on('connection', (ws) => {
    ws.on('message', (data) => {
      const json = JSON.parse(data);
      if(json.Remote){
        console.log('ws init:', json);
        remote2ws[json.Remote]=ws;
        if(serverRemote) ws.send(JSON.stringify({ Remote: serverRemote }));
        wsFlush(json.Remote);
      }
      else{
        console.log('ws incoming json:', json);
      }
    });
  });
}

function wsFlush(Remote){
  const ws = remote2ws[Remote];
  if(!ws){
    console.log('websocket not found', Remote);
    return;
  }
  let packet;
  while((packet=(pendingWSpackets[Remote]||[]).shift())){
    try{
      console.log('<<----------ws---------------\n', Remote, '\n', packet, '\n<<---------------------------');
      if(ws.readyState === ws.OPEN) ws.send(packet);
      else console.log('WebSocket closed sending\n', packet, '\nto', Remote)
    }
    catch(e){
      console.error('error sending\n', packet, '\nto', Remote, '\n', e)
    }
  }
}

core.setNetwork({ doGet, doPost });

// --------------------------------

let forestdb;

function persistenceInit(mongoHostPort){
  return mongodb.MongoClient.connect(mongoHostPort)
    .then((client) => {
      forestdb = client.db('forest');
    });
}

function persist(o){
  if(!o.is) return Promise.resolve();
  const collectionName = (o.is.constructor===String)? o.is:
                        ((o.is.constructor===Array)? o.is.join('-'): null);
  if(!collectionName) return Promise.resolve();
  return forestdb.collection(collectionName)
    .update({ UID: o.UID }, o, { upsert: true })
    .then((result) => { console.log(`updated ${o.UID} in ${collectionName}`); return result.result })
    .catch((err) => { console.log(err, `Failed to insert ${o.UID} in ${collectionName}`); return err });
}

function fetch(uid){
  return forestdb.collections().then(colls=>findOneFirst(colls, uid))
}

function findOneFirst(colls, uid){
  if(!(colls && colls.length)) return Promise.resolve(null);
  return colls[0].findOne({ UID: uid }, { projection: { _id: 0 }}).then(o => o || findOneFirst(colls.slice(1), uid));
}

function recache(){
  return forestdb.collections()
    .then(colls=>Promise.all(colls.map(coll=>coll.find({ Cache: 'keep-active' }, { projection: { _id: 0 }}).toArray()))
                  .then(actives => [].concat(...actives)))
}

function query(is, scope, query){
  return forestdb.collection(is.join('-'))
    .find(toMongo(scope, query.match), { projection: { _id: 0 }})
    .toArray()
    .then(r => r.map(o => query.inline? getInlineVals(o, query.inline): o.UID))
    .catch(e => console.error(e));
}

function toMongo(scope, match){
  return Object.assign({}, ...Object.keys(match).map(k => ({[k]: toMongoProp(k,match[k])})));
}

function toMongoProp(key, val){
  if(val.length===3 && val[1]==='..'){
    return { $gt: val[0], $lt: val[2] };
  }
  return val;
}

function getInlineVals(o, inline){
  return Object.assign({}, ...inline.map(k => o[k] && { [k]: o[k] }), { More: o.UID })
}

core.setPersistence({ persist, fetch, recache, query });

// --------------------------------

function init({httpHost, httpPort, wsPort, mongoHostPort}){
  serverHost=httpHost; serverPort=httpPort;
  return new Promise((resolve, reject) => {
    persistenceInit(mongoHostPort)
      .then(() => {
        app.listen(httpPort, ()=>{
          console.log(`Server started on port ${httpPort}`);
          wsInit({ port: wsPort });
          resolve();
        }).on('error', (err) => reject(err));
      })
      .catch((err) => reject(err));
  });
}

function setRemote(Remote){ serverRemote = Remote; }

export default {
  init,
  setRemote,
  cacheObjects:   core.cacheObjects,
  reCacheObjects: core.reCacheObjects,
  setEvaluator:   core.setEvaluator,
  getObject:      core.getObject,
  spawnObject:    core.spawnObject,
  makeUID:        core.makeUID,
}

<|endoftext|>


<commit_before>var closest = function (el, selector) {
  var matchesSelector = el.matches || el.webkitMatchesSelector || el.mozMatchesSelector || el.msMatchesSelector;

  while (el) {
    if (matchesSelector.call(el, selector)) {
      break;
    }
    el = el.parentElement;
  }
  return el;
};

window.FormDataExtension = function () {
  var _actionListener = new Function();
  var _actionDto = function (index, type, action, responseDto) {
    return {
      index: index,
      type: type,
      action: action,
      time: new Date(),
      response: responseDto
    };
  };
  var _responseDto = function (isPassed, message) {
    return {
      isPassed: isPassed === true,
      message: message || ''
    };
  };

  var _actions = {
    alert: function (event) {
      var dataAlert = event.currentTarget.getAttribute('data-alert');
      if (!dataAlert) {
        return [true, 'Could not find data-alert attribute.'];
      }

      alert(dataAlert);

      return [true];
    },
    confirm: function (event) {
      var dataConfirm = event.currentTarget.getAttribute('data-confirm');
      if (!dataConfirm) {
        return [true, 'Could not find data-confirm attribute.'];
      }

      if (!confirm(dataConfirm)) {
        event.preventDefault();
        return [false, 'Canceled.'];
      }

      return [true];
    },
    submit: function (event) {
      var $targetForm = closest(event.currentTarget, 'form');
      if ($targetForm.length === 0) {
        return [true, 'Could not find form to submit'];
      }

      event.preventDefault();
      $targetForm.submit();

      return [true];
    },
    clear: function (event) {
      var elementsWithData = closest(event.currentTarget, 'form').querySelectorAll('[name]');
      elementsWithData.forEach(function (element) {
        element.value = null;
      });

      return [true];
    }
  };

  return {
    addAction: function (action, callable) {
      if (_actions[action]) {
        window.console.error('[Form-Data-Extension] Action already exists.');
        return false;
      }
      _actions[action] = callable;
      return true;
    },
    removeAction: function (action) {
      if (!_actions[action]) {
        window.console.error('[Form-Data-Extension] Can not find action type: ' + action);
        return false;
      }
      delete _actions[action];
      return true;
    },
    onAction: function (callable) {
      _actionListener = callable;
    },
    eventListener: function (event) {
      var eventResponse = false;
      var actions = (event.currentTarget.getAttribute('data-actions') || '').split(' ');

      actions.every(function (actionType, index) {
        var actionEvent = _actions[actionType];
        var rawResponse = [false, 'Unknown Error!'];
        var response = false;

        if (actionType === 'submit'
          && event.currentTarget.tagName.toUpperCase() === 'FORM'
          && event.type === actionType
        ) {
          eventResponse = true;
          return false;
        }

        if (!actionEvent) {
          window.console.warn('[Form-Data-Extension] Can not find action type: ' + actionType);
          return true;
        }

        try {
          rawResponse = actionEvent(event);
        } catch (error) {
          rawResponse = [false, error.message];
        }

        response = _responseDto.apply(null, rawResponse);
        _actionListener(new _actionDto(index, actionType, actionEvent, response));

        return response.isPassed;
      });

      return eventResponse;
    }
  };
};<commit_msg>Init responseDto<commit_after>var closest = function (el, selector) {
  var matchesSelector = el.matches || el.webkitMatchesSelector || el.mozMatchesSelector || el.msMatchesSelector;

  while (el) {
    if (matchesSelector.call(el, selector)) {
      break;
    }
    el = el.parentElement;
  }
  return el;
};

window.FormDataExtension = function () {
  var _actionListener = new Function();
  var _actionDto = function (index, type, action, responseDto) {
    return {
      index: index,
      type: type,
      action: action,
      time: new Date(),
      response: responseDto
    };
  };
  var _responseDto = function (isPassed, message) {
    return {
      isPassed: isPassed === true,
      message: message || ''
    };
  };

  var _actions = {
    alert: function (event) {
      var dataAlert = event.currentTarget.getAttribute('data-alert');
      if (!dataAlert) {
        return [true, 'Could not find data-alert attribute.'];
      }

      alert(dataAlert);

      return [true];
    },
    confirm: function (event) {
      var dataConfirm = event.currentTarget.getAttribute('data-confirm');
      if (!dataConfirm) {
        return [true, 'Could not find data-confirm attribute.'];
      }

      if (!confirm(dataConfirm)) {
        event.preventDefault();
        return [false, 'Canceled.'];
      }

      return [true];
    },
    submit: function (event) {
      var $targetForm = closest(event.currentTarget, 'form');
      if ($targetForm.length === 0) {
        return [true, 'Could not find form to submit'];
      }

      event.preventDefault();
      $targetForm.submit();

      return [true];
    },
    clear: function (event) {
      var elementsWithData = closest(event.currentTarget, 'form').querySelectorAll('[name]');
      elementsWithData.forEach(function (element) {
        element.value = null;
      });

      return [true];
    }
  };

  return {
    addAction: function (action, callable) {
      if (_actions[action]) {
        window.console.error('[Form-Data-Extension] Action already exists.');
        return false;
      }
      _actions[action] = callable;
      return true;
    },
    removeAction: function (action) {
      if (!_actions[action]) {
        window.console.error('[Form-Data-Extension] Can not find action type: ' + action);
        return false;
      }
      delete _actions[action];
      return true;
    },
    onAction: function (callable) {
      _actionListener = callable;
    },
    eventListener: function (event) {
      var eventResponse = false;
      var actions = (event.currentTarget.getAttribute('data-actions') || '').split(' ');

      actions.every(function (actionType, index) {
        var actionEvent = _actions[actionType];
        var rawResponse = [false, 'Unknown Error!'];
        var response = new _responseDto();

        if (actionType === 'submit'
          && event.currentTarget.tagName.toUpperCase() === 'FORM'
          && event.type === actionType
        ) {
          eventResponse = true;
          return false;
        }

        if (!actionEvent) {
          window.console.warn('[Form-Data-Extension] Can not find action type: ' + actionType);
          return true;
        }

        try {
          rawResponse = actionEvent(event);
        } catch (error) {
          rawResponse = [false, error.message];
        }

        response = _responseDto.apply(null, rawResponse);
        _actionListener(new _actionDto(index, actionType, actionEvent, response));

        return response.isPassed;
      });

      return eventResponse;
    }
  };
};<|endoftext|>


<commit_before>define([
    'jquery',
    'var/blankImg',
    'var/slice',
    'var/css_class',
    'function/trigger',
    'function/attach',
    'function/shortnameTo',
    'function/pasteHtmlAtCaret',
    'function/getOptions',
    'function/saveSelection',
    'function/restoreSelection',
    'function/htmlFromText',
    'function/textFromHtml',
    'function/isObject',
    'function/selector',
    'function/div'
],
function($, blankImg, slice, css_class, trigger, attach, shortnameTo, pasteHtmlAtCaret,
         getOptions, saveSelection, restoreSelection, htmlFromText, textFromHtml, isObject, selector, div)
{
    return function(self, source, options) {
        options = getOptions(options);

        var sourceValFunc = source.is("TEXTAREA") || source.is("INPUT") ? "val" : "text",
            stayFocused = false;

        self.sprite     = options.sprite;
        self.shortnames = options.shortnames;

        var editor, button, picker, tones, emojis, filters, emojisList,
            app = div({"class" : css_class + " " + source.attr("class"), role: "application"},
                editor = self.editor = div('editor').attr({
                    contenteditable: true,
                    placeholder: options["placeholder"] || source.data("placeholder") || source.attr("placeholder") || "",
                    tabindex: 0
                }),
                button = self.button = div('button',
                    div('button-open'),
                    div('button-close')
                ),
                picker = self.picker = div('picker',
                    div('wrapper',
                        filters = div('filters'),
                        emojisList = div('emojis-list',
                            tones = div('tones'),
                            emojis = div('emojis')
                        )
                    )
                ).addClass(selector('picker-position-' + options.pickerPosition, true))
                 .addClass(selector('filters-position-' + options.filtersPosition, true))
                 .addClass('hidden')
            );

        for (var attr = ["dir", "spellcheck", "autocomplete", "autocorrect", "autocapitalize"], j=0; j<5; j++) {
            editor.attr(attr[j], options[attr[j]]);
        }

        $.each(options.filters, function(filter, params) {
            $("", {"class": selector("filter", true) + " " + selector("filter-" + filter, true), "data-filter": filter})
                .wrapInner(shortnameTo(params.icon, self.sprite ? '' : ''))
                .appendTo(filters);
        });

        var flush = function() {
            flush = null;
            $.each(options.filters, function(filter, params) {
                emojisList.append('
' + params.title + '
');
                div('emojis').html(shortnameTo(params.emoji.replace(' ', ','), '<' +
                    (self.sprite ? 'i class="emojione-{uni}"' : 'img class="emojione" src="{img}"') +
                    ' data-name="{name}"/>') . split(','). join('')).appendTo(emojisList);
            });
            options.filters = null;
            attach(self, emojisList.find(".emojibtn"), {click: "emojibtn.click"});
        };

        function calcButtonPosition() {
            var offset = editor[0].offsetWidth - editor[0].clientWidth,
                current = parseInt(button.css('marginRight'));
            if (current !== offset) {
                button.css({marginRight: offset});
                if (options.pickerPosition === 'top') {
                    picker.css({right: parseInt(picker.css('right')) - current + offset});
                }
            }
        }

        var filtersBtns = filters.find(selector("filter"));

        if (options.container) {
            $(options.container).wrapInner(app);
        } else {
            app.insertAfter(source);
        }

        if (options.hideSource) {
            source.hide();
        }

        self.setText(source[sourceValFunc]());
        calcButtonPosition();

        attach(self, window, {resize: "!resize"});
        attach(self, [picker, button], {mousedown: "!mousedown"}, editor);
        attach(self, button, {click: "button.click"});
        attach(self, editor, {paste :"!paste"}, editor);
        attach(self, editor, ["focus", "blur"], function() { return stayFocused ? false : editor; });
        attach(self, [editor, picker], ["mousedown", "mouseup", "click", "keyup", "keydown", "keypress"], editor);
        attach(self, picker.find(".emojionearea-filter"), {click: "filter.click"});

        if (isObject(options.events) && !$.isEmptyObject(options.events)) {
            $.each(options.events, function(event, handler) {
                self.on(event.replace(/_/g, '.'), handler);
            });
        }

        self.on("@filter.click", function(filter) {
                if (filter.is(".active")) {
                    filter.removeClass("active");
                } else {
                    filtersBtns.filter(".active").removeClass("active");
                    filter.addClass("active");
                }
            })

            .on("@button.click", function(button) {
                if (button.is(".active")) {
                    self.hidePicker();
                } else {
                    if (flush) flush();
                    self.showPicker();
                }
            })

            .on("@!paste", function(editor) {
                stayFocused = true;
                // inserts invisible character for fix caret
                pasteHtmlAtCaret('');

                var sel = saveSelection(editor[0]),
                    editorScrollTop = editor.scrollTop(),
                    clipboard = $("
", {contenteditable: true})
                        .css({position: "fixed", left: "-999px", width: "1px", height: "1px", top: "20px", overflow: "hidden"})
                        .appendTo($("BODY"))
                        .focus();

                window.setTimeout(function() {
                    var caretID = "caret-" + (new Date()).getTime();
                    editor.focus();
                    restoreSelection(editor[0], sel);
                    var text = textFromHtml(clipboard.html().replace(/\r\n|\n|\r/g, '
'), self),
                        html = htmlFromText(text, self);
                    pasteHtmlAtCaret(html);
                    clipboard.remove();
                    pasteHtmlAtCaret('');
                    editor.scrollTop(editorScrollTop);
                    var caret = $("#" + caretID),
                        top = caret.offset().top - editor.offset().top,
                        height = editor.height();
                    if (editorScrollTop + top >= height || editorScrollTop > top) {
                        editor.scrollTop(editorScrollTop + top - 2 * height/3);
                    }
                    caret.remove();
                    stayFocused = false;
                    calcButtonPosition();
                    trigger(self, 'paste', [editor, text, html]);
                }, 200);
            })

            .on("@emojibtn.click", function(emojibtn) {
                saveSelection(editor[0]);
                pasteHtmlAtCaret(shortnameTo(emojibtn.children().data("name"),
                    '' : '" src="{img}">')));
            })

            .on("@!resize @keyup @emojibtn.click", calcButtonPosition)

            .on("@!mousedown", function(editor, event) {
                if (!options.autoHideFilters && !app.is(".focused")) {
                    editor.focus();
                }
                event.preventDefault();
                return false;
            })

            .on("@change", function(editor) {
                var html = editor.html().replace(/<\/?(?:div|span|p)[^>]*>/ig, '');
                // clear input, fix: chrome add
 on contenteditable is empty
                if (!html.length || /^
]*>$/i.test(html)) {
                    self.setText('', false);
                }
                source[sourceValFunc](self.getText());
            })

            .on("@focus", function() {
                app.addClass("focused");
            })

            .on("@blur", function(editor) {
                app.removeClass("focused");

                if (options.hidePickerOnBlur) {
                    self.hidePicker();
                }

                var content = editor.html();
                if (self.content !== content) {
                    self.content = content;
                    trigger(self, 'change', [editor]);
                    source.blur().trigger("change");
                } else {
                    source.blur();
                }
            });
    };
});<commit_msg>Update<commit_after>define([
    'jquery',
    'var/blankImg',
    'var/slice',
    'var/css_class',
    'function/trigger',
    'function/attach',
    'function/shortnameTo',
    'function/pasteHtmlAtCaret',
    'function/getOptions',
    'function/saveSelection',
    'function/restoreSelection',
    'function/htmlFromText',
    'function/textFromHtml',
    'function/isObject',
    'function/calcButtonPosition',
    'function/selector',
    'function/div'
],
function($, blankImg, slice, css_class, trigger, attach, shortnameTo, pasteHtmlAtCaret,
         getOptions, saveSelection, restoreSelection, htmlFromText, textFromHtml, isObject,
         calcButtonPosition, selector, div)
{
    return function(self, source, options) {
        options = getOptions(options);

        var sourceValFunc = source.is("TEXTAREA") || source.is("INPUT") ? "val" : "text",
            stayFocused = false;

        self.sprite     = options.sprite;
        self.shortnames = options.shortnames;
        self.pickerPosition = options.pickerPosition;

        var editor, button, picker, tones, emojis, filters, emojisList,
            app = div({"class" : css_class + " " + source.attr("class"), role: "application"},
                editor = self.editor = div('editor').attr({
                    contenteditable: true,
                    placeholder: options["placeholder"] || source.data("placeholder") || source.attr("placeholder") || "",
                    tabindex: 0
                }),
                button = self.button = div('button',
                    div('button-open'),
                    div('button-close')
                ).attr('title', options.buttonTitle),
                picker = self.picker = div('picker',
                    div('wrapper',
                        filters = div('filters'),
                        emojisList = div('emojis-list',
                            tones = div('tones'),
                            emojis = div('emojis')
                        )
                    )
                ).addClass(selector('picker-position-' + options.pickerPosition, true))
                 .addClass(selector('filters-position-' + options.filtersPosition, true))
                 .addClass('hidden')
            );

        for (var attr = ["dir", "spellcheck", "autocomplete", "autocorrect", "autocapitalize"], j=0; j<5; j++) {
            editor.attr(attr[j], options[attr[j]]);
        }

        $.each(options.filters, function(filter, params) {
            $("", {
                "class": selector("filter", true) + " " + selector("filter-" + filter, true),
                "data-filter": filter,
                title: params.title
            })
            .wrapInner(shortnameTo(params.icon, self.sprite ? '' : ''))
            .appendTo(filters);
        });

        editor.data('render_emojis', function() {
            editor.data('render_emojis', null);
            $.each(options.filters, function(filter, params) {
                emojisList.append('
' + params.title + '
');
                div('emojis').html(shortnameTo(params.emoji.replace(' ', ','), '<' +
                    (self.sprite ? 'i class="emojione-{uni}"' : 'img class="emojione" src="{img}"') +
                    ' data-name="{name}"/>') . split(','). join('')).appendTo(emojisList);
            });
            options.filters = null;
            attach(self, emojisList.find(".emojibtn"), {click: "emojibtn.click"});
        });

        var filtersBtns = filters.find(selector("filter"));

        if (options.container) {
            $(options.container).wrapInner(app);
        } else {
            app.insertAfter(source);
        }

        if (options.hideSource) {
            source.hide();
        }

        self.setText(source[sourceValFunc]());
        calcButtonPosition.apply(self);

        attach(self, window, {resize: "!resize"});
        attach(self, [picker, button], {mousedown: "!mousedown"}, editor);
        attach(self, button, {click: "button.click"});
        attach(self, editor, {paste :"!paste"}, editor);
        attach(self, editor, ["focus", "blur"], function() { return stayFocused ? false : editor; });
        attach(self, [editor, picker], ["mousedown", "mouseup", "click", "keyup", "keydown", "keypress"], editor);
        attach(self, picker.find(".emojionearea-filter"), {click: "filter.click"});

        if (isObject(options.events) && !$.isEmptyObject(options.events)) {
            $.each(options.events, function(event, handler) {
                self.on(event.replace(/_/g, '.'), handler);
            });
        }

        self.on("@filter.click", function(filter) {
                if (filter.is(".active")) {
                    filter.removeClass("active");
                } else {
                    filtersBtns.filter(".active").removeClass("active");
                    filter.addClass("active");
                }
            })

            .on("@button.click", function(button) {
                if (button.is(".active")) {
                    self.hidePicker();
                } else {
                    self.showPicker();
                }
            })

            .on("@!paste", function(editor) {
                stayFocused = true;
                // inserts invisible character for fix caret
                pasteHtmlAtCaret('');

                var sel = saveSelection(editor[0]),
                    editorScrollTop = editor.scrollTop(),
                    clipboard = $("
", {contenteditable: true})
                        .css({position: "fixed", left: "-999px", width: "1px", height: "1px", top: "20px", overflow: "hidden"})
                        .appendTo($("BODY"))
                        .focus();

                window.setTimeout(function() {
                    var caretID = "caret-" + (new Date()).getTime();
                    editor.focus();
                    restoreSelection(editor[0], sel);
                    var text = textFromHtml(clipboard.html().replace(/\r\n|\n|\r/g, '
'), self),
                        html = htmlFromText(text, self);
                    pasteHtmlAtCaret(html);
                    clipboard.remove();
                    pasteHtmlAtCaret('');
                    editor.scrollTop(editorScrollTop);
                    var caret = $("#" + caretID),
                        top = caret.offset().top - editor.offset().top,
                        height = editor.height();
                    if (editorScrollTop + top >= height || editorScrollTop > top) {
                        editor.scrollTop(editorScrollTop + top - 2 * height/3);
                    }
                    caret.remove();
                    stayFocused = false;
                    calcButtonPosition.apply(self);
                    trigger(self, 'paste', [editor, text, html]);
                }, 200);
            })

            .on("@emojibtn.click", function(emojibtn) {
                saveSelection(editor[0]);
                pasteHtmlAtCaret(shortnameTo(emojibtn.children().data("name"),
                    '' : '" src="{img}">')));
            })

            .on("@!resize @keyup @emojibtn.click", calcButtonPosition)

            .on("@!mousedown", function(editor, event) {
                if (!options.autoHideFilters && !app.is(".focused")) {
                    editor.focus();
                }
                event.preventDefault();
                return false;
            })

            .on("@change", function(editor) {
                var html = editor.html().replace(/<\/?(?:div|span|p)[^>]*>/ig, '');
                // clear input, fix: chrome add
 on contenteditable is empty
                if (!html.length || /^
]*>$/i.test(html)) {
                    self.setText('', false);
                }
                source[sourceValFunc](self.getText());
            })

            .on("@focus", function() {
                app.addClass("focused");
            })

            .on("@blur", function(editor) {
                app.removeClass("focused");

                if (options.hidePickerOnBlur) {
                    self.hidePicker();
                }

                var content = editor.html();
                if (self.content !== content) {
                    self.content = content;
                    trigger(self, 'change', [editor]);
                    source.blur().trigger("change");
                } else {
                    source.blur();
                }
            });
    };
});<|endoftext|>


<commit_before>import Ember from 'ember';
import DS from 'ember-data';
import LFQueue from 'ember-localforage-adapter/utils/queue';
import LFCache from 'ember-localforage-adapter/utils/cache';
import isAsync from 'ember-localforage-adapter/utils/is-async';
import isObject from 'ember-localforage-adapter/utils/is-object';

export default DS.Adapter.extend(Ember.Evented, {

  defaultSerializer: 'localforage',
  queue: LFQueue.create(),
  cache: LFCache.create(),
  caching: 'model',
  coalesceFindRequests: true,

  shouldBackgroundReloadRecord() {
    return false;
  },

  shouldReloadAll() {
    return true;
  },

  /**
   * This is the main entry point into finding records. The first parameter to
   * this method is the model's name as a string.
   *
   * @method findRecord
   * @param store
   * @param {DS.Model} type
   * @param {Object|String|Integer|null} id
   */
  findRecord(store, type, id) {
    return this._getNamespaceData(type).then((namespaceData) => {
      const record = namespaceData.records[id];

      if (!record) {
        return Ember.RSVP.reject();
      }

      return record;
    });
  },

  findAll(store, type) {
    return this._getNamespaceData(type).then((namespaceData) => {
      const records = [];

      for (let id in namespaceData.records) {
        records.push(namespaceData.records[id]);
      }

      return records;
    });
  },

  findMany(store, type, ids) {
    return this._getNamespaceData(type).then((namespaceData) => {
      const records = [];

      for (let i = 0; i < ids.length; i++) {
        const record = namespaceData.records[ids[i]];

        if (record) {
          records.push(record);
        }
      }

      return records;
    });
  },

  queryRecord(store, type, query) {
    var modelName = type.modelName;
    var proj = this._extractProjectionFromQuery(modelName, type, query);
    var _this = this;
    return new Ember.RSVP.Promise(function(resolve, reject) {
      _this._getNamespaceData(type).then((namespaceData) => {
        const record = _this._query(namespaceData.records, query, true);

        if (!record) {
          reject(new Error(`Record of type ${modelName} with id '${record.id}' is not fulfilling specified query`));
        }

        _this._completeLoadRecord(store, type, record, proj).then(function(completeRecord) {
          resolve(completeRecord);
        });
      }).catch(function(reason) {
        reject(reason);
      });
    });
  },

  /**
   *  Supports queries that look like this:
   *   {
   *     <property to query>: <value or regex (for strings) to match>,
   *     ...
   *   }
   *
   * Every property added to the query is an "AND" query, not "OR"
   *
   * Example:
   * match records with "complete: true" and the name "foo" or "bar"
   *  { complete: true, name: /foo|bar/ }
   */
  query(store, type, query) {
    var modelName = type.modelName;
    var proj = this._extractProjectionFromQuery(modelName, type, query);
    var _this = this;
    return new Ember.RSVP.Promise(function(resolve, reject) {
      _this._getNamespaceData(type).then((namespaceData) => {
        var recordArray = _this._query(namespaceData.records, query);
        let promises = Ember.A();
        for (let i = 0; i < recordArray.length; i++) {
          let record = recordArray[i];
          promises.pushObject(_this._completeLoadRecord(store, type, record, proj));
        }

        Ember.RSVP.all(promises).then(() => {
          resolve(recordArray);
        }).catch(function(reason) {
          reject(reason);
        });
      }).catch(function(reason) {
        reject(reason);
      });
    });
  },

  _query(records, query, singleMatch) {
    const results = singleMatch ? null : [];

    for (let id in records) {
      const record = records[id];
      let isMatching = false;

      for (let property in query) {
        const queryValue = query[property];

        if (queryValue instanceof RegExp) {
          isMatching = queryValue.test(record[property]);
        } else {
          isMatching = record[property] === queryValue;
        }

        if (!isMatching) {
          break; // all criteria should pass
        }
      }

      if (isMatching || Ember.$.isEmptyObject(query)) {
        if (singleMatch) {
          return record;
        }

        results.push(record);
      }
    }

    return results;
  },

  createRecord: updateOrCreate,

  updateRecord: updateOrCreate,

  deleteRecord(store, type, snapshot) {
    return this.queue.attach((resolve) => {
      this._getNamespaceData(type).then((namespaceData) => {
        delete namespaceData.records[snapshot.id];

        this._setNamespaceData(type, namespaceData).then(() => {
          resolve();
        });
      });
    });
  },

  generateIdForRecord() {
    return Math.random().toString(32).slice(2).substr(0, 5);
  },

  // private

  /**
   * Retrieves projection from query and returns it.
   * Retrieved projection removes from the query.
   *
   * @method _extractProjectionFromQuery
   * @private
   *
   * @param {String} modelName The name of the model type.
   * @param {subclass of DS.Model} typeClass Model type.
   * @param {Object} [query] Query parameters.
   * @param {String} query.projection Projection name.
   * @return {Object} Extracted projection from query or null
   *                  if projection is not set in query.
   */
  _extractProjectionFromQuery: function(modelName, typeClass, query) {
    if (query && query.projection) {
      let proj = query.projection;
      if (typeof query.projection === 'string') {
        let projName = query.projection;
        proj = typeClass.projections.get(projName);
      }

      delete query.projection;
      return proj;
    }

    return null;
  },

  /**
   * Completes loading record for given projection.
   *
   * @method _completeLoadingRecord
   * @private
   *
   * @param {subclass of DS.Store} store Store to use for complete loading record.
   * @param {subclass of DS.Model} type Model type.
   * @param {Object} record Main record loaded by adapter.
   * @param {Object} projection Projection for complete loading of record.
   * @return {Object} Completely loaded record with all properties
   *                  include relationships corresponds to given projection
   */
  _completeLoadRecord: function(store, type, record, projection) {
    let promises = Ember.A();
    let attributes = projection.attributes;
    for (var attrName in attributes) {
      if (attributes.hasOwnProperty(attrName)) {
        this._replaceIdToHash(store, type,  record, attributes, attrName, promises);
      }
    }

    return Ember.RSVP.all(promises).then(() => {
      let relationshipNames = Ember.get(type, 'relationshipNames');
      let belongsTo = relationshipNames.belongsTo;
      for (let i = 0; i < belongsTo.length; i++) {
        let relationshipName = belongsTo[i];
        if (!isAsync(type, relationshipName) && !isObject(record[relationshipName])) {
          record[relationshipName] = null;
        }
      }

      let hasMany = relationshipNames.hasMany;
      for (let i = 0; i < hasMany.length; i++) {
        let relationshipName = hasMany[i];
        if (!Ember.isArray(record[relationshipName])) {
          record[relationshipName] = [];
        }
        else {
          if (!isAsync(type, relationshipName)) {
            let hasUnloadedObjects = false;
            for (let j = 0; j < record[relationshipName].length; j++) {
              if (!isObject(record[relationshipName][j])) {
                hasUnloadedObjects = true;
              }
            }

            if (hasUnloadedObjects) {
              record[relationshipName] = [];
            }
          }
        }
      }

      return record;
    });
  },

  _loadRelatedRecord(store, type, id, proj) {
    let relatedRecord = store.peekRecord(proj.modelName, id);
    if (Ember.isNone(relatedRecord)) {
      let options = {
        id: id,
        projection: proj
      };
      return this.queryRecord(store, type, options);
    }
    else {
      let relatedRecordObject = relatedRecord.serialize({includeId: true});
      return this._completeLoadRecord(store, type, relatedRecordObject, proj);
    }
  },

  _replaceIdToHash(store, type,  record, attributes, attrName, promises) {
    let attr = attributes[attrName];
      let relatedModelType = (attr.kind === 'belongsTo' || attr.kind === 'hasMany') ? store.modelFor(attr.modelName) : null;
      switch (attr.kind) {
        case 'attr':
          break;
        case 'belongsTo':
          if (!isAsync(type, attrName)) {
            // let primaryKeyName = this.serializer.get('primaryKey');
            let id = record[attrName];
            if (!Ember.isNone(id)) {
              promises.pushObject(this._loadRelatedRecord(store, relatedModelType, id, attr).then((relatedRecord) => {
                delete record[attrName];
                record[attrName] = relatedRecord;
              }));
            }
          }

          break;
        case 'hasMany':
          if (!isAsync(type, attrName)) {
            if (Ember.isArray(record[attrName])) {
              let ids = Ember.copy(record[attrName]);
              delete record[attrName];
              record[attrName] = [];
              for (var i = 0; i < ids.length; i++) {
                let id = ids[i];
                promises.pushObject(this._loadRelatedRecord(store, relatedModelType, id, attr).then((relatedRecord) => {
                  record[attrName].push(relatedRecord);
                }));
              }
            }
            else {
              record[attrName] = [];
            }
          }

          break;
        default:
          throw new Error(`Unknown kind of projection attribute: ${attr.kind}`);
      }
  },

  _setNamespaceData(type, namespaceData) {
    const modelNamespace = this._modelNamespace(type);

    return this._loadData().then((storage) => {
      if (this.caching !== 'none') {
        this.cache.set(modelNamespace, namespaceData);
      }

      storage[modelNamespace] = namespaceData;

      return window.localforage.setItem(this._adapterNamespace(), storage);
    });
  },

  _getNamespaceData(type) {
    const modelNamespace = this._modelNamespace(type);

    if (this.caching !== 'none') {
      const cache = this.cache.get(modelNamespace);

      if (cache) {
        return Ember.RSVP.resolve(cache);
      }
    }

    return this._loadData().then((storage) => {
      const namespaceData = storage && storage[modelNamespace] || { records: {} };

      if (this.caching === 'model') {
        this.cache.set(modelNamespace, namespaceData);
      } else if (this.caching === 'all') {
        if (storage) {
          this.cache.replace(storage);
        }
      }

      return namespaceData;
    });
  },

  _loadData() {
    return window.localforage.getItem(this._adapterNamespace()).then((storage) => {
      return storage ? storage : {};
    });
  },

  _modelNamespace(type) {
    return type.url || type.modelName;
  },

  _adapterNamespace() {
    return this.get('namespace') || 'DS.LFAdapter';
  }
});

function updateOrCreate(store, type, snapshot) {
  return this.queue.attach((resolve) => {
    this._getNamespaceData(type).then((namespaceData) => {
      const serializer = store.serializerFor(type.modelName);
      const recordHash = serializer.serialize(snapshot, {includeId: true});
      // update(id comes from snapshot) or create(id comes from serialization)
      const id = snapshot.id || recordHash.id;

      namespaceData.records[id] = recordHash;

      this._setNamespaceData(type, namespaceData).then(() => {
        resolve();
      });
    });
  });
}
<commit_msg>Add check for absence of projection<commit_after>import Ember from 'ember';
import DS from 'ember-data';
import LFQueue from 'ember-localforage-adapter/utils/queue';
import LFCache from 'ember-localforage-adapter/utils/cache';
import isAsync from 'ember-localforage-adapter/utils/is-async';
import isObject from 'ember-localforage-adapter/utils/is-object';

export default DS.Adapter.extend(Ember.Evented, {

  defaultSerializer: 'localforage',
  queue: LFQueue.create(),
  cache: LFCache.create(),
  caching: 'model',
  coalesceFindRequests: true,

  shouldBackgroundReloadRecord() {
    return false;
  },

  shouldReloadAll() {
    return true;
  },

  /**
   * This is the main entry point into finding records. The first parameter to
   * this method is the model's name as a string.
   *
   * @method findRecord
   * @param store
   * @param {DS.Model} type
   * @param {Object|String|Integer|null} id
   */
  findRecord(store, type, id) {
    return this._getNamespaceData(type).then((namespaceData) => {
      const record = namespaceData.records[id];

      if (!record) {
        return Ember.RSVP.reject();
      }

      return record;
    });
  },

  findAll(store, type) {
    return this._getNamespaceData(type).then((namespaceData) => {
      const records = [];

      for (let id in namespaceData.records) {
        records.push(namespaceData.records[id]);
      }

      return records;
    });
  },

  findMany(store, type, ids) {
    return this._getNamespaceData(type).then((namespaceData) => {
      const records = [];

      for (let i = 0; i < ids.length; i++) {
        const record = namespaceData.records[ids[i]];

        if (record) {
          records.push(record);
        }
      }

      return records;
    });
  },

  queryRecord(store, type, query) {
    var modelName = type.modelName;
    var proj = this._extractProjectionFromQuery(modelName, type, query);
    var _this = this;
    return new Ember.RSVP.Promise(function(resolve, reject) {
      _this._getNamespaceData(type).then((namespaceData) => {
        const record = _this._query(namespaceData.records, query, true);

        if (!record) {
          reject(new Error(`Record of type ${modelName} with id '${record.id}' is not fulfilling specified query`));
        }

        _this._completeLoadRecord(store, type, record, proj).then(function(completeRecord) {
          resolve(completeRecord);
        });
      }).catch(function(reason) {
        reject(reason);
      });
    });
  },

  /**
   *  Supports queries that look like this:
   *   {
   *     <property to query>: <value or regex (for strings) to match>,
   *     ...
   *   }
   *
   * Every property added to the query is an "AND" query, not "OR"
   *
   * Example:
   * match records with "complete: true" and the name "foo" or "bar"
   *  { complete: true, name: /foo|bar/ }
   */
  query(store, type, query) {
    var modelName = type.modelName;
    var proj = this._extractProjectionFromQuery(modelName, type, query);
    var _this = this;
    return new Ember.RSVP.Promise(function(resolve, reject) {
      _this._getNamespaceData(type).then((namespaceData) => {
        var recordArray = _this._query(namespaceData.records, query);
        let promises = Ember.A();
        for (let i = 0; i < recordArray.length; i++) {
          let record = recordArray[i];
          promises.pushObject(_this._completeLoadRecord(store, type, record, proj));
        }

        Ember.RSVP.all(promises).then(() => {
          resolve(recordArray);
        }).catch(function(reason) {
          reject(reason);
        });
      }).catch(function(reason) {
        reject(reason);
      });
    });
  },

  _query(records, query, singleMatch) {
    const results = singleMatch ? null : [];

    for (let id in records) {
      const record = records[id];
      let isMatching = false;

      for (let property in query) {
        const queryValue = query[property];

        if (queryValue instanceof RegExp) {
          isMatching = queryValue.test(record[property]);
        } else {
          isMatching = record[property] === queryValue;
        }

        if (!isMatching) {
          break; // all criteria should pass
        }
      }

      if (isMatching || Ember.$.isEmptyObject(query)) {
        if (singleMatch) {
          return record;
        }

        results.push(record);
      }
    }

    return results;
  },

  createRecord: updateOrCreate,

  updateRecord: updateOrCreate,

  deleteRecord(store, type, snapshot) {
    return this.queue.attach((resolve) => {
      this._getNamespaceData(type).then((namespaceData) => {
        delete namespaceData.records[snapshot.id];

        this._setNamespaceData(type, namespaceData).then(() => {
          resolve();
        });
      });
    });
  },

  generateIdForRecord() {
    return Math.random().toString(32).slice(2).substr(0, 5);
  },

  // private

  /**
   * Retrieves projection from query and returns it.
   * Retrieved projection removes from the query.
   *
   * @method _extractProjectionFromQuery
   * @private
   *
   * @param {String} modelName The name of the model type.
   * @param {subclass of DS.Model} typeClass Model type.
   * @param {Object} [query] Query parameters.
   * @param {String} query.projection Projection name.
   * @return {Object} Extracted projection from query or null
   *                  if projection is not set in query.
   */
  _extractProjectionFromQuery: function(modelName, typeClass, query) {
    if (query && query.projection) {
      let proj = query.projection;
      if (typeof query.projection === 'string') {
        let projName = query.projection;
        proj = typeClass.projections.get(projName);
      }

      delete query.projection;
      return proj;
    }

    return null;
  },

  /**
   * Completes loading record for given projection.
   *
   * @method _completeLoadingRecord
   * @private
   *
   * @param {subclass of DS.Store} store Store to use for complete loading record.
   * @param {subclass of DS.Model} type Model type.
   * @param {Object} record Main record loaded by adapter.
   * @param {Object} projection Projection for complete loading of record.
   * @return {Object} Completely loaded record with all properties
   *                  include relationships corresponds to given projection
   */
  _completeLoadRecord: function(store, type, record, projection) {
    let promises = Ember.A();
    if (!Ember.isNone(projection)) {
      let attributes = projection.attributes;
      for (var attrName in attributes) {
        if (attributes.hasOwnProperty(attrName)) {
          this._replaceIdToHash(store, type,  record, attributes, attrName, promises);
        }
      }
    }

    return Ember.RSVP.all(promises).then(() => {
      let relationshipNames = Ember.get(type, 'relationshipNames');
      let belongsTo = relationshipNames.belongsTo;
      for (let i = 0; i < belongsTo.length; i++) {
        let relationshipName = belongsTo[i];
        if (!isAsync(type, relationshipName) && !isObject(record[relationshipName])) {
          record[relationshipName] = null;
        }
      }

      let hasMany = relationshipNames.hasMany;
      for (let i = 0; i < hasMany.length; i++) {
        let relationshipName = hasMany[i];
        if (!Ember.isArray(record[relationshipName])) {
          record[relationshipName] = [];
        }
        else {
          if (!isAsync(type, relationshipName)) {
            let hasUnloadedObjects = false;
            for (let j = 0; j < record[relationshipName].length; j++) {
              if (!isObject(record[relationshipName][j])) {
                hasUnloadedObjects = true;
              }
            }

            if (hasUnloadedObjects) {
              record[relationshipName] = [];
            }
          }
        }
      }

      return record;
    });
  },

  _loadRelatedRecord(store, type, id, proj) {
    let relatedRecord = store.peekRecord(proj.modelName, id);
    if (Ember.isNone(relatedRecord)) {
      let options = {
        id: id,
        projection: proj
      };
      return this.queryRecord(store, type, options);
    }
    else {
      let relatedRecordObject = relatedRecord.serialize({includeId: true});
      return this._completeLoadRecord(store, type, relatedRecordObject, proj);
    }
  },

  _replaceIdToHash(store, type,  record, attributes, attrName, promises) {
    let attr = attributes[attrName];
      let relatedModelType = (attr.kind === 'belongsTo' || attr.kind === 'hasMany') ? store.modelFor(attr.modelName) : null;
      switch (attr.kind) {
        case 'attr':
          break;
        case 'belongsTo':
          if (!isAsync(type, attrName)) {
            // let primaryKeyName = this.serializer.get('primaryKey');
            let id = record[attrName];
            if (!Ember.isNone(id)) {
              promises.pushObject(this._loadRelatedRecord(store, relatedModelType, id, attr).then((relatedRecord) => {
                delete record[attrName];
                record[attrName] = relatedRecord;
              }));
            }
          }

          break;
        case 'hasMany':
          if (!isAsync(type, attrName)) {
            if (Ember.isArray(record[attrName])) {
              let ids = Ember.copy(record[attrName]);
              delete record[attrName];
              record[attrName] = [];
              for (var i = 0; i < ids.length; i++) {
                let id = ids[i];
                promises.pushObject(this._loadRelatedRecord(store, relatedModelType, id, attr).then((relatedRecord) => {
                  record[attrName].push(relatedRecord);
                }));
              }
            }
            else {
              record[attrName] = [];
            }
          }

          break;
        default:
          throw new Error(`Unknown kind of projection attribute: ${attr.kind}`);
      }
  },

  _setNamespaceData(type, namespaceData) {
    const modelNamespace = this._modelNamespace(type);

    return this._loadData().then((storage) => {
      if (this.caching !== 'none') {
        this.cache.set(modelNamespace, namespaceData);
      }

      storage[modelNamespace] = namespaceData;

      return window.localforage.setItem(this._adapterNamespace(), storage);
    });
  },

  _getNamespaceData(type) {
    const modelNamespace = this._modelNamespace(type);

    if (this.caching !== 'none') {
      const cache = this.cache.get(modelNamespace);

      if (cache) {
        return Ember.RSVP.resolve(cache);
      }
    }

    return this._loadData().then((storage) => {
      const namespaceData = storage && storage[modelNamespace] || { records: {} };

      if (this.caching === 'model') {
        this.cache.set(modelNamespace, namespaceData);
      } else if (this.caching === 'all') {
        if (storage) {
          this.cache.replace(storage);
        }
      }

      return namespaceData;
    });
  },

  _loadData() {
    return window.localforage.getItem(this._adapterNamespace()).then((storage) => {
      return storage ? storage : {};
    });
  },

  _modelNamespace(type) {
    return type.url || type.modelName;
  },

  _adapterNamespace() {
    return this.get('namespace') || 'DS.LFAdapter';
  }
});

function updateOrCreate(store, type, snapshot) {
  return this.queue.attach((resolve) => {
    this._getNamespaceData(type).then((namespaceData) => {
      const serializer = store.serializerFor(type.modelName);
      const recordHash = serializer.serialize(snapshot, {includeId: true});
      // update(id comes from snapshot) or create(id comes from serialization)
      const id = snapshot.id || recordHash.id;

      namespaceData.records[id] = recordHash;

      this._setNamespaceData(type, namespaceData).then(() => {
        resolve();
      });
    });
  });
}
<|endoftext|>


<commit_before>import Ember from "ember";

const {
    inject: { service },
    computed,
} = Ember;

export default Ember.Component.extend({
    sideMenu: service(),

    progress: computed.alias("sideMenu.progress"),
    isOpen: computed.alias("sideMenu.isOpen"),
    isClosed: computed.alias("sideMenu.isClosed"),
    isSlightlyOpen: computed.alias("sideMenu.isSlightlyOpen"),

    attributeBindings: ["style"],
    classNames: ["side-menu"],

    side: "left",
    width: "70%",
    rootNodeSelector: "body",

    initialTapAreaWidth: 40,
    slightlyOpenWidth: 20,

    positionStyle: computed("width", "side", function () {
        const width = this.get("width");
        const side = this.get("side");

        if (side === "left") {
            return `width: ${width}; right: initial; left: -${width};`;
        }

        return `width: ${width}; left: initial; right: -${width};`;
    }),

    transitionStyle: computed("progress", function () {
        const progress = this.get("progress");
        return (progress === 0 || progress === 100)
                  ? "transition: transform 0.2s ease-out;"
                  : "transition: none;";
    }),

    transformStyle: computed("progress", "side", "isSlightlyOpen", function () {
        const side = this.get("side");
        const isSlightlyOpen = this.get("isSlightlyOpen");
        const slightlyOpenWidth = this.get("slightlyOpenWidth");
        const direction = side === "right" ? "-" : "";
        const unit = isSlightlyOpen ? "px" : "%";
        const progress = isSlightlyOpen ? slightlyOpenWidth : this.get("progress");

        return `transform: translateX(${direction}${progress}${unit});`;
    }),

    style: computed("widthStyle", "transitionStyle", "transformStyle", function () {
        const transformStyle = this.get("transformStyle");
        const transitionStyle = this.get("transitionStyle");
        const positionStyle = this.get("positionStyle");

        return new Ember.Handlebars.SafeString(
            `${transformStyle}${transitionStyle}${positionStyle}`
        );
    }),

    disableScroll: Ember.on("init", Ember.observer("isClosed", function () {
        const isClosed = this.get("isClosed");
        const wasClosed = this.get("wasClosed");
        const rootNode = document.querySelector(this.get("rootNodeSelector"));

        if (isClosed === wasClosed) {
            return;
        }

        if (isClosed) {
            Ember.$(rootNode).removeClass("disable-scroll");
        } else {
            Ember.$(rootNode).addClass("disable-scroll");
        }

        this.set("wasClosed", isClosed);
    })),

    didInsertElement() {
        this._super(...arguments);
        this.setupEventListeners();
    },

    willDestroyElement() {
        this._super(...arguments);
        this.removeEventListeners();
    },

    setupEventListeners() {
        const rootNode = document.querySelector(this.get("rootNodeSelector"));
        const onTouchStart = Ember.run.bind(this, this.rootNodeTouch);

        rootNode.addEventListener("touchstart", onTouchStart);

        Ember.run.schedule("afterRender", () => {
            this.set("onTouchStart", onTouchStart);
        });
    },

    removeEventListeners() {
        const onTouchStart = this.get("onTouchStart");
        const rootNode = document.querySelector(this.get("rootNodeSelector"));

        rootNode.removeEventListener("touchstart", onTouchStart);
    },

    rootNodeTouch() {
        const rootNode = document.querySelector(this.get("rootNodeSelector"));
        const onTouchMove = (event) => {
            event.preventDefault();
            if (this.get("isSlightlyOpen")) {
                this.set("isSlightlyOpen", false);
            }
            this.updateProgress(event.touches[0].pageX);
        };
        const throttledOnTouchMove = (event) => {
            Ember.run.throttle(this, onTouchMove, event, 10);
        };
        const onTouchEnd = Ember.run.bind(this, (event) => {
            rootNode.removeEventListener("touchmove", throttledOnTouchMove);
            rootNode.removeEventListener("touchend", onTouchEnd);

            this.completeMenuTransition(event);
        });

        if (this.needToTrack(event)) {
            this.set("touchStartEvent", event);
            this.setTouchOffset(event);

            if (this.isTapInInitialTapArea(event)) {
                Ember.run.later(() => {
                    if (this.get("isClosed")) {
                        this.set("isSlightlyOpen", true);
                    }
                }, 200);
            }

            rootNode.addEventListener("touchmove", throttledOnTouchMove);
            rootNode.addEventListener("touchend", onTouchEnd);
        }
    },

    setTouchOffset(event) {
        const isOpen = this.get("isOpen");
        const pageX = event.touches[0].pageX;
        const side = this.get("side");

        if (isOpen) {
            if (side === "left") {
                this.set("touchOffset", Math.max(0, this.element.offsetWidth - pageX));
            } else {
                this.set(
                    "touchOffset",
                    Math.max(0, this.element.offsetWidth - (window.innerWidth - pageX))
                );
            }
        } else {
            this.set("touchOffset", 0);
        }
    },

    updateProgress(touchPageX) {
        const elementWidth = this.element.offsetWidth;
        const touchOffset = this.get("touchOffset");
        const side = this.get("side");
        const relativeX = side === "left" ? touchPageX : window.innerWidth - touchPageX;
        const progress = Math.min((relativeX + touchOffset) / elementWidth * 100, 100);

        this.set("progress", progress);
    },

    completeMenuTransition(event) {
        const progress = this.get("progress");
        const touchStartEvent = this.get("touchStartEvent");
        const side = this.get("side");
        const velocityX = this.calculateVelocityX(
            touchStartEvent.touches[0].pageX,
            touchStartEvent.timeStamp,
            event.changedTouches[0].pageX,
            event.timeStamp
        );
        const minClosingVelocity = 0.3;
        const autoCompleteThreshold = 50;
        const isSwipingLeft = velocityX > minClosingVelocity;
        const isSwipingRight = velocityX < -minClosingVelocity;

        const isClosingMovement = (side === "left" && isSwipingLeft) ||
                  (side === "right" && isSwipingRight);
        const isOpeningMovement = (side === "left" && isSwipingRight) ||
                  (side === "right" && isSwipingLeft);

        if (isClosingMovement || progress < autoCompleteThreshold) {
            this.get("sideMenu").close();
        } else if (isOpeningMovement || progress >= autoCompleteThreshold) {
            this.get("sideMenu").open();
        }
    },

    needToTrack(event) {
        return this.get("isOpen") || this.isTapInInitialTapArea(event);
    },

    isTapInInitialTapArea(event) {
        const side = this.get("side");
        const pageX = event.touches[0].pageX;
        const initialTapAreaWidth = this.get("initialTapAreaWidth");

        return (side === "left" && pageX < initialTapAreaWidth) ||
            (side === "right" && pageX > window.innerWidth - initialTapAreaWidth);
    },

    calculateVelocityX(startX, startTimeStamp, endX, endTimeStamp) {
        const deltaX = startX - endX;
        const deltaTime = endTimeStamp - startTimeStamp;

        return deltaX / deltaTime;
    },
});
<commit_msg>doesn't work on firefox bug fix<commit_after>import Ember from "ember";

const {
    inject: { service },
    computed,
} = Ember;

export default Ember.Component.extend({
    sideMenu: service(),

    progress: computed.alias("sideMenu.progress"),
    isOpen: computed.alias("sideMenu.isOpen"),
    isClosed: computed.alias("sideMenu.isClosed"),
    isSlightlyOpen: computed.alias("sideMenu.isSlightlyOpen"),

    attributeBindings: ["style"],
    classNames: ["side-menu"],

    side: "left",
    width: "70%",
    rootNodeSelector: "body",

    initialTapAreaWidth: 40,
    slightlyOpenWidth: 20,

    positionStyle: computed("width", "side", function () {
        const width = this.get("width");
        const side = this.get("side");

        if (side === "left") {
            return `width: ${width}; right: initial; left: -${width};`;
        }

        return `width: ${width}; left: initial; right: -${width};`;
    }),

    transitionStyle: computed("progress", function () {
        const progress = this.get("progress");
        return (progress === 0 || progress === 100)
                  ? "transition: transform 0.2s ease-out;"
                  : "transition: none;";
    }),

    transformStyle: computed("progress", "side", "isSlightlyOpen", function () {
        const side = this.get("side");
        const isSlightlyOpen = this.get("isSlightlyOpen");
        const slightlyOpenWidth = this.get("slightlyOpenWidth");
        const direction = side === "right" ? "-" : "";
        const unit = isSlightlyOpen ? "px" : "%";
        const progress = isSlightlyOpen ? slightlyOpenWidth : this.get("progress");

        return `transform: translateX(${direction}${progress}${unit});`;
    }),

    style: computed("widthStyle", "transitionStyle", "transformStyle", function () {
        const transformStyle = this.get("transformStyle");
        const transitionStyle = this.get("transitionStyle");
        const positionStyle = this.get("positionStyle");

        return new Ember.Handlebars.SafeString(
            `${transformStyle}${transitionStyle}${positionStyle}`
        );
    }),

    disableScroll: Ember.on("init", Ember.observer("isClosed", function () {
        const isClosed = this.get("isClosed");
        const wasClosed = this.get("wasClosed");
        const rootNode = document.querySelector(this.get("rootNodeSelector"));

        if (isClosed === wasClosed) {
            return;
        }

        if (isClosed) {
            Ember.$(rootNode).removeClass("disable-scroll");
        } else {
            Ember.$(rootNode).addClass("disable-scroll");
        }

        this.set("wasClosed", isClosed);
    })),

    didInsertElement() {
        this._super(...arguments);
        this.setupEventListeners();
    },

    willDestroyElement() {
        this._super(...arguments);
        this.removeEventListeners();
    },

    setupEventListeners() {
        const rootNode = document.querySelector(this.get("rootNodeSelector"));
        const onTouchStart = Ember.run.bind(this, this.rootNodeTouch);

        rootNode.addEventListener("touchstart", onTouchStart);

        Ember.run.schedule("afterRender", () => {
            this.set("onTouchStart", onTouchStart);
        });
    },

    removeEventListeners() {
        const onTouchStart = this.get("onTouchStart");
        const rootNode = document.querySelector(this.get("rootNodeSelector"));

        rootNode.removeEventListener("touchstart", onTouchStart);
    },

    rootNodeTouch(evt) {
        const rootNode = document.querySelector(this.get("rootNodeSelector"));
        const onTouchMove = (event) => {
            event.preventDefault();
            if (this.get("isSlightlyOpen")) {
                this.set("isSlightlyOpen", false);
            }
            this.updateProgress(event.touches[0].pageX);
        };
        const throttledOnTouchMove = (event) => {
            Ember.run.throttle(this, onTouchMove, event, 10);
        };
        const onTouchEnd = Ember.run.bind(this, (event) => {
            rootNode.removeEventListener("touchmove", throttledOnTouchMove);
            rootNode.removeEventListener("touchend", onTouchEnd);

            this.completeMenuTransition(event);
        });

        if (this.needToTrack(evt)) {
            this.set("touchStartEvent", evt);
            this.setTouchOffset(evt);

            if (this.isTapInInitialTapArea(evt)) {
                Ember.run.later(() => {
                    if (this.get("isClosed")) {
                        this.set("isSlightlyOpen", true);
                    }
                }, 200);
            }

            rootNode.addEventListener("touchmove", throttledOnTouchMove);
            rootNode.addEventListener("touchend", onTouchEnd);
        }
    },

    setTouchOffset(event) {
        const isOpen = this.get("isOpen");
        const pageX = event.touches[0].pageX;
        const side = this.get("side");

        if (isOpen) {
            if (side === "left") {
                this.set("touchOffset", Math.max(0, this.element.offsetWidth - pageX));
            } else {
                this.set(
                    "touchOffset",
                    Math.max(0, this.element.offsetWidth - (window.innerWidth - pageX))
                );
            }
        } else {
            this.set("touchOffset", 0);
        }
    },

    updateProgress(touchPageX) {
        const elementWidth = this.element.offsetWidth;
        const touchOffset = this.get("touchOffset");
        const side = this.get("side");
        const relativeX = side === "left" ? touchPageX : window.innerWidth - touchPageX;
        const progress = Math.min((relativeX + touchOffset) / elementWidth * 100, 100);

        this.set("progress", progress);
    },

    completeMenuTransition(event) {
        const progress = this.get("progress");
        const touchStartEvent = this.get("touchStartEvent");
        const side = this.get("side");
        const velocityX = this.calculateVelocityX(
            touchStartEvent.touches[0].pageX,
            touchStartEvent.timeStamp,
            event.changedTouches[0].pageX,
            event.timeStamp
        );
        const minClosingVelocity = 0.3;
        const autoCompleteThreshold = 50;
        const isSwipingLeft = velocityX > minClosingVelocity;
        const isSwipingRight = velocityX < -minClosingVelocity;

        const isClosingMovement = (side === "left" && isSwipingLeft) ||
                  (side === "right" && isSwipingRight);
        const isOpeningMovement = (side === "left" && isSwipingRight) ||
                  (side === "right" && isSwipingLeft);

        if (isClosingMovement || progress < autoCompleteThreshold) {
            this.get("sideMenu").close();
        } else if (isOpeningMovement || progress >= autoCompleteThreshold) {
            this.get("sideMenu").open();
        }
    },

    needToTrack(event) {
        return this.get("isOpen") || this.isTapInInitialTapArea(event);
    },

    isTapInInitialTapArea(event) {
        const side = this.get("side");
        const pageX = event.touches[0].pageX;
        const initialTapAreaWidth = this.get("initialTapAreaWidth");

        return (side === "left" && pageX < initialTapAreaWidth) ||
            (side === "right" && pageX > window.innerWidth - initialTapAreaWidth);
    },

    calculateVelocityX(startX, startTimeStamp, endX, endTimeStamp) {
        const deltaX = startX - endX;
        const deltaTime = endTimeStamp - startTimeStamp;

        return deltaX / deltaTime;
    },
});
<|endoftext|>


<commit_before>const StringGenerator = require('./stringgenerator.js');

/**
 *
 *
 * @class NumberGenerator
 */
class NumberGenerator {

  /**
   * Creates an instance of NumberGenerator.
   * @param {any} chartType
   * @memberof NumberGenerator
   */
  constructor(chartType) {
    this.chartType = chartType;

    // Values possible - random, constant, linear, exponential, logarithmic, prime
    this.trend = {
      property: 'random',
      a: 1,
      b: 1
    };

    //Defines a range for the generator on the X axis. Values possible - (lb,ub), (,ub), (lb,), (,)
    this.rangeX = {
      lowerBound: -1000000000,
      upperBound: 1000000000
    };

    //Defines a range for the generator on the Y axis. Values possible - (lb,ub), (,ub), (lb,), (,)
    this.rangeY = {
      lowerBound: -1000000000,
      upperBound: 1000000000
    };
  }

  /**
   *
   *
   * @param {any} property
   * @param {any} value
   * @param {any} a
   * @param {any} b
   * @memberof NumberGenerator
   */
  modifier(property, value1, value2) {
    if (property === 'trend')
      this.trend.property = value1;

    else if (property === 'range') {
      if (value1.length > 1) {
        let val = value1.split(',');
        if (val[0].length > 0)
          this.rangeX.lowerBound = Number(val[0].trim());
        if (val[1].length > 0)
          this.rangeX.upperBound = Number(val[1].trim());
      }
      if(!value2){
        this.rangeY.lowerBound = this.rangeX.lowerBound;
        this.rangeY.upperBound = this.rangeX.upperBound;
      }
      else{
        let val = value2.split(',');
        if (val[0].length > 0)
          this.rangeY.lowerBound = Number(val[0].trim());
        if (val[1].length > 0)
          this.rangeY.upperBound = Number(val[1].trim());
      }
    }
  }

  /**
   *
   *
   * @param {any} type
   * @param {any} n
   * @memberof NumberGenerator
   */
  generate(type, n, label) {
    let spacing = -1,
      i = 0;
    if (type === 'integer' || type === 'decimal') {
      //Adds n unique random numbers to the data this.array
      if (this.trend.property === 'random') {
        if (label) {
          let sg = new StringGenerator(n, label);
          return [this.random(n, type), sg.generate()];
        } else
          return [this.random(n, type), []];
      } else if (this.trend.property === 'linear') {
        if (label) {
          let sg = new StringGenerator(n, label);
          return [this.linear(n, type), sg.generate()];
        } else
          return [this.linear(n, type), []];
      } else if (this.trend.property === 'exp') {
        if (label) {
          let sg = new StringGenerator(n, label);
          return [this.exponential(n, type), sg.generate()];
        } else
          return [this.exponential(n, type), []];
      }
    } else if (label) {
      let sg = new StringGenerator(n, label);
      return sg.generate();
    }
  }

  /**
   *
   *
   * @param {any} n
   * @memberof NumberGenerator
   */
  random(n, type) {
    let spacing = -1,
      i = 0,
      arr = [];
    while (i < n) {
      let num;
      if(type === 'integer')
        num = Math.floor((Math.random() * (this.rangeY.upperBound - this.rangeY.lowerBound)) + this.rangeY.lowerBound);
      else
        num = (Math.random() * (this.rangeY.upperBound - this.rangeY.lowerBound)) + this.rangeY.lowerBound;
      if (arr.indexOf(num) > -1)
        continue;
      else {
        if (type === 'scatter')
          if(type === 'integer')
            arr.push([Math.floor((Math.random() * (this.rangeX.upperBound - this.rangeX.lowerBound)) + this.rangeX.lowerBound), num]);
          else
            arr.push([(Math.random() * (this.rangeX.upperBound - this.rangeX.lowerBound) + this.rangeX.lowerBound), num]);
        else if(type === 'bubble')
          if(type === 'integer')
            arr.push([Math.floor((Math.random() * (this.rangeX.upperBound - this.rangeX.lowerBound)) + this.rangeX.lowerBound), num, Math.floor((Math.random() * 100)+50)]);
          else
            arr.push([(Math.random() * (this.rangeX.upperBound - this.rangeX.lowerBound) + this.rangeX.lowerBound), num, (Math.random() * 100)+50]);
        else
          arr.push(num);
        i++;
      }
    }
    return arr;
  }

  /**
   *
   *
   * @param {any} n
   * @memberof NumberGenerator
   */
  linear(n, type) {
    let arr = [];
    if (this.prevTrend !== this.trend.property)
      this.counter = 0;

    let x = this.generateX(n, type);

    //Defines Slope
    this.trend.a = (this.rangeY.upperBound - this.rangeY.lowerBound) / (x[n - 1] - x[0]);
    //Defines Intercept
    this.trend.b = (this.rangeY.lowerBound - (this.trend.a * x[0]))

    //generate the y axis values
    for (let i = 0; i < n; i++)
      if (this.chartType === 'scatter')
        if(type === 'integer')
          arr.push([x[i], Math.ceil((this.trend.a * x[i]) + this.trend.b)]);
        else
          arr.push([x[i], ((this.trend.a * x[i]) + this.trend.b)]);
      else if(type === 'bubble')
        if(type === 'integer')
          arr.push([x[i], Math.ceil((this.trend.a * x[i]) + this.trend.b), Math.floor((Math.random() * 100)+50)]);
        else
          arr.push([x[i], ((this.trend.a * x[i]) + this.trend.b), (Math.random() * 100)+50]);
      else
        if(type === 'integer')
          arr.push(Math.ceil((this.trend.a * x[i]) + this.trend.b));
        else
          arr.push(((this.trend.a * x[i]) + this.trend.b));

    return arr;
  }

  /**
   *
   *
   * @param {any} n
   * @memberof NumberGenerator
   */
  exponential(n, type) {
    let arr = [];
    if (this.prevTrend !== this.trend.property)
      this.counter = 0;

    let x = this.generateX(n, type);

    //Defines rate of growth in exponential equation
    this.trend.b = Math.log(this.rangeY.upperBound / this.rangeY.lowerBound) / (x[n - 1] - x[0]);
    //Define the constant in the exponential equation
    this.trend.a = this.rangeY.lowerBound / Math.exp(this.trend.b * x[0]);

    //generate the y axis values
    for (let i = 0; i < n; i++) {
      if (type === 'scatter')
        if(type === 'integer')
          arr.push([x[i], Math.floor(this.trend.a * Math.exp(this.trend.b * x[i]))]);
        else
          arr.push([x[i], (this.trend.a * Math.exp(this.trend.b * x[i]))]);
      else if(type === 'bubble')
        if(type === 'integer')
          arr.push([x[i], Math.floor(this.trend.a * Math.exp(this.trend.b * x[i])), Math.floor((Math.random() * 100)+50)]);
        else
          arr.push([x[i], (this.trend.a * Math.exp(this.trend.b * x[i])), (Math.random() * 100)+50]);
      else
        if(type === 'integer')
          arr.push(Math.floor(this.trend.a * Math.exp(this.trend.b * x[i])));
        else
          arr.push((this.trend.a * Math.exp(this.trend.b * x[i])));
    }

    return arr;
  }

  /**
   * Generates Random numbers on the X axis within a given range
   *
   * @param {any} n
   * @returns
   * @memberof NumberGenerator
   */
  generateX(n, type) {
    let x = [],
      i = 0;
    //Generate random numbers on the x axis within the range
    while (i < n) {
      let num;
      if(type === 'integer')
        num = Math.floor((Math.random() * (this.rangeX.upperBound - this.rangeX.lowerBound)) + this.rangeX.lowerBound);
      else
        num = ((Math.random() * (this.rangeX.upperBound - this.rangeX.lowerBound)) + this.rangeX.lowerBound);
      if (x.indexOf(num) > -1)
        continue;
      else {
        x.push(num);
        i++;
      }
    }
    //Sort the x axis numbers numerically. Helps in finding the maximum value of x for both positive and negative range
    x.sort(function (a, b) {
      return a - b
    });
    return x;
  }
}

module.exports = NumberGenerator;

//1. TODO: Add ellipse, parabola, quadratic, rectangular hyperbola, constant

// let X = new NumberGenerator("scatter");

// X.modifier('range', '30, 100');
// X.modifier('trend', 'linear');
// console.log(X.generate('integer', 10));
// // //console.log(c[0]);
// // //console.log(c[1]);

// // //console.log(X);

// // X.modifier('range', '100, 170', '100, 30');
// // X.modifier('trend', 'linear');
// // let d = X.generate('decimal', 10);
// // console.log(d[0]);
// // console.log(d[1]);

// // //console.log(X);

// // /*X.modifier('range', '300, 100');
// // X.modifier('trend', 'linear');
// // let d = X.generate('integer', 5, 'generic1');
// // console.log(d[0]);
// // console.log(d[1]);

// // X.modifier('range', '300, 100');
// // X.modifier('trend', 'linear');
// let e = X.generate('none', 5, 'generic1');
// console.log(e);*/
<commit_msg>Fixed type variable bug<commit_after>const StringGenerator = require('./stringgenerator.js');

/**
 *
 *
 * @class NumberGenerator
 */
class NumberGenerator {

  /**
   * Creates an instance of NumberGenerator.
   * @param {any} chartType
   * @memberof NumberGenerator
   */
  constructor(chartType) {
    this.chartType = chartType;

    // Values possible - random, constant, linear, exponential, logarithmic, prime
    this.trend = {
      property: 'random',
      a: 1,
      b: 1
    };

    //Defines a range for the generator on the X axis. Values possible - (lb,ub), (,ub), (lb,), (,)
    this.rangeX = {
      lowerBound: -1000000000,
      upperBound: 1000000000
    };

    //Defines a range for the generator on the Y axis. Values possible - (lb,ub), (,ub), (lb,), (,)
    this.rangeY = {
      lowerBound: -1000000000,
      upperBound: 1000000000
    };
  }

  /**
   *
   *
   * @param {any} property
   * @param {any} value
   * @param {any} a
   * @param {any} b
   * @memberof NumberGenerator
   */
  modifier(property, value1, value2) {
    if (property === 'trend')
      this.trend.property = value1;

    else if (property === 'range') {
      if (value1.length > 1) {
        let val = value1.split(',');
        if (val[0].length > 0)
          this.rangeX.lowerBound = Number(val[0].trim());
        if (val[1].length > 0)
          this.rangeX.upperBound = Number(val[1].trim());
      }
      if(!value2){
        this.rangeY.lowerBound = this.rangeX.lowerBound;
        this.rangeY.upperBound = this.rangeX.upperBound;
      }
      else{
        let val = value2.split(',');
        if (val[0].length > 0)
          this.rangeY.lowerBound = Number(val[0].trim());
        if (val[1].length > 0)
          this.rangeY.upperBound = Number(val[1].trim());
      }
    }
  }

  /**
   *
   *
   * @param {any} type
   * @param {any} n
   * @memberof NumberGenerator
   */
  generate(type, n, label) {
    let spacing = -1,
      i = 0;
    if (type === 'integer' || type === 'decimal') {
      //Adds n unique random numbers to the data this.array
      if (this.trend.property === 'random') {
        if (label) {
          let sg = new StringGenerator(n, label);
          return [this.random(n, type), sg.generate()];
        } else
          return [this.random(n, type), []];
      } else if (this.trend.property === 'linear') {
        if (label) {
          let sg = new StringGenerator(n, label);
          return [this.linear(n, type), sg.generate()];
        } else
          return [this.linear(n, type), []];
      } else if (this.trend.property === 'exp') {
        if (label) {
          let sg = new StringGenerator(n, label);
          return [this.exponential(n, type), sg.generate()];
        } else
          return [this.exponential(n, type), []];
      }
    } else if (label) {
      let sg = new StringGenerator(n, label);
      return sg.generate();
    }
  }

  /**
   *
   *
   * @param {any} n
   * @memberof NumberGenerator
   */
  random(n, type) {
    let spacing = -1,
      i = 0,
      arr = [];
    while (i < n) {
      let num;
      if(type === 'integer')
        num = Math.floor((Math.random() * (this.rangeY.upperBound - this.rangeY.lowerBound)) + this.rangeY.lowerBound);
      else
        num = (Math.random() * (this.rangeY.upperBound - this.rangeY.lowerBound)) + this.rangeY.lowerBound;
      if (arr.indexOf(num) > -1)
        continue;
      else {
        if (this.chartType === 'scatter')
          if(type === 'integer')
            arr.push([Math.floor((Math.random() * (this.rangeX.upperBound - this.rangeX.lowerBound)) + this.rangeX.lowerBound), num]);
          else
            arr.push([(Math.random() * (this.rangeX.upperBound - this.rangeX.lowerBound) + this.rangeX.lowerBound), num]);
        else if(this.chartType === 'bubble')
          if(type === 'integer')
            arr.push([Math.floor((Math.random() * (this.rangeX.upperBound - this.rangeX.lowerBound)) + this.rangeX.lowerBound), num, Math.floor((Math.random() * 100)+50)]);
          else
            arr.push([(Math.random() * (this.rangeX.upperBound - this.rangeX.lowerBound) + this.rangeX.lowerBound), num, (Math.random() * 100)+50]);
        else
          arr.push(num);
        i++;
      }
    }
    return arr;
  }

  /**
   *
   *
   * @param {any} n
   * @memberof NumberGenerator
   */
  linear(n, type) {
    let arr = [];
    if (this.prevTrend !== this.trend.property)
      this.counter = 0;

    let x = this.generateX(n, type);

    //Defines Slope
    this.trend.a = (this.rangeY.upperBound - this.rangeY.lowerBound) / (x[n - 1] - x[0]);
    //Defines Intercept
    this.trend.b = (this.rangeY.lowerBound - (this.trend.a * x[0]))

    //generate the y axis values
    for (let i = 0; i < n; i++)
      if (this.chartType === 'scatter')
        if(type === 'integer')
          arr.push([x[i], Math.ceil((this.trend.a * x[i]) + this.trend.b)]);
        else
          arr.push([x[i], ((this.trend.a * x[i]) + this.trend.b)]);
      else if(this.chartType === 'bubble')
        if(type === 'integer')
          arr.push([x[i], Math.ceil((this.trend.a * x[i]) + this.trend.b), Math.floor((Math.random() * 100)+50)]);
        else
          arr.push([x[i], ((this.trend.a * x[i]) + this.trend.b), (Math.random() * 100)+50]);
      else
        if(type === 'integer')
          arr.push(Math.ceil((this.trend.a * x[i]) + this.trend.b));
        else
          arr.push(((this.trend.a * x[i]) + this.trend.b));

    return arr;
  }

  /**
   *
   *
   * @param {any} n
   * @memberof NumberGenerator
   */
  exponential(n, type) {
    let arr = [];
    if (this.prevTrend !== this.trend.property)
      this.counter = 0;

    let x = this.generateX(n, type);

    //Defines rate of growth in exponential equation
    this.trend.b = Math.log(this.rangeY.upperBound / this.rangeY.lowerBound) / (x[n - 1] - x[0]);
    //Define the constant in the exponential equation
    this.trend.a = this.rangeY.lowerBound / Math.exp(this.trend.b * x[0]);

    //generate the y axis values
    for (let i = 0; i < n; i++) {
      if (this.chartType === 'scatter')
        if(type === 'integer')
          arr.push([x[i], Math.floor(this.trend.a * Math.exp(this.trend.b * x[i]))]);
        else
          arr.push([x[i], (this.trend.a * Math.exp(this.trend.b * x[i]))]);
      else if(this.chartType === 'bubble')
        if(type === 'integer')
          arr.push([x[i], Math.floor(this.trend.a * Math.exp(this.trend.b * x[i])), Math.floor((Math.random() * 100)+50)]);
        else
          arr.push([x[i], (this.trend.a * Math.exp(this.trend.b * x[i])), (Math.random() * 100)+50]);
      else
        if(type === 'integer')
          arr.push(Math.floor(this.trend.a * Math.exp(this.trend.b * x[i])));
        else
          arr.push((this.trend.a * Math.exp(this.trend.b * x[i])));
    }

    return arr;
  }

  /**
   * Generates Random numbers on the X axis within a given range
   *
   * @param {any} n
   * @returns
   * @memberof NumberGenerator
   */
  generateX(n, type) {
    let x = [],
      i = 0;
    //Generate random numbers on the x axis within the range
    while (i < n) {
      let num;
      if(type === 'integer')
        num = Math.floor((Math.random() * (this.rangeX.upperBound - this.rangeX.lowerBound)) + this.rangeX.lowerBound);
      else
        num = ((Math.random() * (this.rangeX.upperBound - this.rangeX.lowerBound)) + this.rangeX.lowerBound);
      if (x.indexOf(num) > -1)
        continue;
      else {
        x.push(num);
        i++;
      }
    }
    //Sort the x axis numbers numerically. Helps in finding the maximum value of x for both positive and negative range
    x.sort(function (a, b) {
      return a - b
    });
    return x;
  }
}

module.exports = NumberGenerator;

//1. TODO: Add ellipse, parabola, quadratic, rectangular hyperbola, constant

let X = new NumberGenerator("bubble");

X.modifier('range', '30, 100');
X.modifier('trend', 'exp');
console.log(X.generate('integer', 10));
// // //console.log(c[0]);
// // //console.log(c[1]);

// // //console.log(X);

// // X.modifier('range', '100, 170', '100, 30');
// // X.modifier('trend', 'linear');
// // let d = X.generate('decimal', 10);
// // console.log(d[0]);
// // console.log(d[1]);

// // //console.log(X);

// // /*X.modifier('range', '300, 100');
// // X.modifier('trend', 'linear');
// // let d = X.generate('integer', 5, 'generic1');
// // console.log(d[0]);
// // console.log(d[1]);

// // X.modifier('range', '300, 100');
// // X.modifier('trend', 'linear');
// let e = X.generate('none', 5, 'generic1');
// console.log(e);*/
<|endoftext|>


<commit_before>import Ember from 'ember';
import layout from 'untitled-ui/templates/components/ui-button';

export default Ember.Component.extend({
  layout,

  tagName: "",
  kind: "default",
  size: "medium",

  disabled: false,
  loading: false,
  focus: false,
  browserActive: false,
  active: false,

  frame: Ember.computed('kind', function() {
    return `ui-button--${this.get('kind')}`;
  }),

  classes: Ember.computed('size', function() {
    return {
      parent: 'ui-button',
      size: `ui-font-size--${this.get('size')}`
    }
  }),

  isDisabled: Ember.computed.or('disabled', 'loading'),

  states: Ember.computed('isDisabled', 'loading', 'active', 'browserActive', 'focus', function() {
    return {
      disabled: this.get('isDisabled'),
      loading: this.get('loading'),
      active: this.get('browserActive') || this.get('active'),
      focus: this.get('focus')
    }
  }),

  group: Ember.computed('buttonGroup.[]', function() {
    if (Ember.isEmpty(this.get('buttonGroup'))) {
      return null;
    }

    return {
      isFirstChild: this.get('buttonGroup.firstChild') === this,
      isLastChild: this.get('buttonGroup.lastChild') === this
    };
  }),

  $(sel) {
    let el = this._renderNode.firstNode.nextElementSibling;
    return sel ? $(sel, el) : $(el);
  },

  didInsertElement() {
    this.$().on('webkitTransitionEnd transitionend msTransitionEnd oTransitionEnd', () => {
      this.toggleProperty('browserActive');
    });

    this.$().on('focusin', () => {
      this.set('focus', true);
    });

    this.$().on('focusout', () => {
      this.set('focus', false);
    });

    if (this.attrs.register) {
      this.attrs.register(this);
    }
  },

  actions: {
    onclick(event) {
      this.sendAction('onclick');
    }
  }
});
<commit_msg>fix performance warning<commit_after>import Ember from 'ember';
import layout from 'untitled-ui/templates/components/ui-button';

export default Ember.Component.extend({
  layout,

  tagName: "",
  kind: "default",
  size: "medium",

  disabled: false,
  loading: false,
  focus: false,
  browserActive: false,
  active: false,

  frame: Ember.computed('kind', function() {
    return `ui-button--${this.get('kind')}`;
  }),

  classes: Ember.computed('size', function() {
    return {
      parent: 'ui-button',
      size: `ui-font-size--${this.get('size')}`
    }
  }),

  isDisabled: Ember.computed.or('disabled', 'loading'),

  states: Ember.computed('isDisabled', 'loading', 'active', 'browserActive', 'focus', function() {
    return {
      disabled: this.get('isDisabled'),
      loading: this.get('loading'),
      active: this.get('browserActive') || this.get('active'),
      focus: this.get('focus')
    }
  }),

  group: Ember.computed('buttonGroup.[]', function() {
    if (Ember.isEmpty(this.get('buttonGroup'))) {
      return null;
    }

    return {
      isFirstChild: this.get('buttonGroup.firstChild') === this,
      isLastChild: this.get('buttonGroup.lastChild') === this
    };
  }),

  $(sel) {
    let el = this._renderNode.firstNode.nextElementSibling;
    return sel ? $(sel, el) : $(el);
  },

  didInsertElement() {
    this.$().on('webkitTransitionEnd transitionend msTransitionEnd oTransitionEnd', () => {
      this.toggleProperty('browserActive');
    });

    this.$().on('focusin', () => {
      this.set('focus', true);
    });

    this.$().on('focusout', () => {
      this.set('focus', false);
    });

    if (this.attrs.register) {
      Ember.run.next(this, function() {
        this.attrs.register(this);
      });
    }
  },

  actions: {
    onclick(event) {
      this.sendAction('onclick');
    }
  }
});
<|endoftext|>