<commit_before>var async = require('async')

// NOTE: the code below is excessively confusing I'd rather introduce
// new abstract object or something rather than doing recursion after
// recursion in a dynamic language. You have been warned.
exports.addSerializer = function() {
  function AbstractSerializer(object, strategy) {
    this.object = object
    this.strategy = strategy
  }

  AbstractSerializer.prototype = {
    toJSON: function(callback) {
      var that = this
        , strategy = this.strategy
        , json = {}

      // that function selects inner complex properties, we'll use it
      // a bit later
      // complex property is a property that we need to select by
      // defined strategy and has custom structure, e.g. info is a
      // complex property in a example
      // var strategy = {
      //   select: ['id', 'username', 'type', 'info', 'rss'],
      //   info: { select: ['screenName'] }
      // }
      var selectComplexProperties = function(strategy, subfield, object, callback) {
          async.map(strategy, function(field, callback) {
            if (object[field]) { // object has that property, moving on
              json[subfield] = json[subfield] || {}
              json[subfield][field] = object[field]
              callback(object[field])
            } else { // property is missing and as we don't know db
              // structure we'll use model methods that probably
              // and we hope exist
              // we are assuming that there is a function get<Something> where
              // <Something> is a name of a property or an object that
              // we are going to select
              // TODO: not sure we really need this block here... it
              // might happen we'll need to get fields by "'get' +
              // subfield.capitalize()" function
              var name = 'get' + field.capitalize()
              object[name](function(err, res) {
                callback(err, res)
              })
            }
          }, function(err, json) {
            // we have fetched all inner properties
            callback(err)
          })
      }

      var selectPropertiesViaSerializator = function(field, object, serializer, callback) {
        var fn = function(object) {
          var objectSerializer = new serializer(object)
          objectSerializer.toJSON(function(objectJson) {
            json[field] = objectJson
            callback(null)
          })
        }

        if (object) {
          fn(object)
        } else {
          // TODO: code below duplicates code from
          // selectComplexProperties function
          var name = 'get' + field.capitalize()
          that.object[name](function(err, object) {
            fn(object)
          })
        }
      }

      async.forEach(strategy.select, function(field, callback) {
        // TODO: some of strategy might be already exist in the object
        // if (that.object[field]) { // this field is already present
        // in the object

        // this is a complex property
        if (strategy[field] && (strategy[field].select || strategy[field].through)) {
          // now we need to either ensure all properties are there
          // or fetch missing from the db

          // Liberté, égalité, fraternité. Object and array of objects
          // must be living together.
          var objects = that.object
          if (!Array.isArray(objects))
            objects = [objects]

          async.forEach(objects, function(object, callback) {
            var guineaPig = object[field]

            if (strategy[field].select) // this is a plain structure
              selectComplexProperties(strategy[field].select,
                                      field,
                                      guineaPig, callback)
            else if (strategy[field].through) // we have been told to select that
              // property through existing serializator
              selectPropertiesViaSerializator(field,
                                              guineaPig,
                                              strategy[field].through,
                                              callback)
          }, function(err) {
            callback(err)
          })
        } else {
          json[field] = that.object[field]
          callback(null)
        }
      }, function(err) {
        callback(json)
      })
    }
  }

  return AbstractSerializer
}
<commit_msg>AbstractSerializer rewrite.<commit_after>var async = require('async');

exports.addSerializer = function() {
  var AbstractSerializer = function(object, strategy) {
    this.object   = object;
    this.strategy = strategy;
  };

  AbstractSerializer.prototype = {
    END_POINT: 1,
    NESTED_STRATEGY: 2,
    THROUGH_POINT: 3,

    getField: function(field, f) {
      if (!this.object[field]) {
        this.object["get" + field.capitalize()](f);
      } else {
        f(null, this.object[field]);
      }
    },

    decideNode: function(field) {
      if (!this.strategy[field]) {
        return this.END_POINT;
      } else {
        if (this.strategy[field].through) {
          return this.THROUGH_POINT;
        } else {
          return this.NESTED_STRATEGY;
        }
      }
    },

    processMultiObjects: function(objects, strategy, f, serializer) {
      var result = [];
      var jsonAdder = function(done) {
        return function(err, json) {
          result.push(json);
          done(err);
        };
      };

      async.forEach(objects, function(object, done) {
        if (serializer) {
          new serializer(object).toJSON(jsonAdder(done));
        } else {
          new AbstractSerializer(object, strategy).toJSON(jsonAdder(done));
        }
      }, function(err) {
        f(err, result);
      });
    },

    getMaybeObjects: function(field, one, many) {
      this.getField(field, function(err, object) {
        Array.isArray(object) ? many(object) : one(object);
      });
    },

    processNestedStrategy: function(field, f) {
      var serializer = this;

      serializer.getMaybeObjects(field, function(object) {
        new AbstractSerializer(object, serializer.strategy[field]).toJSON(f);
      }, function(objects) {
        serializer.processMultiObjects(objects, serializer.strategy[field], f);
      });
    },

    processThroughPoint: function(field, f) {
      var serializer = this;

      serializer.getMaybeObjects(field, function(object) {
        new serializer.strategy[field].through(object).toJSON(f);
      }, function(objects) {
        serializer.processMultiObjects(objects, null, f, serializer.strategy[field].through);
      });
    },

    processNode: function(jsonAdder) {
      var serializer = this;

      return function(field, done) {
        switch (serializer.decideNode(field)) {

        case serializer.END_POINT:
          serializer.getField(field, jsonAdder(field, done));
          break;

        case serializer.NESTED_STRATEGY:
          serializer.processNestedStrategy(field, jsonAdder(field, done));
          break;

        case serializer.THROUGH_POINT:
          serializer.processThroughPoint(field, jsonAdder(field, done));
          break;
        }
      };
    },

    toJSON: function(f) {
      var json = {};
      var jsonAdder = function(field, done) {
        return function(err, res) {
          json[field] = res;
          done(err);
        };
      };

      async.forEach(this.strategy.select, this.processNode(jsonAdder) , function(err) {
        f(err, json);
      });
    }
  };

  return AbstractSerializer;
};
<|endoftext|>


<commit_before>/*
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 *
*/

// Cordova contact definition:
// http://cordova.apache.org/docs/en/2.5.0/cordova_contacts_contacts.md.html#Contact
// FxOS contact definition:
// https://developer.mozilla.org/en-US/docs/Web/API/mozContact

var Contact = require('./Contact');
var ContactField = require('./ContactField');
var ContactAddress = require('./ContactAddress');
var ContactName = require('./ContactName');

// XXX: a hack to check if id is "empty". Cordova inserts a
// string "this string is supposed to be a unique identifier that will
// never show up on a device" if id is empty
function _hasId(id) {
    if (!id || id.indexOf(' ') >= 0) {
        return false;
    }
    return true;
}

