<commit_before>'use strict';

angular.module('offlineApp').service('offlineService', function($http) {

    var view_model = this;

    /* --------------- Configuration --------------- */

    // API Parameters
    view_model.readAPI = "http://188.166.147.80/get?after=";
    view_model.updateAPI = "http://188.166.147.80/post";
    view_model.createAPI = "http://188.166.147.80/post";
    view_model.primaryKeyProperty = "id";
    view_model.timestampProperty = "timestamp";

    // Default Config:
    view_model.autoSync = 10000; /* Set to zero for no auto synchronisation */
    view_model.pushSync = false;
    view_model.initialSync = true;
    view_model.allowIndexedDB = true; /* Switching to false disables IndexedDB */
    view_model.allowRemote = true;

    // IndexedDB Config:
    view_model.indexedDBDatabaseName = "localDB";
    view_model.indexedDBVersionNumber = 158; /* Increment this to wipe and reset IndexedDB */
    view_model.objectStoreName = "testObjectStore";

    /* --------------- Offlinify Internals --------------- */

    // Service Variables
    view_model.idb = null;
    //view_model.serviceDB = []; /* Local image of the data */
    view_model.localData = [];
    view_model.observerCallbacks = [];
    view_model.lastChecked = new Date("1970-01-01T00:00:00.000Z").toISOString(); /* Initially the epoch */

    // Public Functions
    view_model.registerController = registerController;
    view_model._generateTimestamp = _generateTimestamp;
    view_model.sync = sync;
    view_model.objectUpdate = objectUpdate;
    view_model.wipeIndexedDB = wipeIndexedDB;

    // Determine IndexedDB Support
    view_model.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB || window.shimIndexedDB;
    if(!view_model.allowIndexedDB) view_model.indexedDB = null;

    /* --------------- Create & Update --------------- */

    // Filters create or update ops by queue state:
    function objectUpdate(obj) {
      _.set(obj, view_model.timestampProperty, _generateTimestamp());
      obj = _stripAngularHashKeys(obj);
      if(obj.hasOwnProperty("syncState")) {
        if(obj.syncState > 0) { obj.syncState = 2; }
      } else {
        obj = _.cloneDeep(obj);
        obj.syncState = 0;
        _.set(obj, view_model.primaryKeyProperty, _generateUUID());
      }
      _patchLocal(_stripAngularHashKeys([obj]), function(response) {
        if(view_model.pushSync) sync(_notifyObservers);
      });
     };

    /* --------------- Observer Pattern --------------- */

    function registerObjectStores(array, callback) {

       // Add a new localData entry.

       // Add to indexedDB.

       _.forEach(array, function(item) {

         var exists = _.filter(view_model.localData, {"storeName" : item.name});
         if(exists == -1) {callback(); return; }

         item.data = [];
         view_model.localData.push(item);

       });

    };

    function addIDBObjectStore(name, primaryKey, timestamp, callback) {

      // Here check if IDB is null or not, like registerController did.
      if(view_model.idb == null) {
        _establishIndexedDB(function() {

        });
      } else {

       }

    };

     // Called by the controller to receive updates (observer pattern)
    function registerController(ctrlCallback) {
       ensureIDBReady(function() {
         view_model.observerCallbacks.push(ctrlCallback);
         if(!view_model.initialSync) return;
         view_model.sync(function(response) {
           ctrlCallback(response);
         });
       });
     };

     function ensureIDBReady(callback) {
        if(view_model.idb == null)
          _establishIndexedDB(function() { callback(); });
        else
          callback();
     };

    /* --------------- Synchronisation --------------- */

    // Restores local state on first sync, or patches local and remote changes:
    function sync(callback) {
      var startClock = _generateTimestamp();
      var newLocalRecords = _getLocalRecords(view_model.lastChecked);
      if( newLocalRecords.length == 0 && view_model.serviceDB.length == 0 ) {
        _restoreLocalState( function(localResponse) {
          _patchRemoteChanges(function(remoteResponse) {
            _reduceQueue(function(queueResponse) {
              callback( {dataSource: remoteResponse, currentQueue: queueResponse} );
              console.log("Initial load took: " + (new Date(_generateTimestamp()) - new Date(startClock))/1000 + " sec." );
            });
          });
        });
      } else {
        _patchRemoteChanges(function(remoteResponse) {
          _reduceQueue(function(queueResponse) {
            callback( { dataSource: remoteResponse, currentQueue: queueResponse } );
          });
        });
      }
    };

    // Patches remote edits to serviceDB + IndexedDB:
    function _patchRemoteChanges(callback) {
      if(!view_model.allowRemote) {
          if(_hasIndexedDB()) callback(-1);
          else callback(-2);
          return;
      }
      _getRemoteRecords(function(response) {
        if(response.status == 200) {
          _patchLocal(response.data, function(localResponse) {
            callback(localResponse);
          });
        } else { callback("Could not connect to remote server: (" + response.status + ") error."); }
      });
    };

    // Patches the local storages with a dataset.
    function _patchLocal(data, callback) {
      _patchServiceDB(data);
      view_model.lastChecked = _generateTimestamp();
      if( _hasIndexedDB() ) {
        _putArrayToIndexedDB(data, function() {
          callback(1); // Patched to IDB + ServiceDB
        });
      } else {
        callback(0); // Patched to ServiceDB only.
      }
    };

    function _notifyObservers(status) {
      angular.forEach(view_model.observerCallbacks, function(callback){
        callback(status);
      });
    };

    /* --------------- Queue + State --------------- */

    // Puts IndexedDB store into scope:
    function _restoreLocalState(callback) {
      if(!_hasIndexedDB()) { callback(-1); return; }
      _getIndexedDB(function(idbRecords) {

        var sortedElements = _.reverse(_.sortBy(idbRecords, function(o) {
          return new Date(_.get(o, view_model.timestampProperty)).toISOString();
        }));
        var nonQueueElements = _.filter(sortedElements, {syncState: 1});
        var queueElements = _.filter(sortedElements, function(o) { return o.syncState != 1; });

        if(nonQueueElements.length > 0) {
          view_model.lastChecked = _.get(sortedElements[0], view_model.timestampProperty);
        } else {
          if(queueElements.length > 0)
            view_model.lastChecked = _.get(queueElements[queueElements.length - 1], view_model.timestampProperty);
        }

        _patchServiceDB(idbRecords);
        callback(idbRecords.length);
      });
    };

    // Synchronises elements to remote when connection is available:
    function _reduceQueue(callback) {
      if(!view_model.allowRemote) { callback(-1); return; }

      var createQueue = _.filter(view_model.serviceDB, { "syncState" : 0 });
      var updateQueue = _.filter(view_model.serviceDB, { "syncState" : 2 });

      // Get rid of the create queue:
      _safeArrayPost(createQueue, view_model.createAPI, function(successfulCreates) {
        _safeArrayPost(updateQueue, view_model.updateAPI, function(successfulUpdates) {

          var totalQueue = successfulCreates.concat(successfulUpdates);
          _.forEach(totalQueue, function(value) {
            _.set(value, view_model.timestampProperty, _generateTimestamp());
          });

          _patchLocal(_resetSyncState(totalQueue), function(response) {

            var queueLength = updateQueue.length + createQueue.length;
            var popLength = successfulCreates.length + successfulUpdates.length;

            // Check here for integrity:
            if( queueLength == popLength ) {
              callback(1); // Success: All queue elements synchronised.
            } else {
              callback(0); // Partial success: Some or none were synchronised.
            }

          });
        });
      });

    };

    /* --------------- ServiceDB Interface --------------- */

    function _patchServiceDB(data) {
      var operations = _filterOperations(data);
      _updatesToServiceDB(operations.updateOperations);
      _pushToServiceDB(operations.createOperations);
    };

    function _pushToServiceDB(array) {
      for(var i=0; i<array.length; i++) view_model.serviceDB.push(array[i]);
    };

    function _updatesToServiceDB(array) {
      for(var i=0; i<array.length; i++) {
        var indexJSON = {};
        _.set(indexJSON, view_model.primaryKeyProperty, _.get(array[i], view_model.primaryKeyProperty));
        var matchID = _.findIndex(view_model.serviceDB, indexJSON);
        if(matchID > -1) view_model.serviceDB[matchID] = array[i];
      }
    };

    function _getLocalRecords(sinceTime) {
      return _.filter(view_model.serviceDB, function(o) {
        return new Date(_.get(o, view_model.timestampProperty)).toISOString() > sinceTime;
      });
    };

    /* --------------- Data Handling --------------- */

    /* Filter remote data into create or update operations */
    function _filterOperations(data) {
      var updateOps = [];
      var createOps = [];
      for(var i=0; i<data.length; i++) {
        var queryJSON = {};
        _.set(queryJSON, view_model.primaryKeyProperty, _.get(data[i], view_model.primaryKeyProperty));
        var query = _.findIndex(view_model.serviceDB, queryJSON);
        if( query > -1 ) updateOps.push(data[i]);
        else createOps.push(data[i]);
      }
      return { updateOperations: updateOps, createOperations: createOps };
    }

    function _resetSyncState(records) {
      for(var i=0; i<records.length; i++) {
        records[i].syncState = 1;
      }
      return records;
    };

    /* --------------- Remote --------------- */

    function _postRemote(data, url, callback) {
      // Data should be a single record.
      $http({
          url: url,
          method: "POST",
          data: [data],
          headers: {'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' }
      })
      .then(
        function successCallback(response) {
          callback(response.status); // return response code.
        }, function errorCallback(response) {
          callback(response.status);
        });
    };

    function _getRemoteRecords(callback) {
      $http({
          method: 'GET',
          url: view_model.readAPI + view_model.lastChecked
        })
        .then(
          function successCallback(response) {
            if(response.data.length > 0)
              callback({data: _resetSyncState(response.data), status: 200});
            else
              callback({data: [], status: 200});

        }, function errorCallback(response) {
            callback({data: [], status: response.status});
        });
    };

    // Tries to post an array one-by-one; returns successful elements.
    function _safeArrayPost(array, url, callback) {
      var x = 0;
      var successfulElements = [];
      if(array.length == 0) { callback([]); return; }
      function loopArray(array) {
        _postRemote(array[x],url,function(response) {
          if(response == 200) successfulElements.push(array[x]);
          x++;
          if(x < array.length) { loopArray(array); }
          else { callback(successfulElements); }
        });
      };
      loopArray(array);
    };

    /* --------------- IndexedDB --------------- */

    function _establishIndexedDB(callback) {
      if(!_hasIndexedDB()) { callback(); /* No browser support for IDB */ return; }
      var request = view_model.indexedDB.open(view_model.indexedDBDatabaseName, view_model.indexedDBVersionNumber);
      request.onupgradeneeded = function(e) {
        var db = e.target.result;
        e.target.transaction.onerror = function() { console.error(this.error); };
        if(db.objectStoreNames.contains(view_model.objectStoreName)) {
          db.deleteObjectStore(view_model.objectStoreName);
        }
        var offlineItems = db.createObjectStore(view_model.objectStoreName, { keyPath: view_model.primaryKeyProperty, autoIncrement: false } );
        var dateIndex = offlineItems.createIndex("byDate", view_model.timestampProperty, {unique: false});
        view_model.idb = db;
      };
      request.onsuccess = function(e) {
        view_model.idb = e.target.result;
        callback();
      };
      request.onerror = function() { console.error(this.error); };
    };

    function _hasIndexedDB() {
      return !(view_model.indexedDB === undefined || view_model.indexedDB === null );
    };

    // Get from IndexedDB. This function returns appropriate records.
    function _getIndexedDB(callback) {
      var transaction = _newIDBTransaction();
      var objStore = transaction.objectStore(view_model.objectStoreName);
      var keyRange = IDBKeyRange.lowerBound(0);
      var cursorRequest = objStore.index('byDate').openCursor(keyRange);
      var returnableItems = [];
      transaction.oncomplete = function(e) { callback(_stripAngularHashKeys(returnableItems)); };
      cursorRequest.onsuccess = function(e) {
        var result = e.target.result;
        if (!!result == false) { return; }
        returnableItems.push(result.value);
        result.continue();
      };
      cursorRequest.onerror = function() { console.error("error"); };
    };

    // Apply array of edited objects to IndexedDB.
    function _putArrayToIndexedDB(array, callback) {
      var x = 0;
      if(array.length == 0) {
        callback();
        return;
      }

      var objStore = _newIDBTransaction().objectStore(view_model.objectStoreName);

      function putNext() {
        if(x < array.length) {
          objStore.put(array[x]).onsuccess = function() {
            x++;
            putNext();
          };
        } else {
          callback();
        }
      }
      putNext();

    };

    function _newIDBTransaction() {
      return view_model.idb.transaction([view_model.objectStoreName], 'readwrite');
    };

    // Only used for diagnostics:
    function wipeIndexedDB(callback) {
      var req = view_model.indexedDB.deleteDatabase(view_model.indexedDBDatabaseName);
      req.onsuccess = function(event) { callback(); }
    };

    /* --------------- Utilities --------------- */

    function _generateTimestamp() {
      var d = new Date();
      return d.toISOString();
    };

    // (v4) With thanks to http://stackoverflow.com/a/8809472/3381433
    function _generateUUID() {
      var d = new Date().getTime();
      if(window.performance && typeof window.performance.now === "function"){
          d += performance.now(); // use high-precision timer if available
      }
      var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
          var r = (d + Math.random()*16)%16 | 0;
          d = Math.floor(d/16);
          return (c=='x' ? r : (r&0x3|0x8)).toString(16);
      });
      return uuid;
    };

    function _stripAngularHashKeys(array) {
      for(var i=0; i<array.length; i++) delete array[i].$$hashKey;
      return array;
    };

    /* --------------- Sync Loop -------------- */

    if(view_model.autoSync > 0 && parseInt(view_model.autoSync) === view_model.autoSync) {
      (function syncLoop() {
        setTimeout(function() {
          sync(function(response) {
            _notifyObservers(response);
          });
          syncLoop();
        }, view_model.autoSync);
      })();
    }

  });
