<commit_before>"use strict";

var util = require("substance-util");
var Test = require('substance-test');
var assert = Test.assert;
var Chronicle = require('../index');
var IndexedDBBackend = require("../src/backends/indexeddb_backend");
var TextOperation = require('substance-operator').TextOperation;

// Fixture
// -------

var OP1 = TextOperation.Insert(0, "Lorem amet");
var OP2 = TextOperation.Insert(5, " ipsum");
var OP3 = TextOperation.Insert(12, "dolor ");
var OP4 = TextOperation.Insert(18, "sit ");
var OP5_1 = TextOperation.Insert(5, " sit");
var OP5_2 = TextOperation.Insert(6, "sit ");

var TestDocument = function(chronicle) {
  this.text = "";
  this.chronicle = chronicle;
  chronicle.manage(new Chronicle.TextOperationAdapter(chronicle, this));

  this.setText = function(text) {
    this.text = text;
  };

  this.getText = function() {
    return this.text;
  };

  this.apply = function(op) {
    this.text = op.apply(this.text);
    return this.chronicle.record(op);
  };

  this.toJSON = function() {
    return {
      text: this.text
    };
  };
};

var DB_NAME = "substance.chronicle.test";

// Test
// ========

var IndexedDbBackendTest = function() {
  Test.call(this);
};

IndexedDbBackendTest.Prototype = function() {

  this.fixture = function() {
    var chronicle = Chronicle.create();
    var document = new TestDocument(chronicle);
    this.applyChanges(document);
    return document;
  };

  this.applyChanges = function(document) {
    this.ID1 = document.apply(OP1);
    this.ID2 = document.apply(OP2);
    this.ID3 = document.apply(OP3);
    this.ID4 = document.apply(OP4);
    document.chronicle.open(this.ID1);
    this.ID5_1 = document.apply(OP5_1);
    document.chronicle.open(this.ID1);
    this.ID5_2 = document.apply(OP5_2);
    document.chronicle.open("ROOT");
  };

  this.actions = [
    "Delete exisiting database", function(cb) {
      // delete the database initially
      var __id__ = util.uuid();
      console.log("Deleting test database...", __id__);
      var request = window.indexedDB.deleteDatabase(DB_NAME);
      request.onsuccess = function() {
        console.log("...deleted.", __id__);
        cb(null);
      };
      request.onerror = function(error) {
        console.log("...failed.", __id__);
        cb(error);
      };
    },

    "Open and close database", function(cb) {
      var backend = new IndexedDBBackend(DB_NAME, Chronicle.Index.create());

      util.async.sequential({
        "functions": [
          function(cb) {
            backend.open(cb);
          }
        ],
        "finally": function(err) {
          backend.close(function() {
            cb(err, null);
          });
        }
      }, cb);
    },

    "Save an index", function(cb) {
      var document = this.fixture();
      var backend = new IndexedDBBackend(DB_NAME, document.chronicle.index);
      util.async.sequential({
        "functions": [
          function(cb) {
            backend.open(cb);
          },
          function(cb) {
            backend.save(cb);
          }
        ],
        "finally": function(err) {
          backend.close(function() {
            cb(err, null);
          });
        }
      }, cb);
    },

    "Load the index", function(cb) {
      var self = this;
      var index = Chronicle.Index.create();
      var backend = new IndexedDBBackend(DB_NAME, index);
      util.async.sequential({
        "functions": [
          function(cb) {
            backend.open(cb);
          },
          function(cb) {
            backend.load(cb);
          },
          function() {
            assert.isTrue(index.contains(self.ID1));
            assert.isTrue(index.contains(self.ID2));
            assert.isTrue(index.contains(self.ID3));
            assert.isTrue(index.contains(self.ID4));
            assert.isTrue(index.contains(self.ID5_1));
            assert.isTrue(index.contains(self.ID5_2));
          }
        ],
        "finally": function(err) {
          backend.close(function() {
            cb(err, null);
          });
        }
      }, cb);
    },

    "Save a snapshot", function(cb) {
      var self = this;
      var document = this.fixture();
      var backend = new IndexedDBBackend(DB_NAME, document.chronicle.index);
      document.chronicle.open("ROOT");

      util.async.sequential({
        "functions": [
          function(cb) {
            backend.open(cb);
          },
          function(cb) {
            backend.saveSnapshot("ROOT", document, cb);
          },
          function(cb) {
            document.chronicle.open(self.ID5_1);
            backend.saveSnapshot(self.ID5_1, document, cb);
          }
        ],
        "finally": function(err) {
          backend.close(function() {
            cb(err, null);
          });
        }
      }, cb);
    },

    "List snapshots", function(cb) {
      var self = this;
      var backend = new IndexedDBBackend(DB_NAME, Chronicle.Index.create());
      util.async.sequential({
        "functions": [
          function(cb) {
            backend.open(cb);
          },
          function(cb) {
            backend.listSnapshots(cb);
          },
          function(snapshots, cb) {
            assert.isDefined(snapshots);
            assert.isEqual(2, snapshots.length);
            assert.isTrue(snapshots.indexOf("ROOT") >= 0);
            assert.isTrue(snapshots.indexOf(self.ID5_1) >= 0);
            cb(null);
          }
        ],
        "finally": function(err) {
          backend.close(function() {
            cb(err, null);
          });
        }
      }, cb);
    }
  ];
};
IndexedDbBackendTest.Prototype.prototype = Test.prototype;
IndexedDbBackendTest.Prototype = new IndexedDbBackendTest.Prototype();

Test.registerTest(['Substance.Chronicle', 'IndexedDB Backend'], new IndexedDbBackendTest());
<commit_msg>Node-webkit compatibility.<commit_after>"use strict";

var util = require("substance-util");
var Test = require('substance-test');
var assert = Test.assert;
var Chronicle = require('../index');
var IndexedDBBackend = require("../src/backends/indexeddb_backend");
var TextOperation = require('substance-operator').TextOperation;

// Fixture
// -------

var OP1 = TextOperation.Insert(0, "Lorem amet");
var OP2 = TextOperation.Insert(5, " ipsum");
var OP3 = TextOperation.Insert(12, "dolor ");
var OP4 = TextOperation.Insert(18, "sit ");
var OP5_1 = TextOperation.Insert(5, " sit");
var OP5_2 = TextOperation.Insert(6, "sit ");

var TestDocument = function(chronicle) {
  this.text = "";
  this.chronicle = chronicle;
  chronicle.manage(new Chronicle.TextOperationAdapter(chronicle, this));

  this.setText = function(text) {
    this.text = text;
  };

  this.getText = function() {
    return this.text;
  };

  this.apply = function(op) {
    this.text = op.apply(this.text);
    return this.chronicle.record(op);
  };

  this.toJSON = function() {
    return {
      text: this.text
    };
  };
};

var DB_NAME = "substance.chronicle.test";

// Test
// ========

var IndexedDbBackendTest = function() {
  Test.call(this);
};

IndexedDbBackendTest.Prototype = function() {

  this.fixture = function() {
    var chronicle = Chronicle.create();
    var document = new TestDocument(chronicle);
    this.applyChanges(document);
    return document;
  };

  this.applyChanges = function(doc) {
    this.ID1 = doc.apply(OP1);
    this.ID2 = doc.apply(OP2);
    this.ID3 = doc.apply(OP3);
    this.ID4 = doc.apply(OP4);
    doc.chronicle.open(this.ID1);
    this.ID5_1 = doc.apply(OP5_1);
    doc.chronicle.open(this.ID1);
    this.ID5_2 = doc.apply(OP5_2);
    doc.chronicle.open("ROOT");
  };

  this.actions = [
    "Delete exisiting database", function(cb) {
      // delete the database initially
      var __id__ = util.uuid();
      console.log("Deleting test database...", __id__);
      var request = window.indexedDB.deleteDatabase(DB_NAME);
      request.onsuccess = function() {
        console.log("...deleted.", __id__);
        cb(null);
      };
      request.onerror = function(error) {
        console.log("...failed.", __id__);
        cb(error);
      };
    },

    "Open and close database", function(cb) {
      var backend = new IndexedDBBackend(DB_NAME, Chronicle.Index.create());

      util.async.sequential({
        "functions": [
          function(cb) {
            backend.open(cb);
          }
        ],
        "finally": function(err) {
          backend.close(function() {
            cb(err, null);
          });
        }
      }, cb);
    },

    "Save an index", function(cb) {
      var document = this.fixture();
      var backend = new IndexedDBBackend(DB_NAME, document.chronicle.index);
      util.async.sequential({
        "functions": [
          function(cb) {
            backend.open(cb);
          },
          function(cb) {
            backend.save(cb);
          }
        ],
        "finally": function(err) {
          backend.close(function() {
            cb(err, null);
          });
        }
      }, cb);
    },

    "Load the index", function(cb) {
      var self = this;
      var index = Chronicle.Index.create();
      var backend = new IndexedDBBackend(DB_NAME, index);
      util.async.sequential({
        "functions": [
          function(cb) {
            backend.open(cb);
          },
          function(cb) {
            backend.load(cb);
          },
          function() {
            assert.isTrue(index.contains(self.ID1));
            assert.isTrue(index.contains(self.ID2));
            assert.isTrue(index.contains(self.ID3));
            assert.isTrue(index.contains(self.ID4));
            assert.isTrue(index.contains(self.ID5_1));
            assert.isTrue(index.contains(self.ID5_2));
          }
        ],
        "finally": function(err) {
          backend.close(function() {
            cb(err, null);
          });
        }
      }, cb);
    },

    "Save a snapshot", function(cb) {
      var self = this;
      var document = this.fixture();
      var backend = new IndexedDBBackend(DB_NAME, document.chronicle.index);
      document.chronicle.open("ROOT");

      util.async.sequential({
        "functions": [
          function(cb) {
            backend.open(cb);
          },
          function(cb) {
            backend.saveSnapshot("ROOT", document, cb);
          },
          function(cb) {
            document.chronicle.open(self.ID5_1);
            backend.saveSnapshot(self.ID5_1, document, cb);
          }
        ],
        "finally": function(err) {
          backend.close(function() {
            cb(err, null);
          });
        }
      }, cb);
    },

    "List snapshots", function(cb) {
      var self = this;
      var backend = new IndexedDBBackend(DB_NAME, Chronicle.Index.create());
      util.async.sequential({
        "functions": [
          function(cb) {
            backend.open(cb);
          },
          function(cb) {
            backend.listSnapshots(cb);
          },
          function(snapshots, cb) {
            assert.isDefined(snapshots);
            assert.isEqual(2, snapshots.length);
            assert.isTrue(snapshots.indexOf("ROOT") >= 0);
            assert.isTrue(snapshots.indexOf(self.ID5_1) >= 0);
            cb(null);
          }
        ],
        "finally": function(err) {
          backend.close(function() {
            cb(err, null);
          });
        }
      }, cb);
    }
  ];
};
IndexedDbBackendTest.Prototype.prototype = Test.prototype;
IndexedDbBackendTest.Prototype = new IndexedDbBackendTest.Prototype();