// Extend mozContact prototype to provide update from Cordova
mozContact.prototype.updateFromCordova = function(contact) {

    function exportContactFieldArray(contactFieldArray, key) {
        if (!key) {
            key = 'value';
        }
        var arr = [];
        for (var i=0; i < contactFieldArray.length; i++) {
            arr.push(contactFieldArray[i][key]);
        };
        return arr;
    }

    function exportAddress(addresses) {
        // TODO: check moz address format
        var arr = [];

        for (var i=0; i < addresses.length; i++) {
            var addr = {};
            for (var key in addresses[i]) {
                if (key == 'formatted' || key == 'id') {
                    continue;
                } else if (key == 'type') {
                    addr[key] = [addresses[i][key]];
                } else if (key == 'country') {
                    addr['countryName'] = addresses[i][key];
                } else {
                    addr[key] = addresses[i][key];
                }
            }
            arr.push(addr);
        }
        return arr;
    }

    function exportContactField(data) {
        var contactFields = [];
        for (var i=0; i < data.length; i++) {
            var item = data[i];
            if (item.value) {
                var itemData = {value: item.value};
                if (item.type) {
                    itemData.type = [item.type];
                }
                if (item.pref) {
                    itemData.pref = item.pref;
                }
                contactFields.push(itemData);
            }
        }
        return contactFields;
    }
    // adding simple fields [contactField, eventualMozContactField]
    var nameFields = [['givenName'], ['familyName'],
                      ['honorificPrefix'], ['honorificSuffix'],
                      ['middleName', 'additionalName']];
    var baseArrayFields = [['displayName', 'name'], ['nickname']];
    var baseStringFields = [];
    var j = 0; while(field = nameFields[j++]) {
      if (contact.name[field[0]]) {
        this[field[1] || field[0]] = contact.name[field[0]].split(' ');
      }
    }
    j = 0; while(field = baseArrayFields[j++]) {
      if (contact[field[0]]) {
        this[field[1] || field[0]] = contact[field[0]].split(' ');
      }
    }
    j = 0; while(field = baseStringFields[j++]) {
      if (contact[field[0]]) {
        this[field[1] || field[0]] = contact[field[0]];
      }
    }
    if (contact.birthday) {
      this.bday = new Date(contact.birthday);
    }
    if (contact.emails) {
        var emails = exportContactField(contact.emails)
        this.email = emails;
    }
    if (contact.categories) {
        this.category = exportContactFieldArray(contact.categories);
    }
    if (contact.addresses) {
        this.adr = exportAddress(contact.addresses);
    }
    if (contact.phoneNumbers) {
        this.tel = exportContactField(contact.phoneNumbers);
    }
    if (contact.organizations) {
        // XXX: organizations are saved in 2 arrays - org and jobTitle
        //      depending on the usecase it might generate issues
        //      where wrong title will be added to an organization
        this.org = exportContactFieldArray(contact.organizations, 'name');
        this.jobTitle = exportContactFieldArray(contact.organizations, 'title');
    }
    if (contact.note) {
        this.note = [contact.note];
    }
}

// Extend Cordova Contact prototype to provide update from FFOS contact
Contact.prototype.updateFromMozilla = function(moz) {
    function exportContactField(data) {
        var contactFields = [];
        for (var i=0; i < data.length; i++) {
            var item = data[i];
            var itemData = new ContactField(item.type, item.value, item.pref);
            contactFields.push(itemData);
        }
        return contactFields;
    }

    function makeContactFieldFromArray(data) {
        var contactFields = [];
        for (var i=0; i < data.length; i++) {
            var itemData = new ContactField(null, data[i]);
            contactFields.push(itemData);
        }
        return contactFields;
    }

    function exportAddresses(addresses) {
        // TODO: check moz address format
        var arr = [];

        for (var i=0; i < addresses.length; i++) {
            var addr = {};
            for (var key in addresses[i]) {
                if (key == 'countryName') {
                    addr['country'] = addresses[i][key];
                } else if (key == 'type') {
                    addr[key] = addresses[i][key].join(' ');
                } else {
                    addr[key] = addresses[i][key];
                }
            }
            arr.push(addr);
        }
        return arr;
    }

    function createOrganizations(orgs, jobs) {
        orgs = (orgs) ? orgs : [];
        jobs = (jobs) ? jobs : [];
        var max_length = Math.max(orgs.length, jobs.length);
        var organizations = [];
        for (var i=0; i < max_length; i++) {
            organizations.push(new ContactOrganization(
                  null, null, orgs[i] || null, null, jobs[i] || null));
        }
        return organizations;
    }

    function createFormatted(name) {
        var fields = ['honorificPrefix', 'givenName', 'middleName',
                      'familyName', 'honorificSuffix'];
        var f = '';
        for (var i = 0; i < fields.length; i++) {
            if (name[fields[i]]) {
                if (f) {
                    f += ' ';
                }
                f += name[fields[i]];
            }
        }
        return f;
    }

    if (moz.id) {
        this.id = moz.id;
    }
    var nameFields = [['givenName'], ['familyName'],
                       ['honorificPrefix'], ['honorificSuffix'],
                       ['additionalName', 'middleName']];
    var baseArrayFields = [['name', 'displayName'], 'nickname', ['note']];
    var baseStringFields = [];
    var name = new ContactName();
    var j = 0; while(field = nameFields[j++]) {
        if (moz[field[0]]) {
            name[field[1] || field[0]] = moz[field[0]].join(' ');
        }
    }
    this.name = name;
    j = 0; while(field = baseArrayFields[j++]) {
        if (moz[field[0]]) {
            this[field[1] || field[0]] = moz[field[0]].join(' ');
        }
    }
    j = 0; while(field = baseStringFields[j++]) {
        if (moz[field[0]]) {
            this[field[1] || field[0]] = moz[field[0]];
        }
    }
    // emails
    if (moz.email) {
        this.emails = exportContactField(moz.email);
    }
    // categories
    if (moz.category) {
        this.categories = makeContactFieldFromArray(moz.category);
    }

    // addresses
    if (moz.adr) {
        this.addresses = exportAddresses(moz.adr);
    }

    // phoneNumbers
    if (moz.tel) {
        this.phoneNumbers = exportContactField(moz.tel);
    }
    // birthday
    if (moz.bday) {
      this.birthday = Date.parse(moz.bday);
    }
    // organizations
    if (moz.org || moz.jobTitle) {
        // XXX: organizations array is created from org and jobTitle
        this.organizations = createOrganizations(moz.org, moz.jobTitle);
    }
    // construct a read-only formatted value
    this.name.formatted = createFormatted(this.name);

    /*  Find out how to translate these parameters
        // photo: Blob
        // url: Array with metadata (?)
        // impp: exportIM(contact.ims), TODO: find the moz impp definition
        // anniversary
        // sex
        // genderIdentity
        // key
    */
}

function createMozillaFromCordova(successCB, errorCB, contact) {
    var moz;
    // get contact if exists
    if (_hasId(contact.id)) {
      var search = navigator.mozContacts.find({
        filterBy: ['id'], filterValue: contact.id, filterOp: 'equals'});
      search.onsuccess = function() {
        moz = search.result[0];
        moz.updateFromCordova(contact);
        successCB(moz);
      };
      search.onerror = errorCB;
      return;
    }

    // create empty contact
    moz = new mozContact();
    if ('init' in moz) {
      // 1.2 and below compatibility
      moz.init();
    }
    moz.updateFromCordova(contact);
    successCB(moz);
}

function createCordovaFromMozilla(moz) {
    var contact = new Contact();
    contact.updateFromMozilla(moz);
    return contact;
}

// However API requires the ability to save multiple contacts, it is
// used to save only one element array
function saveContacts(successCB, errorCB, contacts) {
    // a closure which is holding the right moz contact
    function makeSaveSuccessCB(moz) {
        return function(result) {
            // create contact from FXOS contact (might be different than
            // the original one due to differences in API)
            var contact = createCordovaFromMozilla(moz);
            // call callback
            successCB(contact);
        }
    }
    var i=0;
    var contact;
    while(contact = contacts[i++]){
        var moz = createMozillaFromCordova(function(moz) {
          var request = navigator.mozContacts.save(moz);
          // success and/or fail will be called every time a contact is saved
          request.onsuccess = makeSaveSuccessCB(moz);
          request.onerror = errorCB;
        }, function() {}, contact);
    }
}