<commit_msg>IDB skeleton functions<commit_after>'use strict';

angular.module('offlineApp').service('offlineService', function($http) {

    var view_model = this;

    /* --------------- Configuration --------------- */

    // API Parameters
    view_model.readAPI = "http://188.166.147.80/get?after=";
    view_model.updateAPI = "http://188.166.147.80/post";
    view_model.createAPI = "http://188.166.147.80/post";
    view_model.primaryKeyProperty = "id";
    view_model.timestampProperty = "timestamp";

    // Default Config:
    view_model.autoSync = 0; /* Set to zero for no auto synchronisation */
    view_model.pushSync = false;
    view_model.initialSync = true;
    view_model.allowIndexedDB = true; /* Switching to false disables IndexedDB */
    view_model.allowRemote = true;

    // IndexedDB Config:
    view_model.indexedDBDatabaseName = "localDB";
    view_model.indexedDBVersionNumber = 158; /* Increment this to wipe and reset IndexedDB */
    view_model.objectStoreName = "testObjectStore";

    /* --------------- Offlinify Internals --------------- */

    // Service Variables
    view_model.idb = null;
    //view_model.serviceDB = []; /* Local image of the data */
    view_model.localData = [];
    view_model.observerCallbacks = [];
    view_model.lastChecked = new Date("1970-01-01T00:00:00.000Z").toISOString(); /* Initially the epoch */

    // Public Functions
    view_model.registerController = registerController;
    view_model._generateTimestamp = _generateTimestamp;
    view_model.sync = sync;
    view_model.objectUpdate = objectUpdate;
    view_model.wipeIndexedDB = wipeIndexedDB;

    // Determine IndexedDB Support
    view_model.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB || window.shimIndexedDB;
    if(!view_model.allowIndexedDB) view_model.indexedDB = null;

    /* --------------- Create & Update --------------- */

    // Filters create or update ops by queue state:
    function objectUpdate(obj) {
      _.set(obj, view_model.timestampProperty, _generateTimestamp());
      obj = _stripAngularHashKeys(obj);
      if(obj.hasOwnProperty("syncState")) {
        if(obj.syncState > 0) { obj.syncState = 2; }
      } else {
        obj = _.cloneDeep(obj);
        obj.syncState = 0;
        _.set(obj, view_model.primaryKeyProperty, _generateUUID());
      }
      _patchLocal(_stripAngularHashKeys([obj]), function(response) {
        if(view_model.pushSync) sync(_notifyObservers);
      });
     };

    /* --------------- Observer Pattern --------------- */

    function registerObjectStores(array, callback) {

       // Add a new localData entry.

       // Add to indexedDB.

       _.forEach(array, function(item) {

         var exists = _.filter(view_model.localData, {"storeName" : item.name});
         if(exists == -1) {callback(); return; }

         item.data = [];
         view_model.localData.push(item);

       });

    };

    function checkIDBObjectStore(name, callback) {
      if(!_IDBSupport()) { callback(); return; }
      ensureIDBReady(function() {
        console.log(view_moel.idb.objectStoreNames);
      });
    };

    function addIDBObjectStore(name, primaryKey, timestamp, callback) {

      // Here check if IDB is null or not, like registerController did.
      ensureIDBReady(function() {

      });

    };

     // Called by the controller to receive updates (observer pattern)
    function registerController(ctrlCallback) {
       ensureIDBReady(function() {
         view_model.observerCallbacks.push(ctrlCallback);
         if(!view_model.initialSync) return;
         view_model.sync(function(response) {
           ctrlCallback(response);
         });
       });
    };

    function ensureIDBReady(callback) {
        if(view_model.idb == null)
          _establishIndexedDB(function() { callback(); });
        else
          callback();
    };

    /* --------------- Synchronisation --------------- */

    // Restores local state on first sync, or patches local and remote changes:
    function sync(callback) {
      var startClock = _generateTimestamp();
      var newLocalRecords = _getLocalRecords(view_model.lastChecked);
      if( newLocalRecords.length == 0 && view_model.serviceDB.length == 0 ) {
        _restoreLocalState( function(localResponse) {
          _patchRemoteChanges(function(remoteResponse) {
            _reduceQueue(function(queueResponse) {
              callback( {dataSource: remoteResponse, currentQueue: queueResponse} );
              console.log("Initial load took: " + (new Date(_generateTimestamp()) - new Date(startClock))/1000 + " sec." );
            });
          });
        });
      } else {
        _patchRemoteChanges(function(remoteResponse) {
          _reduceQueue(function(queueResponse) {
            callback( { dataSource: remoteResponse, currentQueue: queueResponse } );
          });
        });
      }
    };

    // Patches remote edits to serviceDB + IndexedDB:
    function _patchRemoteChanges(callback) {
      if(!view_model.allowRemote) {
          if(_IDBSupport()) callback(-1);
          else callback(-2);
          return;
      }
      _getRemoteRecords(function(response) {
        if(response.status == 200) {
          _patchLocal(response.data, function(localResponse) {
            callback(localResponse);
          });
        } else { callback("Could not connect to remote server: (" + response.status + ") error."); }
      });
    };

    // Patches the local storages with a dataset.
    function _patchLocal(data, callback) {
      _patchServiceDB(data);
      view_model.lastChecked = _generateTimestamp();
      if( _IDBSupport() ) {
        _putArrayToIndexedDB(data, function() {
          callback(1); // Patched to IDB + ServiceDB
        });
      } else {
        callback(0); // Patched to ServiceDB only.
      }
    };

    function _notifyObservers(status) {
      angular.forEach(view_model.observerCallbacks, function(callback){
        callback(status);
      });
    };

    /* --------------- Queue + State --------------- */

    // Puts IndexedDB store into scope:
    function _restoreLocalState(callback) {
      if(!_IDBSupport()) { callback(-1); return; }
      _getIndexedDB(function(idbRecords) {

        var sortedElements = _.reverse(_.sortBy(idbRecords, function(o) {
          return new Date(_.get(o, view_model.timestampProperty)).toISOString();
        }));
        var nonQueueElements = _.filter(sortedElements, {syncState: 1});
        var queueElements = _.filter(sortedElements, function(o) { return o.syncState != 1; });

        if(nonQueueElements.length > 0) {
          view_model.lastChecked = _.get(sortedElements[0], view_model.timestampProperty);
        } else {
          if(queueElements.length > 0)
            view_model.lastChecked = _.get(queueElements[queueElements.length - 1], view_model.timestampProperty);
        }

        _patchServiceDB(idbRecords);
        callback(idbRecords.length);
      });
    };

    // Synchronises elements to remote when connection is available:
    function _reduceQueue(callback) {
      if(!view_model.allowRemote) { callback(-1); return; }

      var createQueue = _.filter(view_model.serviceDB, { "syncState" : 0 });
      var updateQueue = _.filter(view_model.serviceDB, { "syncState" : 2 });

      // Get rid of the create queue:
      _safeArrayPost(createQueue, view_model.createAPI, function(successfulCreates) {
        _safeArrayPost(updateQueue, view_model.updateAPI, function(successfulUpdates) {

          var totalQueue = successfulCreates.concat(successfulUpdates);
          _.forEach(totalQueue, function(value) {
            _.set(value, view_model.timestampProperty, _generateTimestamp());
          });

          _patchLocal(_resetSyncState(totalQueue), function(response) {

            var queueLength = updateQueue.length + createQueue.length;
            var popLength = successfulCreates.length + successfulUpdates.length;

            // Check here for integrity:
            if( queueLength == popLength ) {
              callback(1); // Success: All queue elements synchronised.
            } else {
              callback(0); // Partial success: Some or none were synchronised.
            }

          });
        });
      });

    };

    /* --------------- ServiceDB Interface --------------- */

    function _patchServiceDB(data) {
      var operations = _filterOperations(data);
      _updatesToServiceDB(operations.updateOperations);
      _pushToServiceDB(operations.createOperations);
    };

    function _pushToServiceDB(array) {
      for(var i=0; i<array.length; i++) view_model.serviceDB.push(array[i]);
    };

    function _updatesToServiceDB(array) {
      for(var i=0; i<array.length; i++) {
        var indexJSON = {};
        _.set(indexJSON, view_model.primaryKeyProperty, _.get(array[i], view_model.primaryKeyProperty));
        var matchID = _.findIndex(view_model.serviceDB, indexJSON);
        if(matchID > -1) view_model.serviceDB[matchID] = array[i];
      }
    };

    function _getLocalRecords(sinceTime) {
      return _.filter(view_model.serviceDB, function(o) {
        return new Date(_.get(o, view_model.timestampProperty)).toISOString() > sinceTime;
      });
    };

    /* --------------- Data Handling --------------- */

    /* Filter remote data into create or update operations */
    function _filterOperations(data) {
      var updateOps = [];
      var createOps = [];
      for(var i=0; i<data.length; i++) {
        var queryJSON = {};
        _.set(queryJSON, view_model.primaryKeyProperty, _.get(data[i], view_model.primaryKeyProperty));
        var query = _.findIndex(view_model.serviceDB, queryJSON);
        if( query > -1 ) updateOps.push(data[i]);
        else createOps.push(data[i]);
      }
      return { updateOperations: updateOps, createOperations: createOps };
    }

    function _resetSyncState(records) {
      for(var i=0; i<records.length; i++) {
        records[i].syncState = 1;
      }
      return records;
    };

    /* --------------- Remote --------------- */

    function _postRemote(data, url, callback) {
      // Data should be a single record.
      $http({
          url: url,
          method: "POST",
          data: [data],
          headers: {'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' }
      })
      .then(
        function successCallback(response) {
          callback(response.status); // return response code.
        }, function errorCallback(response) {
          callback(response.status);
        });
    };

    function _getRemoteRecords(callback) {
      $http({
          method: 'GET',
          url: view_model.readAPI + view_model.lastChecked
        })
        .then(
          function successCallback(response) {
            if(response.data.length > 0)
              callback({data: _resetSyncState(response.data), status: 200});
            else
              callback({data: [], status: 200});

        }, function errorCallback(response) {
            callback({data: [], status: response.status});
        });
    };

    // Tries to post an array one-by-one; returns successful elements.
    function _safeArrayPost(array, url, callback) {
      var x = 0;
      var successfulElements = [];
      if(array.length == 0) { callback([]); return; }
      function loopArray(array) {
        _postRemote(array[x],url,function(response) {
          if(response == 200) successfulElements.push(array[x]);
          x++;
          if(x < array.length) { loopArray(array); }
          else { callback(successfulElements); }
        });
      };
      loopArray(array);
    };

    /* --------------- IndexedDB --------------- */

    function _establishIndexedDB(callback) {
      if(!_IDBSupport()) { callback(); /* No browser support for IDB */ return; }
      var request = view_model.indexedDB.open(view_model.indexedDBDatabaseName, view_model.indexedDBVersionNumber);
      request.onupgradeneeded = function(e) {
        var db = e.target.result;
        e.target.transaction.onerror = function() { console.error(this.error); };
        if(db.objectStoreNames.contains(view_model.objectStoreName)) {
          db.deleteObjectStore(view_model.objectStoreName);
        }
        var offlineItems = db.createObjectStore(view_model.objectStoreName, { keyPath: view_model.primaryKeyProperty, autoIncrement: false } );
        var dateIndex = offlineItems.createIndex("byDate", view_model.timestampProperty, {unique: false});
        view_model.idb = db;
      };
      request.onsuccess = function(e) {
        view_model.idb = e.target.result;
        callback();
      };
      request.onerror = function() { console.error(this.error); };
    };

    function _IDBSupport() {
      return !(view_model.indexedDB === undefined || view_model.indexedDB === null );
    };

    // Get from IndexedDB. This function returns appropriate records.
    function _getIndexedDB(callback) {
      var transaction = _newIDBTransaction();
      var objStore = transaction.objectStore(view_model.objectStoreName);
      var keyRange = IDBKeyRange.lowerBound(0);
      var cursorRequest = objStore.index('byDate').openCursor(keyRange);
      var returnableItems = [];
      transaction.oncomplete = function(e) { callback(_stripAngularHashKeys(returnableItems)); };
      cursorRequest.onsuccess = function(e) {
        var result = e.target.result;
        if (!!result == false) { return; }
        returnableItems.push(result.value);
        result.continue();
      };
      cursorRequest.onerror = function() { console.error("error"); };
    };

    // Apply array of edited objects to IndexedDB.
    function _putArrayToIndexedDB(array, callback) {
      var x = 0;
      if(array.length == 0) {
        callback();
        return;
      }

      var objStore = _newIDBTransaction().objectStore(view_model.objectStoreName);

      function putNext() {
        if(x < array.length) {
          objStore.put(array[x]).onsuccess = function() {
            x++;
            putNext();
          };
        } else {
          callback();
        }
      }
      putNext();

    };

    function _newIDBTransaction() {
      return view_model.idb.transaction([view_model.objectStoreName], 'readwrite');
    };

    // Only used for diagnostics:
    function wipeIndexedDB(callback) {
      var req = view_model.indexedDB.deleteDatabase(view_model.indexedDBDatabaseName);
      req.onsuccess = function(event) { callback(); }
    };

    /* --------------- Utilities --------------- */

    function _generateTimestamp() {
      var d = new Date();
      return d.toISOString();
    };

    // (v4) With thanks to http://stackoverflow.com/a/8809472/3381433
    function _generateUUID() {
      var d = new Date().getTime();
      if(window.performance && typeof window.performance.now === "function"){
          d += performance.now(); // use high-precision timer if available
      }
      var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
          var r = (d + Math.random()*16)%16 | 0;
          d = Math.floor(d/16);
          return (c=='x' ? r : (r&0x3|0x8)).toString(16);
      });
      return uuid;
    };

    function _stripAngularHashKeys(array) {
      for(var i=0; i<array.length; i++) delete array[i].$$hashKey;
      return array;
    };

    /* --------------- Sync Loop -------------- */

    if(view_model.autoSync > 0 && parseInt(view_model.autoSync) === view_model.autoSync) {
      (function syncLoop() {
        setTimeout(function() {
          sync(function(response) {
            _notifyObservers(response);
          });
          syncLoop();
        }, view_model.autoSync);
      })();
    }

  });