Test.registerTest(['Substance.Chronicle', 'IndexedDB Backend'], new IndexedDbBackendTest());
<|endoftext|>


<commit_before>/*global cordova, module*/

var exec = require("cordova/exec")

/*
 * Format of the returned value:
 * {
 *    "connectUrl": "...",
 *    "isSkipAuth": true/false,
 *    "googleWebAppClientID": "...",
 *    "ios": {
 *       "googleClientID": "...",
 *       "googleClientSecret": "...",
 *       "parseAppID": "...",
 *       "parseClientID": "...",
 *    }
 * }
 */

var ConnectionSettings = {
    getSettings: function (successCallback, errorCallback) {
        exec(successCallback, errorCallback, "ConnectionSettings", "getSettings", []);
    }
}

module.exports = ConnectionSettings;
<commit_msg>added Promise to getSetting function<commit_after>/*global cordova, module*/

var exec = require("cordova/exec")

/*
 * Format of the returned value:
 * {
 *    "connectUrl": "...",
 *    "isSkipAuth": true/false,
 *    "googleWebAppClientID": "...",
 *    "ios": {
 *       "googleClientID": "...",
 *       "googleClientSecret": "...",
 *       "parseAppID": "...",
 *       "parseClientID": "...",
 *    }
 * }
 */

var ConnectionSettings = {
    getSettings: function () {
    	return new Promise(function(resolve, reject) {
    		exec(resolve, reject, "ConnectionSettings", "getSettings", []);
    	});
    }
}

module.exports = ConnectionSettings;
<|endoftext|>


<commit_before>// originally from phonegap-nfc.js by Don Coleman
// adapted for react-native-nfc-manager by Richie Hsieh

const {
  createNdefRecord,
  encodeNdefMessage,
  decodeNdefMessage,
  equalToRecordType,
} = require('./ndef');
const constants = require('./constants');
const util = require('./util');
const textHelper = require('./ndef-text');
const uriHelper = require('./ndef-uri');
const wifiSimpleHelper = require('./ndef-wifi-simple');

const WellKnownRecord = {
  emptyRecord() {
    return createNdefRecord(constants.TNF_EMPTY, [], [], []);
  },

  absoluteUriRecord(uri, payload = [], id = []) {
    return createNdefRecord(constants.TNF_ABSOLUTE_URI, uri, id, payload);
  },

  mimeMediaRecord(mimeType, payload, id = []) {
    return createNdefRecord(constants.TNF_MIME_MEDIA, mimeType, id, payload);
  },

  textRecord(text, languageCode, id = []) {
    return createNdefRecord(
      constants.TNF_WELL_KNOWN,
      constants.RTD_TEXT,
      id,
      textHelper.encodePayload(text, languageCode),
    );
  },

  uriRecord(uri, id = []) {
    return createNdefRecord(
      constants.TNF_WELL_KNOWN,
      constants.RTD_URI,
      id,
      uriHelper.encodePayload(uri),
    );
  },

  smartPoster(ndefRecords, id = []) {
    let payload = [];

    if (ndefRecords) {
      // make sure we have an array of something like NDEF records before encoding
      if (
        ndefRecords[0] instanceof Object &&
        ndefRecords[0].hasOwnProperty('tnf')
      ) {
        payload = encodeNdefMessage(ndefRecords);
      } else {
        // assume the caller has already encoded the NDEF records into a byte array
        payload = ndefRecords;
      }
    } else {
      console.log('WARNING: Expecting an array of NDEF records');
    }

    return createNdefRecord(
      constants.TNF_WELL_KNOWN,
      constants.RTD_SMART_POSTER,
      id,
      payload,
    );
  },
};

const ExtraRecord = {
  androidApplicationRecord(packageName) {
    return createNdefRecord(
      constants.TNF_EXTERNAL_TYPE,
      'android.com:pkg',
      [],
      packageName,
    );
  },

  wifiSimpleRecord: function (credentials, id) {
    let payload = wifiSimpleHelper.encodePayload(credentials);
    if (!id) {
      id = [];
    }
    return WellKnownRecord.mimeMediaRecord(constants.MIME_WFA_WSC, payload, id);
  },
};

const NDEF = {
  ...constants,
  ...WellKnownRecord,
  ...ExtraRecord,

  record: createNdefRecord,
  encodeMessage: encodeNdefMessage,
  decodeMessage: decodeNdefMessage,
  isType: equalToRecordType,

  // individual record type helpers
  text: textHelper,
  uri: uriHelper,
  wifiSimple: wifiSimpleHelper,

  // other helpers
  util,
};

module.exports = NDEF;
<commit_msg>refactor: extract externalTypeRecord, and use it for AAR<commit_after>// originally from phonegap-nfc.js by Don Coleman
// adapted for react-native-nfc-manager by Richie Hsieh

const {
  createNdefRecord,
  encodeNdefMessage,
  decodeNdefMessage,
  equalToRecordType,
} = require('./ndef');
const constants = require('./constants');
const util = require('./util');
const textHelper = require('./ndef-text');
const uriHelper = require('./ndef-uri');
const wifiSimpleHelper = require('./ndef-wifi-simple');

const PrimitiveRecord = {
  emptyRecord() {
    return createNdefRecord(constants.TNF_EMPTY, [], [], []);
  },

  absoluteUriRecord(uri, payload = [], id = []) {
    return createNdefRecord(constants.TNF_ABSOLUTE_URI, uri, id, payload);
  },

  mimeMediaRecord(mimeType, payload, id = []) {
    return createNdefRecord(constants.TNF_MIME_MEDIA, mimeType, id, payload);
  },

  externalTypeRecord(externalType, payload, id = []) {
    return createNdefRecord(
      constants.TNF_EXTERNAL_TYPE,
      externalType,
      id,
      payload,
    );
  },
};

const WellKnownRecord = {
  textRecord(text, languageCode, id = []) {
    return createNdefRecord(
      constants.TNF_WELL_KNOWN,
      constants.RTD_TEXT,
      id,
      textHelper.encodePayload(text, languageCode),
    );
  },

  uriRecord(uri, id = []) {
    return createNdefRecord(
      constants.TNF_WELL_KNOWN,
      constants.RTD_URI,
      id,
      uriHelper.encodePayload(uri),
    );
  },

  smartPoster(ndefRecords, id = []) {
    let payload = [];

    if (ndefRecords) {
      // make sure we have an array of something like NDEF records before encoding
      if (
        ndefRecords[0] instanceof Object &&
        ndefRecords[0].hasOwnProperty('tnf')
      ) {
        payload = encodeNdefMessage(ndefRecords);
      } else {
        // assume the caller has already encoded the NDEF records into a byte array
        payload = ndefRecords;
      }
    } else {
      console.log('WARNING: Expecting an array of NDEF records');
    }

    return createNdefRecord(
      constants.TNF_WELL_KNOWN,
      constants.RTD_SMART_POSTER,
      id,
      payload,
    );
  },
};

const ExtraTypeRecord = {
  androidApplicationRecord(packageName, id = []) {
    return PrimitiveRecord.externalTypeRecord(
      'android.com:pkg',
      id,
      packageName,
    );
  },

  wifiSimpleRecord: function (credentials, id = []) {
    let payload = wifiSimpleHelper.encodePayload(credentials);
    return PrimitiveRecord.mimeMediaRecord(constants.MIME_WFA_WSC, payload, id);
  },
};

const NDEF = {
  ...constants,
  ...PrimitiveRecord,
  ...WellKnownRecord,
  ...ExtraTypeRecord,

  record: createNdefRecord,
  encodeMessage: encodeNdefMessage,
  decodeMessage: decodeNdefMessage,
  isType: equalToRecordType,

  // individual record type helpers
  text: textHelper,
  uri: uriHelper,
  wifiSimple: wifiSimpleHelper,

  // other helpers
  util,
};

module.exports = NDEF;
<|endoftext|>