// API provides a list of ids to be removed
function remove(successCB, errorCB, ids) {
    var i=0;
    var id;
    for (var i=0; i < ids.length; i++){
        // throw an error if no id provided
        if (!_hasId(ids[i])) {
            console.error('FFOS: Attempt to remove unsaved contact');
            errorCB(0);
            return;
        }
        // check if provided id actually exists
        var search = navigator.mozContacts.find({
            filterBy: ['id'], filterValue: ids[i], filterOp: 'equals'});
        search.onsuccess = function() {
            if (search.result.length === 0) {
                console.error('FFOS: Attempt to remove a non existing contact');
                errorCB(0);
                return;
            }
            var moz = search.result[0];
            var request = navigator.mozContacts.remove(moz);
            request.onsuccess = successCB;
            request.onerror = errorCB;
        };
        search.onerror = errorCB;
    }
}

var mozContactSearchFields = [['name', 'displayName'], ['givenName'],
    ['familyName'], ['email'], ['tel'], ['jobTitle'], ['note'],
    ['tel', 'phoneNumbers'], ['email', 'emails']];
// Searching by nickname and additionalName is forbidden in  1.3 and below
// Searching by name is forbidden in 1.2 and below

// finds if a key is allowed and returns FFOS name if different
function getMozSearchField(key) {
    if (mozContactSearchFields.indexOf([key]) >= 0) {
        return key;
    }
    for (var i=0; i < mozContactSearchFields.length; i++) {
        if (mozContactSearchFields[i].length > 1) {
            if (mozContactSearchFields[i][1] === key) {
                return mozContactSearchFields[i][0];
            }
        }
    }
    return false;
}

function _getAll(successCB, errorCB, params) {
    // [contactField, eventualMozContactField]
    var getall = navigator.mozContacts.getAll({});
    var contacts = [];

    getall.onsuccess = function() {
        if (getall.result) {
            contacts.push(createCordovaFromMozilla(getall.result));
            getall.continue();
        } else {
            successCB(contacts);
        }
    };
    getall.onerror = errorCB;
}

function search(successCB, errorCB, params) {
    var options = params[1] || {};
    if (!options.filter) {
        return _getAll(successCB, errorCB, params);
    }
    var filterBy = [];
    // filter and translate fields
    for (var i=0; i < params[0].length; i++) {
        var searchField = params[0][i];
        var mozField = getMozSearchField(searchField);
        if (searchField === 'name') {
            // Cordova uses name for search by all name fields.
            filterBy.push('givenName');
            filterBy.push('familyName');
            continue;
        }
        if (searchField === 'displayName' && 'init' in new mozContact()) {
            // ``init`` in ``mozContact`` indicates FFOS version 1.2 or below
            // Searching by name (in moz) is then forbidden
            console.log('FFOS ContactProxy: Unable to search by displayName on FFOS 1.2');
            continue;
        }
        if (mozField) {
            filterBy.push(mozField);
        } else {
            console.log('FXOS ContactProxy: inallowed field passed to search filtered out: ' + searchField);
        }
    }

    var mozOptions = {filterBy: filterBy, filterOp: 'startsWith'};
    if (!options.multiple) {
        mozOptions.filterLimit = 1;
    }
    mozOptions.filterValue = options.filter;
    var request = navigator.mozContacts.find(mozOptions);
    request.onsuccess = function() {
        var contacts = [];
        var mozContacts = request.result;
        var moz = mozContacts[0];
        for (var i=0; i < mozContacts.length; i++) {
            contacts.push(createCordovaFromMozilla(mozContacts[i]));
        }
        successCB(contacts);
    };
    request.onerror = errorCB;
}

module.exports = {
    save: saveContacts,
    remove: remove,
    search: search
};

require("cordova/firefoxos/commandProxy").add("Contacts", module.exports);
<commit_msg>manually merging CB-3208<commit_after>/*
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 *
*/

// Cordova contact definition:
// http://cordova.apache.org/docs/en/2.5.0/cordova_contacts_contacts.md.html#Contact
// FxOS contact definition:
// https://developer.mozilla.org/en-US/docs/Web/API/mozContact

var Contact = require('./Contact');
var ContactField = require('./ContactField');
var ContactAddress = require('./ContactAddress');
var ContactName = require('./ContactName');

// XXX: a hack to check if id is "empty". Cordova inserts a
// string "this string is supposed to be a unique identifier that will
// never show up on a device" if id is empty
function _hasId(id) {
    if (!id || id.indexOf(' ') >= 0) {
        return false;
    }
    return true;
}

// Extend mozContact prototype to provide update from Cordova
function updateFromCordova(contact, fromContact) {

    function exportContactFieldArray(contactFieldArray, key) {
        if (!key) {
            key = 'value';
        }
        var arr = [];
        for (var i=0; i < contactFieldArray.length; i++) {
            arr.push(contactFieldArray[i][key]);
        };
        return arr;
    }

    function exportAddress(addresses) {
        // TODO: check moz address format
        var arr = [];

        for (var i=0; i < addresses.length; i++) {
            var addr = {};
            for (var key in addresses[i]) {
                if (key == 'formatted' || key == 'id') {
                    continue;
                } else if (key == 'type') {
                    addr[key] = [addresses[i][key]];
                } else if (key == 'country') {
                    addr['countryName'] = addresses[i][key];
                } else {
                    addr[key] = addresses[i][key];
                }
            }
            arr.push(addr);
        }
        return arr;
    }

    function exportContactField(data) {
        var contactFields = [];
        for (var i=0; i < data.length; i++) {
            var item = data[i];
            if (item.value) {
                var itemData = {value: item.value};
                if (item.type) {
                    itemData.type = [item.type];
                }
                if (item.pref) {
                    itemData.pref = item.pref;
                }
                contactFields.push(itemData);
            }
        }
        return contactFields;
    }
    // adding simple fields [contactField, eventualMozContactField]
    var nameFields = [['givenName'], ['familyName'],
                      ['honorificPrefix'], ['honorificSuffix'],
                      ['middleName', 'additionalName']];
    var baseArrayFields = [['displayName', 'name'], ['nickname']];
    var baseStringFields = [];
    var j = 0; while(field = nameFields[j++]) {
      if (fromContact.name[field[0]]) {
        contact[field[1] || field[0]] = fromContact.name[field[0]].split(' ');
      }
    }
    j = 0; while(field = baseArrayFields[j++]) {
      if (fromContact[field[0]]) {
        contact[field[1] || field[0]] = fromContact[field[0]].split(' ');
      }
    }
    j = 0; while(field = baseStringFields[j++]) {
      if (fromContact[field[0]]) {
        contact[field[1] || field[0]] = fromContact[field[0]];
      }
    }
    if (fromContact.birthday) {
      contact.bday = new Date(fromContact.birthday);
    }
    if (fromContact.emails) {
        var emails = exportContactField(fromContact.emails)
        contact.email = emails;
    }
    if (fromContact.categories) {
        contact.category = exportContactFieldArray(fromContact.categories);
    }
    if (fromContact.addresses) {
        contact.adr = exportAddress(fromContact.addresses);
    }
    if (fromContact.phoneNumbers) {
        contact.tel = exportContactField(fromContact.phoneNumbers);
    }
    if (fromContact.organizations) {
        // XXX: organizations are saved in 2 arrays - org and jobTitle
        //      depending on the usecase it might generate issues
        //      where wrong title will be added to an organization
        contact.org = exportContactFieldArray(fromContact.organizations, 'name');
        contact.jobTitle = exportContactFieldArray(fromContact.organizations, 'title');
    }
    if (fromContact.note) {
        contact.note = [fromContact.note];
    }
}