<|endoftext|>


<commit_before>/*
*  Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
*
*  Use of this source code is governed by a BSD-style license
*  that can be found in the LICENSE file in the root of the source
*  tree.
*/

'use strict';

/* globals MediaRecorder */

// This code is adapted from
// https://rawgit.com/Miguelao/demos/master/mediarecorder.html

var mediaSource = new MediaSource();
mediaSource.addEventListener('sourceopen', handleSourceOpen, false);
var mediaRecorder;
var recordedBlobs;
var sourceBuffer;

var constraints = {
  audio: true,
  video: true
};

var gumVideo = document.querySelector('video#gum');
var recordedVideo = document.querySelector('video#recorded');
// recordedVideo.src = URL.createObjectURL(mediaSource);

var recordButton = document.querySelector('button#record');
var playButton = document.querySelector('button#play');
var downloadButton = document.querySelector('button#download');
recordButton.onclick = toggleRecording;
playButton.onclick = play;
downloadButton.onclick = download;

// window.isSecureContext could be used for Chrome
var isSecureOrigin = location.protocol === 'https:' ||
location.host === 'localhost';
if (!isSecureOrigin) {
  alert('getUserMedia() must be run from a secure origin: HTTPS or localhost.' +
      '\n\nChanging protocol to HTTPS');
  location.protocol = 'HTTPS';
}

navigator.mediaDevices.getUserMedia(constraints)
.then(function(stream) {
  console.log('getUserMedia() got stream: ', stream);
  window.stream = stream; // make available to browser console
  gumVideo.srcObject = stream; // srcObject is shimmed in adapter.js
}).catch(function(error) {
  console.log('navigator.getUserMedia error: ', error);
});

function handleSourceOpen(event) {
  console.log('MediaSource opened');
  sourceBuffer = mediaSource.addSourceBuffer('video/webm; codecs="vp8"');
  console.log('Source buffer: ', sourceBuffer);
}

function handleDataAvailable(event) {
  if (event.data && event.data.size > 0) {
    recordedBlobs.push(event.data);
  }
}

function handleStop(event) {
  console.log('Recorder stopped: ', event);
}

function toggleRecording() {
  if (recordButton.textContent === 'Start Recording') {
    startRecording();
  } else {
    stopRecording();
    recordButton.textContent = 'Start Recording';
    playButton.disabled = false;
    downloadButton.disabled = false;
  }
}

function startRecording() {
  var options = {mimeType: 'video/vp9'};
  try {
    recordedBlobs = [];
    mediaRecorder = new MediaRecorder(window.stream, options);
  } catch (e) {
    try {
      options = 'video/vp8'; // Chrome 47
      mediaRecorder = new MediaRecorder(window.stream, options);
    } catch (event) {
      alert('MediaRecorder is not supported by this browser.\n\n' +
        'Try Firefox 29 or later, or Chrome 47 or later, with Enable experimental Web Platform features enabled from chrome://flags.');
      console.error('Exception while creating MediaRecorder:', event);
      return;
    }
  }
  console.log('Created MediaRecorder', mediaRecorder, 'with options', options);
  recordButton.textContent = 'Stop Recording';
  playButton.disabled = true;
  downloadButton.disabled = true;
  mediaRecorder.onstop = handleStop;
  mediaRecorder.ondataavailable = handleDataAvailable;
  mediaRecorder.start(10); // collect 10ms of data
  console.log('MediaRecorder started', mediaRecorder);
}

function stopRecording() {
  mediaRecorder.stop();
  console.log('Recorded Blobs: ', recordedBlobs);
  recordedVideo.controls = true;
}

function play() {
  var superBuffer = new Blob(recordedBlobs);
  // Firefox can not yet deal with Buffer srcObjects.
  recordedVideo.src = window.URL.createObjectURL(superBuffer);
}

function download() {
  var blob = new Blob(recordedBlobs, {type: 'video/webm'});
  var url = window.URL.createObjectURL(blob);
  var a = document.createElement('a');
  a.style.display = 'none';
  a.href = url;
  a.download = 'test.webm';
  document.body.appendChild(a);
  a.click();
  setTimeout(function() {
    document.body.removeChild(a);
    window.URL.revokeObjectURL(url);
  }, 100);
}
<commit_msg>Added console.log() for exception when using options object<commit_after>/*
*  Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
*
*  Use of this source code is governed by a BSD-style license
*  that can be found in the LICENSE file in the root of the source
*  tree.
*/

'use strict';

/* globals MediaRecorder */

// This code is adapted from
// https://rawgit.com/Miguelao/demos/master/mediarecorder.html

var mediaSource = new MediaSource();
mediaSource.addEventListener('sourceopen', handleSourceOpen, false);
var mediaRecorder;
var recordedBlobs;
var sourceBuffer;

var constraints = {
  audio: true,
  video: true
};

var gumVideo = document.querySelector('video#gum');
var recordedVideo = document.querySelector('video#recorded');
// recordedVideo.src = URL.createObjectURL(mediaSource);

var recordButton = document.querySelector('button#record');
var playButton = document.querySelector('button#play');
var downloadButton = document.querySelector('button#download');
recordButton.onclick = toggleRecording;
playButton.onclick = play;
downloadButton.onclick = download;

// window.isSecureContext could be used for Chrome
var isSecureOrigin = location.protocol === 'https:' ||
location.host === 'localhost';
if (!isSecureOrigin) {
  alert('getUserMedia() must be run from a secure origin: HTTPS or localhost.' +
      '\n\nChanging protocol to HTTPS');
  location.protocol = 'HTTPS';
}

navigator.mediaDevices.getUserMedia(constraints)
.then(function(stream) {
  console.log('getUserMedia() got stream: ', stream);
  window.stream = stream; // make available to browser console
  gumVideo.srcObject = stream; // srcObject is shimmed in adapter.js
}).catch(function(error) {
  console.log('navigator.getUserMedia error: ', error);
});

function handleSourceOpen(event) {
  console.log('MediaSource opened');
  sourceBuffer = mediaSource.addSourceBuffer('video/webm; codecs="vp8"');
  console.log('Source buffer: ', sourceBuffer);
}

function handleDataAvailable(event) {
  if (event.data && event.data.size > 0) {
    recordedBlobs.push(event.data);
  }
}

function handleStop(event) {
  console.log('Recorder stopped: ', event);
}

function toggleRecording() {
  if (recordButton.textContent === 'Start Recording') {
    startRecording();
  } else {
    stopRecording();
    recordButton.textContent = 'Start Recording';
    playButton.disabled = false;
    downloadButton.disabled = false;
  }
}