<commit_before>var FNCart = Class.create({
  initialize: function() {
    // Intercept the product add form submit event.

    this.boundAddSubmit = this.onAddFormSubmit.bind(this);
    $$('.product_add').each(function(form, index) {
      form.observe('submit', this.boundAddSubmit);
    }, this);

    if($('virtualshop_form')) {
      this.boundAddVirtualSubmit = this.onVirtualShopAddFormSubmit.bind(this);
      $('virtualshop_form').observe('submit', this.boundAddVirtualSubmit);
    }

    if($('save_details_button')) {
      $('save_details_button').observe('click', this.onSaveDetailsButtonClick.bind(this));
    }

    this._prepareCart();
  },

  /**
   *  Handler for the Submit event on the product add forms.
   */
  onAddFormSubmit: function(ev){
    ev.stop();

    // Get the quantity to add.
    quantity = $(ev.target).down('.quantity').value;
    // Bail out if the quantity isn't a number.
    if(!this._isStringANumber(quantity)) return false;

    // url = ev.target.action+quantity+"/";
    url = ev.target.action;

    // If the item is a bundle, submit its sub items and their quantities.
    var serializedData = ev.target.serialize();
    if($(ev.target).up('.has_bundle')) {
      var serializedBundleData = $(ev.target).up('.has_bundle').down('.bundle_product_add').serialize();
      serializedData = serializedBundleData + "&" + serializedData;
    }
    console.log(serializedData);
    this.updateCart(url, serializedData);
  },

  /**
   *  Handler for submission of the add write-in form.
   */
  onAddWriteinFormSubmit: function(ev) {
    ev.stop();
    this.updateCart(ev.target.action, ev.target.serialize());
  },

  /**
   *  Handler for submission of the add virtual shop item form.
   */
  onVirtualShopAddFormSubmit: function(ev) {
    ev.stop();
    var completeAction = function(transport) {
      this._prepareCart();
      if($$('.cart_error').length == 0)
        ev.target.reset();
    }.bind(this);
    this.updateCart(ev.target.action, ev.target.serialize(), completeAction);
  },

  /**
   *  Handler for the remove item button.
   */
  onRemoveItem: function(ev) {
    ev.stop();
    this.updateCart($(ev.target).up('a.remove_item').href);
  },

  /**
   *  Handler for the click event on the empty list link.
   */
  onEmptyList: function(ev) {
    ev.stop();
    this.updateCart("/cart/empty/");
  },

  /**
   *  Handler for the submission of the cart form (Update cart).
   */
  onCartUpdateSubmit: function(ev) {
    ev.stop();
    if(ev.target.serialize() != this.serializedCart) {
      // console.info(ev.target.serialize());
      this.updateCart(ev.target.action, ev.target.serialize());
    } else {
      // console.info('Cart has not changed.');
    }
  },

  /**
   *  Handler for when a key is pressed in the cart inputs.
   */
  onCartKeyUp: function(ev) {
    if(ev.target.serialize() != this.serializedCart)
      $('update_list_button').enable();
    else
      $('update_list_button').disable();
  },

  /**
   *  Updates the cart with a given url and parameters, updating the cart container with the successful return value.
   */
  updateCart: function(url, parameters, completeAction) {

    completeAction = completeAction || function() {this._prepareCart();}.bind(this);

    this._prepareCartForUpdate();

    $('cart_load_indicator').show();

    new Ajax.Updater({success:'cart_content', failure:'cart_notice'}, url, {
      method:'post',
      parameters:parameters,
      onComplete: completeAction
    });
  },

  /**
   *  Handler for click of the save details button.
   */
  onSaveDetailsButtonClick: function(ev) {
    new Ajax.Request('/cart/savedetails/', {
      method: 'post',
      parameters:$('order_details').serialize(),
      onSuccess: function(transport) {
        window.location.replace("/catalogue/aisle/");
      },
      onFailure: function(transport) {

      }
    });
  },

  /**
   *  Prepares the cart at initialize and each time it's been updated.
   */
  _prepareCart: function() {
    this.serializedCart = $('cart_form').serialize();

    this.boundEmptyList = this.onEmptyList.bind(this);
    $('empty_list').observe('click', this.boundEmptyList);

    this.boundRemoveItem = this.onRemoveItem.bind(this);
    $$('#cart_form .remove_item').each(function(alink, index){
      alink.observe('click', this.boundRemoveItem);
    }.bind(this));

    this.boundCartSubmit = this.onCartUpdateSubmit.bind(this);
    $('cart_form').observe('submit', this.boundCartSubmit);

    this.boundCartKeydown = this.onCartKeyUp.bind(this);
    $('cart_form').getInputs().each(function(input, index){
      input.observe('keyup', this.boundCartKeydown);
    }.bind(this));

    // Prepare writein elements.
    $('writein_opener').observe('click', function(ev) {
      ev.stop();
      Effect.toggle('writein_elements','blind');
    });

    this.boundAddWriteinSubmit = this.onAddWriteinFormSubmit.bind(this);
    $('writein_form').observe('submit', this.boundAddWriteinSubmit);

  },

  /**
   *  Prepares cart for update by removing listeners.
   */
  _prepareCartForUpdate: function() {
    $('empty_list').stopObserving('click', this.boundEmptyList);

    $('cart_form').stopObserving('submit', this.boundCartSubmit);

    $$('#cart_form a.remove_item').each(function(alink, index){
      alink.stopObserving('click', this.boundRemoveItem);
    }.bind(this));

    $('cart_form').getInputs().each(function(input, index){
      input.stopObserving('keyup', this.boundCartKeydown);
    }.bind(this));

    $('writein_form').stopObserving('submit', this.boundAddWriteinSubmit);

  },

  /**
   *  Returns boolean determining if the passed string contains only digits.
   */
  _isStringANumber: function(str) {
    return (/^\d+$/.test(str));
  }

});
<commit_msg>Remove stray console statement<commit_after>var FNCart = Class.create({
  initialize: function() {
    // Intercept the product add form submit event.

    this.boundAddSubmit = this.onAddFormSubmit.bind(this);
    $$('.product_add').each(function(form, index) {
      form.observe('submit', this.boundAddSubmit);
    }, this);

    if($('virtualshop_form')) {
      this.boundAddVirtualSubmit = this.onVirtualShopAddFormSubmit.bind(this);
      $('virtualshop_form').observe('submit', this.boundAddVirtualSubmit);
    }

    if($('save_details_button')) {
      $('save_details_button').observe('click', this.onSaveDetailsButtonClick.bind(this));
    }

    this._prepareCart();
  },

  /**
   *  Handler for the Submit event on the product add forms.
   */
  onAddFormSubmit: function(ev){
    ev.stop();

    // Get the quantity to add.
    quantity = $(ev.target).down('.quantity').value;
    // Bail out if the quantity isn't a number.
    if(!this._isStringANumber(quantity)) return false;

    url = ev.target.action;

    // If the item is a bundle, submit its sub items and their quantities.
    var serializedData = ev.target.serialize();
    if($(ev.target).up('.has_bundle')) {
      var serializedBundleData = $(ev.target).up('.has_bundle').down('.bundle_product_add').serialize();
      serializedData = serializedBundleData + "&" + serializedData;
    }

    this.updateCart(url, serializedData);
  },

  /**
   *  Handler for submission of the add write-in form.
   */
  onAddWriteinFormSubmit: function(ev) {
    ev.stop();
    this.updateCart(ev.target.action, ev.target.serialize());
  },

  /**
   *  Handler for submission of the add virtual shop item form.
   */
  onVirtualShopAddFormSubmit: function(ev) {
    ev.stop();
    var completeAction = function(transport) {
      this._prepareCart();
      if($$('.cart_error').length == 0)
        ev.target.reset();
    }.bind(this);
    this.updateCart(ev.target.action, ev.target.serialize(), completeAction);
  },

  /**
   *  Handler for the remove item button.
   */
  onRemoveItem: function(ev) {
    ev.stop();
    this.updateCart($(ev.target).up('a.remove_item').href);
  },

  /**
   *  Handler for the click event on the empty list link.
   */
  onEmptyList: function(ev) {
    ev.stop();
    this.updateCart("/cart/empty/");
  },

  /**
   *  Handler for the submission of the cart form (Update cart).
   */
  onCartUpdateSubmit: function(ev) {
    ev.stop();
    if(ev.target.serialize() != this.serializedCart) {
      // console.info(ev.target.serialize());
      this.updateCart(ev.target.action, ev.target.serialize());
    } else {
      // console.info('Cart has not changed.');
    }
  },

  /**
   *  Handler for when a key is pressed in the cart inputs.
   */
  onCartKeyUp: function(ev) {
    if(ev.target.serialize() != this.serializedCart)
      $('update_list_button').enable();
    else
      $('update_list_button').disable();
  },

  /**
   *  Updates the cart with a given url and parameters, updating the cart container with the successful return value.
   */
  updateCart: function(url, parameters, completeAction) {

    completeAction = completeAction || function() {this._prepareCart();}.bind(this);

    this._prepareCartForUpdate();

    $('cart_load_indicator').show();

    new Ajax.Updater({success:'cart_content', failure:'cart_notice'}, url, {
      method:'post',
      parameters:parameters,
      onComplete: completeAction
    });
  },

  /**
   *  Handler for click of the save details button.
   */
  onSaveDetailsButtonClick: function(ev) {
    new Ajax.Request('/cart/savedetails/', {
      method: 'post',
      parameters:$('order_details').serialize(),
      onSuccess: function(transport) {
        window.location.replace("/catalogue/aisle/");
      },
      onFailure: function(transport) {

      }
    });
  },

  /**
   *  Prepares the cart at initialize and each time it's been updated.
   */
  _prepareCart: function() {
    this.serializedCart = $('cart_form').serialize();

    this.boundEmptyList = this.onEmptyList.bind(this);
    $('empty_list').observe('click', this.boundEmptyList);

    this.boundRemoveItem = this.onRemoveItem.bind(this);
    $$('#cart_form .remove_item').each(function(alink, index){
      alink.observe('click', this.boundRemoveItem);
    }.bind(this));

    this.boundCartSubmit = this.onCartUpdateSubmit.bind(this);
    $('cart_form').observe('submit', this.boundCartSubmit);

    this.boundCartKeydown = this.onCartKeyUp.bind(this);
    $('cart_form').getInputs().each(function(input, index){
      input.observe('keyup', this.boundCartKeydown);
    }.bind(this));

    // Prepare writein elements.
    $('writein_opener').observe('click', function(ev) {
      ev.stop();
      Effect.toggle('writein_elements','blind');
    });

    this.boundAddWriteinSubmit = this.onAddWriteinFormSubmit.bind(this);
    $('writein_form').observe('submit', this.boundAddWriteinSubmit);

  },

  /**
   *  Prepares cart for update by removing listeners.
   */
  _prepareCartForUpdate: function() {
    $('empty_list').stopObserving('click', this.boundEmptyList);

    $('cart_form').stopObserving('submit', this.boundCartSubmit);

    $$('#cart_form a.remove_item').each(function(alink, index){
      alink.stopObserving('click', this.boundRemoveItem);
    }.bind(this));

    $('cart_form').getInputs().each(function(input, index){
      input.stopObserving('keyup', this.boundCartKeydown);
    }.bind(this));

    $('writein_form').stopObserving('submit', this.boundAddWriteinSubmit);

  },

  /**
   *  Returns boolean determining if the passed string contains only digits.
   */
  _isStringANumber: function(str) {
    return (/^\d+$/.test(str));
  }

});
<|endoftext|>


<commit_before>/*global App, Backbone*/
/* Define Backbone Models */
App.Model.PageModel = Backbone.Model.extend({

});

App.models.introPage = new App.Model.PageModel({
  title: 'Welcome',
  header: 'Welcome To FeedHenry',
  text: 'Learn about and interact with our platform',
  nextButtonText: 'Let\'s Start',
  menuItems: [{className: 'cloud-action', imgPath: '/img/touch/apple-touch-icon.png', menuTitle: 'Cloud Action', menuSubTitle: 'Try it now', responsiveClass: 'col-md-3 col-lg-2', iconClass: 'cloud'},
              {className: 'data-browser', imgPath: '/img/touch/apple-touch-icon.png', menuTitle: 'Data Browser', menuSubTitle: 'Use MongoDB', responsiveClass: 'col-md-3 col-lg-2', iconClass: 'table'},
              {className: 'nodejs-page', imgPath: '/img/touch/apple-touch-icon.png', menuTitle: 'Powered By Nodejs', menuSubTitle: 'Cloud code', responsiveClass: 'col-md-3 col-lg-2', iconClass: 'bolt'},
              {className: 'cloud-integration', imgPath: '/img/touch/apple-touch-icon.png', menuTitle: 'Cloud Integration', menuSubTitle: 'Use Plugins', responsiveClass: 'col-md-3 col-lg-2', iconClass: 'gears'},
              {className: 'weather-sample', imgPath: '/img/touch/apple-touch-icon.png', menuTitle: 'Example', menuSubTitle: 'Use Location', responsiveClass: 'col-md-2 col-md-offset-2 col-lg-2 col-lg-offset-0', iconClass: 'sun'},
              {className: 'stats-analytics', imgPath: '/img/touch/apple-touch-icon.png', menuTitle: 'Stats', menuSubTitle: 'See the data', responsiveClass: 'col-md-2 col-md-offset-4 col-lg-2 col-lg-offset-0', iconClass: 'bar-chart'} ]
});

App.models.cloudcallPage = new App.Model.PageModel({
  title: 'Cloud Action',
  header: 'Cloud Action',
  text: 'This client App is now connected to its matching Node.js cloud code on FeedHenry. Tap the button to request a response from the cloud.',
  buttonText: 'Call Cloud',
  extraRes: 'Now check your stats page in the studio to see that this cloud call happen.'
});

App.models.statsPage = new App.Model.PageModel({
  title: 'Stats',
  header: 'Stats',
  titleWidth: '50',
  text: 'In addition to all of our App install/usage Analytics, you can also instrument your cloud code to see exactly how it is performing. Go to the Stats section of the Studio and see if your device has been detected.'
});

App.models.nodePage = new App.Model.PageModel({
  title: 'Powered By Nodejs',
  header: 'Powered By Nodejs',
  text: 'FeedHenry Apps are made much more powerful by our Node.js cloud code. You have all of the functionality of Node available to you including all of its modules. This means you get powerful responsive business logic doing all the heavy lifting for your application.',
  titleWidth: '150'
});