// Extend Cordova Contact prototype to provide update from FFOS contact
Contact.prototype.updateFromMozilla = function(moz) {
    function exportContactField(data) {
        var contactFields = [];
        for (var i=0; i < data.length; i++) {
            var item = data[i];
            var itemData = new ContactField(item.type, item.value, item.pref);
            contactFields.push(itemData);
        }
        return contactFields;
    }

    function makeContactFieldFromArray(data) {
        var contactFields = [];
        for (var i=0; i < data.length; i++) {
            var itemData = new ContactField(null, data[i]);
            contactFields.push(itemData);
        }
        return contactFields;
    }

    function exportAddresses(addresses) {
        // TODO: check moz address format
        var arr = [];

        for (var i=0; i < addresses.length; i++) {
            var addr = {};
            for (var key in addresses[i]) {
                if (key == 'countryName') {
                    addr['country'] = addresses[i][key];
                } else if (key == 'type') {
                    addr[key] = addresses[i][key].join(' ');
                } else {
                    addr[key] = addresses[i][key];
                }
            }
            arr.push(addr);
        }
        return arr;
    }

    function createOrganizations(orgs, jobs) {
        orgs = (orgs) ? orgs : [];
        jobs = (jobs) ? jobs : [];
        var max_length = Math.max(orgs.length, jobs.length);
        var organizations = [];
        for (var i=0; i < max_length; i++) {
            organizations.push(new ContactOrganization(
                  null, null, orgs[i] || null, null, jobs[i] || null));
        }
        return organizations;
    }

    function createFormatted(name) {
        var fields = ['honorificPrefix', 'givenName', 'middleName',
                      'familyName', 'honorificSuffix'];
        var f = '';
        for (var i = 0; i < fields.length; i++) {
            if (name[fields[i]]) {
                if (f) {
                    f += ' ';
                }
                f += name[fields[i]];
            }
        }
        return f;
    }

    if (moz.id) {
        this.id = moz.id;
    }
    var nameFields = [['givenName'], ['familyName'],
                       ['honorificPrefix'], ['honorificSuffix'],
                       ['additionalName', 'middleName']];
    var baseArrayFields = [['name', 'displayName'], 'nickname', ['note']];
    var baseStringFields = [];
    var name = new ContactName();
    var j = 0; while(field = nameFields[j++]) {
        if (moz[field[0]]) {
            name[field[1] || field[0]] = moz[field[0]].join(' ');
        }
    }
    this.name = name;
    j = 0; while(field = baseArrayFields[j++]) {
        if (moz[field[0]]) {
            this[field[1] || field[0]] = moz[field[0]].join(' ');
        }
    }
    j = 0; while(field = baseStringFields[j++]) {
        if (moz[field[0]]) {
            this[field[1] || field[0]] = moz[field[0]];
        }
    }
    // emails
    if (moz.email) {
        this.emails = exportContactField(moz.email);
    }
    // categories
    if (moz.category) {
        this.categories = makeContactFieldFromArray(moz.category);
    }

    // addresses
    if (moz.adr) {
        this.addresses = exportAddresses(moz.adr);
    }

    // phoneNumbers
    if (moz.tel) {
        this.phoneNumbers = exportContactField(moz.tel);
    }
    // birthday
    if (moz.bday) {
      this.birthday = Date.parse(moz.bday);
    }
    // organizations
    if (moz.org || moz.jobTitle) {
        // XXX: organizations array is created from org and jobTitle
        this.organizations = createOrganizations(moz.org, moz.jobTitle);
    }
    // construct a read-only formatted value
    this.name.formatted = createFormatted(this.name);

    /*  Find out how to translate these parameters
        // photo: Blob
        // url: Array with metadata (?)
        // impp: exportIM(contact.ims), TODO: find the moz impp definition
        // anniversary
        // sex
        // genderIdentity
        // key
    */
}

function createMozillaFromCordova(successCB, errorCB, contact) {
    var moz;
    // get contact if exists
    if (_hasId(contact.id)) {
      var search = navigator.mozContacts.find({
        filterBy: ['id'], filterValue: contact.id, filterOp: 'equals'});
      search.onsuccess = function() {
        moz = search.result[0];
        updateFromCordova(moz, contact);
        successCB(moz);
      };
      search.onerror = errorCB;
      return;
    }

    // create empty contact
    moz = new mozContact();
    // if ('init' in moz) {
      // 1.2 and below compatibility
      // moz.init();
    // }
    updateFromCordova(moz, contact);
    successCB(moz);
}

function createCordovaFromMozilla(moz) {
    var contact = new Contact();
    contact.updateFromMozilla(moz);
    return contact;
}

// However API requires the ability to save multiple contacts, it is
// used to save only one element array
function saveContacts(successCB, errorCB, contacts) {
    // a closure which is holding the right moz contact
    function makeSaveSuccessCB(moz) {
        return function(result) {
            // create contact from FXOS contact (might be different than
            // the original one due to differences in API)
            var contact = createCordovaFromMozilla(moz);
            // call callback
            successCB(contact);
        }
    }
    var i=0;
    var contact;
    while(contact = contacts[i++]){
        var moz = createMozillaFromCordova(function(moz) {
          var request = navigator.mozContacts.save(moz);
          // success and/or fail will be called every time a contact is saved
          request.onsuccess = makeSaveSuccessCB(moz);
          request.onerror = errorCB;
        }, function() {}, contact);
    }
}

// API provides a list of ids to be removed
function remove(successCB, errorCB, ids) {
    var i=0;
    var id;
    for (var i=0; i < ids.length; i++){
        // throw an error if no id provided
        if (!_hasId(ids[i])) {
            console.error('FFOS: Attempt to remove unsaved contact');
            errorCB(0);
            return;
        }
        // check if provided id actually exists
        var search = navigator.mozContacts.find({
            filterBy: ['id'], filterValue: ids[i], filterOp: 'equals'});
        search.onsuccess = function() {
            if (search.result.length === 0) {
                console.error('FFOS: Attempt to remove a non existing contact');
                errorCB(0);
                return;
            }
            var moz = search.result[0];
            var request = navigator.mozContacts.remove(moz);
            request.onsuccess = successCB;
            request.onerror = errorCB;
        };
        search.onerror = errorCB;
    }
}

var mozContactSearchFields = [['name', 'displayName'], ['givenName'],
    ['familyName'], ['email'], ['tel'], ['jobTitle'], ['note'],
    ['tel', 'phoneNumbers'], ['email', 'emails']];
// Searching by nickname and additionalName is forbidden in  1.3 and below
// Searching by name is forbidden in 1.2 and below

// finds if a key is allowed and returns FFOS name if different
function getMozSearchField(key) {
    if (mozContactSearchFields.indexOf([key]) >= 0) {
        return key;
    }
    for (var i=0; i < mozContactSearchFields.length; i++) {
        if (mozContactSearchFields[i].length > 1) {
            if (mozContactSearchFields[i][1] === key) {
                return mozContactSearchFields[i][0];
            }
        }
    }
    return false;
}

function _getAll(successCB, errorCB, params) {
    // [contactField, eventualMozContactField]
    var getall = navigator.mozContacts.getAll({});
    var contacts = [];

    getall.onsuccess = function() {
        if (getall.result) {
            contacts.push(createCordovaFromMozilla(getall.result));
            getall.continue();
        } else {
            successCB(contacts);
        }
    };
    getall.onerror = errorCB;
}