function startRecording() {
  var options = {mimeType: 'video/vp9'};
  try {
    recordedBlobs = [];
    mediaRecorder = new MediaRecorder(window.stream, options);
  } catch (e) {
    try {
      console.log('Unable to create MediaRecorder with options Object: ', e);
      options = 'video/vp8'; // Chrome 47
      mediaRecorder = new MediaRecorder(window.stream, options);
    } catch (exc) {
      alert('MediaRecorder is not supported by this browser.\n\n' +
        'Try Firefox 29 or later, or Chrome 47 or later, with Enable experimental Web Platform features enabled from chrome://flags.');
      console.error('Exception while creating MediaRecorder:', exc);
      return;
    }
  }
  console.log('Created MediaRecorder', mediaRecorder, 'with options', options);
  recordButton.textContent = 'Stop Recording';
  playButton.disabled = true;
  downloadButton.disabled = true;
  mediaRecorder.onstop = handleStop;
  mediaRecorder.ondataavailable = handleDataAvailable;
  mediaRecorder.start(10); // collect 10ms of data
  console.log('MediaRecorder started', mediaRecorder);
}

function stopRecording() {
  mediaRecorder.stop();
  console.log('Recorded Blobs: ', recordedBlobs);
  recordedVideo.controls = true;
}

function play() {
  var superBuffer = new Blob(recordedBlobs);
  // Firefox can not yet deal with Buffer srcObjects.
  recordedVideo.src = window.URL.createObjectURL(superBuffer);
}

function download() {
  var blob = new Blob(recordedBlobs, {type: 'video/webm'});
  var url = window.URL.createObjectURL(blob);
  var a = document.createElement('a');
  a.style.display = 'none';
  a.href = url;
  a.download = 'test.webm';
  document.body.appendChild(a);
  a.click();
  setTimeout(function() {
    document.body.removeChild(a);
    window.URL.revokeObjectURL(url);
  }, 100);
}
<|endoftext|>


<commit_before>module.exports = {
    "parser": "babel-eslint",
    "extends": "google",
    "rules": {
        "camelcase": 1,
        "curly": 0,
        "eol-last": 0,
        "indent": ["error", 4],
        "new-cap": 1,
        "no-extend-native": 0,
        "no-loop-func": 0,
        "no-unused-vars": 1,
        "require-jsdoc": 0
    }
};<commit_msg>Remove warning for max line length<commit_after>module.exports = {
    "parser": "babel-eslint",
    "extends": "google",
    "rules": {
        "camelcase": 1,
        "curly": 0,
        "eol-last": 0,
        "indent": ["error", 4],
        "max-len": 0,
        "new-cap": 1,
        "no-extend-native": 0,
        "no-loop-func": 0,
        "no-unused-vars": 1,
        "require-jsdoc": 0
    }
};<|endoftext|>


<commit_before>module.exports = {
  // start with google standard style
  //     https://github.com/google/eslint-config-google/blob/master/index.js
  "extends": ["eslint:recommended", "google"],
  "env": {
    "node": true,
    "es6": true
  },
  "rules": {
    // 2 == error, 1 == warning, 0 == off
    "indent": [2, 2, {
      "SwitchCase": 1,
      "VariableDeclarator": 2
    }],
    "max-len": [2, 100, {
      "ignoreComments": true,
      "ignoreUrls": true,
      "tabWidth": 2
    }],
    "no-empty": [2, {
      "allowEmptyCatch": true
    }],
    "no-implicit-coercion": [2, {
      "boolean": false,
      "number": true,
      "string": true
    }],
    "no-unused-expressions": [2, {
      "allowShortCircuit": true,
      "allowTernary": false
    }],
    "no-unused-vars": [2, {
      "vars": "all",
      "args": "after-used",
      "argsIgnorePattern": "(^reject$|^_$)",
      "varsIgnorePattern": "(^_$)"
    }],
    "quotes": [2, "single"],
    "strict": [2, "global"],
    "prefer-const": 2,
    curly: [2, "multi-line"],

    // Disabled rules
    "require-jsdoc": 0,
    "valid-jsdoc": 0,
    "comma-dangle": 0,
    "arrow-parens": 0,
  },
  "parserOptions": {
    "ecmaVersion": 6,
    "ecmaFeatures": {
      "globalReturn": true,
      "jsx": false,
      "experimentalObjectRestSpread": false
    },
    "sourceType": "script"
  }
}
<commit_msg>Fix: Allow template literals in linting (#2310)<commit_after>module.exports = {
  // start with google standard style
  //     https://github.com/google/eslint-config-google/blob/master/index.js
  "extends": ["eslint:recommended", "google"],
  "env": {
    "node": true,
    "es6": true
  },
  "rules": {
    // 2 == error, 1 == warning, 0 == off
    "indent": [2, 2, {
      "SwitchCase": 1,
      "VariableDeclarator": 2
    }],
    "max-len": [2, 100, {
      "ignoreComments": true,
      "ignoreUrls": true,
      "tabWidth": 2
    }],
    "no-empty": [2, {
      "allowEmptyCatch": true
    }],
    "no-implicit-coercion": [2, {
      "boolean": false,
      "number": true,
      "string": true
    }],
    "no-unused-expressions": [2, {
      "allowShortCircuit": true,
      "allowTernary": false
    }],
    "no-unused-vars": [2, {
      "vars": "all",
      "args": "after-used",
      "argsIgnorePattern": "(^reject$|^_$)",
      "varsIgnorePattern": "(^_$)"
    }],
    "strict": [2, "global"],
    "prefer-const": 2,
    "curly": [2, "multi-line"],

    // Disabled rules
    "require-jsdoc": 0,
    "valid-jsdoc": 0,
    "comma-dangle": 0,
    "arrow-parens": 0,
  },
  "parserOptions": {
    "ecmaVersion": 6,
    "ecmaFeatures": {
      "globalReturn": true,
      "jsx": false,
      "experimentalObjectRestSpread": false
    },
    "sourceType": "script"
  }
}
<|endoftext|>


<commit_before>import Iframe from "react-iframe";

var React = require('react');
import Dialog from "react-toolbox/lib/dialog";
import PureComponent from '../PureComponent';
import kmStyle from "../km.module.css";

export const AVAILABLE_GENESET_COHORTS = [
  "TCGA Liver Cancer (LIHC)",
  "TCGA Cervical Cancer (CESC)",
  "TCGA Sarcoma (SARC)",
  "TCGA Thymoma (THYM)",
  "TCGA Mesothelioma (MESO)",
  "TCGA Pancreatic Cancer (PAAD)",
  "TCGA Ocular melanomas (UVM)",
  "TCGA Rectal Cancer (READ)",
  "TCGA Lung Squamous Cell Carcinoma (LUSC)",
  "TCGA Stomach Cancer (STAD)",
  "TCGA Colon Cancer (COAD)",
  "TCGA Lower Grade Glioma (LGG)",
  "TCGA Thyroid Cancer (THCA)",
  "TCGA Kidney Chromophobe (KICH)",
  "TCGA Kidney Papillary Cell Carcinoma (KIRP)",
  "TCGA Bile Duct Cancer (CHOL)",
  "TCGA Glioblastoma (GBM)",
  "TCGA Lung Adenocarcinoma (LUAD)",
  "TCGA Ovarian Cancer (OV)",
  "TCGA Kidney Clear Cell Carcinoma (KIRC)",
  "TCGA Endometrioid Cancer (UCEC)",
  "TCGA Head and Neck Cancer (HNSC)",
  "TCGA Adrenocortical Cancer (ACC)",
  "TCGA Prostate Cancer (PRAD)",
  "TCGA Uterine Carcinosarcoma (UCS)",
  "TCGA Melanoma (SKCM)",
  "TCGA Pheochromocytoma & Paraganglioma (PCPG)",
  "TCGA Breast Cancer (BRCA)",
  "TCGA Testicular Cancer (TGCT)",
  "TCGA Acute Myeloid Leukemia (LAML)",
  "TCGA Large B-cell Lymphoma (DLBC)",
  "TCGA Bladder Cancer (BLCA)",
  "TCGA Esophageal Cancer (ESCA)",
  "Cancer Cell Line Encyclopedia (Breast)"
];

// export const GENESETS_VIEWER_URL = 'https://xenageneset.berkeleybop.io/xena/#';
export const GENESETS_VIEWER_URL = 'http://localhost:3000';

export class GeneSetViewDialog extends PureComponent {

    render() {

        let {showGeneSetWizard, onHide, geneSetUrl} = this.props;
        const actions = [
            {
            children: [close],
            className: kmStyle.warningDialogClose,
            onClick: this.props.onHide
            },
        ];

        return (
            <Dialog
                actions={actions}
                active={showGeneSetWizard}
                className={kmStyle.mainDialog}
                onEscKeyDown={onHide}
                onOverlayClick={onHide}
                theme={{
                  dialog: kmStyle.dialogBase,
                  wrapper: kmStyle.dialogWrapper,
                }}
                >

Differential Geneset Visualization

            </Dialog>
        );
    }
}
<commit_msg>fixed close color<commit_after>import Iframe from "react-iframe";

var React = require('react');
import Dialog from "react-toolbox/lib/dialog";
import PureComponent from '../PureComponent';
import kmStyle from "../km.module.css";

export const AVAILABLE_GENESET_COHORTS = [
  "TCGA Liver Cancer (LIHC)",
  "TCGA Cervical Cancer (CESC)",
  "TCGA Sarcoma (SARC)",
  "TCGA Thymoma (THYM)",
  "TCGA Mesothelioma (MESO)",
  "TCGA Pancreatic Cancer (PAAD)",
  "TCGA Ocular melanomas (UVM)",
  "TCGA Rectal Cancer (READ)",
  "TCGA Lung Squamous Cell Carcinoma (LUSC)",
  "TCGA Stomach Cancer (STAD)",
  "TCGA Colon Cancer (COAD)",
  "TCGA Lower Grade Glioma (LGG)",
  "TCGA Thyroid Cancer (THCA)",
  "TCGA Kidney Chromophobe (KICH)",
  "TCGA Kidney Papillary Cell Carcinoma (KIRP)",
  "TCGA Bile Duct Cancer (CHOL)",
  "TCGA Glioblastoma (GBM)",
  "TCGA Lung Adenocarcinoma (LUAD)",
  "TCGA Ovarian Cancer (OV)",
  "TCGA Kidney Clear Cell Carcinoma (KIRC)",
  "TCGA Endometrioid Cancer (UCEC)",
  "TCGA Head and Neck Cancer (HNSC)",
  "TCGA Adrenocortical Cancer (ACC)",
  "TCGA Prostate Cancer (PRAD)",
  "TCGA Uterine Carcinosarcoma (UCS)",
  "TCGA Melanoma (SKCM)",
  "TCGA Pheochromocytoma & Paraganglioma (PCPG)",
  "TCGA Breast Cancer (BRCA)",
  "TCGA Testicular Cancer (TGCT)",
  "TCGA Acute Myeloid Leukemia (LAML)",
  "TCGA Large B-cell Lymphoma (DLBC)",
  "TCGA Bladder Cancer (BLCA)",
  "TCGA Esophageal Cancer (ESCA)",
  "Cancer Cell Line Encyclopedia (Breast)"
];

// export const GENESETS_VIEWER_URL = 'https://xenageneset.berkeleybop.io/xena/#';
export const GENESETS_VIEWER_URL = 'http://localhost:3000';

export class GeneSetViewDialog extends PureComponent {

    render() {

        let {showGeneSetWizard, onHide, geneSetUrl} = this.props;
        const actions = [
            {
            children: [close],
            className: kmStyle.mainDialogClose,
            onClick: this.props.onHide
            },
        ];

        return (
            <Dialog
                actions={actions}
                active={showGeneSetWizard}
                className={kmStyle.mainDialog}
                onEscKeyDown={onHide}
                onOverlayClick={onHide}
                theme={{
                  dialog: kmStyle.dialogBase,
                  wrapper: kmStyle.dialogWrapper,
                }}
                >

Differential Geneset Visualization

            </Dialog>
        );
    }
}
<|endoftext|>


