<commit_before>/* eslint max-len:0 */
define(function(require, exports) {
    'use strict';

    var partials  = require('handlebars').partials;
    require('./helpers/handlebars.partials');
    var paragraph = partials.paragraph;
    var code      = partials.code;
    var gist      = partials.gist;
    var demo      = partials.demo;
    var image     = partials.image;
    var banner    = partials.banner;
    var quote     = partials.quote;
    var list      = partials.list;

    exports.feed = [
        {
            type: 'post',
            postId: '1',
            name: 'coming-soon',
            date: '20161106',
            title: 'Coming Soon',
            subtitle: '',
            body: [
                paragraph('We are working on meaningful content to make the world better.'),
                paragraph('When we are ready to share, we will post it here in the Omaha JS Feed.  Keep us in mind, and check back soon.'),
                paragraph('Before you leave, please check out our code section!'),
                image(null, {height: 300})
            ]
        }
    ];
    exports.code = [
        {
            order: 1,
            type: 'project',
            tags: ['yeoman', 'node', 'CLI', 'scaffold'],
            technology: 'yeoman',
            date: '',
            name: 'generator-omaha',
            url: 'https://github.com/omahajs/generator-omaha',
            title: 'Omaha Generator',
            subtitle: 'Yeoman generator for projects, web apps, and servers'
        },
        {
            order: 2,
            type: 'project',
            tags: ['eslint', 'linting', 'quality'],
            technology: 'eslint',
            date: '',
            name: 'eslint-config-omaha-prime-grade',
            url: 'https://github.com/omahajs/eslint-config-omaha-prime-grade',
            title: 'Code Quality Rules',
            subtitle: '"Prime Grade" ESLint Shareable Config'
        }
    ];
    exports.about = [
        {
            type: 'about',
            body: [
                paragraph('Omaha JS is about crafting sustainable, high-quality software with modern front-end technologies.  In simple terms, software is sustainable when it can be maintained indefinitely and Omaha JS seeks to help make creating sustainable software more attainable in real-world settings.'),
                paragraph('With this mission in mind, we have some key ideals driving us:'),
                quote('Writing quality code is a process that must be practiced.'),
                paragraph('Writing sustainable code is not always easy and like most things that are worth doing, practice is required. When it comes to crafting code, writing more of it usually helps.  However, just writing code is not enough! You must also read, research, experiment, download, install, configure, execute, upload, contribute, comment, clone, fork, discuss, persevere, review, follow, and grow.'),
                quote('Practice patterns before plug-ins.'),
                paragraph('Software developers encode the patterns of reality.  We should understand patterns to accomplish tasks and have the ability to choose the appropiate tool for any task.  As tools change, it is the skill and privilege of software developers to learn and adapt, as required.'),
                quote('The process of writing quality code must be sustainable in order to be viable in real-world situations.'),
                paragraph('"A developer went out to craft some code.  As he wrote his code, some of it was un-used and became dead code.  Other code was used right away.  But the code did not have tests and soon became impossible to change as requirements evolved.  Other code was added to old production code that was bloated, and not well-maintained.  The legacy code smothered the tender new code and it became un-used.  Still other code was added to a well-maintained code base with numerous well-written tests and quality metrics.  The code grew and was re-factored to be thirty, sixty, and even a hundred times more useful than when it was written."'),
                list('Credits',
                    [
                        '[Nebraska Font by Daniel Heikkinen](http://www.dafont.com/nebraska.font)',
                        '[IcoMoon Icon font](http://www.icomoon.io)',
                        '[SVG Hero Patterns](http://www.heropatterns.com/)',
                        '[highlight.js (syntax highlighting for code blocks)](https://highlightjs.org/)'
                    ]
                ),
                image('../assets/images/cornjs.png', {height: 250})
            ]
        }
    ];
});
<commit_msg>Added coming soon post<commit_after>/* eslint max-len:0 */
define(function(require, exports) {
    'use strict';

    var partials  = require('handlebars').partials;
    require('./helpers/handlebars.partials');
    var paragraph = partials.paragraph;
    var code      = partials.code;
    var gist      = partials.gist;
    var demo      = partials.demo;
    var image     = partials.image;
    var banner    = partials.banner;
    var quote     = partials.quote;
    var list      = partials.list;

    exports.feed = [
        {
            type: 'post',
            postId: '1',
            name: 'coming-soon',
            date: '20161106',
            title: 'Coming Soon',
            subtitle: 'A source for quality content about code and quality code for making content',
            body: [
                quote('A source for quality content about code and quality code for making content'),
                paragraph('We are working on meaningful content to make the world better.'),
                paragraph('When we are ready to share, we will post it here in the Omaha JS Feed.  Keep us in mind, and check back soon.'),
                paragraph('Before you leave, please check out our code section!'),
                image(null, {height: 300})
            ]
        }
    ];
    exports.code = [
        {
            order: 1,
            type: 'project',
            tags: ['yeoman', 'node', 'CLI', 'scaffold'],
            technology: 'yeoman',
            date: '',
            name: 'generator-omaha',
            url: 'https://github.com/omahajs/generator-omaha',
            title: 'Omaha Generator',
            subtitle: 'Yeoman generator for projects, web apps, and servers'
        },
        {
            order: 2,
            type: 'project',
            tags: ['eslint', 'linting', 'quality'],
            technology: 'eslint',
            date: '',
            name: 'eslint-config-omaha-prime-grade',
            url: 'https://github.com/omahajs/eslint-config-omaha-prime-grade',
            title: 'Code Quality Rules',
            subtitle: '"Prime Grade" ESLint Shareable Config'
        }
    ];
    exports.about = [
        {
            type: 'about',
            body: [
                paragraph('Omaha JS is about crafting sustainable, high-quality software with modern front-end technologies.  In simple terms, software is sustainable when it can be maintained indefinitely and Omaha JS seeks to help make creating sustainable software more attainable in real-world settings.'),
                paragraph('With this mission in mind, we have some key ideals driving us:'),
                quote('Writing quality code is a process that must be practiced.'),
                paragraph('Writing sustainable code is not always easy and like most things that are worth doing, practice is required. When it comes to crafting code, writing more of it usually helps.  However, just writing code is not enough! You must also read, research, experiment, download, install, configure, execute, upload, contribute, comment, clone, fork, discuss, persevere, review, follow, and grow.'),
                quote('Practice patterns before plug-ins.'),
                paragraph('Software developers encode the patterns of reality.  We should understand patterns to accomplish tasks and have the ability to choose the appropiate tool for any task.  As tools change, it is the skill and privilege of software developers to learn and adapt, as required.'),
                quote('The process of writing quality code must be sustainable in order to be viable in real-world situations.'),
                paragraph('"A developer went out to craft some code.  As he wrote his code, some of it was un-used and became dead code.  Other code was used right away.  But the code did not have tests and soon became impossible to change as requirements evolved.  Other code was added to old production code that was bloated, and not well-maintained.  The legacy code smothered the tender new code and it became un-used.  Still other code was added to a well-maintained code base with numerous well-written tests and quality metrics.  The code grew and was re-factored to be thirty, sixty, and even a hundred times more useful than when it was written."'),
                list('Credits',
                    [
                        '[Nebraska Font by Daniel Heikkinen](http://www.dafont.com/nebraska.font)',
                        '[IcoMoon Icon font](http://www.icomoon.io)',
                        '[SVG Hero Patterns](http://www.heropatterns.com/)',
                        '[highlight.js (syntax highlighting for code blocks)](https://highlightjs.org/)'
                    ]
                ),
                image('../assets/images/cornjs.png', {height: 250})
            ]
        }
    ];
});
<|endoftext|>


<commit_before>import { Meteor } from 'meteor/meteor';
import { Template } from 'meteor/templating';
import { Session } from 'meteor/session';
import { _ } from 'meteor/underscore';
import { Topic } from '/imports/topic';
import { Minutes } from '/imports/minutes';
import { MeetingSeries } from '/imports/meetingseries';
import { Label } from '/imports/label';
import { $ } from 'meteor/jquery';
import { handleError } from '/client/helpers/handleError';
import {createTopic} from './helpers/create-topic';
import {select2search} from '/imports/client/ResponsibleSearch';

Session.setDefault('topicEditTopicId', null);

let _minutesID; // the ID of these minutes
let _meetingSeries; // ATTENTION - this var. is not reactive!

Template.topicEdit.onCreated(function () {
    _minutesID = this.data;
    console.log('Template topicEdit created with minutesID '+_minutesID);
    let aMin = new Minutes(_minutesID);
    _meetingSeries = new MeetingSeries(aMin.parentMeetingSeriesID());
});

let getEditTopic = function() {
    let topicId = Session.get('topicEditTopicId');

    if (_minutesID === null ||  topicId === null) {
        return false;
    }

    return new Topic(_minutesID, topicId);
};

function configureSelect2Responsibles() {
    let selectResponsibles = $('#id_selResponsible');
    selectResponsibles.find('optgroup')     // clear all <option>s
        .remove();
    let delayTime = Meteor.settings.public.isEnd2EndTest ? 0 : 50;

    select2search(selectResponsibles, delayTime, true, _minutesID);

    // select the options that where stored with this topic last time
    let topic = getEditTopic();
    if (topic && topic._topicDoc && topic._topicDoc.responsibles) {
        selectResponsibles.val(topic._topicDoc.responsibles);
    }
    selectResponsibles.trigger('change');
}