App.models.databrowserPage = new App.Model.PageModel({
  title: 'Data Browser',
  header: 'Data Browser',
  text: 'Enter your name and then tap the button to save your name in our cloud database',
  moreInfo: 'Now go to the Data Browser section of the App Studio and see if you can find your name stored there.',
  inputFieldLabel: 'Please Enter Your Name',
  buttonText: 'Save',
  extraRes: 'Your data is now saved. Please go to studio and see your data using the Data Browser.'
});

App.models.weatherPage = new App.Model.PageModel({
  title: 'Location Example',
  header: 'Location Example',
  geoText: 'Let’s grab your location and do something with it. Tap the button to find your location',
  geoButtonText : 'Get My Location',
  geoLabelText: 'My Location',
  getWeatherText: 'Now we are going to use that location to request weather information from an online web-service via our cloud code. Tap the button to do this.',
  getWeatherButtonText: 'Get My Weather Info',
  titleWidth: '150'
});

App.models.integrationPage = new App.Model.PageModel({
  title: 'Cloud Integration',
  header: 'Cloud Integration',
  titleWidth: '121',
  text: 'You can easily integrate with all of your existing back-end systems using Cloud Plugins. Check out the the App Studio to see how to connect to Databases, Messaging, Analytics, SaaS, eCommerce, Logging, Data Storage, File Storage, Testing, Tools and File Storage. A few examples including:',
  plugins:[{img:'mysql.png'},
          {img: 'oracle.png'},
          {img: 'stripe.png'},
          {img: 'loggly.png'},
          {img: 's3.png'},
          {img: 'mixpanel.png'},
          {img: 'salesforce.png'},
          {img: 'sendgrid.png'},
          {img: 'paypal.png'}]
});<commit_msg>update file /www/js/app/models/page.js<commit_after>/*global App, Backbone*/
/* Define Backbone Models */
App.Model.PageModel = Backbone.Model.extend({

});

App.models.introPage = new App.Model.PageModel({
  title: 'Welcome',
  header: 'Red Hat Mobile Bank',
  text: 'Learn about and interact with our platform',
  nextButtonText: 'Let\'s Start',
  menuItems: [{className: 'cloud-action', imgPath: '/img/touch/apple-touch-icon.png', menuTitle: 'Cloud Action', menuSubTitle: 'Try it now', responsiveClass: 'col-md-3 col-lg-2', iconClass: 'cloud'},
              {className: 'data-browser', imgPath: '/img/touch/apple-touch-icon.png', menuTitle: 'Data Browser', menuSubTitle: 'Use MongoDB', responsiveClass: 'col-md-3 col-lg-2', iconClass: 'table'},
              {className: 'nodejs-page', imgPath: '/img/touch/apple-touch-icon.png', menuTitle: 'Powered By Nodejs', menuSubTitle: 'Cloud code', responsiveClass: 'col-md-3 col-lg-2', iconClass: 'bolt'},
              {className: 'cloud-integration', imgPath: '/img/touch/apple-touch-icon.png', menuTitle: 'Cloud Integration', menuSubTitle: 'Use Plugins', responsiveClass: 'col-md-3 col-lg-2', iconClass: 'gears'},
              {className: 'weather-sample', imgPath: '/img/touch/apple-touch-icon.png', menuTitle: 'Example', menuSubTitle: 'Use Location', responsiveClass: 'col-md-2 col-md-offset-2 col-lg-2 col-lg-offset-0', iconClass: 'sun'},
              {className: 'stats-analytics', imgPath: '/img/touch/apple-touch-icon.png', menuTitle: 'Stats', menuSubTitle: 'See the data', responsiveClass: 'col-md-2 col-md-offset-4 col-lg-2 col-lg-offset-0', iconClass: 'bar-chart'} ]
});

App.models.cloudcallPage = new App.Model.PageModel({
  title: 'Cloud Action',
  header: 'Cloud Action',
  text: 'This client App is now connected to its matching Node.js cloud code on FeedHenry. Tap the button to request a response from the cloud.',
  buttonText: 'Call Cloud',
  extraRes: 'Now check your stats page in the studio to see that this cloud call happen.'
});

App.models.statsPage = new App.Model.PageModel({
  title: 'Stats',
  header: 'Stats',
  titleWidth: '50',
  text: 'In addition to all of our App install/usage Analytics, you can also instrument your cloud code to see exactly how it is performing. Go to the Stats section of the Studio and see if your device has been detected.'
});

App.models.nodePage = new App.Model.PageModel({
  title: 'Powered By Nodejs',
  header: 'Powered By Nodejs',
  text: 'FeedHenry Apps are made much more powerful by our Node.js cloud code. You have all of the functionality of Node available to you including all of its modules. This means you get powerful responsive business logic doing all the heavy lifting for your application.',
  titleWidth: '150'
});

App.models.databrowserPage = new App.Model.PageModel({
  title: 'Data Browser',
  header: 'Data Browser',
  text: 'Enter your name and then tap the button to save your name in our cloud database',
  moreInfo: 'Now go to the Data Browser section of the App Studio and see if you can find your name stored there.',
  inputFieldLabel: 'Please Enter Your Name',
  buttonText: 'Save',
  extraRes: 'Your data is now saved. Please go to studio and see your data using the Data Browser.'
});

App.models.weatherPage = new App.Model.PageModel({
  title: 'Location Example',
  header: 'Location Example',
  geoText: 'Let’s grab your location and do something with it. Tap the button to find your location',
  geoButtonText : 'Get My Location',
  geoLabelText: 'My Location',
  getWeatherText: 'Now we are going to use that location to request weather information from an online web-service via our cloud code. Tap the button to do this.',
  getWeatherButtonText: 'Get My Weather Info',
  titleWidth: '150'
});

App.models.integrationPage = new App.Model.PageModel({
  title: 'Cloud Integration',
  header: 'Cloud Integration',
  titleWidth: '121',
  text: 'You can easily integrate with all of your existing back-end systems using Cloud Plugins. Check out the the App Studio to see how to connect to Databases, Messaging, Analytics, SaaS, eCommerce, Logging, Data Storage, File Storage, Testing, Tools and File Storage. A few examples including:',
  plugins:[{img:'mysql.png'},
          {img: 'oracle.png'},
          {img: 'stripe.png'},
          {img: 'loggly.png'},
          {img: 's3.png'},
          {img: 'mixpanel.png'},
          {img: 'salesforce.png'},
          {img: 'sendgrid.png'},
          {img: 'paypal.png'}]
});<|endoftext|>


<commit_before><commit_msg>Return statement on comparison<commit_after><|endoftext|>


<commit_before>// @flow

// wait other players and prepare map\settings for Game view

// list of settings, can NOT be changed, this is start game state
// - defaultMoney
// - map
// - unitLimit
// - userList

import type {Node} from 'react';
import React, {Component} from 'react';
import {connect} from 'react-redux';
import MainModel from './../../lib/main-model/index';
import ReactJson from 'react-json-view';

import {user} from './../../module/user';
import {socket, type SocketMessageType} from './../../module/socket';
import Game from './../../components/game';
import type {AllRoomSettingsType, PushedStatePayloadType, ServerUserType} from './../../module/server-api';
// import uiStyle from './../../components/ui/ui.scss';
// import serviceStyle from './../../../css/service.scss';
// import type {AuthType} from './../../components/auth/reducer';
import * as serverApi from './../../module/server-api';
import mapGuide from './../../maps/map-guide';
import {getCommanderDataByUserIndex} from './../../components/game/model/helper';

import {type MatchType} from './../../app/routes';
import type {BuildingType, MapType, MapUserType, UnitType} from './../../maps/type';

import Page from './../../components/ui/page';
import Button from './../../components/ui/button';
import ButtonLink from './../../components/ui/button-link';
import ButtonListWrapper from './../../components/ui/button-list-wrapper';
import Header from './../../components/ui/header';
import Form from './../../components/ui/form';
import Label from './../../components/ui/label';
import FormHeader from './../../components/ui/form-header';
import Fieldset from './../../components/ui/fieldset';

type StateType = {|
    settings?: AllRoomSettingsType,
    userList: Array<ServerUserType>,
    model: MainModel,
    isGameStart: boolean
|};

type PropsType = {|
    match: MatchType
|};

class Room extends Component<PropsType, StateType> {
    props: PropsType;
    state: StateType;

    constructor() {
        super();

        const view = this;

        view.state = {
            userList: [],
            model: new MainModel(),
            isGameStart: false
        };
    }

    async componentDidMount(): Promise<string> {
        const view = this;
        const {props, state} = view;

        const roomId = props.match.params.roomId || '';

        if (roomId === '') {
            console.error('room id is not define!!!');
            return '';
        }

        // try to join into room, in case disconnected, nah
        // const socketInitialPromiseResult = await socket.attr.initialPromise;
        // const joinResult = await serverApi.joinRoom(roomId, user.getId(), socket.getId());

        // const settingsResponse = await serverApi.getAllRoomSettings(roomId);
        // const usersResponse = await serverApi.getAllRoomUsers(roomId);

        // view.setState({
        // settings: settingsResponse.settings,
        // userList: usersResponse.users,
        // model: new MainModel()
        // });

        view.bindEventListeners();

        await view.onServerUserListChange();

        return roomId;
    }

    async componentWillUnmount(): Promise<void> {
        const view = this;
        const {props, state} = view;
        const {model} = state;
        const roomId = props.match.params.roomId || '';

        model.destroy();

        // const leaveRoomResult = await serverApi.leaveRoom(roomId, user.getId());
    }

    bindEventListeners() {
        const view = this;
        const {props, state} = view;
        const {model} = state;

        model.listenTo(socket.attr.model, 'message', async (message: SocketMessageType): Promise<void> => {
            await view.onMessage(message);
        });
    }

    unbindEventListeners() {
        const view = this;
        const {props, state} = view;
        const {model} = state;

        model.stopListening();
    }

    async onServerUserListChange(): Promise<void> {
        const view = this;
        const {props, state} = view;
        const roomId = props.match.params.roomId || '';

        if (roomId === '') {
            console.error('room id is not define!!!');
            return Promise.resolve();
        }

        const roomDataSettings = await serverApi.getAllRoomSettings(roomId);
        const defaultMoney = roomDataSettings.settings.map.defaultMoney;
        const roomDataUsers = await serverApi.getAllRoomUsers(roomId);

        view.setState({
            userList: roomDataUsers.users
        });

        roomDataSettings.settings.map.userList = roomDataUsers
            .users.map((serverUser: ServerUserType, userIndex: number): MapUserType => {
                return {
                    userId: serverUser.userId,
                    money: defaultMoney,
                    teamId: mapGuide.teamIdList[userIndex],
                    commander: {
                        type: getCommanderDataByUserIndex(userIndex),
                        buyCount: 0
                    }
                };
            });

        roomDataSettings.settings.map.activeUserId = roomDataSettings.settings.map.userList[0].userId;

        await serverApi.setRoomSetting(roomId, {map: roomDataSettings.settings.map});

        view.setState({
            settings: roomDataSettings.settings
        });

        return Promise.resolve();
    }