<commit_before>odoo.define('web.planner.common', function (require) {
"use strict";

var core = require('web.core');
var Dialog = require('web.Dialog');
var Model = require('web.Model');
var Widget = require('web.Widget');
var session = require('web.session');
var utils = require('web.utils');

var QWeb = core.qweb;

var _t = core._t;

var Page = core.Class.extend({
    init: function (dom, page_index) {
        var $dom = $(dom);
        this.dom = dom;
        this.hide_from_menu = $dom.attr('hide-from-menu');
        this.hide_mark_as_done = $dom.attr('hide-mark-as-done');
        this.done = false;
        this.menu_item = null;
        this.title = $dom.find('[data-menutitle]').data('menutitle');

        var page_id = this.title.replace(/\s/g, '') + page_index;
        this.set_page_id(page_id);
    },
    set_page_id: function (id) {
        this.id = id;
        $(this.dom).attr('id', id);
    },
    toggle_done: function () {
        this.done = ! this.done;
    },
    get_category_name: function (category_selector) {
        var $page = $(this.dom);

        return $page.parents(category_selector).attr('menu-category-id');
    },
});

var PlannerDialog = Widget.extend({
    template: "PlannerDialog",
    pages: [],
    menu_items: [],
    currently_shown_page: null,
    currently_active_menu_item: null,
    category_selector: 'div[menu-category-id]',
    events: {
        'click li a[href^="#"]:not([data-toggle="collapse"])': 'change_page',
        'click button.mark_as_done': 'click_on_done',
        'click a.btn-next': 'change_to_next_page',
        'click .o_planner_close_block span': 'close_modal',
    },
    init: function(parent, planner) {
        this._super(parent);
        this.planner = planner;
        this.cookie_name = this.planner.planner_application + '_last_page';
        this.set('progress', 0);
        this.pages = [];
        this.menu_items = [];
    },
    /**
     * Fetch the planner's rendered template
     */
    willStart: function() {
        var self = this;
        var res = this._super.apply(this, arguments).then(function() {
            return (new Model('web.planner')).call('render',
                [self.planner.view_id[0], self.planner.planner_application],
                {context: session.user_context});
        }).then(function(template) {
            self.$res = $(template);
        });
        return res;
    },
    start: function() {
        var self = this;
        return this._super.apply(this, arguments).then(function() {
            self.$el.on('keyup', "textarea", function() {
                if (this.scrollHeight != this.clientHeight) {
                    this.style.height = this.scrollHeight + "px";
                }
            });
            self.$res.find('.o_planner_page').andSelf().filter('.o_planner_page').each(function(index, dom_page) {
                var page = new Page(dom_page, index);
                self.pages.push(page);
            });

            var $menu = self.render_menu();  // wil use self.$res
            self.$('.o_planner_menu ul').html($menu);
            self.menu_items = self.$('.o_planner_menu li');

            self.pages.forEach(function(page) {
                page.menu_item = self._find_menu_item_by_page_id(page.id);
            });
            self.$el.find('.o_planner_content_wrapper').append(self.$res);

            // update the planner_data with the new inputs of the view
            var actual_vals = self._get_values();
            self.planner.data = _.defaults(self.planner.data, actual_vals);
            // set the default value
            self._set_values(self.planner.data);
            // show last opened page
            self._show_last_open_page();
            self.prepare_planner_event();
        });
    },
    /**
     * This method should be overridden in other planners to bind their custom events once the
     * view is loaded.
     */
    prepare_planner_event: function() {
        var self = this;
        this.on('change:progress', this, function() {
            self.trigger('planner_progress_changed', self.get('progress'));
        });
        this.on('planner_progress_changed', this, this.update_ui_progress_bar);
        this.set('progress', this.planner.progress); // set progress to trigger initial UI update
    },
    _render_done_page: function (page) {
        var mark_as_done_button = this.$('.mark_as_done')
        var mark_as_done_li = mark_as_done_button.find('i');
        var next_button = this.$('a.btn-next');
        var active_menu = $(page.menu_item).find('span');
        if (page.done) {
            active_menu.addClass('fa-check');
            mark_as_done_button.removeClass('btn-primary');
            mark_as_done_li.removeClass('fa-square-o');
            mark_as_done_button.addClass('btn-default');
            mark_as_done_li.addClass('fa-check-square-o');
            next_button.removeClass('btn-default');
            next_button.addClass('btn-primary');

            // page checked animation
            $(page.dom).addClass('marked');
            setTimeout(function() {
                $(page.dom).removeClass('marked');
            }, 1000);
        } else {
            active_menu.removeClass('fa-check');
            mark_as_done_button.removeClass('btn-default');
            mark_as_done_li.removeClass('fa-check-square-o');
            mark_as_done_button.addClass('btn-primary');
            mark_as_done_li.addClass('fa-square-o');
            next_button.removeClass('btn-primary');
            next_button.addClass('btn-default');
        }
        if (page.hide_mark_as_done) {
            next_button.removeClass('btn-default').addClass('btn-primary');
        }
    },
    _show_last_open_page: function () {
        var last_open_page = utils.get_cookie(this.cookie_name);

        if (! last_open_page) {
            last_open_page = this.planner.data.last_open_page || false;
        }

        if (last_open_page && this._find_page_by_id(last_open_page)) {
            this._display_page(last_open_page);
        } else {
            this._display_page(this.pages[0].id);
        }
    },
    update_ui_progress_bar: function(percent) {
        this.$(".progress-bar").css('width', percent+"%");
        this.$(".o_progress_text").text(percent+"%");
    },
    _create_menu_item: function(page, menu_items, menu_item_page_map) {
        var $page = $(page.dom);
        var $menu_item_element = $page.find('h1[data-menutitle]');
        var menu_title = $menu_item_element.data('menutitle') || $menu_item_element.text();

        menu_items.push(menu_title);
        menu_item_page_map[menu_title] = page.id;
    },
    render_menu: function() {
        var self = this;
        var orphan_pages = [];
        var menu_categories = [];
        var menu_item_page_map = {};

        // pages with no category
        self.pages.forEach(function(page) {
            if (! page.hide_from_menu && ! page.get_category_name(self.category_selector)) {
                self._create_menu_item(page, orphan_pages, menu_item_page_map);
            }
        });

        // pages with a category
        self.$res.filter(self.category_selector).each(function(index, menu_category) {
            var $menu_category = $(menu_category);
            var menu_category_item = {
                name: $menu_category.attr('menu-category-id'),
                classes: $menu_category.attr('menu-classes'),
                menu_items: [],
            };

            self.pages.forEach(function(page) {
                if (! page.hide_from_menu && page.get_category_name(self.category_selector) === menu_category_item.name) {
                    self._create_menu_item(page, menu_category_item.menu_items, menu_item_page_map);
                }
            });

            menu_categories.push(menu_category_item);

            // remove the branding used to separate the pages
            self.$res = self.$res.not($menu_category);
            self.$res = self.$res.add($menu_category.contents());
        });

        var menu = QWeb.render('PlannerMenu', {
            'orphan_pages': orphan_pages,
            'menu_categories': menu_categories,
            'menu_item_page_map': menu_item_page_map
        });

        return menu;
    },
    get_next_page_id: function() {
        var self = this;
        var current_page_found = false;
        var next_page_id = null;
        this.pages.every(function(page) {
            if (current_page_found) {
                next_page_id = page.id;
                return false;
            }

            if (page.id === self.currently_shown_page.id) {
                current_page_found = true;
            }

            return true;
        });

        return next_page_id;
    },
    change_to_next_page: function(ev) {
        var next_page_id = this.get_next_page_id();

        ev.preventDefault();

        if (next_page_id) {
            this._display_page(next_page_id);
        }
    },
    change_page: function(ev) {
        ev.preventDefault();
        var page_id = $(ev.currentTarget).attr('href').replace('#', '');
        this._display_page(page_id);
    },
    _find_page_by_id: function (id) {
        var result = _.find(this.pages, function (page) {
            return page.id === id;
        });

        return result;
    },
    _find_menu_item_by_page_id: function (page_id) {
        var result = _.find(this.menu_items, function (menu_item) {
            var $menu_item = $(menu_item);
            return $($menu_item.find('a')).attr('href') === '#' + page_id;
        });

        return result;
    },
    _display_page: function(page_id) {
        var mark_as_done_button = this.$('button.mark_as_done');
        var next_button = this.$('a.btn-next');
        var page = this._find_page_by_id(page_id);
        if (this.currently_active_menu_item) {
            $(this.currently_active_menu_item).removeClass('active');
        }

        var menu_item = this._find_menu_item_by_page_id(page_id);
        $(menu_item).addClass('active');
        this.currently_active_menu_item = menu_item;

        if (this.currently_shown_page) {
            $(this.currently_shown_page.dom).removeClass('show');
        }

        $(page.dom).addClass('show');
        this.currently_shown_page = page;

        if (! this.get_next_page_id()) {
            next_button.hide();
        } else {
            next_button.show();
        }

        if (page.hide_mark_as_done) {
            mark_as_done_button.hide();
        } else {
            mark_as_done_button.show();
        }

        this._render_done_page(this.currently_shown_page);

        this.planner.data.last_open_page = page_id;
        utils.set_cookie(this.cookie_name, page_id, 8*60*60); // create cookie for 8h
        this.$(".modal-body").scrollTop("0");
        autosize(this.$("textarea"));

        this.$('.o_currently_shown_page').text(this.currently_shown_page.title);
    },
    // planner data functions
    _get_values: function(page){
        // if no page_id, take the complete planner
        var base_elem = page ? $(page.dom) : this.$(".o_planner_page");
        var values = {};
        // get the selector for all the input and mark_button
        // only INPUT (select, textearea, input, checkbox and radio), and BUTTON (.mark_button#) are observed
        var inputs = base_elem.find("textarea[id^='input_element'], input[id^='input_element'], select[id^='input_element'], button[id^='mark_button']");
        _.each(inputs, function(elem){
            var $elem = $(elem);
            var tid = $elem.attr('id');
            if ($elem.prop("tagName") === 'BUTTON'){
                if($elem.hasClass('fa-check-square-o')){
                    values[tid] = 'marked';
                }else{
                    values[tid] = '';
                }
            }
            if ($elem.prop("tagName") === 'INPUT' || $elem.prop("tagName") === 'TEXTAREA'){
                var ttype = $elem.attr('type');
                if (ttype === 'checkbox' || ttype === 'radio'){
                    values[tid] = '';
                    if ($elem.is(':checked')){
                        values[tid] = 'checked';
                    }
                }else{
                    values[tid] = $elem.val();
                }
            }
        });

        this.pages.forEach(function(page) {
            values[page.id] = page.done;
        });
        return values;
    },
    _set_values: function(values){
        var self = this;
        _.each(values, function(val, id){
            var $elem = self.$('[id="'+id+'"]');
            if ($elem.prop("tagName") === 'BUTTON'){
                if(val === 'marked'){
                    $elem.addClass('fa-check-square-o btn-default').removeClass('fa-square-o btn-primary');
                    self.$("li a[href=#"+$elem.data('pageid')+"] span").addClass('fa-check');
                }
            }
            if ($elem.prop("tagName") === 'INPUT' || $elem.prop("tagName") === 'TEXTAREA'){
                var ttype = $elem.attr("type");
                if (ttype  === 'checkbox' || ttype === 'radio'){
                    if (val === 'checked') {
                       $elem.attr('checked', 'checked');
                    }
                }else{
                    $elem.val(val);
                }
            }
        });

        this.pages.forEach(function(page) {
            page.done = values[page.id];
            self._render_done_page(page);
        });
    },
    update_planner: function(){
        // update the planner.data with the inputs
        var vals = this._get_values(this.currently_shown_page);
        this.planner.data = _.extend(this.planner.data, vals);

        // re compute the progress percentage
        var total_pages = 0;
        var done_pages = 0;

        this.pages.forEach(function(page) {
            if (! page.hide_mark_as_done) {
                total_pages++;
            }
            if (page.done) {
                done_pages++;
            }
        });

        var percent = parseInt((done_pages / total_pages) * 100, 10);
        this.set('progress', percent);
        this.planner.progress = percent;
        // save data and progress in database
        this._save_planner_data();
    },
    _save_planner_data: function() {
        return (new Model('web.planner')).call('write', [this.planner.id, {'data': JSON.stringify(this.planner.data), 'progress': this.planner.progress}]);
    },
    click_on_done: function(ev) {
        ev.preventDefault();
        this.currently_shown_page.toggle_done();
        this._render_done_page(this.currently_shown_page);
        this.update_planner();
    },
    close_modal: function(ev) {
        ev.preventDefault();
        this.$el.modal('hide');
        this.$el.detach();
    },
    destroy: function() {
        this.$el.modal('hide');
        return this._super.apply(this, arguments);
    }
});

return {
    PlannerDialog: PlannerDialog,
};

});