function search(successCB, errorCB, params) {
    var options = params[1] || {};
    if (!options.filter) {
        return _getAll(successCB, errorCB, params);
    }
    var filterBy = [];
    // filter and translate fields
    for (var i=0; i < params[0].length; i++) {
        var searchField = params[0][i];
        var mozField = getMozSearchField(searchField);
        if (searchField === 'name') {
            // Cordova uses name for search by all name fields.
            filterBy.push('givenName');
            filterBy.push('familyName');
            continue;
        }
        if (searchField === 'displayName' && 'init' in new mozContact()) {
            // ``init`` in ``mozContact`` indicates FFOS version 1.2 or below
            // Searching by name (in moz) is then forbidden
            console.log('FFOS ContactProxy: Unable to search by displayName on FFOS 1.2');
            continue;
        }
        if (mozField) {
            filterBy.push(mozField);
        } else {
            console.log('FXOS ContactProxy: inallowed field passed to search filtered out: ' + searchField);
        }
    }

    var mozOptions = {filterBy: filterBy, filterOp: 'startsWith'};
    if (!options.multiple) {
        mozOptions.filterLimit = 1;
    }
    mozOptions.filterValue = options.filter;
    var request = navigator.mozContacts.find(mozOptions);
    request.onsuccess = function() {
        var contacts = [];
        var mozContacts = request.result;
        var moz = mozContacts[0];
        for (var i=0; i < mozContacts.length; i++) {
            contacts.push(createCordovaFromMozilla(mozContacts[i]));
        }
        successCB(contacts);
    };
    request.onerror = errorCB;
}

module.exports = {
    save: saveContacts,
    remove: remove,
    search: search
};

require("cordova/firefoxos/commandProxy").add("Contacts", module.exports);<|endoftext|>


<commit_before>'use strict';

const webpack = require('webpack');
const WATCH = process.argv.indexOf('--watch') > -1;

module.exports = function(config) {
  config.set({

    // base path that will be used to resolve all patterns (eg. files, exclude)
    basePath: './',

    // frameworks to use
    // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
    frameworks: ['jasmine'],

    // list of files / patterns to load in the browser
    files: [
      'test/entry.ts'
    ],

    // list of files to exclude
    exclude: [
    ],

    // preprocess matching files before serving them to the browser
    // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
    preprocessors: {
      'test/entry.ts': ['webpack', 'sourcemap']
    },

    webpack: {
      resolve: {
        extensions: ['', '.ts', '.js'],
        alias: {
          sinon: 'sinon/pkg/sinon'
        }
      },
      module: {
        preLoaders: [{
          test: /\.ts$/, loader: 'tslint-loader', exclude: /node_modules/
        }],
        loaders: [{
          test: /\.ts$/, loader: 'ts-loader', exclude: /node_modules/
        }, {
          test: /sinon.js$/, loader: 'imports-loader?define=>false,require=>false'
        }],
        postLoaders: [{
          test: /src\/.+\.ts$/,
          exclude: /(test|node_modules)/,
          loader: 'sourcemap-istanbul-instrumenter-loader?force-sourcemap=true'
        }]
      },
      tslint: {
        emitErrors: !WATCH,
        failOnHint: false
      },
      plugins: [
        new webpack.SourceMapDevToolPlugin({
          filename: null,
          test: /\.(ts|js)($|\?)/i
        })
      ].concat(WATCH ? [] : [new webpack.NoErrorsPlugin()])
    },

    remapIstanbulReporter: {
      reports: { // eslint-disable-line
        'html': 'coverage/html',
        'text-summary': null
      }
    },

    // test results reporter to use
    // possible values: 'dots', 'progress'
    // available reporters: https://npmjs.org/browse/keyword/karma-reporter
    reporters: ['progress', 'coverage', 'karma-remap-istanbul'],

    // web server port
    port: 9876,

    // enable / disable colors in the output (reporters and logs)
    colors: true,

    // level of logging
    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
    logLevel: config.LOG_INFO,

    // enable / disable watching file and executing tests whenever any file changes
    autoWatch: WATCH,

    // start these browsers
    // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
    browsers: ['PhantomJS'],

    // Continuous Integration mode
    // if true, Karma captures browsers, runs the tests and exits
    singleRun: !WATCH
  });
};
<commit_msg>fix: Change to 'inline-source-map' to fix npm test<commit_after>'use strict';

const webpack = require('webpack');
const WATCH = process.argv.indexOf('--watch') > -1;

module.exports = function(config) {
  config.set({

    // base path that will be used to resolve all patterns (eg. files, exclude)
    basePath: './',

    // frameworks to use
    // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
    frameworks: ['jasmine'],

    // list of files / patterns to load in the browser
    files: [
      'test/entry.ts'
    ],

    // list of files to exclude
    exclude: [
    ],

    // preprocess matching files before serving them to the browser
    // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
    preprocessors: {
      'test/entry.ts': ['webpack', 'sourcemap']
    },

    webpack: {
      resolve: {
        extensions: ['', '.ts', '.js'],
        alias: {
          sinon: 'sinon/pkg/sinon'
        }
      },
      module: {
        preLoaders: [{
          test: /\.ts$/, loader: 'tslint-loader', exclude: /node_modules/
        }],
        loaders: [{
          test: /\.ts$/, loader: 'ts-loader', exclude: /node_modules/
        }, {
          test: /sinon.js$/, loader: 'imports-loader?define=>false,require=>false'
        }],
        postLoaders: [{
          test: /src\/.+\.ts$/,
          exclude: /(test|node_modules)/,
          loader: 'sourcemap-istanbul-instrumenter-loader?force-sourcemap=true'
        }]
      },
      tslint: {
        emitErrors: !WATCH,
        failOnHint: false
      },
      devtool: 'inline-source-map'
    },

    remapIstanbulReporter: {
      reports: { // eslint-disable-line
        'html': 'coverage/html',
        'text-summary': null
      }
    },

    // test results reporter to use
    // possible values: 'dots', 'progress'
    // available reporters: https://npmjs.org/browse/keyword/karma-reporter
    reporters: ['progress', 'coverage', 'karma-remap-istanbul'],

    // web server port
    port: 9876,

    // enable / disable colors in the output (reporters and logs)
    colors: true,

    // level of logging
    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
    logLevel: config.LOG_INFO,

    // enable / disable watching file and executing tests whenever any file changes
    autoWatch: WATCH,

    // start these browsers
    // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
    browsers: ['PhantomJS'],

    // Continuous Integration mode
    // if true, Karma captures browsers, runs the tests and exits
    singleRun: !WATCH
  });
};
<|endoftext|>


<commit_before>import React, { Component } from 'react';

class ToolbarDefault extends Component {
    constructor(props) {
        super(props);
    }

    render(){
        const {columnKeys, config, columns, filter } = this.props;
        const Filter = config.filter;

        const filters = columnKeys.map((currentColumnKey) => {
            let renderFilter = true;
            if(columns
            && columns[currentColumnKey]
            && columns[currentColumnKey].hideTools){
                renderFilter = false;
            }
            if(renderFilter){
                const filterObject = filter.filter((obj) => obj.columnName === currentColumnKey)[0];
                const query = filterObject ? filterObject.expression : '';
                return(

                    <Filter config={config} column={currentColumnKey} query={query} />

            );}
            else {return(	);}
        });

        return(

                {filters}

        );
    }
}

export default ToolbarDefault;<commit_msg>add prop types<commit_after>import React, { Component, PropTypes } from 'react';

class ToolbarDefault extends Component {
    constructor(props) {
        super(props);
    }

    render(){
        const {columnKeys, config, columns, filter } = this.props;
        const Filter = config.filter;

        const filters = columnKeys.map((currentColumnKey) => {
            let renderFilter = true;
            if(columns
            && columns[currentColumnKey]
            && columns[currentColumnKey].hideTools){
                renderFilter = false;
            }
            if(renderFilter){
                const filterObject = filter.filter((obj) => obj.columnName === currentColumnKey)[0];
                const query = filterObject ? filterObject.expression : '';
                return(

                    <Filter config={config} column={currentColumnKey} query={query} />

            );}
            else {return(	);}
        });

        return(

                {filters}

        );
    }
}