    async onMessage(message: SocketMessageType): Promise<void> { // eslint-disable-line complexity, max-statements
        const view = this;
        const {props, state} = view;
        // const {model} = state;
        // const roomId = props.match.params.roomId || '';

        /*
        if (!state.settings) {
            console.error('state.settings is not defined');
            return Promise.resolve();
        }
        */

        if (state.isGameStart === true) {
            console.error('unbindEventListeners should prevent this IF');
            return Promise.resolve();
        }

        switch (message.type) {
            case 'room__take-turn':
                break;

            case 'room__drop-turn':
                break;

            case 'room__join-into-room':
            case 'room__leave-from-room':
            case 'room__user-disconnected':
                await view.onServerUserListChange();
                break;

            case 'room__push-state':
                if (message.states.last.state.isGameStart === true) {
                    console.warn('---> The game has begun!!!');

                    view.unbindEventListeners();

                    view.setState({
                        isGameStart: true,
                        settings: null, // wipe extra data
                        userList: [] // wipe extra data
                    });

                    return Promise.resolve();
                }

                break;

            default:
                console.error('---> view - room - unsupported message type: ' + message.type);
        }

        return Promise.resolve();
    }

    async startGame(): Promise<void> {
        const view = this;
        const {props, state} = view;
        const roomId = props.match.params.roomId || '';

        const userList: Array<ServerUserType> = state.userList
            .map((userItem: ServerUserType, userIndex: number): ServerUserType => ({
                socketId: userItem.socketId,
                userId: userItem.userId,
                teamId: mapGuide.teamIdList[userIndex]
            }));

        const takeTurnResult = await serverApi.takeTurn(roomId, user.getId());

        const map: MapType | void = state.settings && state.settings.map;

        if (!map) {
            console.error('---> map is not exists!!!!');
            return;
        }

        [].concat(map.buildings, map.units).forEach((userItem: BuildingType | UnitType) => { // eslint-disable-line complexity
            const userItemUserId = typeof userItem.userId === 'string' ? userItem.userId : null;

            if (typeof userItem.id !== 'string' || userItem.id === '') {
                userItem.id = [userItem.x, userItem.y, Math.random()].join('_'); // eslint-disable-line no-param-reassign
            }

            if (userItemUserId === null) {
                return;
            }

            const userIndex = parseInt(userItemUserId.replace(/\D/g, ''), 10) || 0;

            const userData: ServerUserType | void = userList[userIndex];

            if (!userData) {
                Reflect.deleteProperty(userItem, 'userId');
                return;
            }

            userItem.userId = userData.userId; // eslint-disable-line no-param-reassign
        });

        /*
        const setSettingResult = await serverApi.setRoomSetting(roomId, {
            userList
        });
        */

        const setSettingResult = await serverApi.setRoomSetting(roomId, {
            map
        });

        const newState: PushedStatePayloadType = {
            isGameStart: true,
            activeUserId: user.getId(),
            map
        };

        const pushStateResult = await serverApi.pushState(roomId, user.getId(), {
            type: 'room__push-state',
            state: newState
        });
    }

    render(): Node { // eslint-disable-line complexity
        const view = this;
        const {props, state} = view;
        const roomId = props.match.params.roomId || '';

        if (state.isGameStart === true) {
            return <Game roomId={roomId}/>;
        }

        return <Page>
            Room

            <Form>

                <Fieldset>
                    <FormHeader>Map:</FormHeader>
                    {state.settings && state.settings.map.meta.en.name}
                </Fieldset>

                <Fieldset>
                    <FormHeader>User List:</FormHeader>

                    {(state.userList && state.userList || [])
                        .map((userData: ServerUserType, userIndex: number): Node =>

                            {userIndex === 0 ?
 : null}
                            userId: {userData.userId}

                            teamId: {userData.teamId}

                            socketId: {userData.socketId}

)}

                </Fieldset>

                <Button onClick={async (): Promise<void> => {
                    await view.startGame();
                }}>
                    start
                </Button>

            </Form>
        </Page>;
    }
}

export default connect(
    (state: {}): {} => ({
        // app: state.app
    }),
    {}
)(Room);
<commit_msg>room page - update<commit_after>// @flow

// wait other players and prepare map\settings for Game view

// list of settings, can NOT be changed, this is start game state
// - defaultMoney
// - map
// - unitLimit
// - userList

import type {Node} from 'react';
import React, {Component} from 'react';
import {connect} from 'react-redux';
import MainModel from './../../lib/main-model/index';
import ReactJson from 'react-json-view';

import {user} from './../../module/user';
import {socket, type SocketMessageType} from './../../module/socket';
import Game from './../../components/game';
import type {AllRoomSettingsType, PushedStatePayloadType, ServerUserType} from './../../module/server-api';
// import uiStyle from './../../components/ui/ui.scss';
// import serviceStyle from './../../../css/service.scss';
// import type {AuthType} from './../../components/auth/reducer';
import * as serverApi from './../../module/server-api';
import mapGuide from './../../maps/map-guide';
import {getCommanderDataByUserIndex} from './../../components/game/model/helper';

import {type MatchType} from './../../app/routes';
import type {BuildingType, MapType, MapUserType, UnitType} from './../../maps/type';

import Page from './../../components/ui/page';
import Button from './../../components/ui/button';
import ButtonLink from './../../components/ui/button-link';
import ButtonListWrapper from './../../components/ui/button-list-wrapper';
import Header from './../../components/ui/header';
import Form from './../../components/ui/form';
import Label from './../../components/ui/label';
import FormHeader from './../../components/ui/form-header';
import Fieldset from './../../components/ui/fieldset';

type StateType = {|
    settings?: AllRoomSettingsType,
    userList: Array<ServerUserType>,
    model: MainModel,
    isGameStart: boolean
|};

type PropsType = {|
    match: MatchType
|};

class Room extends Component<PropsType, StateType> {
    props: PropsType;
    state: StateType;

    constructor() {
        super();

        const view = this;

        view.state = {
            userList: [],
            model: new MainModel(),
            isGameStart: false
        };
    }

    async componentDidMount(): Promise<string> {
        const view = this;
        const {props, state} = view;

        const roomId = props.match.params.roomId || '';

        if (roomId === '') {
            console.error('room id is not define!!!');
            return '';
        }

        // try to join into room, in case disconnected, nah
        // const socketInitialPromiseResult = await socket.attr.initialPromise;
        // const joinResult = await serverApi.joinRoom(roomId, user.getId(), socket.getId());

        // const settingsResponse = await serverApi.getAllRoomSettings(roomId);
        // const usersResponse = await serverApi.getAllRoomUsers(roomId);

        // view.setState({
        // settings: settingsResponse.settings,
        // userList: usersResponse.users,
        // model: new MainModel()
        // });

        view.bindEventListeners();

        await view.onServerUserListChange();

        return roomId;
    }

    async componentWillUnmount(): Promise<void> {
        const view = this;
        const {props, state} = view;
        const {model} = state;
        const roomId = props.match.params.roomId || '';

        model.destroy();

        // const leaveRoomResult = await serverApi.leaveRoom(roomId, user.getId());
    }

    bindEventListeners() {
        const view = this;
        const {props, state} = view;
        const {model} = state;

        model.listenTo(socket.attr.model, 'message', async (message: SocketMessageType): Promise<void> => {
            await view.onMessage(message);
        });
    }

    unbindEventListeners() {
        const view = this;
        const {props, state} = view;
        const {model} = state;

        model.stopListening();
    }

    async onServerUserListChange(): Promise<void> {
        const view = this;
        const {props, state} = view;
        const roomId = props.match.params.roomId || '';

        if (roomId === '') {
            console.error('room id is not define!!!');
            return Promise.resolve();
        }

        const roomDataSettings = await serverApi.getAllRoomSettings(roomId);
        const defaultMoney = roomDataSettings.settings.map.defaultMoney;
        const roomDataUsers = await serverApi.getAllRoomUsers(roomId);

        view.setState({
            userList: roomDataUsers.users
        });

        roomDataSettings.settings.map.userList = roomDataUsers
            .users.map((serverUser: ServerUserType, userIndex: number): MapUserType => {
                return {
                    userId: serverUser.userId,
                    money: defaultMoney,
                    teamId: mapGuide.teamIdList[userIndex],
                    commander: {
                        type: getCommanderDataByUserIndex(userIndex),
                        buyCount: 0
                    }
                };
            });

        roomDataSettings.settings.map.activeUserId = roomDataSettings.settings.map.userList[0].userId;

        await serverApi.setRoomSetting(roomId, {map: roomDataSettings.settings.map});

        view.setState({
            settings: roomDataSettings.settings
        });

        return Promise.resolve();
    }

    async onMessage(message: SocketMessageType): Promise<void> { // eslint-disable-line complexity, max-statements
        const view = this;
        const {props, state} = view;
        // const {model} = state;
        // const roomId = props.match.params.roomId || '';

        /*
        if (!state.settings) {
            console.error('state.settings is not defined');
            return Promise.resolve();
        }
        */

        if (state.isGameStart === true) {
            console.error('unbindEventListeners should prevent this IF');
            return Promise.resolve();
        }

        switch (message.type) {
            case 'room__take-turn':
                break;

            case 'room__drop-turn':
                break;

            case 'room__join-into-room':
            case 'room__leave-from-room':
            case 'room__user-disconnected':
                await view.onServerUserListChange();
                break;

            case 'room__push-state':
                if (message.states.last.state.isGameStart === true) {
                    console.warn('---> The game has begun!!!');

                    view.unbindEventListeners();

                    view.setState({
                        isGameStart: true,
                        settings: null, // wipe extra data
                        userList: [] // wipe extra data
                    });

                    return Promise.resolve();
                }

                break;

            default:
                console.error('---> view - room - unsupported message type: ' + message.type);
        }

        return Promise.resolve();
    }

    async startGame(): Promise<void> {
        const view = this;
        const {props, state} = view;
        const roomId = props.match.params.roomId || '';

        const userList: Array<ServerUserType> = state.userList
            .map((userItem: ServerUserType, userIndex: number): ServerUserType => ({
                socketId: userItem.socketId,
                userId: userItem.userId,
                teamId: mapGuide.teamIdList[userIndex]
            }));

        const takeTurnResult = await serverApi.takeTurn(roomId, user.getId());

        const map: MapType | void = state.settings && state.settings.map;

        if (!map) {
            console.error('---> map is not exists!!!!');
            return;
        }

        [].concat(map.buildings, map.units).forEach((userItem: BuildingType | UnitType) => { // eslint-disable-line complexity
            const userItemUserId = typeof userItem.userId === 'string' ? userItem.userId : null;

            if (typeof userItem.id !== 'string' || userItem.id === '') {
                userItem.id = [userItem.x, userItem.y, Math.random()].join('_'); // eslint-disable-line no-param-reassign
            }

            if (userItemUserId === null) {
                return;
            }

            const userIndex = parseInt(userItemUserId.replace(/\D/g, ''), 10) || 0;

            const userData: ServerUserType | void = userList[userIndex];

            if (!userData) {
                Reflect.deleteProperty(userItem, 'userId');
                return;
            }

            userItem.userId = userData.userId; // eslint-disable-line no-param-reassign
        });

        /*
        const setSettingResult = await serverApi.setRoomSetting(roomId, {
            userList
        });
        */

        const setSettingResult = await serverApi.setRoomSetting(roomId, {
            map
        });

        const newState: PushedStatePayloadType = {
            isGameStart: true,
            activeUserId: user.getId(),
            map
        };

        const pushStateResult = await serverApi.pushState(roomId, user.getId(), {
            type: 'room__push-state',
            state: newState
        });
    }