<commit_msg>[FIX] web_planner: show the planner when the state is 0%<commit_after>odoo.define('web.planner.common', function (require) {
"use strict";

var core = require('web.core');
var Dialog = require('web.Dialog');
var Model = require('web.Model');
var Widget = require('web.Widget');
var session = require('web.session');
var utils = require('web.utils');

var QWeb = core.qweb;

var _t = core._t;

var Page = core.Class.extend({
    init: function (dom, page_index) {
        var $dom = $(dom);
        this.dom = dom;
        this.hide_from_menu = $dom.attr('hide-from-menu');
        this.hide_mark_as_done = $dom.attr('hide-mark-as-done');
        this.done = false;
        this.menu_item = null;
        this.title = $dom.find('[data-menutitle]').data('menutitle');

        var page_id = this.title.replace(/\s/g, '') + page_index;
        this.set_page_id(page_id);
    },
    set_page_id: function (id) {
        this.id = id;
        $(this.dom).attr('id', id);
    },
    toggle_done: function () {
        this.done = ! this.done;
    },
    get_category_name: function (category_selector) {
        var $page = $(this.dom);

        return $page.parents(category_selector).attr('menu-category-id');
    },
});

var PlannerDialog = Widget.extend({
    template: "PlannerDialog",
    pages: [],
    menu_items: [],
    currently_shown_page: null,
    currently_active_menu_item: null,
    category_selector: 'div[menu-category-id]',
    events: {
        'click li a[href^="#"]:not([data-toggle="collapse"])': 'change_page',
        'click button.mark_as_done': 'click_on_done',
        'click a.btn-next': 'change_to_next_page',
        'click .o_planner_close_block span': 'close_modal',
    },
    init: function(parent, planner) {
        this._super(parent);
        this.planner = planner;
        this.cookie_name = this.planner.planner_application + '_last_page';
        this.set('progress', 0);
        this.pages = [];
        this.menu_items = [];
    },
    /**
     * Fetch the planner's rendered template
     */
    willStart: function() {
        var self = this;
        var res = this._super.apply(this, arguments).then(function() {
            return (new Model('web.planner')).call('render',
                [self.planner.view_id[0], self.planner.planner_application],
                {context: session.user_context});
        }).then(function(template) {
            self.$res = $(template);
        });
        return res;
    },
    start: function() {
        var self = this;
        return this._super.apply(this, arguments).then(function() {
            self.$el.on('keyup', "textarea", function() {
                if (this.scrollHeight != this.clientHeight) {
                    this.style.height = this.scrollHeight + "px";
                }
            });
            self.$res.find('.o_planner_page').andSelf().filter('.o_planner_page').each(function(index, dom_page) {
                var page = new Page(dom_page, index);
                self.pages.push(page);
            });

            var $menu = self.render_menu();  // wil use self.$res
            self.$('.o_planner_menu ul').html($menu);
            self.menu_items = self.$('.o_planner_menu li');

            self.pages.forEach(function(page) {
                page.menu_item = self._find_menu_item_by_page_id(page.id);
            });
            self.$el.find('.o_planner_content_wrapper').append(self.$res);

            // update the planner_data with the new inputs of the view
            var actual_vals = self._get_values();
            self.planner.data = _.defaults(self.planner.data, actual_vals);
            // set the default value
            self._set_values(self.planner.data);
            // show last opened page
            self._show_last_open_page();
            self.prepare_planner_event();
        });
    },
    /**
     * This method should be overridden in other planners to bind their custom events once the
     * view is loaded.
     */
    prepare_planner_event: function() {
        var self = this;
        this.on('change:progress', this, function() {
            self.trigger('planner_progress_changed', self.get('progress'));
        });
        this.on('planner_progress_changed', this, this.update_ui_progress_bar);

        // re-apply the minimal value of 5 percent if the user manually undid every page
        if (this.planner.progress === 0 && this.get('progress') === 0) {
            this.planner.progress = 5;
            this.set('progress', 5);
        }

        this.set('progress', this.planner.progress); // set progress to trigger initial UI update
    },
    _render_done_page: function (page) {
        var mark_as_done_button = this.$('.mark_as_done')
        var mark_as_done_li = mark_as_done_button.find('i');
        var next_button = this.$('a.btn-next');
        var active_menu = $(page.menu_item).find('span');
        if (page.done) {
            active_menu.addClass('fa-check');
            mark_as_done_button.removeClass('btn-primary');
            mark_as_done_li.removeClass('fa-square-o');
            mark_as_done_button.addClass('btn-default');
            mark_as_done_li.addClass('fa-check-square-o');
            next_button.removeClass('btn-default');
            next_button.addClass('btn-primary');

            // page checked animation
            $(page.dom).addClass('marked');
            setTimeout(function() {
                $(page.dom).removeClass('marked');
            }, 1000);
        } else {
            active_menu.removeClass('fa-check');
            mark_as_done_button.removeClass('btn-default');
            mark_as_done_li.removeClass('fa-check-square-o');
            mark_as_done_button.addClass('btn-primary');
            mark_as_done_li.addClass('fa-square-o');
            next_button.removeClass('btn-primary');
            next_button.addClass('btn-default');
        }
        if (page.hide_mark_as_done) {
            next_button.removeClass('btn-default').addClass('btn-primary');
        }
    },
    _show_last_open_page: function () {
        var last_open_page = utils.get_cookie(this.cookie_name);

        if (! last_open_page) {
            last_open_page = this.planner.data.last_open_page || false;
        }

        if (last_open_page && this._find_page_by_id(last_open_page)) {
            this._display_page(last_open_page);
        } else {
            this._display_page(this.pages[0].id);
        }
    },
    update_ui_progress_bar: function(percent) {
        this.$(".progress-bar").css('width', percent+"%");
        this.$(".o_progress_text").text(percent+"%");
    },
    _create_menu_item: function(page, menu_items, menu_item_page_map) {
        var $page = $(page.dom);
        var $menu_item_element = $page.find('h1[data-menutitle]');
        var menu_title = $menu_item_element.data('menutitle') || $menu_item_element.text();

        menu_items.push(menu_title);
        menu_item_page_map[menu_title] = page.id;
    },
    render_menu: function() {
        var self = this;
        var orphan_pages = [];
        var menu_categories = [];
        var menu_item_page_map = {};

        // pages with no category
        self.pages.forEach(function(page) {
            if (! page.hide_from_menu && ! page.get_category_name(self.category_selector)) {
                self._create_menu_item(page, orphan_pages, menu_item_page_map);
            }
        });

        // pages with a category
        self.$res.filter(self.category_selector).each(function(index, menu_category) {
            var $menu_category = $(menu_category);
            var menu_category_item = {
                name: $menu_category.attr('menu-category-id'),
                classes: $menu_category.attr('menu-classes'),
                menu_items: [],
            };

            self.pages.forEach(function(page) {
                if (! page.hide_from_menu && page.get_category_name(self.category_selector) === menu_category_item.name) {
                    self._create_menu_item(page, menu_category_item.menu_items, menu_item_page_map);
                }
            });

            menu_categories.push(menu_category_item);

            // remove the branding used to separate the pages
            self.$res = self.$res.not($menu_category);
            self.$res = self.$res.add($menu_category.contents());
        });

        var menu = QWeb.render('PlannerMenu', {
            'orphan_pages': orphan_pages,
            'menu_categories': menu_categories,
            'menu_item_page_map': menu_item_page_map
        });

        return menu;
    },
    get_next_page_id: function() {
        var self = this;
        var current_page_found = false;
        var next_page_id = null;
        this.pages.every(function(page) {
            if (current_page_found) {
                next_page_id = page.id;
                return false;
            }

            if (page.id === self.currently_shown_page.id) {
                current_page_found = true;
            }

            return true;
        });

        return next_page_id;
    },
    change_to_next_page: function(ev) {
        var next_page_id = this.get_next_page_id();

        ev.preventDefault();

        if (next_page_id) {
            this._display_page(next_page_id);
        }
    },
    change_page: function(ev) {
        ev.preventDefault();
        var page_id = $(ev.currentTarget).attr('href').replace('#', '');
        this._display_page(page_id);
    },
    _find_page_by_id: function (id) {
        var result = _.find(this.pages, function (page) {
            return page.id === id;
        });

        return result;
    },
    _find_menu_item_by_page_id: function (page_id) {
        var result = _.find(this.menu_items, function (menu_item) {
            var $menu_item = $(menu_item);
            return $($menu_item.find('a')).attr('href') === '#' + page_id;
        });

        return result;
    },
    _display_page: function(page_id) {
        var mark_as_done_button = this.$('button.mark_as_done');
        var next_button = this.$('a.btn-next');
        var page = this._find_page_by_id(page_id);
        if (this.currently_active_menu_item) {
            $(this.currently_active_menu_item).removeClass('active');
        }

        var menu_item = this._find_menu_item_by_page_id(page_id);
        $(menu_item).addClass('active');
        this.currently_active_menu_item = menu_item;

        if (this.currently_shown_page) {
            $(this.currently_shown_page.dom).removeClass('show');
        }

        $(page.dom).addClass('show');
        this.currently_shown_page = page;

        if (! this.get_next_page_id()) {
            next_button.hide();
        } else {
            next_button.show();
        }

        if (page.hide_mark_as_done) {
            mark_as_done_button.hide();
        } else {
            mark_as_done_button.show();
        }

        this._render_done_page(this.currently_shown_page);

        this.planner.data.last_open_page = page_id;
        utils.set_cookie(this.cookie_name, page_id, 8*60*60); // create cookie for 8h
        this.$(".modal-body").scrollTop("0");
        autosize(this.$("textarea"));

        this.$('.o_currently_shown_page').text(this.currently_shown_page.title);
    },
    // planner data functions
    _get_values: function(page){
        // if no page_id, take the complete planner
        var base_elem = page ? $(page.dom) : this.$(".o_planner_page");
        var values = {};
        // get the selector for all the input and mark_button
        // only INPUT (select, textearea, input, checkbox and radio), and BUTTON (.mark_button#) are observed
        var inputs = base_elem.find("textarea[id^='input_element'], input[id^='input_element'], select[id^='input_element'], button[id^='mark_button']");
        _.each(inputs, function(elem){
            var $elem = $(elem);
            var tid = $elem.attr('id');
            if ($elem.prop("tagName") === 'BUTTON'){
                if($elem.hasClass('fa-check-square-o')){
                    values[tid] = 'marked';
                }else{
                    values[tid] = '';
                }
            }
            if ($elem.prop("tagName") === 'INPUT' || $elem.prop("tagName") === 'TEXTAREA'){
                var ttype = $elem.attr('type');
                if (ttype === 'checkbox' || ttype === 'radio'){
                    values[tid] = '';
                    if ($elem.is(':checked')){
                        values[tid] = 'checked';
                    }
                }else{
                    values[tid] = $elem.val();
                }
            }
        });

        this.pages.forEach(function(page) {
            values[page.id] = page.done;
        });
        return values;
    },
    _set_values: function(values){
        var self = this;
        _.each(values, function(val, id){
            var $elem = self.$('[id="'+id+'"]');
            if ($elem.prop("tagName") === 'BUTTON'){
                if(val === 'marked'){
                    $elem.addClass('fa-check-square-o btn-default').removeClass('fa-square-o btn-primary');
                    self.$("li a[href=#"+$elem.data('pageid')+"] span").addClass('fa-check');
                }
            }
            if ($elem.prop("tagName") === 'INPUT' || $elem.prop("tagName") === 'TEXTAREA'){
                var ttype = $elem.attr("type");
                if (ttype  === 'checkbox' || ttype === 'radio'){
                    if (val === 'checked') {
                       $elem.attr('checked', 'checked');
                    }
                }else{
                    $elem.val(val);
                }
            }
        });

        this.pages.forEach(function(page) {
            page.done = values[page.id];
            self._render_done_page(page);
        });
    },
    update_planner: function(){
        // update the planner.data with the inputs
        var vals = this._get_values(this.currently_shown_page);
        this.planner.data = _.extend(this.planner.data, vals);

        // re compute the progress percentage
        var total_pages = 0;
        var done_pages = 0;

        this.pages.forEach(function(page) {
            if (! page.hide_mark_as_done) {
                total_pages++;
            }
            if (page.done) {
                done_pages++;
            }
        });

        var percent = parseInt((done_pages / total_pages) * 100, 10);
        if (percent === 0) {
            percent = 5;
        }
        this.set('progress', percent);

        this.planner.progress = percent;
        // save data and progress in database
        this._save_planner_data();
    },
    _save_planner_data: function() {
        return (new Model('web.planner')).call('write', [this.planner.id, {'data': JSON.stringify(this.planner.data), 'progress': this.planner.progress}]);
    },
    click_on_done: function(ev) {
        ev.preventDefault();
        this.currently_shown_page.toggle_done();
        this._render_done_page(this.currently_shown_page);
        this.update_planner();
    },
    close_modal: function(ev) {
        ev.preventDefault();
        this.$el.modal('hide');
        this.$el.detach();
    },
    destroy: function() {
        this.$el.modal('hide');
        return this._super.apply(this, arguments);
    }
});

return {
    PlannerDialog: PlannerDialog,
};

});