function configureSelect2Labels() {
    let aMin = new Minutes(_minutesID);
    let aSeries = aMin.parentMeetingSeries();

    let selectLabels = $('#id_item_selLabels');
    selectLabels.find('option')     // clear all <option>s
        .remove();

    let selectOptions = [];

    aSeries.getAvailableLabels().forEach(label => {
        selectOptions.push ({id: label._id, text: label.name});
    });

    selectLabels.select2({
        placeholder: 'Select...',
        tags: true,                     // Allow freetext adding
        tokenSeparators: [',', ';'],
        data: selectOptions             // push <option>s data
    });

    // select the options that where stored with this topic last time
    let editItem = getEditTopic();
    if (editItem) {
        selectLabels.val(editItem.getLabelsRawArray());
    }
    selectLabels.trigger('change');
}

Template.topicEdit.helpers({
    'getTopicSubject': function() {
        let topic = getEditTopic();
        return (topic) ? topic._topicDoc.subject : '';
    }
});

Template.topicEdit.events({
    'submit #frmDlgAddTopic': async function (evt, tmpl) {
        evt.preventDefault();

        let editTopic = getEditTopic();
        let topicDoc = {};
        if (editTopic) {
            _.extend(topicDoc, editTopic._topicDoc);
        }

        let labels = tmpl.$('#id_item_selLabels').val();
        if (!labels) labels = [];
        let aMinute = new Minutes(_minutesID);
        let aSeries = aMinute.parentMeetingSeries();
        labels = labels.map(labelId => {
            let label = Label.createLabelById(aSeries, labelId);
            if (null === label) {
            // we have no such label -> it's brand new
                label = new Label({name: labelId});
                label.save(aSeries._id);
            }
            return label.getId();
        });

        topicDoc.subject = tmpl.find('#id_subject').value;
        topicDoc.responsibles = $('#id_selResponsible').val();
        topicDoc.labels = labels;

        const aTopic = createTopic(_minutesID, aSeries._id, topicDoc);
        aTopic.save().catch(handleError);
        $('#dlgAddTopic').modal('hide');
    },

    'hidden.bs.modal #dlgAddTopic': function (evt, tmpl) {
        $('#frmDlgAddTopic')[0].reset();
        let subjectNode = tmpl.$('#id_subject');
        subjectNode.parent().removeClass('has-error');

        // reset the session vars to indicate that edit mode has been closed
        Session.set('topicEditTopicId', null);
    },

    'show.bs.modal #dlgAddTopic': function () {
        configureSelect2Responsibles();
        let selectLabels = $('#id_item_selLabels');
        if (selectLabels) {
            selectLabels.val([]).trigger('change');
        }
        configureSelect2Labels();
        let saveButton = $('#btnTopicSave');
        let cancelButton = $('#btnTopicCancel');
        saveButton.prop('disabled',false);
        cancelButton.prop('disabled',false);
    },

    'shown.bs.modal #dlgAddTopic': function (evt, tmpl) {
        $('#dlgAddTopic').find('input').trigger('change');    // ensure new values trigger placeholder animation
        tmpl.find('#id_subject').focus();
        configureSelect2Labels();
    },

    'select2:selecting #id_selResponsible'(evt) {
        console.log('selecting:'+evt.params.args.data.id + '/'+evt.params.args.data.text);
    },

    'select2:select #id_selResponsible'(evt) {
        console.log('select:'+evt.params.data.id + '/'+evt.params.data.text);
        let respId = evt.params.data.id;
        let respName = evt.params.data.text;
        let aUser = Meteor.users.findOne(respId);
        if (! aUser && respId === respName) {    // we have a free-text user here!
            _meetingSeries.addAdditionalResponsible(respName);
            _meetingSeries.save();
        }
    }
});
<commit_msg>fixed the bug with empty responsibles in a 2. minute<commit_after>import { Meteor } from 'meteor/meteor';
import { Template } from 'meteor/templating';
import { Session } from 'meteor/session';
import { _ } from 'meteor/underscore';
import { Topic } from '/imports/topic';
import { Minutes } from '/imports/minutes';
import { MeetingSeries } from '/imports/meetingseries';
import { Label } from '/imports/label';
import { $ } from 'meteor/jquery';
import { handleError } from '/client/helpers/handleError';
import {createTopic} from './helpers/create-topic';
import {select2search} from '/imports/client/ResponsibleSearch';

Session.setDefault('topicEditTopicId', null);

let _minutesID; // the ID of these minutes
let _meetingSeries; // ATTENTION - this var. is not reactive!

Template.topicEdit.onCreated(function () {
    _minutesID = this.data;
    console.log('Template topicEdit created with minutesID '+_minutesID);
    let aMin = new Minutes(_minutesID);
    _meetingSeries = new MeetingSeries(aMin.parentMeetingSeriesID());
});

let getEditTopic = function() {
    let topicId = Session.get('topicEditTopicId');

    if (_minutesID === null ||  topicId === null) {
        return false;
    }

    return new Topic(_minutesID, topicId);
};

function configureSelect2Responsibles() {
    let selectResponsibles = $('#id_selResponsible');
    selectResponsibles.find('option')     // clear all <option>s
        .remove();
    let delayTime = Meteor.settings.public.isEnd2EndTest ? 0 : 50;

    select2search(selectResponsibles, delayTime, true, _minutesID);

    // select the options that where stored with this topic last time
    let topic = getEditTopic();
    if (topic && topic._topicDoc && topic._topicDoc.responsibles) {
        topic._topicDoc.responsibles.forEach(responsibleId => {
            let responsibleUser = Meteor.users.findOne(responsibleId);
            if (!responsibleUser) { //free text user
                responsibleUser = {fullname: responsibleId};
            } else {
                (new Minutes(_minutesID)).formatResponsibles(responsibleUser, 'username', responsibleUser.profile);
            }
            selectResponsibles.append('<option value="' + responsibleId + '" selected="selected">' + responsibleUser.fullname + '</option>');
        });
        selectResponsibles.val(topic._topicDoc.responsibles);
    }
    selectResponsibles.trigger('change');
}

function configureSelect2Labels() {
    let aMin = new Minutes(_minutesID);
    let aSeries = aMin.parentMeetingSeries();

    let selectLabels = $('#id_item_selLabels');
    selectLabels.find('option')     // clear all <option>s
        .remove();

    let selectOptions = [];

    aSeries.getAvailableLabels().forEach(label => {
        selectOptions.push ({id: label._id, text: label.name});
    });

    selectLabels.select2({
        placeholder: 'Select...',
        tags: true,                     // Allow freetext adding
        tokenSeparators: [',', ';'],
        data: selectOptions             // push <option>s data
    });

    // select the options that where stored with this topic last time
    let editItem = getEditTopic();
    if (editItem) {
        selectLabels.val(editItem.getLabelsRawArray());
    }
    selectLabels.trigger('change');
}

Template.topicEdit.helpers({
    'getTopicSubject': function() {
        let topic = getEditTopic();
        return (topic) ? topic._topicDoc.subject : '';
    }
});

Template.topicEdit.events({
    'submit #frmDlgAddTopic': async function (evt, tmpl) {
        evt.preventDefault();

        let editTopic = getEditTopic();
        let topicDoc = {};
        if (editTopic) {
            _.extend(topicDoc, editTopic._topicDoc);
        }

        let labels = tmpl.$('#id_item_selLabels').val();
        if (!labels) labels = [];
        let aMinute = new Minutes(_minutesID);
        let aSeries = aMinute.parentMeetingSeries();
        labels = labels.map(labelId => {
            let label = Label.createLabelById(aSeries, labelId);
            if (null === label) {
            // we have no such label -> it's brand new
                label = new Label({name: labelId});
                label.save(aSeries._id);
            }
            return label.getId();
        });

        topicDoc.subject = tmpl.find('#id_subject').value;
        topicDoc.responsibles = $('#id_selResponsible').val();
        topicDoc.labels = labels;

        const aTopic = createTopic(_minutesID, aSeries._id, topicDoc);
        aTopic.save().catch(handleError);
        $('#dlgAddTopic').modal('hide');
    },

    'hidden.bs.modal #dlgAddTopic': function (evt, tmpl) {
        $('#frmDlgAddTopic')[0].reset();
        let subjectNode = tmpl.$('#id_subject');
        subjectNode.parent().removeClass('has-error');

        // reset the session vars to indicate that edit mode has been closed
        Session.set('topicEditTopicId', null);
    },

    'show.bs.modal #dlgAddTopic': function () {
        configureSelect2Responsibles();
        let selectLabels = $('#id_item_selLabels');
        if (selectLabels) {
            selectLabels.val([]).trigger('change');
        }
        configureSelect2Labels();
        let saveButton = $('#btnTopicSave');
        let cancelButton = $('#btnTopicCancel');
        saveButton.prop('disabled',false);
        cancelButton.prop('disabled',false);
    },

    'shown.bs.modal #dlgAddTopic': function (evt, tmpl) {
        $('#dlgAddTopic').find('input').trigger('change');    // ensure new values trigger placeholder animation
        tmpl.find('#id_subject').focus();
        configureSelect2Labels();
    },

    'select2:selecting #id_selResponsible'(evt) {
        console.log('selecting:'+evt.params.args.data.id + '/'+evt.params.args.data.text);
    },

    'select2:select #id_selResponsible'(evt) {
        console.log('select:'+evt.params.data.id + '/'+evt.params.data.text);
        let respId = evt.params.data.id;
        let respName = evt.params.data.text;
        let aUser = Meteor.users.findOne(respId);
        if (! aUser && respId === respName) {    // we have a free-text user here!
            _meetingSeries.addAdditionalResponsible(respName);
            _meetingSeries.save();
        }
    }
});
<|endoftext|>