ToolbarDefault.propTypes = {
    config: PropTypes.object.isRequired, // TODO: refine
    columnKeys: PropTypes.array.isRequired,
    columns: PropTypes.object.isRequired // TODO: refine

};

export default ToolbarDefault;<|endoftext|>


<commit_before>/*
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 *
*/

// somehow call this function by this:
// exec(success, fail, "Contacts", "save", [dupContact]);
// Cordova contact definition:
// http://cordova.apache.org/docs/en/2.5.0/cordova_contacts_contacts.md.html#Contact
// FxOS contact definition:
// https://developer.mozilla.org/en-US/docs/Web/API/mozContact
function saveContacts(successCB, errorCB, contacts) {
    // success and fail will be called every time a contact is saved
    for (var contact in contacts) {
        var request;

        function success(result) {
            // TODO: this will need to amend the result
            successCB(result);
        }

        function exportContactFieldArray(contactFieldArray, key) {
            if (!key) {
                key = 'value';
            }

            var arr = [];

            for (var i in contactFieldArray) {
                arr.push(contactFieldArray[i][key]);
            };

            return arr;
        }

        function exportAddress (addresses) {
            // TODO: check moz address format
            var arr = [];

            for (var i in addresses) {
                var addr = {};

                for (var key in addresses[i]) {
                    addr[key] = addresses[i][key];
                }

                arr.push(addr);

            }

            return arr;
        }

        // prepare mozContact object
        // TODO: find a way to link existing mozContact and Contact by ID
        var moz = new mozContact({
            name: [contact.name.familyName,
                   contact.name.givenName,
                   contact.name.middleName,
                   contact.name.nickname],
            honorificPrefix: [contact.name.honorificPrefix],
            givenName: [contact.name.givenName],
            familyName: [contact.name.familyName],
            honorificSuffix: [contact.name.honorificSuffix],
            nickname: [contact.nickname],
            email: exportContactFieldArray(contact.emails),
            // photo: Blob
            // url: Array with metadata (?)
            category: exportContactFieldArray(contact.categories),
            adr: exportAddress(contact.addresses),
            tel: exportContactFieldArray(contact.phoneNumbers),
            org: exportContactFieldArray(contact.organizations, 'name'),
            jobTitle: exportContactFieldArray(contact.organizations, 'title'),
            bday: contact.birthday,
            note: contact.note,
            // impp: exportIM(contact.ims), TODO: find the moz impp definition
            // anniversary
            // sex
            // genderIdentity
            // key
        });

        request = navigator.mozContacts.save(moz);
        request.onsuccess = success;
        request.onerror = errorCB;
    }
}

module.exports = {
    save: saveContacts,
    remove: function(){},
    search: function(){},
};

require("cordova/firefoxos/commandProxy").add("Contacts", module.exports);
<commit_msg>attempt to save is failing trying to limit the translated contact fields to name and familyName, but still failing<commit_after>/*
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 *
*/

// somehow call this function by this:
// exec(success, fail, "Contacts", "save", [dupContact]);
// Cordova contact definition:
// http://cordova.apache.org/docs/en/2.5.0/cordova_contacts_contacts.md.html#Contact
// FxOS contact definition:
// https://developer.mozilla.org/en-US/docs/Web/API/mozContact
function saveContacts(successCB, errorCB, contacts) {
    // success and fail will be called every time a contact is saved
    function success(result) {
        // TODO: this will need to amend the result
        console.log('SUCCESS from moz');
        successCB(result);
    }
    function error(e) {
        console.log('BOO from moz');
        errorCB(e);
    }

    function exportContactFieldArray(contactFieldArray, key) {
        if (!key) {
            key = 'value';
        }

        var arr = [];

        for (var i in contactFieldArray) {
            arr.push(contactFieldArray[i][key]);
        };

        return arr;
    }

    function exportAddress (addresses) {
        // TODO: check moz address format
        var arr = [];

        for (var i in addresses) {
            var addr = {};

            for (var key in addresses[i]) {
                addr[key] = addresses[i][key];
            }

            arr.push(addr);

        }

        return arr;
    }
    var i=0;
    var contact;

    while(contact = contacts[i++]){
        var request;
        // prepare mozContact object
        var translatedContact = {};
        var nameArray = [];
        var j = 0;
        var field;
        var fields = ['honorificPrefix', 'familyName', 'givenName', 'middleName', 'nickname'];
        while(field = fields[j++]) {
            if (contact.name[field]) {
                nameArray.push(contact.name[field]);
            }
        }
        translatedContact.name = nameArray.join(' ');
        if (contact.name.honorificPrefix) {
            translatedContact.honorificPrefix = contact.name.honorificPrefix;
        }
        translatedContact.familyName = contact.name.familyName;
        /*
            honorificPrefix: [contact.name.honorificPrefix],
            givenName: [contact.name.givenName],
            familyName: [contact.name.familyName],
            honorificSuffix: [contact.name.honorificSuffix],
            nickname: [contact.nickname],
            email: exportContactFieldArray(contact.emails),
            // photo: Blob
            // url: Array with metadata (?)
            category: exportContactFieldArray(contact.categories),
            adr: exportAddress(contact.addresses),
            tel: exportContactFieldArray(contact.phoneNumbers),
            org: exportContactFieldArray(contact.organizations, 'name'),
            jobTitle: exportContactFieldArray(contact.organizations, 'title'),
            bday: contact.birthday,
            note: contact.note,
            // impp: exportIM(contact.ims), TODO: find the moz impp definition
            // anniversary
            // sex
            // genderIdentity
            // key
        }
        */
        // TODO: find a way to link existing mozContact and Contact by ID
        var moz = new mozContact(translatedContact);
        // XXX: moz.name is undefined

        request = navigator.mozContacts.save(moz);
        request.onsuccess = success;
        request.onerror = error;
    }
}

module.exports = {
    save: saveContacts,
    remove: function(){},
    search: function(){},
};

require("cordova/firefoxos/commandProxy").add("Contacts", module.exports);
<|endoftext|>


<commit_before>//jshint strict: false
module.exports = function(config) {
  config.set({

    basePath: './app',

    files: [
      'bower_components/angular/angular.js',
      'bower_components/angular-route/angular-route.js',
      'bower_components/angular-mocks/angular-mocks.js',
      'components/**/*.js',
      'view*/**/*.js'
    ],

    autoWatch: true,

    frameworks: ['jasmine'],

    browsers: ['Chrome'],

    plugins: [
      'karma-chrome-launcher',
      'karma-firefox-launcher',
      'karma-jasmine',
      'karma-junit-reporter'
    ],

    junitReporter: {
      outputFile: 'test_out/unit.xml',
      suite: 'unit'
    }

  });
};
<commit_msg>adding firefox on test karma<commit_after>//jshint strict: false
module.exports = function(config) {
  config.set({

    basePath: './app',

    files: [
      'bower_components/angular/angular.js',
      'bower_components/angular-route/angular-route.js',
      'bower_components/angular-mocks/angular-mocks.js',
      'components/**/*.js',
      'view*/**/*.js'
    ],

    autoWatch: true,

    frameworks: ['jasmine'],

    browsers: ['Firefox','Chrome'],

    plugins: [
      'karma-chrome-launcher',
      'karma-firefox-launcher',
      'karma-jasmine',
      'karma-junit-reporter'
    ],

    junitReporter: {
      outputFile: 'test_out/unit.xml',
      suite: 'unit'
    }

  });
};
<|endoftext|>


<commit_before>var TJAWriter = function() {
    this.segments = [];
    this.properties = {};
};

// TJAEvent and TJAComment

var TJAEvent = function(index, name, arg) {
    this.index = index;
    this.name = name.toUpperCase();
    this.arg = arg;
    this.line = '#' + this.name + (typeof arg == 'undefined' ? '' : ' ' + arg);
};