<|endoftext|>


<commit_before>module.exports = {
  extends: 'airbnb-base',
  parserOptions: {
    sourceType: 'script',
  },
  plugins: [
    'import',
  ],
  rules: {
    'no-console': 'error',
    'newline-before-return': 'error',
    'no-ternary': 'error',
  },
};
<commit_msg>Adds curly as error in .eslintrc.js<commit_after>module.exports = {
  extends: 'airbnb-base',
  parserOptions: {
    sourceType: 'script',
  },
  plugins: [
    'import',
  ],
  rules: {
    'curly': 'error',
    'no-console': 'error',
    'no-ternary': 'error',
    'newline-before-return': 'error',
  },
};
<|endoftext|>


<commit_before>import angular from 'angular';
import uiRouter from 'angular-ui-router';
import routing from './settings.routes';

import c3 from 'c3';

export class SettingsController {
  /*@ngInject*/
  constructor($scope,$http) {
    $scope.search = function(){
      $http({url:'/api/db/descriptors',method:'GET'}).then(function(data){
        $scope.list = data.data;
        $scope.selected = $scope.list[0].CAN_Id;
        $scope.new = new Object();
        $scope.new.CAN_Id = 0;
        $scope.new.PDO_Description = '';
        $scope.new.map = [];
        $scope.new.map.push({description:'',length:0,offset:0,dataType:''});
        $scope.loadForEdit();
      });
    };
    $scope.search();
    $scope.loadForEdit = function(){
      $http({url:`/api/db/descriptors/${$scope.selected}`,method:'GET'}).then(function(data){
        console.log(data);
        $scope.edit = data.data;
        $scope.edit.CAN_Id = '0x' + $scope.edit.CAN_Id.toString(16);
      });
    };
    $scope.createNew = function(){
      $scope.new.CAN_Id = parseInt($scope.new.CAN_Id, 16);
      $http({url:`/api/db/descriptors/${$scope.new.CAN_Id}`,method:'PUT',data:$scope.new}).then(function(data){
        console.log(data);
        $scope.search();
      },function(msg){
        alert(msg.data);
      });
    };
    $scope.submit = function(){
      var edit = $scope.edit;
      edit.CAN_Id = parseInt(edit.CAN_Id, 16);
      for(var i=0;i<edit.map.length;i++){
        edit.map[i].offset = parseInt(edit.map[i].offset);
        edit.map[i].length = parseInt(edit.map[i].length);
        if(edit.map[i].array) edit.map[i].array.subLength = parseInt(edit.map[i].array.subLength);
        if(edit.map[i].dataType!="array") delete edit.map[i].array;
      }
      $http({url:`/api/db/descriptors/${$scope.selected}/`,method:'PUT',data:edit}).then(function(data){
        console.log(data.data);
        edit.CAN_Id = '0x' + edit.CAN_Id.toString(16);
        // $scope.search();
      },function(msg){
        console.log(msg.data);
        alert(msg.data);
        $scope.loadForEdit();
      });
    };
    $scope.delete = function(deleteMap){
      if(deleteMap){
        $http({url:`/api/db/descriptors/${$scope.selected}/`,params:deleteMap,method:'DELETE'}).then(function(data){
          console.log(data.data);
          alert("success");
          $scope.loadForEdit();
        },function(res){
          alert(res.data);
        });
      }
      else{
        $http({url:`/api/db/descriptors/${$scope.selected}/`,method:'DELETE'}).then(function(data){
          console.log(data.data);
          alert("success");
          $scope.search();
        },function(res){
          alert(res.data);
        });
      }
    }
  }

  $onInit() {
  }
}

export default angular.module('dataLoggerWebApp.settings', [uiRouter])
  .config(routing)
  .component('settings', {
    template: require('./settings.html'),
    controller: SettingsController
  })
  .name;
<commit_msg>database fix UI not refreshing<commit_after>import angular from 'angular';
import uiRouter from 'angular-ui-router';
import routing from './settings.routes';

import c3 from 'c3';

export class SettingsController {
  /*@ngInject*/
  constructor($scope,$http) {
    $scope.search = function(){
      $http({url:'/api/db/descriptors',method:'GET'}).then(function(data){
        $scope.list = data.data;
        $scope.selected = $scope.list[0].CAN_Id;
        $scope.new = new Object();
        $scope.new.CAN_Id = 0;
        $scope.new.PDO_Description = '';
        $scope.new.map = [];
        $scope.new.map.push({description:'',length:0,offset:0,dataType:''});
        $scope.loadForEdit();
      });
    };
    $scope.search();
    $scope.loadForEdit = function(){
      $http({url:`/api/db/descriptors/${$scope.selected}`,method:'GET'}).then(function(data){
        console.log(data);
        $scope.edit = data.data;
        $scope.edit.CAN_Id = '0x' + $scope.edit.CAN_Id.toString(16);
      });
    };
    $scope.createNew = function(){
      $scope.new.CAN_Id = parseInt($scope.new.CAN_Id, 16);
      $http({url:`/api/db/descriptors/${$scope.new.CAN_Id}`,method:'PUT',data:$scope.new}).then(function(data){
        console.log(data);
        $scope.search();
      },function(msg){
        alert(msg.data);
      });
    };
    $scope.submit = function(){
      var edit = $scope.edit;
      edit.CAN_Id = parseInt(edit.CAN_Id, 16);
      for(var i=0;i<edit.map.length;i++){
        edit.map[i].offset = parseInt(edit.map[i].offset);
        edit.map[i].length = parseInt(edit.map[i].length);
        if(edit.map[i].array) edit.map[i].array.subLength = parseInt(edit.map[i].array.subLength);
        if(edit.map[i].dataType!="array") delete edit.map[i].array;
      }
      $http({url:`/api/db/descriptors/${$scope.selected}/`,method:'PUT',data:edit}).then(function(data){
        console.log(data.data);
        $scope.search();
      },function(msg){
        console.log(msg.data);
        alert(msg.data);
        $scope.loadForEdit();
      });
    };
    $scope.delete = function(deleteMap){
      if(deleteMap){
        $http({url:`/api/db/descriptors/${$scope.selected}/`,params:deleteMap,method:'DELETE'}).then(function(data){
          console.log(data.data);
          alert("success");
          $scope.loadForEdit();
        },function(res){
          alert(res.data);
        });
      }
      else{
        $http({url:`/api/db/descriptors/${$scope.selected}/`,method:'DELETE'}).then(function(data){
          console.log(data.data);
          alert("success");
          $scope.search();
        },function(res){
          alert(res.data);
        });
      }
    }
  }

  $onInit() {
  }
}

export default angular.module('dataLoggerWebApp.settings', [uiRouter])
  .config(routing)
  .component('settings', {
    template: require('./settings.html'),
    controller: SettingsController
  })
  .name;
<|endoftext|>


<commit_before>import React from 'react'
import PropTypes from 'prop-types'

export default function LiqenCreator ({ onSubmit, answer, question }) {
  const tags = answer.map(
    (a, i) => a.annotations.length > 0
          ? {a.tag}
          : {a.tag}
  )

  const pluralize = (tags) => {
    const ret = []
    const exceptLast = tags.slice(0, -1)
    const last = tags[tags.length - 1]

    exceptLast.forEach(tag => {
      ret.push(tag)
      ret.push(', ')
    })
    ret[ret.length - 1] = ' and '
    ret.push(last)

    return ret
  }

  return (

{question}

          Highlight at least one
          {
            tags.length === 1 ? tags : pluralize(tags)
          }
           in the text to answer this question

          {
            answer.map(({tag, annotations}, i) => (

                # {tag}
                <AnnotationList annotations={annotations} />

            ))
          }

          <button
            className="btn btn-primary"
            disabled={!onSubmit}
            onClick={() => onSubmit()}>Send Liqen</button>

  )
}

LiqenCreator.propTypes = {
  question: PropTypes.string.isRequired,
  answer: PropTypes.arrayOf(
    PropTypes.shape({
      tag: PropTypes.string.isRequired,
      annotations: PropTypes.arrayOf(
        PropTypes.shape({
          fragment: PropTypes.string.isRequired
        })
      )
    })
  ).isRequired,
  onSubmit: PropTypes.func
}

class AnnotationList extends React.Component {
  render () {
    return (

        {
          this.props.annotations.length === 1 && (

              {this.props.annotations[0].fragment}

          )
        }
        {
          this.props.annotations.length > 1 && (

Multiple annotations

          )
        }

    )
  }
}
<commit_msg>Add "show/hide details" buttton in Liqen Creator<commit_after>import React from 'react'
import PropTypes from 'prop-types'