<commit_before>module.exports = function (RED) {
  var spawn = require('child_process').spawn;
  var util = require('util');

  function indentLines(fnCode, depth) {
    return fnCode.split('\n').map((line) => Array(depth).join(' ') + line).join('\n')
  }

  function spawnFn(self) {
    self.child = spawn('python', ['-uc', self.func.code], {stdio: ['pipe', 'pipe', 'pipe', 'ipc']});
    self.child.stdout.on('data', function (data) {
      self.log(data.toString());
    });
    self.child.stderr.on('data', function (data) {
      self.error(data.toString());
    });
    self.child.on('close', function (exitCode) {
      if (exitCode) {
        self.error(`Python Function process exited with code ${exitCode}`);
        if (self.func.attempts) {
          spawnFn(self);
          self.func.attempts--;
        } else {
          self.error(`Function '${self.name}' has failed more than 10 times. Fix it and deploy again`)
          self.status({fill: 'red', shape: 'dot', text: 'Stopped, see debug panel'});
        }
      }
    });
    self.child.on('message', function (response) {
      switch (response.ctx) {
        case 'send':
          sendResults(self, response.msgid, response.value);
          break;
        case 'log':
        case 'warn':
        case 'error':
        case 'status':
          self[response.ctx].apply(self, response.value);
          break;
        default:
          throw new Error(`Don't know what to do with ${response.ctx}`);
      }
    });
    self.log(`Python function '${self.name}' running on PID ${self.child.pid}`);
    self.status({fill: 'green', shape: 'dot', text: 'Running'});
  }

  function sendResults(self, _msgid, msgs) {
      if (msgs == null) {
          return;
      } else if (!util.isArray(msgs)) {
          msgs = [msgs];
      }
      var msgCount = 0;
      for (var m=0;m<msgs.length;m++) {
          if (msgs[m]) {
              if (util.isArray(msgs[m])) {
                  for (var n=0; n < msgs[m].length; n++) {
                      msgs[m][n]._msgid = _msgid;
                      msgCount++;
                  }
              } else {
                  msgs[m]._msgid = _msgid;
                  msgCount++;
              }
          }
      }
      if (msgCount>0) {
          self.send(msgs);
      }
  }

  function PythonFunction(config) {
    var self = this;
    RED.nodes.createNode(self, config);
    self.name = config.name;
    self.func = {
      code: `
import os
import json

channel = os.fdopen(3, "r+")

class Msg(object):
    SEND = 'send'
    LOG = 'log'
    WARN = 'warn'
    ERROR = 'error'
    STATUS = 'status'

    def __init__(self, ctx, value, msgid):
        self.ctx = ctx
        self.value = value
        self.msgid = msgid

    def dumps(self):
        return json.dumps(vars(self)) + "\\n"

    @classmethod
    def loads(cls, json_string):
        return cls(**json.loads(json_string))

class Node(object):
    def __init__(self, msgid, channel):
        self.__msgid = msgid
        self.__channel = channel

    def send(self, msg):
        msg = Msg(Msg.SEND, msg, self.__msgid)
        self.send_to_node(msg)

    def log(self, *args):
        msg = Msg(Msg.LOG, args, self.__msgid)
        self.send_to_node(msg)

    def warn(self, *args):
        msg = Msg(Msg.WARN, args, self.__msgid)
        self.send_to_node(msg)

    def error(self, *args):
        msg = Msg(Msg.ERROR, args, self.__msgid)
        self.send_to_node(msg)

    def status(self, *args):
        msg = Msg(Msg.STATUS, args, self.__msgid)
        self.send_to_node(msg)

    def send_to_node(self, msg):
        self.__channel.write(msg.dumps())

def python_function(msg):
` + indentLines(config.func, 4) +
`
while True:
    raw_msg = channel.readline()
    if not raw_msg:
        raise RuntimeError('Received EOF!')
    msg = json.loads(raw_msg)
    msgid = msg["_msgid"]
    node = Node(msgid, channel)
    res_msgs = python_function(msg)
    node.send(res_msgs)
`,
      attempts: 10
    };
    spawnFn(self);
    self.on('input', function(msg) {
      var cache = [];
      jsonMsg = JSON.stringify(msg, function(key, value) {
          if (typeof value === 'object' && value !== null) {
              if (cache.indexOf(value) !== -1) {
                  // Circular reference found, discard key
                  return;
              }
              // Store value in our collection
              cache.push(value);
          }
          return value;
      });
      cache = null; // Enable garbage collection
      self.child.send(JSON.parse(jsonMsg));
    });
    self.on('close', function () {
      self.child.kill();
    });
  }
  RED.nodes.registerType('python-function', PythonFunction);
};
<commit_msg>Update python-function.js<commit_after>module.exports = function (RED) {
  var spawn = require('child_process').spawn;
  var util = require('util');

  function indentLines(fnCode, depth) {
    return fnCode.split('\n').map((line) => Array(depth).join(' ') + line).join('\n')
  }

  function spawnFn(self) {
    self.child = spawn('python', ['-uc', self.func.code], {stdio: ['pipe', 'pipe', 'pipe', 'ipc']});
    self.child.stdout.on('data', function (data) {
      self.log(data.toString());
    });
    self.child.stderr.on('data', function (data) {
      self.error(data.toString());
    });
    self.child.on('close', function (exitCode) {
      if (exitCode) {
        self.error(`Python Function process exited with code ${exitCode}`);
        if (self.func.attempts) {
          spawnFn(self);
          self.func.attempts--;
        } else {
          self.error(`Function '${self.name}' has failed more than 10 times. Fix it and deploy again`)
          self.status({fill: 'red', shape: 'dot', text: 'Stopped, see debug panel'});
        }
      }
    });
    self.child.on('message', function (response) {
      switch (response.ctx) {
        case 'send':
          sendResults(self, response.msgid, response.value);
          break;
        case 'log':
        case 'warn':
        case 'error':
        case 'status':
          self[response.ctx].apply(self, response.value);
          break;
        default:
          throw new Error(`Don't know what to do with ${response.ctx}`);
      }
    });
    self.log(`Python function '${self.name}' running on PID ${self.child.pid}`);
    self.status({fill: 'green', shape: 'dot', text: 'Running'});
  }

  function sendResults(self, _msgid, msgs) {
      if (msgs == null) {
          return;
      } else if (!util.isArray(msgs)) {
          msgs = [msgs];
      }
      var msgCount = 0;
      for (var m=0;m<msgs.length;m++) {
          if (msgs[m]) {
              if (util.isArray(msgs[m])) {
                  for (var n=0; n < msgs[m].length; n++) {
                      msgs[m][n]._msgid = _msgid;
                      msgCount++;
                  }
              } else {
                  msgs[m]._msgid = _msgid;
                  msgCount++;
              }
          }
      }
      if (msgCount>0) {
          self.send(msgs);
      }
  }

  function PythonFunction(config) {
    var self = this;
    RED.nodes.createNode(self, config);
    self.name = config.name;
    self.func = {
      code: `
import os
import json

channel = os.fdopen(3, "r+", buffering=0)

class Msg(object):
    SEND = 'send'
    LOG = 'log'
    WARN = 'warn'
    ERROR = 'error'
    STATUS = 'status'

    def __init__(self, ctx, value, msgid):
        self.ctx = ctx
        self.value = value
        self.msgid = msgid

    def dumps(self):
        return json.dumps(vars(self)) + "\\n"

    @classmethod
    def loads(cls, json_string):
        return cls(**json.loads(json_string))

class Node(object):
    def __init__(self, msgid, channel):
        self.__msgid = msgid
        self.__channel = channel

    def send(self, msg):
        msg = Msg(Msg.SEND, msg, self.__msgid)
        self.send_to_node(msg)

    def log(self, *args):
        msg = Msg(Msg.LOG, args, self.__msgid)
        self.send_to_node(msg)

    def warn(self, *args):
        msg = Msg(Msg.WARN, args, self.__msgid)
        self.send_to_node(msg)

    def error(self, *args):
        msg = Msg(Msg.ERROR, args, self.__msgid)
        self.send_to_node(msg)

    def status(self, *args):
        msg = Msg(Msg.STATUS, args, self.__msgid)
        self.send_to_node(msg)

    def send_to_node(self, msg):
        self.__channel.write(msg.dumps())

def python_function(msg):
` + indentLines(config.func, 4) +
`
while True:
    raw_msg = channel.readline()
    if not raw_msg:
        raise RuntimeError('Received EOF!')
    msg = json.loads(raw_msg)
    msgid = msg["_msgid"]
    node = Node(msgid, channel)
    res_msgs = python_function(msg)
    node.send(res_msgs)
`,
      attempts: 10
    };
    spawnFn(self);
    self.on('input', function(msg) {
      var cache = [];
      jsonMsg = JSON.stringify(msg, function(key, value) {
          if (typeof value === 'object' && value !== null) {
              if (cache.indexOf(value) !== -1) {
                  // Circular reference found, discard key
                  return;
              }
              // Store value in our collection
              cache.push(value);
          }
          return value;
      });
      cache = null; // Enable garbage collection
      self.child.send(JSON.parse(jsonMsg));
    });
    self.on('close', function () {
      self.child.kill();
    });
  }
  RED.nodes.registerType('python3-function', PythonFunction);
};
<|endoftext|>