    render(): Node { // eslint-disable-line complexity
        const view = this;
        const {props, state} = view;
        const roomId = props.match.params.roomId || '';
        const amIMasterPlayer = (state.userList && state.userList || [])
            .map((userData: ServerUserType): string => userData.userId)
            .indexOf(user.getId()) === 0;

        if (state.isGameStart === true) {
            return <Game roomId={roomId}/>;
        }

        return <Page>
            Room

            <Form>

                <Fieldset>
                    <FormHeader>Map:</FormHeader>
                    {state.settings && state.settings.map.meta.en.name}
                </Fieldset>

                <Fieldset>
                    <FormHeader>User List:</FormHeader>

                    {(state.userList && state.userList || [])
                        .map((userData: ServerUserType, userIndex: number): Node =>

                            {userIndex === 0 ?
 : null}
                            userId: {userData.userId}

                            teamId: {userData.teamId}

                            socketId: {userData.socketId}

)}

                </Fieldset>

                {amIMasterPlayer ?
                    <Button
                        onClick={async (): Promise<void> => {
                            await view.startGame();
                        }}>
                        start
                    </Button> :
                    <Button>
                        wait for start...
                    </Button>}

            </Form>
        </Page>;
    }
}

export default connect(
    (state: {}): {} => ({
        // app: state.app
    }),
    {}
)(Room);
<|endoftext|>


<commit_before>'use strict';

/*
 * Divide the array of functions in partials and Promise.all on each partial in series.
 */
module.exports = function (concurrency, array) {
    var partials = [];
    var i;

    if (concurrency === Infinity) {
        return Promise.all(array.map(function (fn) { return fn(); }));
    }

    for (i = 0; i < array.length; i += concurrency) {
        partials.push(array.slice(i, i + concurrency));
    }

    return partials.reduce(function (promise, partial) {
        promise = promise.then(function (result) {
            return Promise.all(partial.map(function (fn) { return fn(); }))
            .then(function (partialResult) {
                return result.concat(partialResult);
            });
        });

        return promise;
    }, Promise.resolve([]));
};
<commit_msg>update promise/limitConcurrency — lint, es6<commit_after>'use strict'

function runPending (pending, running, results, concurrency) {
  const partialRunning = []
  let fn

  while (pending.length && running.length < concurrency) {
    fn = pending.shift()

    partialRunning.push(fn)
    running.push(fn)
  }

  return Promise.all(partialRunning.map((fn) =>
    Promise.resolve(fn()).then((value) => {
      running.splice(running.indexOf(fn), 1)
      results.splice(results.indexOf(fn), 1, value)

      return pending.length && runPending(pending, running, results, concurrency)
    })
  ))
}

// Run a set of functions in parallel but with limited conccurrency

module.exports = function (concurrency, fns) {
  if (concurrency === Infinity) {
    try {
      return Promise.all(fns.map((fn) => fn()))
    } catch (err) {
      return Promise.reject(err)
    }
  }

  const results = Array.from(fns)

  return runPending(fns, [], results, concurrency)
    .then(() => results)
}
<|endoftext|>


<commit_before>/**
 * Created by desver_f on 05/03/17.
 */

import { observable, computed, action } from 'mobx';
import _ from 'lodash';
import session from '../stores/session';
import autobind from 'autobind-decorator';
import * as Intra from '../api/intra';

@autobind
class Tokens {
    @observable tokenValues = {};
    @observable error = {};
    @observable value = {};
    @observable selectedToken = -1;
    @observable tokens = session.tokens;

    tokenStates = {
        validating: (id) => this.setTokensValue(id),
        error: (id) => this.setError(id),
        default: () => {
            this.value = {};
            this.error = {};
        },
    };
    async validateToken(id) {
        const response = await Intra.validateToken(this.tokens[id].tokenLink, this.tokenValues[id]);

        return Object.keys(response).length === 0;
    }

    @action resetTokens() {
        this.tokenValues = {};
        this.selectedToken = -1;
    }

    @action removeValidatedToken() {
        this.tokens = _.filter([ ...this.tokens.slice() ], (_, i) => i !== this.selectedToken);

        this.resetTokens();
    }

    @action refreshAfterAnimation(isTokenValidated) {
        if (isTokenValidated) {
            this.removeValidatedToken();
        }

        this.resetTokens();
    }

    @action updateValues(text, id) {
        this.tokenValues = {
            ...this.tokenValues,
            [id]: text
        };
    }

    @action selectToken(id) {
        this.selectedToken = id;
    }

    @action setTokensValue(id) {
        this.value = {
            [id]: true,
        };
    }

    @action setError(id) {
        this.error = {
            [id]: true
        };
    }

    @action setState({ state, id }) {
        this.tokenStates[state](id);
    }

    @computed get nbTokens() {
        return this.tokens.length;
    }
}

const tokensStore = new Tokens();
export default tokensStore;
<commit_msg>Prevented tokens from being validated if there are less than 8 characters<commit_after>/**
 * Created by desver_f on 05/03/17.
 */

import { observable, computed, action } from 'mobx';
import _ from 'lodash';
import session from '../stores/session';
import autobind from 'autobind-decorator';
import * as Intra from '../api/intra';

@autobind
class Tokens {
    @observable tokenValues = {};
    @observable error = {};
    @observable value = {};
    @observable selectedToken = -1;
    @observable tokens = session.tokens;

    tokenStates = {
        validating: (id) => this.setTokensValue(id),
        error: (id) => this.setError(id),
        default: () => {
            this.value = {};
            this.error = {};
        },
    };
    async validateToken(id) {
        const response = await Intra.validateToken(this.tokens[id].tokenLink, this.tokenValues[id]);

        return Object.keys(response).length === 0;
    }

    @action resetTokens() {
        this.tokenValues = {};
        this.selectedToken = -1;
    }

    @action removeValidatedToken() {
        this.tokens = _.filter([ ...this.tokens.slice() ], (_, i) => i !== this.selectedToken);

        this.resetTokens();
    }

    @action refreshAfterAnimation(isTokenValidated) {
        if (isTokenValidated) {
            this.removeValidatedToken();
        }

        this.resetTokens();
    }

    @action updateValues(text, id) {
        this.tokenValues = {
            ...this.tokenValues,
            [id]: text
        };
    }

    @action selectToken(id) {
        if (this.tokenValues[id].length === 8) {
            this.selectedToken = id;
        }
    }

    @action setTokensValue(id) {
        this.value = {
            [id]: true,
        };
    }

    @action setError(id) {
        this.error = {
            [id]: true
        };
    }

    @action setState({ state, id }) {
        this.tokenStates[state](id);
    }

    @computed get nbTokens() {
        return this.tokens.length;
    }
}

const tokensStore = new Tokens();
export default tokensStore;
<|endoftext|>