export default function LiqenCreator ({ onSubmit, answer, question }) {
  const tags = answer.map(
    (a, i) => a.annotations.length > 0
          ? {a.tag}
          : {a.tag}
  )

  const pluralize = (tags) => {
    const ret = []
    const exceptLast = tags.slice(0, -1)
    const last = tags[tags.length - 1]

    exceptLast.forEach(tag => {
      ret.push(tag)
      ret.push(', ')
    })
    ret[ret.length - 1] = ' and '
    ret.push(last)

    return ret
  }

  return (

{question}

          Highlight at least one
          {
            tags.length === 1 ? tags : pluralize(tags)
          }
           in the text to answer this question

          {
            answer.map(({tag, annotations}, i) => (

                # {tag}
                <AnnotationList annotations={annotations} />

            ))
          }

          <button
            className="btn btn-primary"
            disabled={!onSubmit}
            onClick={() => onSubmit()}>Send Liqen</button>

  )
}

LiqenCreator.propTypes = {
  question: PropTypes.string.isRequired,
  answer: PropTypes.arrayOf(
    PropTypes.shape({
      tag: PropTypes.string.isRequired,
      annotations: PropTypes.arrayOf(
        PropTypes.shape({
          fragment: PropTypes.string.isRequired
        })
      )
    })
  ).isRequired,
  onSubmit: PropTypes.func
}

class AnnotationList extends React.Component {
  constructor (props) {
    super(props)
    this.state = {expanded: false}
    this.expand = this.expand.bind(this)
  }

  expand () {
    this.setState({expanded: true})
  }

  compress () {
    this.setState({expanded: false})
  }

  render () {
    return (

        {
          this.props.annotations.length === 1 && (

              {this.props.annotations[0].fragment}

          )
        }
        {
          this.props.annotations.length > 1 && !this.state.expanded && (

              {this.props.annotations.length} annotations
               this.expand()}
              >
                Show

          )
        }
        {
          this.props.annotations.length > 1 && this.state.expanded && (

                {
                  this.props.annotations.map(({fragment}, i) => (

{fragment}

                  ))
                }

               this.compress()}
              >
                Hide

          )
        }

    )
  }
}
<|endoftext|>


<commit_before>import Ember from 'ember';
import moment from 'moment';
import interact from 'interact';
import OccurrenceComponent from '../occurrence';

export default OccurrenceComponent.extend({
  classNameBindings: [':as-calendar-timetable-occurrence'],

  timetable: null,
  dayWidth: Ember.computed.oneWay('timetable.dayWidth'),
  referenceElement: Ember.computed.oneWay('timetable.referenceElement'),
  occurrenceTemplateName: 'components/as-calendar/timetable/occurrence',

  actions: {
    delete: function() {
      this.sendAction('onRemove', this.get('model.content'));
    }
  },

  _calendar: Ember.computed.oneWay('model.calendar'),
  _dayEndingTime: Ember.computed.oneWay('day.endingTime'),
  _dragBottomDistance: null,
  _dragTopDistance: null,
  _dragVerticalOffset: null,
  _preview: Ember.computed.oneWay('_calendar.occurrencePreview'),

  _setupInteractions: Ember.on('didInsertElement', function() {
    interact(this.$()[0])
      .resizable({ edges: { bottom: '.resize-handle' } })
      .draggable({})
      .on('resizestart', (event) => {
        Ember.run(this, this._resizeStart, event);
      })
      .on('resizemove', (event) => {
        Ember.run(this, this._resizeMove, event);
      })
      .on('resizeend', (event) => {
        Ember.run(this, this._resizeEnd, event);
      })
      .on('dragstart', (event) => {
        Ember.run(this, this._dragStart, event);
      })
      .on('dragmove', (event) => {
        Ember.run(this, this._dragMove, event);
      })
      .on('dragend', (event) => {
        Ember.run(this, this._dragEnd, event);
      });
  }),

  _resizeStart: function() {
    this.set('_calendar.occurrencePreview', this.get('model').copy());
  },

  _resizeMove: function(event) {
    var newDuration = moment.duration(
      Math.floor(event.rect.height / this.get('timeSlotHeight')) *
      this.get('timeSlotDuration').as('ms')
    );

    var changes = {
      endsAt: moment(this.get('_startingTime')).add(newDuration).toDate()
    };

    if (this._validateChanges(changes)) {
      this.sendAction('onUpdate', this.get('_preview.content'), changes);
    }
  },

  _resizeEnd: function() {
    this.sendAction('onUpdate', this.get('content'), {
      endsAt: this.get('_preview.content.endsAt')
    });

    this.set('_calendar.occurrencePreview', null);
  },

  _dragStart: function() {
    var $this = this.$();
    var $referenceElement = Ember.$(this.get('referenceElement'));

    this.set('_calendar.occurrencePreview', this.get('model').copy());
    this.set('_dragVerticalOffset', 0);
    this.set('_dragTopDistance', $referenceElement.offset().top - $this.offset().top);

    this.set('_dragBottomDistance',
             ($referenceElement.offset().top + $referenceElement.height()) -
             ($this.offset().top + $this.height()));
  },

  _dragMove: function(event) {
    this.set('_dragVerticalOffset', this.get('_dragVerticalOffset') + event.dy);

    var offsetX = event.pageX - Ember.$(this.get('referenceElement')).offset().left;

    var verticalDrag = Math.min(
      Math.max(this.get('_dragVerticalOffset'), this.get('_dragTopDistance')),
      this.get('_dragBottomDistance')
    );

    var verticalOffset = moment.duration(
      Math.floor(verticalDrag / this.get('timeSlotHeight')) * this.get('timeSlotDuration')
    );

    var horizontalOffset = moment.duration(
      Math.floor(offsetX / this.get('dayWidth')) -
      this.get('day.offset'),
      'days'
    );

    var startsAt = moment(this.get('_startingTime')).add(verticalOffset).add(horizontalOffset);

    var changes = {
      startsAt: startsAt.toDate(),
      endsAt: moment(startsAt).add(this.get('_duration')).toDate()
    };

    if (this._validateChanges(changes)) {
      this.sendAction('onUpdate', this.get('_preview.content'), changes);
    }
  },

  _dragEnd: function() {
    this.sendAction('onUpdate', this.get('content'), {
      startsAt: this.get('_preview.content.startsAt'),
      endsAt: this.get('_preview.content.endsAt')
    });

    this.set('_calendar.occurrencePreview', null);
    this.set('_dragVerticalOffset', null);
  },

  _validateChanges: function(changes) {
    var newPreview = this.get('_preview').copy();

    newPreview.get('content').setProperties(changes);

    return newPreview.get('startingTime') >= newPreview.get('day.startingTime') &&
           newPreview.get('endingTime') <= newPreview.get('day.endingTime') &&
           newPreview.get('duration') >= this.get('timeSlotDuration');
  }
});
<commit_msg>Enable smart dragging on left and right of the borders<commit_after>import Ember from 'ember';
import moment from 'moment';
import interact from 'interact';
import OccurrenceComponent from '../occurrence';

export default OccurrenceComponent.extend({
  classNameBindings: [':as-calendar-timetable-occurrence'],

  timetable: null,
  dayWidth: Ember.computed.oneWay('timetable.dayWidth'),
  referenceElement: Ember.computed.oneWay('timetable.referenceElement'),
  occurrenceTemplateName: 'components/as-calendar/timetable/occurrence',

  actions: {
    delete: function() {
      this.sendAction('onRemove', this.get('model.content'));
    }
  },

  _calendar: Ember.computed.oneWay('model.calendar'),
  _dayEndingTime: Ember.computed.oneWay('day.endingTime'),
  _dragBottomDistance: null,
  _dragTopDistance: null,
  _dragVerticalOffset: null,
  _preview: Ember.computed.oneWay('_calendar.occurrencePreview'),

  _setupInteractions: Ember.on('didInsertElement', function() {
    interact(this.$()[0])
      .resizable({ edges: { bottom: '.resize-handle' } })
      .draggable({})
      .on('resizestart', (event) => {
        Ember.run(this, this._resizeStart, event);
      })
      .on('resizemove', (event) => {
        Ember.run(this, this._resizeMove, event);
      })
      .on('resizeend', (event) => {
        Ember.run(this, this._resizeEnd, event);
      })
      .on('dragstart', (event) => {
        Ember.run(this, this._dragStart, event);
      })
      .on('dragmove', (event) => {
        Ember.run(this, this._dragMove, event);
      })
      .on('dragend', (event) => {
        Ember.run(this, this._dragEnd, event);
      });
  }),

  _resizeStart: function() {
    this.set('_calendar.occurrencePreview', this.get('model').copy());
  },

  _resizeMove: function(event) {
    var newDuration = moment.duration(
      Math.floor(event.rect.height / this.get('timeSlotHeight')) *
      this.get('timeSlotDuration').as('ms')
    );

    var changes = {
      endsAt: moment(this.get('_startingTime')).add(newDuration).toDate()
    };

    if (this._validateChanges(changes)) {
      this.sendAction('onUpdate', this.get('_preview.content'), changes);
    }
  },

  _resizeEnd: function() {
    this.sendAction('onUpdate', this.get('content'), {
      endsAt: this.get('_preview.content.endsAt')
    });

    this.set('_calendar.occurrencePreview', null);
  },

  _dragStart: function() {
    var $this = this.$();
    var $referenceElement = Ember.$(this.get('referenceElement'));

    this.set('_calendar.occurrencePreview', this.get('model').copy());
    this.set('_dragVerticalOffset', 0);
    this.set('_dragTopDistance', $referenceElement.offset().top - $this.offset().top);

    this.set('_dragBottomDistance',
             ($referenceElement.offset().top + $referenceElement.height()) -
             ($this.offset().top + $this.height()));
  },

  _dragMove: function(event) {
    var $referenceElement = Ember.$(this.get('referenceElement'));

    var offsetX = this._clamp(
      event.pageX - $referenceElement.offset().left,
      0,
      $referenceElement.width() - 1
    );

    this.set('_dragVerticalOffset', this.get('_dragVerticalOffset') + event.dy);

    var verticalDrag = this._clamp(
      this.get('_dragVerticalOffset'),
      this.get('_dragTopDistance'),
      this.get('_dragBottomDistance')
    );

    var verticalOffset = moment.duration(
      Math.floor(verticalDrag / this.get('timeSlotHeight')) * this.get('timeSlotDuration')
    );

    var horizontalOffset = moment.duration(
      Math.floor(offsetX / this.get('dayWidth')) -
      this.get('day.offset'),
      'days'
    );

    var startsAt = moment(this.get('_startingTime')).add(verticalOffset).add(horizontalOffset);

    var changes = {
      startsAt: startsAt.toDate(),
      endsAt: moment(startsAt).add(this.get('_duration')).toDate()
    };

    if (this._validateChanges(changes)) {
      this.sendAction('onUpdate', this.get('_preview.content'), changes);
    }
  },

  _dragEnd: function() {
    this.sendAction('onUpdate', this.get('content'), {
      startsAt: this.get('_preview.content.startsAt'),
      endsAt: this.get('_preview.content.endsAt')
    });

    this.set('_calendar.occurrencePreview', null);
    this.set('_dragVerticalOffset', null);
    this.set('_dragTopDistance', null);
    this.set('_dragBottomDistance', null);
  },

  _validateChanges: function(changes) {
    var newPreview = this.get('_preview').copy();

    newPreview.get('content').setProperties(changes);

    return newPreview.get('startingTime') >= newPreview.get('day.startingTime') &&
           newPreview.get('endingTime') <= newPreview.get('day.endingTime') &&
           newPreview.get('duration') >= this.get('timeSlotDuration');
  },

  _clamp: function(number, min, max) {
    return Math.max(min, Math.min(number, max));
  }
});
<|endoftext|>