var TJAComment = function(index, content, breakLine) {
    this.index = index;
    this.content = content;
    if (!breakLine) this.keepLine = true;
    this.line = (breakLine ? '// ' : ' // ') + content;
};

var TJASegment = function(initSize) {
    this.size = initSize || 0;
    this.notes = [];
    for (var i = 0; i < this.size; ++i)
        this.notes.push('0');
    this.events = [];
    this.endEvents = [];
    this.eventSorted = true;
};

(function() {

    var TJAEventProto = {
        toString: function() {
            return this.line;
        }
    };

    TJAEvent.prototype = TJAEventProto;
    TJAComment.prototype = TJAEventProto;

    TJASegment.prototype = {
        toString: function() {
            var res = this.size == 0 ? '0' : '';

            if (!this.eventSorted) {
                this.events.sort(function (a, b) {
                    return a.index - b.index;
                })
                this.eventSorted = true;
            }

            var i_event = 0;
            var event = this.events.length == 0 ? null : this.events[0];
            var needBreakLine = false;
            var eventAtBegin = true;
            for (var index = 0; index < this.size; ++index) {
                var note = this.notes[index] || '0';
                while (event && event.index <= index) {
                    if (!(event.keepLine || (eventAtBegin && index == 0))) {
                        eventAtBegin = false;
                        res += '\r\n';
                    }
                    res += event;
                    ++i_event;
                    event = i_event < this.events.length ? this.events[i_event] : null;
                    needBreakLine = true;
                }
                if (needBreakLine) {
                    res += '\r\n';
                    needBreakLine = false;
                }
                res += note;
            }

            res += ',';

            for (i_event in this.endEvents) {
                event = this.endEvents[i_event];
                if (!event.keepLine)
                    res += '\r\n';
                res += event;
                needBreakLine = true;
            }

            return res;
        },

        addEvent: function(event) {
            if (typeof event == 'string')
                event = new TJAComment(0, event, false);
            this.events.push(event);
            this.eventSorted = false;
        },

        addEndEvent: function(event) {
            if (typeof event == 'string')
                event = new TJAComment(0, event, false);
            this.endEvents.push(event);
        }

    };

    // TJAWriter

    TJAWriter.prototype = {
        startEvent: new TJAEvent(0, 'START'),
        endEvent: new TJAEvent(0, 'END'),

        //constructor: TJAWriter,

        prop: function(key, value) {
            this.properties[key.toUpperCase()] = (typeof value != 'undefined') ? value : '[Unknown]';
        },

        generateString: function() {
            var res = '';
            for (var prop in this.properties) {
                var value = this.properties[prop];
                res += prop + ':' + value + '\r\n';
            }
            res += '\r\n';
            res += this.startEvent + '\r\n';
            for (var i_seg in this.segments) {
                var segment = this.segments[i_seg];
                res += segment + '\r\n';
            }
            res += this.endEvent + '\r\n';
            res += '\r\n';
            return res;
        },

        addTestData: function() {
            this.properties['PROP2'] = 'first';
            this.properties['PROP1'] = 765765;
            this.properties['PROP3'] = 'value';

            var segment = new TJASegment(16);
            segment.notes = '1020112010201';
            segment.addEvent(new TJAEvent(8, 'BARLINEOFF'))
            segment.addEvent(new TJAEvent(12, 'SCROLL', 1.0))
            this.segments.push(segment);

            segment = new TJASegment(8);
            segment.notes = '2010221020102';
            segment.addEvent(new TJAEvent(0, 'TeSTeVeNT', 'TeSTaRGuMeNT'));
            segment.addEvent(new TJAEvent(8, 'err..', 'nothing.'));
            this.segments.push(segment);

            segment = new TJASegment(8);
            segment.notes = '12345678';
            segment.addEvent(new TJAComment(1, 'This is a comment.'));
            segment.addEvent(new TJAComment(2, 'This is a comment on new line.', true));
            segment.addEndEvent('This is a comment after comma.');
            this.segments.push(segment);

            segment = new TJASegment(0);
            segment.addEndEvent(new TJAEvent(0, 'EndEvent'));
            this.segments.push(segment);

            /*
            Expected Output:
            PROP2:first
            PROP1:765765
            PROP3:value

            #START
            10201120
            #BARLINEOFF
            1020
            #SCROLL 1
            1000,
            #TESTEVENT TeSTaRGuMeNT
            20102210,
            1 // This is a comment.
            2
            // This is a comment on new line.
            345678, // This is a comment after comma.
            0,
            #ENDEVENT
            #END
            */

        },

    };

})();

<commit_msg>Fix newline in TJAWriter<commit_after>var TJAWriter = function() {
    this.segments = [];
    this.properties = {};
};

// TJAEvent and TJAComment

var TJAEvent = function(index, name, arg) {
    this.index = index;
    this.name = name.toUpperCase();
    this.arg = arg;
    this.line = '#' + this.name + (typeof arg == 'undefined' ? '' : ' ' + arg);
};

var TJAComment = function(index, content, breakLine) {
    this.index = index;
    this.content = content;
    if (!breakLine) this.keepLine = true;
    this.line = (breakLine ? '// ' : ' // ') + content;
};

var TJASegment = function(initSize) {
    this.size = initSize || 0;
    this.notes = [];
    for (var i = 0; i < this.size; ++i)
        this.notes.push('0');
    this.events = [];
    this.endEvents = [];
    this.eventSorted = true;
};

(function() {

    var TJAEventProto = {
        toString: function() {
            return this.line;
        }
    };

    TJAEvent.prototype = TJAEventProto;
    TJAComment.prototype = TJAEventProto;

    TJASegment.prototype = {
        toString: function() {
            var res = this.size == 0 ? '0' : '';

            if (!this.eventSorted) {
                this.events.sort(function (a, b) {
                    return a.index - b.index;
                })
                this.eventSorted = true;
            }

            var i_event = 0;
            var event = this.events.length == 0 ? null : this.events[0];
            var eventAtBegin = true;
            for (var index = 0; index < this.size; ++index) {
                var note = this.notes[index] || '0';
                while (event && event.index <= index) {
                    if (!(event.keepLine || (eventAtBegin && index == 0))) {
                        eventAtBegin = false;
                        res += '\r\n';
                    }
                    res += event;
                    res += '\r\n';
                    ++i_event;
                    event = i_event < this.events.length ? this.events[i_event] : null;
                }
                res += note;
            }

            res += ',';

            for (i_event in this.endEvents) {
                event = this.endEvents[i_event];
                if (!event.keepLine)
                    res += '\r\n';
                res += event;
                res += '\r\n';
            }

            return res;
        },

        addEvent: function(event) {
            if (typeof event == 'string')
                event = new TJAComment(0, event, false);
            this.events.push(event);
            this.eventSorted = false;
        },

        addEndEvent: function(event) {
            if (typeof event == 'string')
                event = new TJAComment(0, event, false);
            this.endEvents.push(event);
        }

    };

    // TJAWriter

    TJAWriter.prototype = {
        startEvent: new TJAEvent(0, 'START'),
        endEvent: new TJAEvent(0, 'END'),

        //constructor: TJAWriter,

        prop: function(key, value) {
            this.properties[key.toUpperCase()] = (typeof value != 'undefined') ? value : '[Unknown]';
        },

        generateString: function() {
            var res = '';
            for (var prop in this.properties) {
                var value = this.properties[prop];
                res += prop + ':' + value + '\r\n';
            }
            res += '\r\n';
            res += this.startEvent + '\r\n';
            for (var i_seg in this.segments) {
                var segment = this.segments[i_seg];
                res += segment + '\r\n';
            }
            res += this.endEvent + '\r\n';
            res += '\r\n';
            return res;
        },

        addTestData: function() {
            this.properties['PROP2'] = 'first';
            this.properties['PROP1'] = 765765;
            this.properties['PROP3'] = 'value';

            var segment = new TJASegment(16);
            segment.notes = '1020112010201';
            segment.addEvent(new TJAEvent(8, 'BARLINEOFF'))
            segment.addEvent(new TJAEvent(12, 'SCROLL', 1.0))
            this.segments.push(segment);

            segment = new TJASegment(8);
            segment.notes = '2010221020102';
            segment.addEvent(new TJAEvent(0, 'TeSTeVeNT', 'TeSTaRGuMeNT'));
            segment.addEvent(new TJAEvent(8, 'err..', 'nothing.'));
            this.segments.push(segment);

            segment = new TJASegment(8);
            segment.notes = '12345678';
            segment.addEvent(new TJAComment(1, 'This is a comment.'));
            segment.addEvent(new TJAComment(2, 'This is a comment on new line.', true));
            segment.addEndEvent('This is a comment after comma.');
            this.segments.push(segment);

            segment = new TJASegment(0);
            segment.addEndEvent(new TJAEvent(0, 'EndEvent'));
            this.segments.push(segment);

            /*
            Expected Output:
            PROP2:first
            PROP1:765765
            PROP3:value

            #START
            10201120
            #BARLINEOFF
            1020
            #SCROLL 1
            1000,
            #TESTEVENT TeSTaRGuMeNT
            20102210,
            1 // This is a comment.
            2
            // This is a comment on new line.
            345678, // This is a comment after comma.
            0,
            #ENDEVENT
            #END
            */

        },

    };

})();