<commit_before>//Submote by Zarlach
$(document).ready(function()
{
    //Global vars
    var subMotes    = {};           // subMotes[emote] = source
    var subEmotes   = {};           // subEmotes[emote] = id
    var bttvEmotes  = {};           // bttvEmotes[emote] = source

    //Check if emotes are already stored locally
    if (localStorage.getItem("subEmotes") === null)
        loadSubEmotes();
    else
    {
        subEmotes           = JSON.parse(localStorage.getItem("subEmotes"));
        var genDate         = Date.parse(subEmotes['generated_at']);
        var currentDate     = new Date();
        var expirationDate  = genDate+3600*1000/2;  //30 minutes

        //Check if expired
        if(currentDate.getTime() >= expirationDate)
        {
            loadSubEmotes();
        }
    }

    if (localStorage.getItem("bttvEmotes") === null) loadBetterTTVemotes();
    else bttvEmotes = JSON.parse(localStorage.getItem("bttvEmotes"));

    /**
     * Check for new chat messages
     */
    $('.chat-lines').on('DOMNodeInserted', function(e)
    {
        var element = e.target;
        var line    = $(element).last();

        //Check if it's a message
        if (line.last().length
            && line.find('.message').last().html() !== undefined
            && line.not('.admin'))
        {
            var message = line.find('.message').last();
            var badges  = line.find('.badges');
            var from    = line.find('.from').html().toLowerCase();

            //Parse message
            if(from !== 'jtv')
            {
                parseMessage(message, subEmotes);
                parseMessage(message, bttvEmotes);
            }

            //Append turbo
            if(from === 'zarlach' && badges.last().find('.submote-dev').length === 0)
            {
                $(badges).last().append('

');
                $(badges).last().find('.submote-dev').css({
                    'width': '18px',
                    'height': '18px',
                    'background': 'url("https://raw.githubusercontent.com/Zarlach/Submote/master/images/dev-badge.png")',
                    'background-repat': 'no-repeat',
                    'background-size': '18px 18px'
                });
            }
        }
    });

    /**
     * Load subscriber emotes from web
     */
    function loadSubEmotes()
    {
        $.getJSON('https:twitchemotes.com/api_cache/v2/subscriber.json', function(data)
        {
            $.each(data['channels'], function(channel, properties)
            {
                $.each(properties['emotes'], function(list, emote){
                    subEmotes[emote['code']] = emote['image_id'];
                });
            });

            $.each(data['meta'], function(key, val){
                subEmotes[key] = val;
            });
        })
        .done(function()
        {
            subEmotes['provider'] = 'twitch';
            localStorage.setItem("subEmotes", JSON.stringify(subEmotes));
        })
        .fail(function()
        {
            //sendError('Submote could not load sub emotes');
        });
    }

    /**
     * Load BetterTTV emotes from web
     */
    function loadBetterTTVemotes()
    {
        $.getJSON('https://cdn.betterttv.net/emotes/emotes.json', function(data){
            $.each(data, function(key, value){
                bttvEmotes[value['regex'].replace('\\', '')] = value['url'].replace('//', 'https://');
            });
        })
        .done(function(){
            bttvEmotes['provider'] = 'betterttv';
            localStorage.setItem("bttvEmotes", JSON.stringify(bttvEmotes));
        })
        .fail(function(){
            //sendError('Submote could not load BetterTTV emotes');
        });
    }

    /**
     * Scan message and replace words with emotes!
     *
     * @param  {element} ele        Message element
     * @param  {object} list        List of emotes
     * @param  {string} provider    Emote provider
     */
    function parseMessage(ele, list)
    {
        var msg         = ele.html();
        var split       = msg.replace(/(<([^>]+)>)/ig,'').trim().split(' ');
            split       = $.grep(split,function(n){ return(n) });
        var words       = [];
        var provider    = list['provider'];
        var regex;
        var word;

        //Remove duplicates
        $.each(split, function(i, el){
            if($.inArray(el, words) === -1) words.push(el);
        });

        //
        for (var i = 0; i <= words.length; i++) {
            word = words[i];

            if(validateFilter(word, list, provider))
            {
                regex = new RegExp('\\b'+word+'\\b(?=[^"]*(?:"[^"]*"[^"]*)*$)', 'g');
                msg = msg.replace(regex, generateEmoteImage(word, list[word], provider));
            }
        }

        ele.html(msg.trim());
    }

    /**
     * Generate emote image HTML tag.
     *
     * @param  {int} id Emote ID
     * @return {string}    HTML img tag
     */
    function generateEmoteImage(emote, source, provider)
    {
        switch(provider){
            case 'twitch':
                return '';
                break;
            case 'betterttv':
                return '';
                break;
        }
    }

    /**
     * Run selected word through a filter
     *
     * @param  {string}     word     Selected word
     * @param  {object}     list     Emote list
     * @param  {string}     provider Emote provider
     * @return {boolean}    Returns whether or not the word passed the filter
     */
    function validateFilter(word, list, provider)
    {
        var regex = new RegExp('^(?=.*[a-z])(?=.*[A-Z]).+$');

        //General filter
        if(list[word] === undefined
            || !regex.test(word)
            || word === 'provider'
            || word === 'generated_at'
            || word.length === 0)
            return false;

        //Provider specific filter
        switch(provider){
            case 'twitch':
                if (word[0] !== word[0].toLowerCase()
                    || word === 'double'
                    || word === 'triple'
                    || word === 'quadra'
                    || word === 'penta'
                    || word === 'kill'
                    || word === 'snap')
                    return false;

                break;
            case 'betterttv':

                break;
        }

        return true;
    }

    /**
     * In case something fails, let the user know!
     *
     * @param  {string} message Error description
     */
    function sendError(message)
    {
        $('body').append('

'+message+'
Please refresh

');
    }
});<commit_msg>Syntax error 2<commit_after>//Submote by Zarlach
$(document).ready(function()
{
    //Global vars
    var subMotes    = {};           // subMotes[emote] = source
    var subEmotes   = {};           // subEmotes[emote] = id
    var bttvEmotes  = {};           // bttvEmotes[emote] = source

    //Check if emotes are already stored locally
    if (localStorage.getItem("subEmotes") === null)
        loadSubEmotes();
    else
    {
        subEmotes           = JSON.parse(localStorage.getItem("subEmotes"));
        var genDate         = Date.parse(subEmotes['generated_at']);
        var currentDate     = new Date();
        var expirationDate  = genDate+3600*1000/2;  //30 minutes

        //Check if expired
        if(currentDate.getTime() >= expirationDate)
        {
            loadSubEmotes();
        }
    }

    if (localStorage.getItem("bttvEmotes") === null) loadBetterTTVemotes();
    else bttvEmotes = JSON.parse(localStorage.getItem("bttvEmotes"));

    /**
     * Check for new chat messages
     */
    $('.chat-lines').on('DOMNodeInserted', function(e)
    {
        var element = e.target;
        var line    = $(element).last();

        //Check if it's a message
        if (line.last().length
            && line.find('.message').last().html() !== undefined
            && line.not('.admin'))
        {
            var message = line.find('.message').last();
            var badges  = line.find('.badges');
            var from    = line.find('.from').html().toLowerCase();

            //Parse message
            if(from !== 'jtv')
            {
                parseMessage(message, subEmotes);
                parseMessage(message, bttvEmotes);
            }

            //Append turbo
            if(from === 'zarlach' && badges.last().find('.submote-dev').length === 0)
            {
                $(badges).last().append('

');
                $(badges).last().find('.submote-dev').css({
                    'width': '18px',
                    'height': '18px',
                    'background-image': 'url("https://raw.githubusercontent.com/Zarlach/Submote/master/images/dev-badge.png")',
                    'background-repat': 'no-repeat',
                    'background-size': '18px 18px'
                });
            }
        }
    });

    /**
     * Load subscriber emotes from web
     */
    function loadSubEmotes()
    {
        $.getJSON('https:twitchemotes.com/api_cache/v2/subscriber.json', function(data)
        {
            $.each(data['channels'], function(channel, properties)
            {
                $.each(properties['emotes'], function(list, emote){
                    subEmotes[emote['code']] = emote['image_id'];
                });
            });

            $.each(data['meta'], function(key, val){
                subEmotes[key] = val;
            });
        })
        .done(function()
        {
            subEmotes['provider'] = 'twitch';
            localStorage.setItem("subEmotes", JSON.stringify(subEmotes));
        })
        .fail(function()
        {
            //sendError('Submote could not load sub emotes');
        });
    }

    /**
     * Load BetterTTV emotes from web
     */
    function loadBetterTTVemotes()
    {
        $.getJSON('https://cdn.betterttv.net/emotes/emotes.json', function(data){
            $.each(data, function(key, value){
                bttvEmotes[value['regex'].replace('\\', '')] = value['url'].replace('//', 'https://');
            });
        })
        .done(function(){
            bttvEmotes['provider'] = 'betterttv';
            localStorage.setItem("bttvEmotes", JSON.stringify(bttvEmotes));
        })
        .fail(function(){
            //sendError('Submote could not load BetterTTV emotes');
        });
    }

    /**
     * Scan message and replace words with emotes!
     *
     * @param  {element} ele        Message element
     * @param  {object} list        List of emotes
     * @param  {string} provider    Emote provider
     */
    function parseMessage(ele, list)
    {
        var msg         = ele.html();
        var split       = msg.replace(/(<([^>]+)>)/ig,'').trim().split(' ');
            split       = $.grep(split,function(n){ return(n) });
        var words       = [];
        var provider    = list['provider'];
        var regex;
        var word;

        //Remove duplicates
        $.each(split, function(i, el){
            if($.inArray(el, words) === -1) words.push(el);
        });

        //
        for (var i = 0; i <= words.length; i++) {
            word = words[i];

            if(validateFilter(word, list, provider))
            {
                regex = new RegExp('\\b'+word+'\\b(?=[^"]*(?:"[^"]*"[^"]*)*$)', 'g');
                msg = msg.replace(regex, generateEmoteImage(word, list[word], provider));
            }
        }

        ele.html(msg.trim());
    }

    /**
     * Generate emote image HTML tag.
     *
     * @param  {int} id Emote ID
     * @return {string}    HTML img tag
     */
    function generateEmoteImage(emote, source, provider)
    {
        switch(provider){
            case 'twitch':
                return '';
                break;
            case 'betterttv':
                return '';
                break;
        }
    }

    /**
     * Run selected word through a filter
     *
     * @param  {string}     word     Selected word
     * @param  {object}     list     Emote list
     * @param  {string}     provider Emote provider
     * @return {boolean}    Returns whether or not the word passed the filter
     */
    function validateFilter(word, list, provider)
    {
        var regex = new RegExp('^(?=.*[a-z])(?=.*[A-Z]).+$');

        //General filter
        if(list[word] === undefined
            || !regex.test(word)
            || word === 'provider'
            || word === 'generated_at'
            || word.length === 0)
            return false;

        //Provider specific filter
        switch(provider){
            case 'twitch':
                if (word[0] !== word[0].toLowerCase()
                    || word === 'double'
                    || word === 'triple'
                    || word === 'quadra'
                    || word === 'penta'
                    || word === 'kill'
                    || word === 'snap')
                    return false;

                break;
            case 'betterttv':

                break;
        }

        return true;
    }

    /**
     * In case something fails, let the user know!
     *
     * @param  {string} message Error description
     */
    function sendError(message)
    {
        $('body').append('

'+message+'
Please refresh

');
    }
});<|endoftext|>


<commit_before>chrome.runtime.onMessage.addListener(function (message, sender, sendResponse) {
    if (message.text && (message.text === "add_calendar")) {
        setTimeout(function () {
            addCalendar();
        }, 1);
    }
});

var addCalendar = function () {
    if (document.URL.indexOf('/am=t') < 0) {
        alert('Please open Details');
        return;
    }
    var e = encodeURIComponent;
    var q = document.querySelectorAll.bind(document);
    var text = function () {
        var wp = q('.waypoint-address:not([style*="display"]) .first-line span');
        var from = wp[0].innerText;
        var to = wp[wp.length - 1].innerText;
        return e(from + ' -> ' + to);
    };
    var details = function () {
        return e(document.URL);
    };
    var dates = function () {
        var z = function(x) {
            return ('00' + x).slice(-2);
        };
        var utcMsec = function (msec) {
            var d = new Date(msec);
            return d.getUTCFullYear() + z(d.getUTCMonth() + 1) + z(d.getUTCDate()) + 'T' + z(d.getUTCHours()) + z(d.getUTCMinutes()) + '00Z';
        };
        var parseTransitTime = function (span) {
            var h = parseInt(span.innerText.split(' ')[0].split(':')[0], 10);
            var m = parseInt(span.innerText.split(' ')[0].split(':')[1], 10);
            var ampm = span.innerText.split(' ')[1];
            if (ampm === 'PM' && h < 12) {
                h += 12;
            }
            return { hour: h, minutes: m };
        };
        var transitTime = q('.cards-directions-transit-trip-time span');
        var start = parseTransitTime(transitTime[0]);
        var end = parseTransitTime(transitTime[1]);
        var duration = q('.cards-directions-duration-value')[0].innerText;
        var today = new Date();
        var todayLabel = q('.date-input')[0].innerText;
        if (todayLabel.match(/(\d+)\s*月\s*(\d+)\s*日/)) {
            var monthNames = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
            todayLabel = monthNames[parseInt(RegExp.$1, 10) - 1] + ' ' + RegExp.$2;
        }
        var startMsec = Date.parse(todayLabel + ' ' + today.getFullYear() + ' ' + start.hour + ':' + start.minutes + ':00');
        if (!startMsec) return;
        if (startMsec < today.getTime()) {
        var sd = new Date(startMsec);
            sd.setMonth(sd.getMonth() + 1);
            startMsec = sd.getTime();
        }
        if (duration.match(/(\d+)\s*h\s*(\d+)\s*min/) || duration.match(/(\d+)\s*時間\s*(\d+)\s*分/)) {
            var endMsec = startMsec + (parseInt(RegExp.$1) * 60 + parseInt(RegExp.$2)) * 60 * 1000;
        } else if (duration.match(/(\d+)\s*min/) || duration.match(/(\d+)\s*分/)) {
            var endMsec = startMsec + parseInt(RegExp.$1) * 60 * 1000;
        }
        return utcMsec(startMsec) + '/' + utcMsec(endMsec);
    };
    var d = dates();
    if (d) {
        var href = 'http://www.google.com/calendar/event?action=TEMPLATE&text=' + text() + '&details=' + details() + '&dates='+ d + '&location=&trp=true';
        window.open(href);
    }
};

<commit_msg>format timezone but not work on google calendar<commit_after>chrome.runtime.onMessage.addListener(function (message, sender, sendResponse) {
    if (message.text && (message.text === "add_calendar")) {
        chrome.storage.sync.get({
            timeZone: Math.floor((new Date()).getTimezoneOffset() / 60),
	}, function (items) {
            addCalendar(items.timeZone);
        });
    }
});

var addCalendar = function (timeZoneOffsetHour) {
    if (document.URL.indexOf('/am=t') < 0) {
        alert('Please open Details');
        return;
    }
    var e = encodeURIComponent;
    var q = document.querySelectorAll.bind(document);
    var text = function () {
        var wp = q('.waypoint-address:not([style*="display"]) .first-line span');
        var from = wp[0].innerText;
        var to = wp[wp.length - 1].innerText;
        return e(from + ' -> ' + to);
    };
    var details = function () {
        return e(document.URL);
    };
    var dates = function () {
        var z = function(x) {
	    return ('00' + x).slice(-2);
        };
        var zz = function(x) {
            var sign = '+';
	    if (x < 0) sign = '-';
	    return sign + ('00' + x).slice(-2);
        };
	var timeZone = function (hour) {
            hour = parseInt(hour, 10);
	    if (hour === 0) return 'Z';
	    var n = hour.toFixed(2).split('.');
	    return zz(n[0]) + ':' + z(n[1] / 100 * 60);
        };
        var utcMsec = function (msec) {
            var d = new Date(msec);
            return d.getUTCFullYear() + z(d.getUTCMonth() + 1) + z(d.getUTCDate()) + 'T' + z(d.getUTCHours()) + z(d.getUTCMinutes()) + '00' + timeZone(timeZoneOffsetHour);
        };
        var parseTransitTime = function (span) {
            var h = parseInt(span.innerText.split(' ')[0].split(':')[0], 10);
            var m = parseInt(span.innerText.split(' ')[0].split(':')[1], 10);
            var ampm = span.innerText.split(' ')[1];
            if (ampm === 'PM' && h < 12) {
                h += 12;
            }
            return { hour: h, minutes: m };
        };
        var transitTime = q('.cards-directions-transit-trip-time span');
        var start = parseTransitTime(transitTime[0]);
        var end = parseTransitTime(transitTime[1]);
        var duration = q('.cards-directions-duration-value')[0].innerText;
        var today = new Date();
        var todayLabel = q('.date-input')[0].innerText;
        if (todayLabel.match(/(\d+)\s*月\s*(\d+)\s*日/)) {
            var monthNames = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
            todayLabel = monthNames[parseInt(RegExp.$1, 10) - 1] + ' ' + RegExp.$2;
        }
        var startMsec = Date.parse(todayLabel + ' ' + today.getFullYear() + ' ' + start.hour + ':' + start.minutes + ':00');
        if (!startMsec) return;
        if (startMsec < today.getTime()) {
        var sd = new Date(startMsec);
            sd.setMonth(sd.getMonth() + 1);
            startMsec = sd.getTime();
        }
        if (duration.match(/(\d+)\s*h\s*(\d+)\s*min/) || duration.match(/(\d+)\s*時間\s*(\d+)\s*分/)) {
            var endMsec = startMsec + (parseInt(RegExp.$1) * 60 + parseInt(RegExp.$2)) * 60 * 1000;
        } else if (duration.match(/(\d+)\s*min/) || duration.match(/(\d+)\s*分/)) {
            var endMsec = startMsec + parseInt(RegExp.$1) * 60 * 1000;
        }
        return utcMsec(startMsec) + '/' + utcMsec(endMsec);
    };
    var d = dates();
    if (d) {
        var href = 'http://www.google.com/calendar/event?action=TEMPLATE&text=' + text() + '&details=' + details() + '&dates='+ d + '&location=&trp=true';
        window.open(href);
    }
};

<|endoftext|>


<commit_before>var Utils = require('./utils')

module.exports = (function() {
  var QueryInterface = function(sequelize) {
    this.sequelize = sequelize
    this.QueryGenerator = require('./connectors/' + this.sequelize.options.connector + '/query-generator')
  }
  Utils.addEventEmitter(QueryInterface)

  QueryInterface.prototype.createTable = function(tableName, attributes, options) {
    var _query = query.call(this, this.QueryGenerator.createTableQuery(tableName, attributes, options))
      , self   = this

    _query
      .success(function(){ self.emit('createTable', null) })
      .error(function(err){ self.emit('createTable', err)})

    return _query
  }

  QueryInterface.prototype.dropTable = function(tableName) {
    var _query = query.call(this, this.QueryGenerator.dropTableQuery(tableName))
      , self   = this

    _query
      .success(function(){ self.emit('dropTable', null) })
      .error(function(err){ self.emit('dropTable', err)})

    return _query
  }

  QueryInterface.prototype.dropAllTables = function() {
    var self = this

    return new Utils.CustomEventEmitter(function(emitter) {
      var chainer = new Utils.QueryChainer()

      self.showAllTables().success(function(tableNames) {
        tableNames.forEach(function(tableName) {
          chainer.add(self.sequelize.getQueryInterface().dropTable(tableName))
        })
        chainer
          .run()
          .success(function() {
            self.emit('dropAllTables', null)
            emitter.emit('success', null)
          })
          .error(function(err) {
            self.emit('dropAllTables', err)
            emitter.emit('failure', err)
          })
      }).error(function(err) {
        self.emit('dropAllTables', err)
        emitter.emit('failure', err)
      })
    }).run()
  }

  QueryInterface.prototype.renameTable = function(before, after) {
    var _query = query.call(this, this.QueryGenerator.renameTableQuery(before, after))
      , self   = this

    _query
      .success(function(){ self.emit('renameTable', null) })
      .error(function(err){ self.emit('renameTable', err)})

    return _query

  }

  QueryInterface.prototype.showAllTables = function() {
    var self = this

    return new Utils.CustomEventEmitter(function(emitter) {
      self.sequelize.query('SHOW TABLES;', null, { raw: true }).success(function(data) {
        var tableNames = Sequelize.Utils._.map(data, function(hash) {
          return Sequelize.Utils._.values(hash)
        })

        self.emit('showAllTables', null)
        emitter.emit('success', Sequelize.Utils._.flatten(tableNames))
      }).error(function(err) {
        self.emit('showAllTables', err)
        emitter.emit('failure', err)
      })
    }).run()
  }

  QueryInterface.prototype.addColumn = function() {

  }

  QueryInterface.prototype.removeColumn = function() {

  }

  QueryInterface.prototype.changeColumn = function() {

  }

  QueryInterface.prototype.renameColumn = function() {

  }

  QueryInterface.prototype.addIndex = function() {

  }

  QueryInterface.prototype.removeIndex = function() {

  }

  QueryInterface.prototype.insert = function(model, tableName, values) {
    var self = this

    return new Utils.CustomEventEmitter(function(emitter) {
      query
        .call(self, self.QueryGenerator.insertQuery(tableName, values), model)
        .success(function(obj) {
          self.emit('insert', null)

          obj.isNewRecord = false
          emitter.emit('success', obj)
        })
        .error(function(err) {
          self.emit('insert', err)
          emitter.emit('failure', err)
        })
    }).run()
  }

  QueryInterface.prototype.update = function(model, tableName, values, identifier) {
    var sql    = this.QueryGenerator.updateQuery(tableName, values, identifier)
      , _query = query.call(this, sql, model)
      , self   = this

    _query
      .success(function(){ self.emit('update', null) })
      .error(function(err){ self.emit('update', err)})

    return _query
  }

  QueryInterface.prototype.delete = function(model, tableName, identifier) {
    var sql    = this.QueryGenerator.deleteQuery(tableName, identifier)
      , _query = query.call(this, sql, model)
      , self   = this

    _query
      .success(function(){ self.emit('delete', null) })
      .error(function(err){ self.emit('delete', err)})

    return _query
  }

  QueryInterface.prototype.select = function(factory, tableName, options, queryOptions) {
    queryOptions = queryOptions || {}

    var sql    = this.QueryGenerator.selectQuery(tableName, options)
      , _query = query.call(this, sql, factory, queryOptions)
      , self   = this

    _query
      .success(function(){ self.emit('select', null) })
      .error(function(err){ self.emit('select', err)})

    return _query
  }

  QueryInterface.prototype.rawSelect = function(tableName, options, attributeSelector) {
    var self = this

    if(attributeSelector == undefined)
      throw new Error('Please pass an attribute selector!')

    return new Utils.CustomEventEmitter(function(emitter) {
      var sql    = self.QueryGenerator.selectQuery(tableName, options)
        , _query = query.call(self, sql, null, { plain: true, raw: true })

      _query.success(function(data) {
        self.emit('rawSelect', null)
        emitter.emit('success', data[attributeSelector])
      }).error(function(err) {
        self.emit('rawSelect', err)
        emitter.emit('failure', err)
      })
    }).run()
  }

  // private

  var query = function() {
    var args = Utils._.map(arguments, function(arg, _) { return arg })

    // add this as callee via the second argument
    if(arguments.length == 1) args.push(this)

    return this.sequelize.query.apply(this.sequelize, args)
  }

  return QueryInterface
})()
<commit_msg>describeTable + addColumn<commit_after>var Utils     = require('./utils')
  , DataTypes = require('./data-types')

module.exports = (function() {
  var QueryInterface = function(sequelize) {
    this.sequelize = sequelize
    this.QueryGenerator = require('./connectors/' + this.sequelize.options.connector + '/query-generator')
  }
  Utils.addEventEmitter(QueryInterface)

  QueryInterface.prototype.createTable = function(tableName, attributes, options) {
    var _query = query.call(this, this.QueryGenerator.createTableQuery(tableName, attributes, options))
      , self   = this

    _query
      .success(function(){ self.emit('createTable', null) })
      .error(function(err){ self.emit('createTable', err)})

    return _query
  }

  QueryInterface.prototype.dropTable = function(tableName) {
    var _query = query.call(this, this.QueryGenerator.dropTableQuery(tableName))
      , self   = this

    _query
      .success(function(){ self.emit('dropTable', null) })
      .error(function(err){ self.emit('dropTable', err)})

    return _query
  }

  QueryInterface.prototype.dropAllTables = function() {
    var self = this

    return new Utils.CustomEventEmitter(function(emitter) {
      var chainer = new Utils.QueryChainer()

      self.showAllTables().success(function(tableNames) {
        tableNames.forEach(function(tableName) {
          chainer.add(self.sequelize.getQueryInterface().dropTable(tableName))
        })
        chainer
          .run()
          .success(function() {
            self.emit('dropAllTables', null)
            emitter.emit('success', null)
          })
          .error(function(err) {
            self.emit('dropAllTables', err)
            emitter.emit('failure', err)
          })
      }).error(function(err) {
        self.emit('dropAllTables', err)
        emitter.emit('failure', err)
      })
    }).run()
  }

  QueryInterface.prototype.renameTable = function(before, after) {
    var _query = query.call(this, this.QueryGenerator.renameTableQuery(before, after))
      , self   = this

    _query
      .success(function(){ self.emit('renameTable', null) })
      .error(function(err){ self.emit('renameTable', err)})

    return _query

  }

  QueryInterface.prototype.showAllTables = function() {
    var self = this

    return new Utils.CustomEventEmitter(function(emitter) {
      self.sequelize.query('SHOW TABLES;', null, { raw: true }).success(function(data) {
        var tableNames = Sequelize.Utils._.map(data, function(hash) {
          return Sequelize.Utils._.values(hash)
        })

        self.emit('showAllTables', null)
        emitter.emit('success', Sequelize.Utils._.flatten(tableNames))
      }).error(function(err) {
        self.emit('showAllTables', err)
        emitter.emit('failure', err)
      })
    }).run()
  }

  QueryInterface.prototype.describeTable = function(tableName) {
    var self = this

    return new Utils.CustomEventEmitter(function(emitter) {
      self.sequelize.query('DESCRIBE `' + tableName + '`;', null, { raw: true }).success(function(data) {
        emitter.emit('success', data)
      }).error(function(err) {
        emitter.emit('failure', err)
      })
    }).run()
  }

  QueryInterface.prototype.addColumn = function(tableName, attributeName, dataTypeOrOptions) {
    var attributes = {}

    if(Utils._.values(DataTypes).indexOf(dataTypeOrOptions) > -1)
      attributes[attributeName] = { type: dataTypeOrOptions, allowNull: false }
    else
      attributes[attributeName] = dataTypeOrOptions

    var options = Utils.simplifyAttributes(attributes)
      , query   = this.QueryGenerator.addColumnQuery(tableName, options)
      , self    = this

    return new Utils.CustomEventEmitter(function(emitter) {
      self.sequelize.query(query).success(function() {
        self.emit('addColumn', null)
        emitter.emit('success', null)
      }).error(function(err) {
        self.emit('addColumn', err)
        emitter.emit('failure', err)
      })
    }).run()
  }

  QueryInterface.prototype.removeColumn = function() {

  }

  QueryInterface.prototype.changeColumn = function() {

  }

  QueryInterface.prototype.renameColumn = function() {

  }

  QueryInterface.prototype.addIndex = function() {

  }

  QueryInterface.prototype.removeIndex = function() {

  }

  QueryInterface.prototype.insert = function(model, tableName, values) {
    var self = this

    return new Utils.CustomEventEmitter(function(emitter) {
      query
        .call(self, self.QueryGenerator.insertQuery(tableName, values), model)
        .success(function(obj) {
          self.emit('insert', null)

          obj.isNewRecord = false
          emitter.emit('success', obj)
        })
        .error(function(err) {
          self.emit('insert', err)
          emitter.emit('failure', err)
        })
    }).run()
  }

  QueryInterface.prototype.update = function(model, tableName, values, identifier) {
    var sql    = this.QueryGenerator.updateQuery(tableName, values, identifier)
      , _query = query.call(this, sql, model)
      , self   = this

    _query
      .success(function(){ self.emit('update', null) })
      .error(function(err){ self.emit('update', err)})

    return _query
  }

  QueryInterface.prototype.delete = function(model, tableName, identifier) {
    var sql    = this.QueryGenerator.deleteQuery(tableName, identifier)
      , _query = query.call(this, sql, model)
      , self   = this

    _query
      .success(function(){ self.emit('delete', null) })
      .error(function(err){ self.emit('delete', err)})

    return _query
  }

  QueryInterface.prototype.select = function(factory, tableName, options, queryOptions) {
    queryOptions = queryOptions || {}

    var sql    = this.QueryGenerator.selectQuery(tableName, options)
      , _query = query.call(this, sql, factory, queryOptions)
      , self   = this

    _query
      .success(function(){ self.emit('select', null) })
      .error(function(err){ self.emit('select', err)})

    return _query
  }

  QueryInterface.prototype.rawSelect = function(tableName, options, attributeSelector) {
    var self = this

    if(attributeSelector == undefined)
      throw new Error('Please pass an attribute selector!')

    return new Utils.CustomEventEmitter(function(emitter) {
      var sql    = self.QueryGenerator.selectQuery(tableName, options)
        , _query = query.call(self, sql, null, { plain: true, raw: true })

      _query.success(function(data) {
        self.emit('rawSelect', null)
        emitter.emit('success', data[attributeSelector])
      }).error(function(err) {
        self.emit('rawSelect', err)
        emitter.emit('failure', err)
      })
    }).run()
  }

  // private

  var query = function() {
    var args = Utils._.map(arguments, function(arg, _) { return arg })

    // add this as callee via the second argument
    if(arguments.length == 1) args.push(this)

    return this.sequelize.query.apply(this.sequelize, args)
  }

  return QueryInterface
})()
<|endoftext|>


<commit_before>import _ from 'underscore';

import FolderModel from 'girder/models/FolderModel';
import ItemModel from 'girder/models/ItemModel';
import Model from 'girder/models/Model';
import { restRequest, uploadHandlers, getUploadChunkSize } from 'girder/rest';

var FileModel = Model.extend({
    resourceName: 'file',
    resumeInfo: null,

    _wrapData: function (data, type) {
        var wrapped = data;

        if (!(data instanceof Blob)) {
            if (!_.isArray(data)) {
                wrapped = [data];
            }
            wrapped = new Blob(wrapped, {
                type: type
            });
        }

        return wrapped;
    },

    /**
     * Upload into an existing file object (i.e. this model) to change its
     * contents. This does not change the name or MIME type of the existing
     * file.
     * @param data A browser File object, browser Blob object, or raw data to be uploaded.
     */
    updateContents: function (data) {
        data = this._wrapData(data);

        this.upload(null, data, {
            path: 'file/' + this.get('_id') + '/contents',
            type: 'PUT',
            data: {
                size: data.size
            }
        });
    },

    /**
     * Upload data to a new file in a given container
     * @param Model A constructor for the parent model (either FolderModel or ItemModel).
     * @param model The parent model to upload to, or a string containing the id thereof.
     * @param data The data to upload - either a string, File object, or Blob object.
     * @param name The name of the file to create (optional if data is a File).
     * @param type The mime type of the file (optional).
     */
    _uploadToContainer: function (Model, model, data, name, type) {
        if (_.isString(model)) {
            model = new Model({
                _id: model
            });
        }

        data = this._wrapData(data, type);
        data.name = name;

        this.upload(model, data);
    },

    /**
     * Upload data to a new file in a given folder
     * @param parentFolder The parent folder to upload to, or a string containing the id thereof.
     * @param data The data to upload - either a string, File object, or Blob object.
     * @param name The name of the file to create (optional if data is a File).
     * @param type The mime type of the file (optional).
     */
    uploadToFolder: function (parentFolder, data, name, type) {
        this._uploadToContainer(FolderModel, parentFolder, data, name, type);
    },

    /**
     * Upload data to a new file in a given folder
     * @param parentItem The parent item to upload to, or a string containing the id thereof.
     * @param data The data to upload - either a string, File object, or Blob object.
     * @param name The name of the file to create (optional if data is a File).
     * @param type The mime type of the file (optional).
     */
    uploadToItem: function (parentItem, data, name, type) {
        this._uploadToContainer(ItemModel, parentItem, data, name, type);
    },

    /**
     * Upload a file. Handles uploading into all of the core assetstore types.
     * @param parentModel The parent folder or item to upload into.
     * @param file The browser File object to be uploaded.
     * @param [_restParams] Override the rest request parameters. This is meant
     * for internal use; do not pass this parameter.
     * @param [otherParams] Optional Object containing other parameters for the
     * initUpload request.
     */
    upload: function (parentModel, file, _restParams, otherParams) {
        this.startByte = 0;
        this.resumeInfo = null;
        this.uploadHandler = null;
        _restParams = _restParams || {
            path: 'file',
            type: 'POST',
            data: _.extend({
                parentType: parentModel.resourceName,
                parentId: parentModel.get('_id'),
                name: file.name,
                size: file.size,
                mimeType: file.type
            }, otherParams)
        };

        // Authenticate and generate the upload token for this file
        restRequest(_restParams).done(_.bind(function (upload) {
            var behavior = upload.behavior;
            if (behavior && uploadHandlers[behavior]) {
                this.uploadHandler = new uploadHandlers[behavior]({
                    upload: upload,
                    parentModel: parentModel,
                    file: file
                });
                this.uploadHandler.on({
                    'g:upload.complete': function (params) {
                        this.set(params);
                        this.trigger('g:upload.complete', params);
                        this.uploadHandler = null;
                    },
                    'g:upload.chunkSent': function (params) {
                        this.trigger('g:upload.chunkSent', params);
                    },
                    'g:upload.error': function (params) {
                        this.trigger('g:upload.error', params);
                    },
                    'g:upload.errorStarting': function (params) {
                        this.trigger('g:upload.errorStarting', params);
                    },
                    'g:upload.progress': function (params) {
                        this.trigger('g:upload.progress', {
                            startByte: params.startByte,
                            loaded: params.loaded,
                            total: params.file.size,
                            file: params.file
                        });
                    }
                }, this);
                return this.uploadHandler.execute();
            }

            if (file.size > 0) {
                // Begin uploading chunks of this file
                this._uploadChunk(file, upload._id);
            } else {
                // Empty file, so we are done
                this.set(upload);
                this.trigger('g:upload.complete');
            }
        }, this)).error(_.bind(function (resp) {
            var text = 'Error: ', identifier;

            if (resp.status === 0) {
                text += 'Connection to the server interrupted.';
            } else {
                text += resp.responseJSON.message;
                identifier = resp.responseJSON.identifier;
            }
            this.trigger('g:upload.errorStarting', {
                message: text,
                identifier: identifier,
                response: resp
            });
        }, this));
    },

    /**
     * If an error was triggered during the upload of this file, call this
     * in order to attempt to resume it.
     */
    resumeUpload: function () {
        if (this.uploadHandler !== null && this.uploadHandler.resume) {
            return this.uploadHandler.resume();
        }

        // Request the actual offset we need to resume at
        restRequest({
            path: 'file/offset',
            type: 'GET',
            data: {
                uploadId: this.resumeInfo.uploadId
            },
            error: null
        }).done(_.bind(function (resp) {
            this.startByte = resp.offset;
            this._uploadChunk(this.resumeInfo.file, this.resumeInfo.uploadId);
        }, this)).error(_.bind(function (resp) {
            var msg;

            if (resp.status === 0) {
                msg = 'Could not connect to the server.';
            } else {
                msg = 'An error occurred when resuming upload, check console.';
            }
            this.trigger('g:upload.error', {
                message: msg,
                response: resp
            });
        }, this));
    },

    abortUpload: function () {
        if (!this.resumeInfo || !this.resumeInfo.uploadId) {
            return;
        }
        restRequest({
            path: 'system/uploads',
            type: 'DELETE',
            data: {
                uploadId: this.resumeInfo.uploadId
            },
            error: null,
            done: null
        });
    },

    _uploadChunk: function (file, uploadId) {
        var endByte = Math.min(this.startByte + getUploadChunkSize(), file.size);

        this.chunkLength = endByte - this.startByte;
        var sliceFn = file.webkitSlice ? 'webkitSlice' : 'slice';
        var blob = file[sliceFn](this.startByte, endByte);
        var model = this;

        var fd = new FormData();
        fd.append('offset', this.startByte);
        fd.append('uploadId', uploadId);
        fd.append('chunk', blob);

        restRequest({
            path: 'file/chunk',
            type: 'POST',
            dataType: 'json',
            data: fd,
            contentType: false,
            processData: false,
            success: function (resp) {
                model.trigger('g:upload.chunkSent', {
                    bytes: endByte - model.startByte
                });

                if (endByte === file.size) {
                    model.startByte = 0;
                    model.resumeInfo = null;
                    model.set(resp);
                    model.trigger('g:upload.complete');
                } else {
                    model.startByte = endByte;
                    model._uploadChunk(file, uploadId);
                }
            },
            error: function (resp) {
                var text = 'Error: ', identifier;

                if (resp.status === 0) {
                    text += 'Connection to the server interrupted.';
                } else {
                    text += resp.responseJSON.message;
                    identifier = resp.responseJSON.identifier;
                }

                model.resumeInfo = {
                    uploadId: uploadId,
                    file: file
                };

                model.trigger('g:upload.error', {
                    message: text,
                    identifier: identifier,
                    response: resp
                });
            },
            xhr: function () {
                // Custom XHR so we can register a progress handler
                var xhr = new window.XMLHttpRequest();
                xhr.upload.addEventListener('progress', function (e) {
                    model._uploadProgress(file, e);
                });
                return xhr;
            }
        });
    },

    /**
     * Progress callback from XHR during upload. This will trigger useful
     * progress events under the 'upload.progress' tag.
     */
    _uploadProgress: function (file, event) {
        if (!event.lengthComputable) {
            return;
        }

        // We only want to count bytes of the actual file, not the bytes of
        // the request body corresponding to the other form parameters model
        // we are also sending.
        var loaded = this.chunkLength + event.loaded - event.total;

        if (loaded >= 0) {
            this.trigger('g:upload.progress', {
                startByte: this.startByte,
                loaded: loaded,
                total: file.size,
                file: file
            });
        }
    }
});

export default FileModel;
<commit_msg>Import S3 upload handler via FileModel<commit_after>import _ from 'underscore';

import FolderModel from 'girder/models/FolderModel';
import ItemModel from 'girder/models/ItemModel';
import Model from 'girder/models/Model';
import { restRequest, uploadHandlers, getUploadChunkSize } from 'girder/rest';
import 'girder/utilities/S3UploadHandler';  // imported for side effect

var FileModel = Model.extend({
    resourceName: 'file',
    resumeInfo: null,

    _wrapData: function (data, type) {
        var wrapped = data;

        if (!(data instanceof Blob)) {
            if (!_.isArray(data)) {
                wrapped = [data];
            }
            wrapped = new Blob(wrapped, {
                type: type
            });
        }

        return wrapped;
    },

    /**
     * Upload into an existing file object (i.e. this model) to change its
     * contents. This does not change the name or MIME type of the existing
     * file.
     * @param data A browser File object, browser Blob object, or raw data to be uploaded.
     */
    updateContents: function (data) {
        data = this._wrapData(data);

        this.upload(null, data, {
            path: 'file/' + this.get('_id') + '/contents',
            type: 'PUT',
            data: {
                size: data.size
            }
        });
    },

    /**
     * Upload data to a new file in a given container
     * @param Model A constructor for the parent model (either FolderModel or ItemModel).
     * @param model The parent model to upload to, or a string containing the id thereof.
     * @param data The data to upload - either a string, File object, or Blob object.
     * @param name The name of the file to create (optional if data is a File).
     * @param type The mime type of the file (optional).
     */
    _uploadToContainer: function (Model, model, data, name, type) {
        if (_.isString(model)) {
            model = new Model({
                _id: model
            });
        }

        data = this._wrapData(data, type);
        data.name = name;

        this.upload(model, data);
    },

    /**
     * Upload data to a new file in a given folder
     * @param parentFolder The parent folder to upload to, or a string containing the id thereof.
     * @param data The data to upload - either a string, File object, or Blob object.
     * @param name The name of the file to create (optional if data is a File).
     * @param type The mime type of the file (optional).
     */
    uploadToFolder: function (parentFolder, data, name, type) {
        this._uploadToContainer(FolderModel, parentFolder, data, name, type);
    },

    /**
     * Upload data to a new file in a given folder
     * @param parentItem The parent item to upload to, or a string containing the id thereof.
     * @param data The data to upload - either a string, File object, or Blob object.
     * @param name The name of the file to create (optional if data is a File).
     * @param type The mime type of the file (optional).
     */
    uploadToItem: function (parentItem, data, name, type) {
        this._uploadToContainer(ItemModel, parentItem, data, name, type);
    },

    /**
     * Upload a file. Handles uploading into all of the core assetstore types.
     * @param parentModel The parent folder or item to upload into.
     * @param file The browser File object to be uploaded.
     * @param [_restParams] Override the rest request parameters. This is meant
     * for internal use; do not pass this parameter.
     * @param [otherParams] Optional Object containing other parameters for the
     * initUpload request.
     */
    upload: function (parentModel, file, _restParams, otherParams) {
        this.startByte = 0;
        this.resumeInfo = null;
        this.uploadHandler = null;
        _restParams = _restParams || {
            path: 'file',
            type: 'POST',
            data: _.extend({
                parentType: parentModel.resourceName,
                parentId: parentModel.get('_id'),
                name: file.name,
                size: file.size,
                mimeType: file.type
            }, otherParams)
        };

        // Authenticate and generate the upload token for this file
        restRequest(_restParams).done(_.bind(function (upload) {
            var behavior = upload.behavior;
            if (behavior && uploadHandlers[behavior]) {
                this.uploadHandler = new uploadHandlers[behavior]({
                    upload: upload,
                    parentModel: parentModel,
                    file: file
                });
                this.uploadHandler.on({
                    'g:upload.complete': function (params) {
                        this.set(params);
                        this.trigger('g:upload.complete', params);
                        this.uploadHandler = null;
                    },
                    'g:upload.chunkSent': function (params) {
                        this.trigger('g:upload.chunkSent', params);
                    },
                    'g:upload.error': function (params) {
                        this.trigger('g:upload.error', params);
                    },
                    'g:upload.errorStarting': function (params) {
                        this.trigger('g:upload.errorStarting', params);
                    },
                    'g:upload.progress': function (params) {
                        this.trigger('g:upload.progress', {
                            startByte: params.startByte,
                            loaded: params.loaded,
                            total: params.file.size,
                            file: params.file
                        });
                    }
                }, this);
                return this.uploadHandler.execute();
            }

            if (file.size > 0) {
                // Begin uploading chunks of this file
                this._uploadChunk(file, upload._id);
            } else {
                // Empty file, so we are done
                this.set(upload);
                this.trigger('g:upload.complete');
            }
        }, this)).error(_.bind(function (resp) {
            var text = 'Error: ', identifier;

            if (resp.status === 0) {
                text += 'Connection to the server interrupted.';
            } else {
                text += resp.responseJSON.message;
                identifier = resp.responseJSON.identifier;
            }
            this.trigger('g:upload.errorStarting', {
                message: text,
                identifier: identifier,
                response: resp
            });
        }, this));
    },

    /**
     * If an error was triggered during the upload of this file, call this
     * in order to attempt to resume it.
     */
    resumeUpload: function () {
        if (this.uploadHandler !== null && this.uploadHandler.resume) {
            return this.uploadHandler.resume();
        }

        // Request the actual offset we need to resume at
        restRequest({
            path: 'file/offset',
            type: 'GET',
            data: {
                uploadId: this.resumeInfo.uploadId
            },
            error: null
        }).done(_.bind(function (resp) {
            this.startByte = resp.offset;
            this._uploadChunk(this.resumeInfo.file, this.resumeInfo.uploadId);
        }, this)).error(_.bind(function (resp) {
            var msg;

            if (resp.status === 0) {
                msg = 'Could not connect to the server.';
            } else {
                msg = 'An error occurred when resuming upload, check console.';
            }
            this.trigger('g:upload.error', {
                message: msg,
                response: resp
            });
        }, this));
    },

    abortUpload: function () {
        if (!this.resumeInfo || !this.resumeInfo.uploadId) {
            return;
        }
        restRequest({
            path: 'system/uploads',
            type: 'DELETE',
            data: {
                uploadId: this.resumeInfo.uploadId
            },
            error: null,
            done: null
        });
    },

    _uploadChunk: function (file, uploadId) {
        var endByte = Math.min(this.startByte + getUploadChunkSize(), file.size);

        this.chunkLength = endByte - this.startByte;
        var sliceFn = file.webkitSlice ? 'webkitSlice' : 'slice';
        var blob = file[sliceFn](this.startByte, endByte);
        var model = this;

        var fd = new FormData();
        fd.append('offset', this.startByte);
        fd.append('uploadId', uploadId);
        fd.append('chunk', blob);

        restRequest({
            path: 'file/chunk',
            type: 'POST',
            dataType: 'json',
            data: fd,
            contentType: false,
            processData: false,
            success: function (resp) {
                model.trigger('g:upload.chunkSent', {
                    bytes: endByte - model.startByte
                });

                if (endByte === file.size) {
                    model.startByte = 0;
                    model.resumeInfo = null;
                    model.set(resp);
                    model.trigger('g:upload.complete');
                } else {
                    model.startByte = endByte;
                    model._uploadChunk(file, uploadId);
                }
            },
            error: function (resp) {
                var text = 'Error: ', identifier;

                if (resp.status === 0) {
                    text += 'Connection to the server interrupted.';
                } else {
                    text += resp.responseJSON.message;
                    identifier = resp.responseJSON.identifier;
                }

                model.resumeInfo = {
                    uploadId: uploadId,
                    file: file
                };

                model.trigger('g:upload.error', {
                    message: text,
                    identifier: identifier,
                    response: resp
                });
            },
            xhr: function () {
                // Custom XHR so we can register a progress handler
                var xhr = new window.XMLHttpRequest();
                xhr.upload.addEventListener('progress', function (e) {
                    model._uploadProgress(file, e);
                });
                return xhr;
            }
        });
    },

    /**
     * Progress callback from XHR during upload. This will trigger useful
     * progress events under the 'upload.progress' tag.
     */
    _uploadProgress: function (file, event) {
        if (!event.lengthComputable) {
            return;
        }

        // We only want to count bytes of the actual file, not the bytes of
        // the request body corresponding to the other form parameters model
        // we are also sending.
        var loaded = this.chunkLength + event.loaded - event.total;

        if (loaded >= 0) {
            this.trigger('g:upload.progress', {
                startByte: this.startByte,
                loaded: loaded,
                total: file.size,
                file: file
            });
        }
    }
});

export default FileModel;
<|endoftext|>