<commit_before>
(function() {

  /**
   * manages all the wizards which render carto
   */
  cdb.admin.mod.CartoWizard = cdb.core.View.extend({

    events: {
      'click  .vis_options a':    '_updateNavigation',
      'click  .wizard_arrows a':  '_onArrowClick'
    },

    initialize: function() {
      _.bindAll(this, '_updateNavigation');

      var self = this;
      self.active = false;
      this.cartoStylesGeneration = new cdb.admin.CartoStyles({
        table: this.options.table
      });

      this.model.bind('change:wizard_properties', this._updateForms, this);

      this.position = 0; // Navigation bar
      this.tabs = new cdb.admin.Tabs();
      this.panels = new cdb.ui.common.TabPane();
      this.addView(this.tabs);
      this.addView(this.panels);
      this.tabs.linkToPanel(this.panels);

      this.tabs.bind('click', this._applyStyle, this);
    },

    _updateForms: function() {
      var self = this;
      if (self.model.get('wizard_properties')) {
        var wizard = self.model.get('wizard_properties');
        var type = wizard.type;
        var p = self.panels.getPane(type);
        if(p) {
          p.setCarpropertiesSilent(wizard.properties);
          self.panels.active(type);
        }
      }
    },

    _onArrowClick: function(ev) {
      this.killEvent(ev);
      var $target = $(ev.target);

      if ($target.hasClass("disabled")) return false;

      var side = $target.attr('href').replace("#","");
      this._moveNavigation(side);
    },

    _updateNavigation: function(ev) {
      // // Get wizard type
      // var wizard    = this.model.get('wizard_properties')
      //   , type      = wizard.type
      //   , $li       = this.$el.find('li a[href="#' + type + '"]').parent()
      //   , new_pos   = $li.index()
      //   , count     = (Math.abs(this.position - new_pos))
      //   , side      = (this.position > new_pos) ? 'left' : 'right';

      // if (count > 0)
      //   this._moveNavigation(side,count);
    },

    _moveNavigation: function(side) {
      if (this.animation) return false;

      var $ul         = this.$el.find("ul.vis_options")
        , gap         = 3
        , list_size   = $ul.find("li").size()
        , move        = $ul.find("li").outerWidth() || 100
        , block_width = $ul.parent().outerWidth() || 380
        , list_width  = list_size * (move + 5/*margin*/)
        , left        = parseInt($ul.css("left").replace("px","")) || 0
        , $right      = this.$el.find('a.right')
        , $left       = this.$el.find('a.left')
        , self        = this;

      // Check move
      if (side == "left") {
        if (-(left) < move) {
          move = -left;
        }
        this.position--;
      } else {
        if ((block_width - left) >= list_width) {
          return false;
        } else if ((list_width + left) < move) {
          move = list_width + left;
        }
        this.position++;
      }

      // Check arrows
      if (this.position + gap >= list_size) {
        $right.addClass("disabled");
      } else {
        $right.removeClass("disabled");
      }
      if (this.position==0) {
        $left.addClass("disabled");
      } else {
        $left.removeClass("disabled");
      }

      // Go side
      this.animation = true;
      if (side == "left") {
        $ul.animate({
          'left': '+=' + move + 'px'
        }, 200, function() { self.animation = false; });
      } else {
        $ul.animate({
          left: '-=' + move + 'px'
        }, 200, function() { self.animation = false; });
      }
    },

    _setThumbnails: function() {
      var classes = this.options.table.geomColumnTypes().join("-");
      this.$el.find('.vis_options li a').each(function(i,el){
        $(el).addClass(classes);
      });
    },

    activated: function() {
      var self = this;

      self.active = true;

      // do the same with layer style, if user changes the style too fast
      // lots of tiles are requested to the tile server
      var changeLayerStyle = _.debounce(function(st, sql) {
        var attrs = {
          wizard_properties: self.cartoStylesGeneration.toJSON()
        };
        if(sql) {
          attrs.query =  sql;
          attrs.query_generated = true;
        }
        self.model.set(attrs, {silent: true} );
        self.model.save({tile_style: st}, { wait: true });
      }, 600);

      // this is the sole entry point where the cartocss is changed.
      // wizards only should change cartoStylesGeneration model
      this.cartoStylesGeneration.bind('change:style change:sql', function() {
        changeLayerStyle(
          self.cartoStylesGeneration.get('style'),
          self.cartoStylesGeneration.get('sql')
        );
      }, this);
    },

    deactivated: function() {
      this.active = false;
      this.cartoStylesGeneration.unbind(null, null, this);
    },

    _applyStyle: function(name) {
      this.panels.getActivePane().cartoProperties.trigger('change');//change();
    },

    // depending on the geometry type some wizards should be disabled
    enableTabs: function() {
      var _enableMap = {
        'point': ['polygon', 'bubble', 'density'],
        'line':['polygon', 'choropleth', 'bubble'],
        'polygon': ['polygon', 'choropleth', 'bubble']
      };

      this.tabs.disableAll();

      // enable the wizard suitable for the current geom types
      var geomTypes = this.options.table.geomColumnTypes();
      for(var g in geomTypes) {
        var type = geomTypes[g];
        var toEnable = _enableMap[type];
        for(var e in toEnable) {
          this.tabs.enable(toEnable[e]);
        }
      }
    },

    render: function() {
      this.$el.append(this.getTemplate('table/menu_modules/views/carto_wizard')());
      this.tabs.setElement(this.$('ul.vis_options'));
      this.panels.setElement(this.$('.forms'));

      // apply tipsy for nav - a
      this.$('ul.vis_options li a').each(function(i,ele) {
        $(ele).tipsy({ gravity: 's', fade: true });
      });

      // render the wizards
      this.panels.addTab('polygon', new SimpleWizard({
        table: this.options.table,
        model: this.cartoStylesGeneration,
        style: this.model.get('style')
      }).render());

      this.panels.addTab('bubble', new BubbleWizard({
        model: this.cartoStylesGeneration,
        table: this.options.table,
        style: this.model.get('style')
      }).render());

      this.panels.addTab('choropleth', new ChoroplethWizard({
        model: this.cartoStylesGeneration,
        table: this.options.table,
        style: this.model.get('style')
      }).render());

      this.panels.addTab('density', new DensityWizard({
        model: this.cartoStylesGeneration,
        table: this.options.table,
        style: this.model.get('style')
      }).render());

      this.panels.active('polygon');
      this._updateForms();
      this.enableTabs();

      // var wizard = this.model.get('wizard_properties')
      //   , type = wizard.type;
      // this._updateNavigation(type);

      this._setThumbnails();

      return this;
    }

  });

  /**
   * simple wizard tab
   * take this as base for other wizards
   */
  var SimpleWizard = cdb.core.View.extend({

    initialize: function() {
      var self = this;
      this.cartoProperties = new cdb.core.Model();
      this.type = 'polygon';
      this.geomForm = cdb.admin.forms.simple_form[this.options.table.geomColumnTypes()[0]];
      this.setFormProperties();

      //TODO: change this when table support more than one geom type
      this.form = new cdb.forms.Form({
        form_data: this.options.form || this.geomForm,
        model: this.cartoProperties
      });
      this.addView(this.form);

      this._bindChanges();

      this.options.table.bind('change:schema', function() {
        this.setFormProperties();
        this.render();
      }, this);
    },

    _bindChanges: function() {
      var self = this;
      this.cartoProperties.bind('change', function() {
        self.model.set({
          type: self.type,
          properties: _.clone(self.cartoProperties.attributes),
          sql: null
        });
      }, this);
      this.cartoProperties.bind('change:text-name', this.showTextFields, this);
    },

    showTextFields: function() {
      var self = this;
      var v = self.form.getFieldByName('Label Font');
      var vhalo = self.form.getFieldByName('Label Halo');
      if(self.cartoProperties.get('text-name') === 'None') {
        v && v.hide();
        vhalo && vhalo.hide();
      } else {
        v && v.show();
        vhalo && vhalo.show();
      }
    },

    _unbindChanges: function() {
      this.cartoProperties.unbind(null, null, this);
    },

    // changes properties WITHOUT updating the model, only refresh the
    // UI
    setCarpropertiesSilent: function(p) {
      this._unbindChanges();
      this.cartoProperties.set(p);
      this.showTextFields();
      this._bindChanges();
    },

    render: function() {
      this.$el.html('');

      var $wrapper = $("<div>").addClass("wrapper")
        , $content = $("<div>").addClass("content");

      $content.append(this.form.render().el);
      $wrapper.append($content);

      this.$el.append($wrapper)
      this.showTextFields();

      // Custom scroll actions! :)
      this.custom_scroll = new cdb.admin.CustomScrolls({
        el: $wrapper
      });
      this.addView(this.custom_scroll);

      return this;
    },

    setFormProperties: function() {
      var field = this._searchFieldByName('Label Text');
      if(field) {
        var b = field.form['text-name'].extra = ['None'].concat(this.options.table.columnNamesByType('string').concat(this.options.table.columnNamesByType('number')));
        field.form['text-name'].value = 'None';
      }
    },

    /**
     * search inside the source fields for the field by name.
     * Returns the field
     */
    _searchFieldByName: function(name) {
      return _.find(this.options.form || this.geomForm, function(f) {
        return f.name === name;
      });
    }

  });

  /**
   * bubble
   */
  var BubbleWizard = SimpleWizard.extend({

    initialize: function() {
      this.options.form = cdb.admin.forms.bubble_form;
      this.setFormProperties();
      SimpleWizard.prototype.initialize.call(this);

      this.type = 'bubble';

      this.add_related_model(this.options.table);
      this.options.table.bind('change:schema', function() {
        this.setFormProperties();
        this.render();
      }, this);
    },

    setFormProperties: function() {
      var b = this.options.form[0].form.property.extra = this.options.table.columnNamesByType('number');
      this.options.form[0].form.property.value = b[0];
    }

  });

  cdb._BubbleWizard = BubbleWizard;

  /**
   * choropleth
   */
  var ChoroplethWizard = SimpleWizard.extend({

    initialize: function() {
      this.options.form = cdb.admin.forms.choropleth[this.options.table.geomColumnTypes()[0]];
      this.setFormProperties();
      SimpleWizard.prototype.initialize.call(this);

      this.type = 'choropleth';

      this.add_related_model(this.options.table);
      this.options.table.bind('change:schema', function() {
        this.setFormProperties();
        this.render();
      }, this);
    },

    setFormProperties: function() {
      var b = this.options.form[0].form.property.extra = this.options.table.columnNamesByType('number');
      this.options.form[0].form.property.value = b[0];
    }

  });

  cdb._ChoroplethWizard = ChoroplethWizard;

  //cdb._BubbleWizard = BubbleWizard;
  var DensityWizard = SimpleWizard.extend({

    initialize: function() {
      this.options.form = cdb.admin.forms.density;
      SimpleWizard.prototype.initialize.call(this);

      this.type = 'density';

      this.add_related_model(this.options.table);
      this.options.table.bind('change:schema', function() {
        this.render();
      }, this);
    },

    _bindChanges: function() {
      var self = this;
      this.cartoProperties.bind('change', function() {
        var table = self.options.table;
        var prop = 'cartodb_id';

        if(this.cartoProperties.get('geometry_type') === 'Rectangles') {
          self.sql = _.template("WITH hgrid AS (SELECT CDB_RectangleGrid(ST_Expand(CDB_XYZ_Extent({x},{y},{z}), CDB_XYZ_Resolution({z}) * <%= size %>), CDB_XYZ_Resolution({z}) * <%= size %>, CDB_XYZ_Resolution({z}) * <%= size %>) as cell) SELECT hgrid.cell as the_geom_webmercator, count(i.<%=prop%>) as points_count FROM hgrid, <%= table_name %> i where ST_Intersects(i.the_geom_webmercator, hgrid.cell) GROUP BY hgrid.cell")({
            prop: prop,
            table: table.get('name'),
            size: this.cartoProperties.get('polygon-size')
          });

        } else {
          self.sql = _.template("WITH hgrid AS (SELECT CDB_HexagonGrid(ST_Expand(CDB_XYZ_Extent({x},{y},{z}), CDB_XYZ_Resolution({z}) * <%= size %>), CDB_XYZ_Resolution({z}) * <%= size %>) as cell) SELECT hgrid.cell as the_geom_webmercator, count(i.<%=prop%>) as points_count FROM hgrid, <%= table_name %> i where ST_Intersects(i.the_geom_webmercator, hgrid.cell) GROUP BY hgrid.cell")({
            prop: prop,
            table: table.get('name'),
            size: this.cartoProperties.get('polygon-size')
          });
        }
        self.model.set({
          type: self.type,
          properties: _.clone(self.cartoProperties.attributes),
          sql: self.sql
        });
      }, this);
    }
  });

})();
<commit_msg>prevent error on loading<commit_after>(function() {

  /**
   * manages all the wizards which render carto
   */
  cdb.admin.mod.CartoWizard = cdb.core.View.extend({

    events: {
      'click  .vis_options a':    '_updateNavigation',
      'click  .wizard_arrows a':  '_onArrowClick'
    },

    initialize: function() {
      _.bindAll(this, '_updateNavigation');

      var self = this;
      self.active = false;
      this.cartoStylesGeneration = new cdb.admin.CartoStyles({
        table: this.options.table
      });

      this.model.bind('change:wizard_properties', this._updateForms, this);

      this.position = 0; // Navigation bar
      this.tabs = new cdb.admin.Tabs();
      this.panels = new cdb.ui.common.TabPane();
      this.addView(this.tabs);
      this.addView(this.panels);
      this.tabs.linkToPanel(this.panels);

      this.tabs.bind('click', this._applyStyle, this);
    },

    _updateForms: function() {
      var self = this;
      if (self.model.get('wizard_properties')) {
        var wizard = self.model.get('wizard_properties');
        var type = wizard.type;
        var p = self.panels.getPane(type);
        if(p) {
          p.setCarpropertiesSilent(wizard.properties);
          self.panels.active(type);
        }
      }
    },

    _onArrowClick: function(ev) {
      this.killEvent(ev);
      var $target = $(ev.target);

      if ($target.hasClass("disabled")) return false;

      var side = $target.attr('href').replace("#","");
      this._moveNavigation(side);
    },

    _updateNavigation: function(ev) {
      // // Get wizard type
      // var wizard    = this.model.get('wizard_properties')
      //   , type      = wizard.type
      //   , $li       = this.$el.find('li a[href="#' + type + '"]').parent()
      //   , new_pos   = $li.index()
      //   , count     = (Math.abs(this.position - new_pos))
      //   , side      = (this.position > new_pos) ? 'left' : 'right';

      // if (count > 0)
      //   this._moveNavigation(side,count);
    },

    _moveNavigation: function(side) {
      if (this.animation) return false;

      var $ul         = this.$el.find("ul.vis_options")
        , gap         = 3
        , list_size   = $ul.find("li").size()
        , move        = $ul.find("li").outerWidth() || 100
        , block_width = $ul.parent().outerWidth() || 380
        , list_width  = list_size * (move + 5/*margin*/)
        , left        = parseInt($ul.css("left").replace("px","")) || 0
        , $right      = this.$el.find('a.right')
        , $left       = this.$el.find('a.left')
        , self        = this;

      // Check move
      if (side == "left") {
        if (-(left) < move) {
          move = -left;
        }
        this.position--;
      } else {
        if ((block_width - left) >= list_width) {
          return false;
        } else if ((list_width + left) < move) {
          move = list_width + left;
        }
        this.position++;
      }

      // Check arrows
      if (this.position + gap >= list_size) {
        $right.addClass("disabled");
      } else {
        $right.removeClass("disabled");
      }
      if (this.position==0) {
        $left.addClass("disabled");
      } else {
        $left.removeClass("disabled");
      }

      // Go side
      this.animation = true;
      if (side == "left") {
        $ul.animate({
          'left': '+=' + move + 'px'
        }, 200, function() { self.animation = false; });
      } else {
        $ul.animate({
          left: '-=' + move + 'px'
        }, 200, function() { self.animation = false; });
      }
    },

    _setThumbnails: function() {
      var classes = this.options.table.geomColumnTypes().join("-");
      this.$el.find('.vis_options li a').each(function(i,el){
        $(el).addClass(classes);
      });
    },

    activated: function() {
      var self = this;

      self.active = true;

      // do the same with layer style, if user changes the style too fast
      // lots of tiles are requested to the tile server
      var changeLayerStyle = _.debounce(function(st, sql) {
        var attrs = {
          wizard_properties: self.cartoStylesGeneration.toJSON()
        };
        if(sql) {
          attrs.query =  sql;
          attrs.query_generated = true;
        }
        self.model.set(attrs, {silent: true} );
        self.model.save({tile_style: st}, { wait: true });
      }, 600);

      // this is the sole entry point where the cartocss is changed.
      // wizards only should change cartoStylesGeneration model
      this.cartoStylesGeneration.bind('change:style change:sql', function() {
        changeLayerStyle(
          self.cartoStylesGeneration.get('style'),
          self.cartoStylesGeneration.get('sql')
        );
      }, this);
    },

    deactivated: function() {
      this.active = false;
      this.cartoStylesGeneration.unbind(null, null, this);
    },

    _applyStyle: function(name) {
      this.panels.getActivePane().cartoProperties.trigger('change');//change();
    },

    // depending on the geometry type some wizards should be disabled
    enableTabs: function() {
      var _enableMap = {
        'point': ['polygon', 'bubble', 'density'],
        'line':['polygon', 'choropleth', 'bubble'],
        'polygon': ['polygon', 'choropleth', 'bubble']
      };

      this.tabs.disableAll();

      // enable the wizard suitable for the current geom types
      var geomTypes = this.options.table.geomColumnTypes();
      for(var g in geomTypes) {
        var type = geomTypes[g];
        var toEnable = _enableMap[type];
        for(var e in toEnable) {
          this.tabs.enable(toEnable[e]);
        }
      }
    },

    render: function() {
      this.$el.append(this.getTemplate('table/menu_modules/views/carto_wizard')());
      this.tabs.setElement(this.$('ul.vis_options'));
      this.panels.setElement(this.$('.forms'));

      // apply tipsy for nav - a
      this.$('ul.vis_options li a').each(function(i,ele) {
        $(ele).tipsy({ gravity: 's', fade: true });
      });

      // render the wizards
      this.panels.addTab('polygon', new SimpleWizard({
        table: this.options.table,
        model: this.cartoStylesGeneration,
        style: this.model.get('style')
      }).render());

      this.panels.addTab('bubble', new BubbleWizard({
        model: this.cartoStylesGeneration,
        table: this.options.table,
        style: this.model.get('style')
      }).render());

      this.panels.addTab('choropleth', new ChoroplethWizard({
        model: this.cartoStylesGeneration,
        table: this.options.table,
        style: this.model.get('style')
      }).render());

      this.panels.addTab('density', new DensityWizard({
        model: this.cartoStylesGeneration,
        table: this.options.table,
        style: this.model.get('style')
      }).render());

      this.panels.active('polygon');
      this._updateForms();
      this.enableTabs();

      // var wizard = this.model.get('wizard_properties')
      //   , type = wizard.type;
      // this._updateNavigation(type);

      this._setThumbnails();

      return this;
    }

  });

  /**
   * simple wizard tab
   * take this as base for other wizards
   */
  var SimpleWizard = cdb.core.View.extend({

    initialize: function() {
      var self = this;
      this.cartoProperties = new cdb.core.Model();
      this.type = 'polygon';
      this.geomForm = cdb.admin.forms.simple_form[this.options.table.geomColumnTypes()[0]];
      this.setFormProperties();

      //TODO: change this when table support more than one geom type
      this.form = new cdb.forms.Form({
        form_data: this.options.form || this.geomForm,
        model: this.cartoProperties
      });
      this.addView(this.form);

      this._bindChanges();

      this.options.table.bind('change:schema', function() {
        this.setFormProperties();
        this.render();
      }, this);
    },

    _bindChanges: function() {
      var self = this;
      this.cartoProperties.bind('change', function() {
        self.model.set({
          type: self.type,
          properties: _.clone(self.cartoProperties.attributes),
          sql: null
        });
      }, this);
      this.cartoProperties.bind('change:text-name', this.showTextFields, this);
    },

    showTextFields: function() {
      var self = this;
      var v = self.form.getFieldByName('Label Font');
      var vhalo = self.form.getFieldByName('Label Halo');
      if(self.cartoProperties.get('text-name') === 'None') {
        v && v.hide();
        vhalo && vhalo.hide();
      } else {
        v && v.show();
        vhalo && vhalo.show();
      }
    },

    _unbindChanges: function() {
      this.cartoProperties.unbind(null, null, this);
    },

    // changes properties WITHOUT updating the model, only refresh the
    // UI
    setCarpropertiesSilent: function(p) {
      this._unbindChanges();
      this.cartoProperties.set(p);
      this.showTextFields();
      this._bindChanges();
    },

    render: function() {
      this.$el.html('');

      var $wrapper = $("<div>").addClass("wrapper")
        , $content = $("<div>").addClass("content");

      $content.append(this.form.render().el);
      $wrapper.append($content);

      this.$el.append($wrapper)
      this.showTextFields();

      // Custom scroll actions! :)
      this.custom_scroll = new cdb.admin.CustomScrolls({
        el: $wrapper
      });
      this.addView(this.custom_scroll);

      return this;
    },

    setFormProperties: function() {
      var field = this._searchFieldByName('Label Text');
      if(field) {
        var b = field.form['text-name'].extra = ['None'].concat(this.options.table.columnNamesByType('string').concat(this.options.table.columnNamesByType('number')));
        field.form['text-name'].value = 'None';
      }
    },

    /**
     * search inside the source fields for the field by name.
     * Returns the field
     */
    _searchFieldByName: function(name) {
      return _.find(this.options.form || this.geomForm, function(f) {
        return f.name === name;
      });
    }

  });

  /**
   * bubble
   */
  var BubbleWizard = SimpleWizard.extend({

    initialize: function() {
      this.options.form = cdb.admin.forms.bubble_form;
      this.setFormProperties();
      SimpleWizard.prototype.initialize.call(this);

      this.type = 'bubble';

      this.add_related_model(this.options.table);
      this.options.table.bind('change:schema', function() {
        this.setFormProperties();
        this.render();
      }, this);
    },

    setFormProperties: function() {
      if(this.options && this.options.form && this.options.form.length > 0) {
        var b = this.options.form[0].form.property.extra = this.options.table.columnNamesByType('number');
        this.options.form[0].form.property.value = b[0];
      }
    }

  });

  cdb._BubbleWizard = BubbleWizard;

  /**
   * choropleth
   */
  var ChoroplethWizard = SimpleWizard.extend({

    initialize: function() {
      this.options.form = cdb.admin.forms.choropleth[this.options.table.geomColumnTypes()[0]];
      this.setFormProperties();
      SimpleWizard.prototype.initialize.call(this);

      this.type = 'choropleth';

      this.add_related_model(this.options.table);
      this.options.table.bind('change:schema', function() {
        this.setFormProperties();
        this.render();
      }, this);
    },

    setFormProperties: function() {
      if(this.options && this.options.form && this.options.form.length > 0) {
        var b = this.options.form[0].form.property.extra = this.options.table.columnNamesByType('number');
        this.options.form[0].form.property.value = b[0];
      }
    }

  });

  cdb._ChoroplethWizard = ChoroplethWizard;

  //cdb._BubbleWizard = BubbleWizard;
  var DensityWizard = SimpleWizard.extend({

    initialize: function() {
      this.options.form = cdb.admin.forms.density;
      SimpleWizard.prototype.initialize.call(this);

      this.type = 'density';

      this.add_related_model(this.options.table);
      this.options.table.bind('change:schema', function() {
        this.render();
      }, this);
    },

    _bindChanges: function() {
      var self = this;
      this.cartoProperties.bind('change', function() {
        var table = self.options.table;
        var prop = 'cartodb_id';

        if(this.cartoProperties.get('geometry_type') === 'Rectangles') {
          self.sql = _.template("WITH hgrid AS (SELECT CDB_RectangleGrid(ST_Expand(CDB_XYZ_Extent({x},{y},{z}), CDB_XYZ_Resolution({z}) * <%= size %>), CDB_XYZ_Resolution({z}) * <%= size %>, CDB_XYZ_Resolution({z}) * <%= size %>) as cell) SELECT hgrid.cell as the_geom_webmercator, count(i.<%=prop%>) as points_count FROM hgrid, <%= table_name %> i where ST_Intersects(i.the_geom_webmercator, hgrid.cell) GROUP BY hgrid.cell")({
            prop: prop,
            table: table.get('name'),
            size: this.cartoProperties.get('polygon-size')
          });

        } else {
          self.sql = _.template("WITH hgrid AS (SELECT CDB_HexagonGrid(ST_Expand(CDB_XYZ_Extent({x},{y},{z}), CDB_XYZ_Resolution({z}) * <%= size %>), CDB_XYZ_Resolution({z}) * <%= size %>) as cell) SELECT hgrid.cell as the_geom_webmercator, count(i.<%=prop%>) as points_count FROM hgrid, <%= table_name %> i where ST_Intersects(i.the_geom_webmercator, hgrid.cell) GROUP BY hgrid.cell")({
            prop: prop,
            table: table.get('name'),
            size: this.cartoProperties.get('polygon-size')
          });
        }
        self.model.set({
          type: self.type,
          properties: _.clone(self.cartoProperties.attributes),
          sql: self.sql
        });
      }, this);
    }
  });

})();
<|endoftext|>