<|endoftext|>


<commit_before>module.exports = function (config) {
	config.set({
		basePath : '',
		frameworks: ['jasmine', 'requirejs'],
        files: [
//			'../lib/**/*.js',
//			'../test/spec/*.js'
//            {pattern: 'test-main.js', included: true},
            'test-main.js',
            {pattern: '*test.js', included: false},
            {pattern: 'sqr*.js', included: false},
            {pattern: 'lib/**/*.js', included: false},
        ],
        exclude: [
			'lib/requireconfig.js',
			'karma.conf.js'
        ],
		browsers : ['PhantomJS'],
		reporters : ['progress', 'spec', 'coverage', 'html'],
		preprocessors: {
			//'../lib/**/*.js': 'coverage'
            '*.js': 'coverage',
            '**/*.js': 'coverage',
		},
		coverageReporter: {
			type : 'text'//,
			//dir : 'coverage/'
		},
		colors: true,
		logLevel: config.LOG_DEBUG,
		htmlReporter: {
			outputFile: 'results/unit-tests.html'
		},
		singleRun: true,
		plugins : [
			'karma-spec-reporter',
			'karma-phantomjs-launcher',
			'karma-jasmine',
			'karma-coverage',
            'karma-htmlfile-reporter',
            'karma-requirejs'
		]
	});
};
<commit_msg>go for coverage v2<commit_after>module.exports = function (config) {
	config.set({
		basePath : '',
		frameworks: ['jasmine', 'requirejs'],
        files: [
//			'../lib/**/*.js',
//			'../test/spec/*.js'
//            {pattern: 'test-main.js', included: true},
            'test-main.js',
            {pattern: '*test.js', included: false},
            {pattern: 'sqr*.js', included: false},
            {pattern: 'lib/**/*.js', included: false},
        ],
        exclude: [
			'lib/requireconfig.js',
			'karma.conf.js'
        ],
		browsers : ['PhantomJS'],
		reporters : ['progress', 'spec', 'coverage', 'html'],
		preprocessors: {
			//'../lib/**/*.js': 'coverage'
            '*.js': 'coverage',
            'lib/**/*.js': 'coverage',
		},
		coverageReporter: {
			type : 'text'//,
			//dir : 'coverage/'
		},
		colors: true,
		logLevel: config.LOG_DEBUG,
		htmlReporter: {
			outputFile: 'results/unit-tests.html'
		},
		singleRun: true,
		plugins : [
			'karma-spec-reporter',
			'karma-phantomjs-launcher',
			'karma-jasmine',
			'karma-coverage',
            'karma-htmlfile-reporter',
            'karma-requirejs'
		]
	});
};
<|endoftext|>


<commit_before>/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is Ajax.org Code Editor (ACE).
 *
 * The Initial Developer of the Original Code is
 * Ajax.org B.V.
 * Portions created by the Initial Developer are Copyright (C) 2010
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *      Fabian Jakobs <fabian AT ajax DOT org>
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

define(function(require, exports, module) {

var event = require("pilot/event");

var RenderLoop = function(onRender) {
    this.onRender = onRender;
    this.pending = false;
    this.changes = 0;
};

(function() {

    this.schedule = function(change) {
        //this.onRender(change);
        //return;
        this.changes = this.changes | change;
        if (!this.pending) {
            this.pending = true;
            var _self = this;
            this.setTimeoutZero(function() {
                _self.pending = false;
                var changes = _self.changes;
                _self.changes = 0;
                _self.onRender(changes);
            })
        }
    };

    if (window.postMessage) {

        this.messageName = "zero-timeout-message";

        this.setTimeoutZero = function(callback) {
            if (!this.attached) {
                var _self = this;
                event.addListener(window, "message", function(e) {
                    if (_self.callback && e.data == _self.messageName) {
                        event.stopPropagation(e);
                        _self.callback();
                    }
                });
                this.attached = true;
            }
            this.callback = callback;
            window.postMessage(this.messageName, "*");
        }

    } else {

        this.setTimeoutZero = function(callback) {
            setTimeout(callback, 0);
        }
    }

}).call(RenderLoop.prototype);

exports.RenderLoop = RenderLoop;
});
<commit_msg>use requestAnimationFrame to allow new browsers to schedule renders for better performance<commit_after>/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is Ajax.org Code Editor (ACE).
 *
 * The Initial Developer of the Original Code is
 * Ajax.org B.V.
 * Portions created by the Initial Developer are Copyright (C) 2010
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *      Fabian Jakobs <fabian AT ajax DOT org>
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

define(function(require, exports, module) {

var event = require("pilot/event");

var RenderLoop = function(onRender) {
    this.onRender = onRender;
    this.pending = false;
    this.changes = 0;
};

(function() {

    this.schedule = function(change) {
        //this.onRender(change);
        //return;
        this.changes = this.changes | change;
        if (!this.pending) {
            this.pending = true;
            var _self = this;
            this.setTimeoutZero(function() {
                _self.pending = false;
                var changes = _self.changes;
                _self.changes = 0;
                _self.onRender(changes);
            })
        }
    };

    this.setTimeoutZero = window.requestAnimationFrame ||
         window.webkitRequestAnimationFrame ||
         window.mozRequestAnimationFrame ||
         window.oRequestAnimationFrame ||
         window.msRequestAnimationFrame;

    if (this.setTimeoutZero) {

        this.setTimeoutZero = this.setTimeoutZero.bind(window)
    } else if (window.postMessage) {

        this.messageName = "zero-timeout-message";

        this.setTimeoutZero = function(callback) {
            if (!this.attached) {
                var _self = this;
                event.addListener(window, "message", function(e) {
                    if (_self.callback && e.data == _self.messageName) {
                        event.stopPropagation(e);
                        _self.callback();
                    }
                });
                this.attached = true;
            }
            this.callback = callback;
            window.postMessage(this.messageName, "*");
        }

    } else {

        this.setTimeoutZero = function(callback) {
            setTimeout(callback, 0);
        }
    }

}).call(RenderLoop.prototype);

exports.RenderLoop = RenderLoop;
});
<|endoftext|>