<commit_before>/**
 * A tree wich displays an overview of all poiConfigs
 */
qx.Class.define("qxex.ui.table.TableWithModelWidget", {
  extend : qx.ui.core.Widget,
  include : qx.locale.MTranslation, //for this.trc to work

  construct : function(TableConstructor,ModelConstructor){
    this.base(arguments);
    this._setLayout(new qx.ui.layout.Grow());
    this.__TableConstructor = TableConstructor;
    this.__ModelConstructor = ModelConstructor;
  },

  events : {
    "changeSelectionData" : "qx.event.type.Data",
    "cellTapData" : "qx.event.type.Data",
    "cellDbltapData" : "qx.event.type.Data",
    "cellContextmenuData" : "qx.event.type.Data"
  },

  members : {

    __primaryKeyColumnIdx : -1,

    _table : null,
    _model : null,
    _tcm : null,

    /**
     * Must be populated by deriving classes
     *
     * @type {Object}
     */
    _columns : null,

    /**
     * Will be filled with the objects in columns after a call to __setUpColumns();
     *
     * @type {Array}
     */
    __columnsArr : null,

    /*
    ---------------------------------------------------------------------------
      PUBLIC METHODS
    ---------------------------------------------------------------------------
    */

    /**
     * if we want to further customize the model before a call to create;
     */
    createModel : function(){
      this._model = new this.__ModelConstructor();
    },

    /**
     * Must be called after _columns contains all needed columns!
     */
    create : function(){

      if(!this._model){
        this.createModel();
        this.__setUpColumns();
      }

      this._table = new this.__TableConstructor(this._model);
      this._tcm = this._table.getTableColumnModel();

      // Set up column renderers
      for (key in this._columns){
        var col = this._columns[key];
        col.configure && col.configure.call(this,col.idx, this._model, this._tcm);
      }
      this._addListeners();

      //add children to widget
      this._add(this._table);
      return this;
    },

    setPrimaryKeyColumn : function(idx){
      if(this.__primaryKeyColumnIdx != -1){
        //can only be set once
      }else{
        this.__primaryKeyColumnIdx = idx;
        this._model.addIndex(idx);
      }
    },

    /*
    ---------------------------------------------------------------------------
      PUBLIC CRUD METHODS to modify state of rows. https://de.wikipedia.org/wiki/CRUD
      CRUD              SQL-92    REST
      -----------------------------------------
      Create            INSERT    PUT oder POST
      Read (Retrieve)   SELECT    GET
      Update            UPDATE    PATCH oder PUT
      Delete (Destroy)  DELETE    DELETE

      Access based on Primary Key!
    ---------------------------------------------------------------------------
    */

    insertData : function(primaryKey, data){
      var view = 0;

      var overwritingExistingData = this.deleteData(primaryKey);

      var row = this.__makeRowFromData(data);
      this._model.addRows([row],false,true); //data changed

      return overwritingExistingData;
    },

    readData : function(primaryKey){
      var view = 0;
      var rowIdx = this.__getRowIdxFromPrimaryKey(primaryKey,view);
      if(rowIdx != null){
        return this._getDataFromRowIdx(rowIdx,view);
      }
      return null;
    },

    updateData : function(primaryKey, colIdx, value){
      var view = 0;
      var rowIdx = this.__getRowIdxFromPrimaryKey(primaryKey,view);
      if(rowIdx != null){
        this._model.setValue(colIdx,rowIdx,value,view);
        return true;
      }else{
        return false;
      }
    },

    deleteData : function(primaryKey){
      var view = 0;
      var rowIdx = this.__getRowIdxFromPrimaryKey(primaryKey,view);
      if(rowIdx != null){
        var retVal = this._model.removeRows(rowId,1,view);
        return true;
      }else{
        return false;
      }
    },

    //Convenience Bulk CRUD interface:
    readAllData : function(){
      var array = [];
      var arrarr = this._model.getData();   //Simple.model.Default points this to __rowData, wich is the currently selected view and not all the data?? do we want this?
      for (var irow=0; irow<arrarr.length; irow++) {
        var row = arrarr[irow];
        var data = this.__extractDataFromRow(row);
        array.push(data);
      }
      return array;
    },

    deleteAllData : function(){
      this._model.clearAllRows();
    },

    /*
    ---------------------------------------------------------------------------
      RRIVATE METHODS
    ---------------------------------------------------------------------------
    */

    __makeRowFromData : function(data){
      var row = [];
      for (var key in this._columns){
        var fun = this._columns[key].fun;
        var result = fun.call(this,data);
        row.push(result);
      }
      this.__insertDataIntoRow(row,data);
      return row;
    },

    __insertDataIntoRow : function(row,data){
      row.__data = data;
    },

    __extractDataFromRow : function(row){
      if(!row) return null;
      return row.__data;
    },

    //protected since needed in TableWithModelWidgetForTree.js
    _getDataFromRowIdx : function(rowIdx, view){
      var row = this._model.getRowReference(rowIdx,view);
      return this.__extractDataFromRow(row);
    },

    /**
     * Lookup the row index of the data entry by using its primary key.
     *
     * @param primaryKey {String} the primaryKey of the data entry.
     * @param view {Number} the view, usually 0 works fine.
     * @return {number | null} if no row with this primary key exists.
     */
    __getRowIdxFromPrimaryKey : function(primaryKey,view){
      if(this.__primaryKeyColumnIdx==-1){
        //no primary key column configured
        this.error("__getRowIdxFromPrimaryKey no primaryKeyColumnIdx configured!");
        return null;
      }
      var rowIdx = this._model.locate(this.__primaryKeyColumnIdx,primaryKey,view);
      return rowIdx;
    },

    __setUpColumns : function(){
    	// Set the columns
      var key, column_names = [], column_labels=[];
      this.__columnsArr = [];
      var idx=0;

      for (key in this._columns){
        var col = this._columns[key]
        col.idx=idx++;

        var label = key;
        if(col.headerLabel){
          label=col.headerLabel.call(this);
        }
        column_names[col.idx] = key;
	    column_labels[col.idx] = label;
	    this.__columnsArr[col.idx] = col; //so we can reference by idx and not by id. Needed in event listeners
      }
      this._model.setColumns(column_labels,column_names);
    },

    _addListeners : function(){
      // Add cell click listeners

      //http://demo.qooxdoo.org/devel/apiviewer/#qx.ui.table.Table~cellTap!event
      var allMouseEvents = ["cellTap","cellDbltap","cellContextmenu"];

      allMouseEvents.forEach(function(mouseEventName){
        this._table.addListener(mouseEventName,function(e){
          var iCol = e.getColumn();
          var iRow = e.getRow();
          var isShiftPressed = e.isShiftPressed();
          var isCtrlPressed = e.isCtrlPressed();

          //TODO: make sure we never make a copy of the row, then __data is gone
          var view = undefined; // click from gui -> iRow is that of current view, not unfiltered view (0).
          var data = this._getDataFromRowIdx(iRow,view);

          var column = this.__columnsArr[iCol];

          var transferObj = {data : data, column : column, isShiftPressed : isShiftPressed, isCtrlPressed : isCtrlPressed};
          var mouseEventNameWithData = mouseEventName+"Data";

          this.debug(mouseEventNameWithData,iRow);
          this.fireDataEvent(mouseEventNameWithData,transferObj);

          if(this.__columnsArr[iCol][mouseEventNameWithData]){
            this.debug("calling column "+mouseEventNameWithData+" function handler");
            this.__columnsArr[iCol][mouseEventNameWithData].call(this,data,isShiftPressed,isCtrlPressed);
          }
        },this);
      },this);

      // Add selection change listeners
      this._table.getSelectionModel().addListener("changeSelection", function(e){
        var sel = [];
        var rowIndexe = [];

        this._table.getSelectionModel().iterateSelection(function(iRow) {
          var view = 0; // click from gui -> iRow is that of current view, not unfiltered view (0).
          var data = this._getDataFromRowIdx(iRow,view);
          sel.push(data);
          rowIndexe.push(iRow);
        },this);
        this.debug("changeSelectionData",sel,rowIndexe);
        this.fireDataEvent("changeSelectionData",sel);
      },this);

    }
  }
});
<commit_msg>insertAllData added<commit_after>/**
 * A tree wich displays an overview of all poiConfigs
 */
qx.Class.define("qxex.ui.table.TableWithModelWidget", {
  extend : qx.ui.core.Widget,
  include : qx.locale.MTranslation, //for this.trc to work

  construct : function(TableConstructor,ModelConstructor){
    this.base(arguments);
    this._setLayout(new qx.ui.layout.Grow());
    this.__TableConstructor = TableConstructor;
    this.__ModelConstructor = ModelConstructor;
  },

  events : {
    "changeSelectionData" : "qx.event.type.Data",
    "cellTapData" : "qx.event.type.Data",
    "cellDbltapData" : "qx.event.type.Data",
    "cellContextmenuData" : "qx.event.type.Data"
  },

  members : {

    __primaryKeyColumnIdx : -1,

    _table : null,
    _model : null,
    _tcm : null,

    /**
     * Must be populated by deriving classes
     *
     * @type {Object}
     */
    _columns : null,

    /**
     * Will be filled with the objects in columns after a call to __setUpColumns();
     *
     * @type {Array}
     */
    __columnsArr : null,

    /*
    ---------------------------------------------------------------------------
      PUBLIC METHODS
    ---------------------------------------------------------------------------
    */

    /**
     * if we want to further customize the model before a call to create;
     */
    createModel : function(){
      this._model = new this.__ModelConstructor();
    },

    /**
     * Must be called after _columns contains all needed columns!
     */
    create : function(){

      if(!this._model){
        this.createModel();
        this.__setUpColumns();
      }

      this._table = new this.__TableConstructor(this._model);
      this._tcm = this._table.getTableColumnModel();

      // Set up column renderers
      for (key in this._columns){
        var col = this._columns[key];
        col.configure && col.configure.call(this,col.idx, this._model, this._tcm);
      }
      this._addListeners();

      //add children to widget
      this._add(this._table);
      return this;
    },

    setPrimaryKeyColumn : function(idx){
      if(this.__primaryKeyColumnIdx != -1){
        //can only be set once
      }else{
        this.__primaryKeyColumnIdx = idx;
        this._model.addIndex(idx);
      }
    },

    /*
    ---------------------------------------------------------------------------
      PUBLIC CRUD METHODS to modify state of rows. https://de.wikipedia.org/wiki/CRUD
      CRUD              SQL-92    REST
      -----------------------------------------
      Create            INSERT    PUT oder POST
      Read (Retrieve)   SELECT    GET
      Update            UPDATE    PATCH oder PUT
      Delete (Destroy)  DELETE    DELETE

      Access based on Primary Key!
    ---------------------------------------------------------------------------
    */

    insertData : function(primaryKey, data){
      var overwritingExistingData = this.deleteData(primaryKey);

      var row = this.__makeRowFromData(data);
      this._model.addRows([row],false,true); //data changed

      return overwritingExistingData;
    },

    readData : function(primaryKey){
      var view = 0;
      var rowIdx = this.__getRowIdxFromPrimaryKey(primaryKey,view);
      if(rowIdx != null){
        return this._getDataFromRowIdx(rowIdx,view);
      }
      return null;
    },

    updateData : function(primaryKey, colIdx, value){
      var view = 0;
      var rowIdx = this.__getRowIdxFromPrimaryKey(primaryKey,view);
      if(rowIdx != null){
        this._model.setValue(colIdx,rowIdx,value,view);
        return true;
      }else{
        return false;
      }
    },

    deleteData : function(primaryKey){
      var view = 0;
      var rowIdx = this.__getRowIdxFromPrimaryKey(primaryKey,view);
      if(rowIdx != null){
        var retVal = this._model.removeRows(rowIdx,1,view);
        return true;
      }else{
        return false;
      }
    },

    ///////
    //Convenience Bulk CRUD interface:

    insertAllData : function(array){
      var rows = array.map(function(data){return this.__makeRowFromData(data)},this);
      this._model.addRows(rows,false,true); //data changed
    },

    readAllData : function(){
      var array = [];
      var arrarr = this._model.getData();   //Simple.model.Default points this to __rowData, wich is the currently selected view and not all the data?? do we want this?
      for (var irow=0; irow<arrarr.length; irow++) {
        var row = arrarr[irow];
        var data = this.__extractDataFromRow(row);
        array.push(data);
      }
      return array;
    },

    deleteAllData : function(){
      this._model.clearAllRows();
    },

    /*
    ---------------------------------------------------------------------------
      RRIVATE METHODS
    ---------------------------------------------------------------------------
    */

    __makeRowFromData : function(data){
      var row = [];
      for (var key in this._columns){
        var fun = this._columns[key].fun;
        var result = fun.call(this,data);
        row.push(result);
      }
      this.__insertDataIntoRow(row,data);
      return row;
    },

    __insertDataIntoRow : function(row,data){
      row.__data = data;
    },

    __extractDataFromRow : function(row){
      if(!row) return null;
      return row.__data;
    },

    //protected since needed in TableWithModelWidgetForTree.js
    _getDataFromRowIdx : function(rowIdx, view){
      var row = this._model.getRowReference(rowIdx,view);
      return this.__extractDataFromRow(row);
    },

    /**
     * Lookup the row index of the data entry by using its primary key.
     *
     * @param primaryKey {String} the primaryKey of the data entry.
     * @param view {Number} the view, usually 0 works fine.
     * @return {number | null} if no row with this primary key exists.
     */
    __getRowIdxFromPrimaryKey : function(primaryKey,view){
      if(this.__primaryKeyColumnIdx==-1){
        //no primary key column configured
        this.error("__getRowIdxFromPrimaryKey no primaryKeyColumnIdx configured!");
        return null;
      }
      var rowIdx = this._model.locate(this.__primaryKeyColumnIdx,primaryKey,view);
      return rowIdx;
    },

    __setUpColumns : function(){
    	// Set the columns
      var key, column_names = [], column_labels=[];
      this.__columnsArr = [];
      var idx=0;

      for (key in this._columns){
        var col = this._columns[key];
        col.idx=idx++;

        var label = key;
        if(col.headerLabel){
          label=col.headerLabel.call(this);
        }
        column_names[col.idx] = key;
	    column_labels[col.idx] = label;
	    this.__columnsArr[col.idx] = col; //so we can reference by idx and not by id. Needed in event listeners
      }
      this._model.setColumns(column_labels,column_names);
    },

    _addListeners : function(){
      // Add cell click listeners

      //http://demo.qooxdoo.org/devel/apiviewer/#qx.ui.table.Table~cellTap!event
      var allMouseEvents = ["cellTap","cellDbltap","cellContextmenu"];

      allMouseEvents.forEach(function(mouseEventName){
        this._table.addListener(mouseEventName,function(e){
          var iCol = e.getColumn();
          var iRow = e.getRow();
          var isShiftPressed = e.isShiftPressed();
          var isCtrlPressed = e.isCtrlPressed();

          //TODO: make sure we never make a copy of the row, then __data is gone
          var view = undefined; // click from gui -> iRow is that of current view, not unfiltered view (0).
          var data = this._getDataFromRowIdx(iRow,view);

          var column = this.__columnsArr[iCol];

          var transferObj = {data : data, column : column, isShiftPressed : isShiftPressed, isCtrlPressed : isCtrlPressed};
          var mouseEventNameWithData = mouseEventName+"Data";

          this.debug(mouseEventNameWithData,iRow);
          this.fireDataEvent(mouseEventNameWithData,transferObj);

          if(this.__columnsArr[iCol][mouseEventNameWithData]){
            this.debug("calling column "+mouseEventNameWithData+" function handler");
            this.__columnsArr[iCol][mouseEventNameWithData].call(this,data,isShiftPressed,isCtrlPressed);
          }
        },this);
      },this);

      // Add selection change listeners
      this._table.getSelectionModel().addListener("changeSelection", function(e){
        var sel = [];
        var rowIndexe = [];

        this._table.getSelectionModel().iterateSelection(function(iRow) {
          var view = 0; // click from gui -> iRow is that of current view, not unfiltered view (0).
          var data = this._getDataFromRowIdx(iRow,view);
          sel.push(data);
          rowIndexe.push(iRow);
        },this);
        this.debug("changeSelectionData",sel,rowIndexe);
        this.fireDataEvent("changeSelectionData",sel);
      },this);

    }
  }
});
<|endoftext|>


<commit_before>'use strict';

function EntityWall(world, entity) {
    this.__init__(world, entity);
}

EntityWall.prototype = {

    __init__: function(world, entity) {
        this.base_wall = new FloatingWall(400, 600, null, null, world, true);

        if (!is_defined(entity)) {
            this.base_wall.add_row_3D_text(false, -1, 'Entity Wall', TYPE_INPUT);

            this.base_wall.set_to_saveable();
        } else {
            // -1+Second wall to save as a test....;D?+input+rgba(255, 255, 255, 0.0)@

            this.base_wall.set_entity(entity);

            var width = this.base_wall.get_value(ENTITY_PROPERTY_WIDTH);
            var height = this.base_wall.get_value(ENTITY_PROPERTY_HEIGHT);
            var position = this.base_wall.get_value(ENTITY_PROPERTY_POSITION);
            var normal = this.base_wall.get_value(ENTITY_PROPERTY_NORMAL);

            this.base_wall.width = width;
            this.base_wall.height = height;
            this.base_wall.set_position(position.x, position.y, position.z);
            this.base_wall.set_normal(normal.x, normal.y, normal.z);

            this.base_wall.dimensions_changed();

            var rows_2D = this.base_wall.get_value(ENTITY_PROPERTY_2D_ROWS);
            var rows_3D = this.base_wall.get_value(ENTITY_PROPERTY_3D_ROWS);

            // Parse the 2D rows.
            if (is_defined(rows_2D)) {
                rows_2D = rows_2D.split('@');
                for (var r2 = 0; r2 < rows_2D.length; r2++) {
                    l('PARSE THE FOLLOWING 2D ROW!!!');
                    l(rows_2D[r2]);
                    if (rows_2D[r2] !== '') {

                        // INDEX {ROW}          - 0
                        // INDEX {X_START}      - 1
                        // INDEX {X_END}        - 2
                        // INDEX {TEXT}         - 3
                        // INDEX {TYPE}         - 4
                        // INDEX {COLOR}        - 5
                        // INDEX {SYNTAX_RULES} - 6

                        var d = rows_2D[r2].split('+');

                        this.base_wall.add_row_2D_text([d[1], d[2]], d[0], d[3], d[4], d[6], d[5]);
                    }
                }
            }

            // Parse the 3D rows.
            if (is_defined(rows_3D)) {
                rows_3D = rows_3D.split('@');
                for (var r3 = 0; r3 < rows_3D.length; r3++) {
                    l('PARSE THE FOLLOWING 3D ROW!!!');
                    l(rows_3D[r3]);
                    if (rows_3D[r3] !== '') {

                        // INDEX {ROW}      - 0
                        // INDEX {CENTERED} - 1
                        // INDEX {TEXT}     - 2
                        // INDEX {TYPE}     - 3
                        // INDEX {COLOR}    - 4

                        var d = rows_3D[r3].split('+');

                        if (d[1] === 'true' || d[1] === 'True') {
                            d[1] = true;
                        } else if (d[1] === 'false' || d[1] === 'False') {
                            d[1] = false;
                        }

                        this.base_wall.add_row_3D_text(d[1], d[0], d[2], d[3], d[4]);
                    }
                }
            }

            this.base_wall.refresh_position_and_look_at();
        }

        // Create the standard functionality of the entity wall.
        this.create_new_entity_button = this.base_wall.add_row_2D_text([0, 1], 0, 'Create New Entity', TYPE_BUTTON, null, COLOR_GREEN);
        this.create_new_entity_button.set_engage_function(this._create_new_entity_button_pressed.bind(this));

        // Regardless if created or loaded the following operations must be taken.
        // TODO : This should be automatic? Remove the need to explicitly need to write this code.
        this.base_wall.world.interactive_objects.push(this.base_wall);
        this.base_wall.world.root_attachables.push(this.base_wall);
        this.base_wall.refresh_position_and_look_at();

        /*__   __   ___      ___  ___          ___          ___      ___   ___
         /  ` |__) |__   /\   |  |__     |\ | |__  |  |    |__  |\ |  |  |  |  \ /    |  |  /\  |    |
         \__, |  \ |___ /~~\  |  |___    | \| |___ |/\|    |___ | \|  |  |  |   |     |/\| /~~\ |___ |___ */
        this.wall_create_new_entity = new FloatingWall(600, 400, null, null, this.base_wall.world, false, COLOR_FLOATING_WALL_SUCCESS);
        this.wall_create_new_entity.set_attachment_depth_offset(10);
        this.wall_create_new_entity.add_row_3D_text(false, -1, 'Create New Entity', TYPE_TITLE, COLOR_GREEN);
        this.wall_create_new_entity.add_close_button();
        this.wall_create_new_entity.attach_to(this.create_new_entity_button);
        this.wall_create_new_entity.hide_self_and_all_child_attachments_recursively();
    },

    _create_new_entity_button_pressed: function() {
        this.wall_create_new_entity.display_self_and_all_child_attachments_recursively();
        this.wall_create_new_entity.refresh_position_and_look_at();
    }

};<commit_msg>general updating 144<commit_after>'use strict';

function EntityWall(world, entity) {
    this.__init__(world, entity);
}

EntityWall.prototype = {

    __init__: function(world, entity) {
        this.base_wall = new FloatingWall(400, 600, null, null, world, true);

        if (!is_defined(entity)) {
            this.base_wall.add_row_3D_text(false, -1, 'Entity Wall', TYPE_INPUT);

            this.base_wall.set_to_saveable();
        } else {
            // -1+Second wall to save as a test....;D?+input+rgba(255, 255, 255, 0.0)@

            this.base_wall.set_entity(entity);

            var width = this.base_wall.get_value(ENTITY_PROPERTY_WIDTH);
            var height = this.base_wall.get_value(ENTITY_PROPERTY_HEIGHT);
            var position = this.base_wall.get_value(ENTITY_PROPERTY_POSITION);
            var normal = this.base_wall.get_value(ENTITY_PROPERTY_NORMAL);

            this.base_wall.width = width;
            this.base_wall.height = height;
            this.base_wall.set_position(position.x, position.y, position.z);
            this.base_wall.set_normal(normal.x, normal.y, normal.z);

            this.base_wall.dimensions_changed();

            var rows_2D = this.base_wall.get_value(ENTITY_PROPERTY_2D_ROWS);
            var rows_3D = this.base_wall.get_value(ENTITY_PROPERTY_3D_ROWS);

            // Parse the 2D rows.
            if (is_defined(rows_2D)) {
                rows_2D = rows_2D.split('@');
                for (var r2 = 0; r2 < rows_2D.length; r2++) {
                    l('PARSE THE FOLLOWING 2D ROW!!!');
                    l(rows_2D[r2]);
                    if (rows_2D[r2] !== '') {

                        // INDEX {ROW}          - 0
                        // INDEX {X_START}      - 1
                        // INDEX {X_END}        - 2
                        // INDEX {TEXT}         - 3
                        // INDEX {TYPE}         - 4
                        // INDEX {COLOR}        - 5
                        // INDEX {SYNTAX_RULES} - 6

                        var d = rows_2D[r2].split('+');

                        this.base_wall.add_row_2D_text([d[1], d[2]], d[0], d[3], d[4], d[6], d[5]);
                    }
                }
            }

            // Parse the 3D rows.
            if (is_defined(rows_3D)) {
                rows_3D = rows_3D.split('@');
                for (var r3 = 0; r3 < rows_3D.length; r3++) {
                    l('PARSE THE FOLLOWING 3D ROW!!!');
                    l(rows_3D[r3]);
                    if (rows_3D[r3] !== '') {

                        // INDEX {ROW}      - 0
                        // INDEX {CENTERED} - 1
                        // INDEX {TEXT}     - 2
                        // INDEX {TYPE}     - 3
                        // INDEX {COLOR}    - 4

                        var d = rows_3D[r3].split('+');

                        if (d[1] === 'true' || d[1] === 'True') {
                            d[1] = true;
                        } else if (d[1] === 'false' || d[1] === 'False') {
                            d[1] = false;
                        }

                        this.base_wall.add_row_3D_text(d[1], d[0], d[2], d[3], d[4]);
                    }
                }
            }

            this.base_wall.refresh_position_and_look_at();
        }

        // Create the standard functionality of the entity wall.
        this.create_new_entity_button = this.base_wall.add_row_2D_text([0, 1], 0, 'Create New Entity', TYPE_BUTTON, null, COLOR_GREEN);
        this.create_new_entity_button.set_engage_function(this._create_new_entity_button_pressed.bind(this));

        // Regardless if created or loaded the following operations must be taken.
        // TODO : This should be automatic? Remove the need to explicitly need to write this code.
        this.base_wall.world.interactive_objects.push(this.base_wall);
        this.base_wall.world.root_attachables.push(this.base_wall);
        this.base_wall.refresh_position_and_look_at();

        /*__   __   ___      ___  ___          ___          ___      ___   ___
         /  ` |__) |__   /\   |  |__     |\ | |__  |  |    |__  |\ |  |  |  |  \ /    |  |  /\  |    |
         \__, |  \ |___ /~~\  |  |___    | \| |___ |/\|    |___ | \|  |  |  |   |     |/\| /~~\ |___ |___ */
        this.wall_create_new_entity = new FloatingWall(600, 400, null, null, this.base_wall.world, false, COLOR_FLOATING_WALL_SUCCESS);
        this.wall_create_new_entity.set_attachment_depth_offset(10);
        this.wall_create_new_entity.add_row_3D_text(false, -1, 'Create New Entity', TYPE_TITLE, COLOR_GREEN);
        this.wall_create_new_entity.add_close_button();
        this.wall_create_new_entity.attach_to(this.create_new_entity_button);

        // TODO : Create all the default fields =)
        //this.wall_create_new_entity.add_row_2D_text();

        this.wall_create_new_entity.hide_self_and_all_child_attachments_recursively();
    },

    _create_new_entity_button_pressed: function() {
        this.wall_create_new_entity.display_self_and_all_child_attachments_recursively();
        this.wall_create_new_entity.refresh_position_and_look_at();
    }

};<|endoftext|>


<commit_before>const parseDiff = require('parse-diff')
const shouldExcludeFile = require('./should-exclude-file')
const getDiff = require('./get-diff')
const getDetails = require('./get-details')
const checkForBody = require('./check-for-body')
const configSchema = require('./config-schema')
const generateLabel = require('./generate-label')

module.exports = async (context, handler) => {
  context.todos = []

  const [configFile, diff] = await Promise.all([
    // Grab the config file
    context.config('config.yml'),
    // Get the diff for this commit or PR
    getDiff(context)
  ])

  const config = await configSchema.validate(configFile && configFile.todo ? configFile.todo : {})
  const keywords = Array.isArray(config.keyword) ? config.keyword : [config.keyword]

  // Ensure that all the labels we need are present
  const labels = await generateLabel(context, config)

  // RegEx that matches lines with the configured keywords
  const regex = new RegExp(`.*(?<keyword>${keywords.join('|')})\\s?:?(?<title>.*)`)

  // Parse the diff as files
  const files = parseDiff(diff)

  await Promise.all(files.map(async file => {
    if (shouldExcludeFile(context.log, file.to, config.exclude)) return

    // Loop through every chunk in the file
    await Promise.all(file.chunks.map(async chunk => {
      // Chunks can have multiple changes
      await Promise.all(chunk.changes.map(async (change, index) => {
        // Only act on added lines
        // :TODO: Also handle deleted TODO lines
        if (change.type !== 'add') return

        // Attempt to find a matching line: TODO Something something
        const matches = regex.exec(change.content)
        if (!matches) return

        // Trim whitespace to ensure a clean title
        const title = matches.groups.title.trim()

        // This might have matched a minified file, or something
        // huge. GitHub wouldn't allow this anyway, so let's just ignore it.
        if (!title || title.length > 256) return

        // Get the details of this commit or PR
        const deets = getDetails({ context, config, chunk, line: change.ln || change.ln2 })

        const { owner, repo } = context.repo()
        context.log(`Item found [${title}] in [${owner}/${repo}] at [${deets.sha}]`)

        // Run the handler for this webhook listener
        return handler({
          keyword: matches.groups.keyword,
          bodyComment: checkForBody(chunk.changes, index, config),
          filename: file.to,
          title,
          config,
          chunk,
          index,
          labels,
          ...deets
        })
      }))
    }))
  }))
}
<commit_msg>Catch the error<commit_after>const parseDiff = require('parse-diff')
const shouldExcludeFile = require('./should-exclude-file')
const getDiff = require('./get-diff')
const getDetails = require('./get-details')
const checkForBody = require('./check-for-body')
const configSchema = require('./config-schema')
const generateLabel = require('./generate-label')

module.exports = async (context, handler) => {
  context.todos = []

  let configFile, diff
  try {
    [configFile, diff] = await Promise.all([
      // Grab the config file
      context.config('config.yml'),
      // Get the diff for this commit or PR
      getDiff(context)
    ])
  } catch (err) {
    context.log.error(err)
    return
  }

  const config = await configSchema.validate(configFile && configFile.todo ? configFile.todo : {})
  const keywords = Array.isArray(config.keyword) ? config.keyword : [config.keyword]

  // Ensure that all the labels we need are present
  const labels = await generateLabel(context, config)

  // RegEx that matches lines with the configured keywords
  const regex = new RegExp(`.*(?<keyword>${keywords.join('|')})\\s?:?(?<title>.*)`)

  // Parse the diff as files
  const files = parseDiff(diff)

  await Promise.all(files.map(async file => {
    if (shouldExcludeFile(context.log, file.to, config.exclude)) return

    // Loop through every chunk in the file
    await Promise.all(file.chunks.map(async chunk => {
      // Chunks can have multiple changes
      await Promise.all(chunk.changes.map(async (change, index) => {
        // Only act on added lines
        // :TODO: Also handle deleted TODO lines
        if (change.type !== 'add') return

        // Attempt to find a matching line: TODO Something something
        const matches = regex.exec(change.content)
        if (!matches) return

        // Trim whitespace to ensure a clean title
        const title = matches.groups.title.trim()

        // This might have matched a minified file, or something
        // huge. GitHub wouldn't allow this anyway, so let's just ignore it.
        if (!title || title.length > 256) return

        // Get the details of this commit or PR
        const deets = getDetails({ context, config, chunk, line: change.ln || change.ln2 })

        const { owner, repo } = context.repo()
        context.log(`Item found [${title}] in [${owner}/${repo}] at [${deets.sha}]`)

        // Run the handler for this webhook listener
        return handler({
          keyword: matches.groups.keyword,
          bodyComment: checkForBody(chunk.changes, index, config),
          filename: file.to,
          title,
          config,
          chunk,
          index,
          labels,
          ...deets
        })
      }))
    }))
  }))
}
<|endoftext|>


<commit_before>(function (enyo, scope) {
	/**
	* The extended {@glossary event} [object]{@glossary Object} that is provided
	* when the [onSetupItem]{@link enyo.Repeater#onSetupItem} event is fired.
	*
	* @typedef {Object} enyo.Repeater~SetupItemEvent
	* @property {Number} index - The item's index.
	* @property {Object} item - The item control, for decoration.
	*/

	/**
	* Fires when each item is created.
	*
	* @event enyo.Repeater#onSetupItem
	* @type {Object}
	* @property {Object} sender - The [component]{@link enyo.Component} that most recently
	*	propagated the {@glossary event}.
	* @property {enyo.Repeater~SetupItemEvent} event - An [object]{@glossary Object} containing
	*	event information.
	* @public
	*/

	/**
	* {@link enyo.Repeater} is a simple [control]{@link enyo.Control} for making lists of items.
	*
	* The [components]{@link enyo.Component} of a repeater are copied for each item created,
	* and are wrapped in a control that keeps the state of the item index.
	*
	* ```javascript
	* {kind: 'Repeater', count: 2, onSetupItem: 'setImageSource', components: [
	*	{kind: 'Image'}
	* ]}
	*
	* setImageSource: function(inSender, inEvent) {
	*	var index = inEvent.index;
	*	var item = inEvent.item;
	*	item.$.image.setSrc(this.imageSources[index]);
	*	return true;
	* }
	* ```
	*
	* Be sure to return `true` from your `onSetupItem` handler to avoid having other
	* {@glossary event} handlers further up the tree try to modify your item control.
	*
	* For more information, see the documentation on
	* [Lists](building-apps/layout/lists.html) in the Enyo Developer Guide.
	*
	* @class enyo.Repeater
	* @extends enyo.Control
	* @ui
	* @public
	*/
	enyo.kind(
		/** @lends enyo.Repeater.prototype */ {

		/**
		* @private
		*/
		name: 'enyo.Repeater',

		/**
		* @private
		*/
		kind: 'enyo.Control',

		/**
		* @private
		*/
		published:
			/** @lends enyo.Repeater.prototype */ {

			/**
			* The number of items to be repeated.
			*
			* @type {Number}
			* @default 0
			* @public
			*/
			count: 0
		},

		/**
		* @private
		*/
		events: {
			onSetupItem: ''
		},

		/**
		* @method
		* @private
		*/
		create: enyo.inherit(function (sup) {
			return function() {
				sup.apply(this, arguments);
				this.countChanged();
			};
		}),

		/**
		* @method
		* @private
		*/
		initComponents: enyo.inherit(function (sup) {
			return function() {
				this.itemComponents = this.components || this.kindComponents;
				this.components = this.kindComponents = null;
				sup.apply(this, arguments);
			};
		}),

		/**
		* @private
		*/
		countChanged: function () {
			this.build();
		},

		/**
		* @private
		*/
		itemAtIndex: function (idx) {
			return this.controlAtIndex(idx);
		},

		/**
		* Renders the [collection]{@link enyo.Collection} of items. This will delete any
		* existing items and recreate the [repeater]{@link enyo.Repeater} if called after
		* the repeater has been rendered. This is called automatically when the
		* [count]{@link enyo.Repeater#count} property changes. To set the `count` property
		* and force a re-render, such as when a [data model]{@link enyo.Model} changes,
		* use `set('count', newCount, true)`, where the last parameter forces the change
		* handler to be called, even if the `count` remains the same.
		*
		* @fires enyo.Repeater#onSetupItem
		* @public
		*/
		build: function () {
			this.destroyClientControls();
			for (var i=0, c; i<this.count; i++) {
				c = this.createComponent({kind: 'enyo.OwnerProxy', index: i});
				// do this as a second step so 'c' is the owner of the created components
				c.createComponents(this.itemComponents);
				// invoke user's setup code
				this.doSetupItem({index: i, item: c});
			}
			this.render();
		},
		/**
		* Renders a specific item in the [collection]{@link enyo.Collection}. This does not
		* destroy the item, but just calls the `onSetupItem` {@glossary event} handler again
		* for it, so any state stored in the item is preserved.
		*
		* @param {Number} idx - The index of the item to render.
		* @fires enyo.Repeater#onSetupItem
		* @public
		*/
		renderRow: function (idx) {
			var c = this.itemAtIndex(idx);
			this.doSetupItem({index: idx, item: c});
		}
	});

	/**
	* Sometimes client [controls]{@link enyo.Control} are intermediated with null-controls.
	* These overrides reroute [events]{@glossary event} from such controls to the nominal
	* [delegate]{@glossary delegate}, as would happen in the absence of intermediation.
	*
	* @class enyo.OwnerProxy
	* @extends enyo.Component
	* @private
	*/
	enyo.kind(
		/** @lends enyo.OwnerProxy.prototype */ {

		/**
		* @private
		*/
		name: 'enyo.OwnerProxy',

		/**
		* @private
		*/
		kind: 'enyo.Component',

		/**
		* @private
		*/
		tag: null,

		/**
		* @method
		* @private
		*/
		decorateEvent: enyo.inherit(function (sup) {
			return function(inEventName, inEvent, inSender) {
				if (inEvent) {
					// preserve an existing index property.
					if (enyo.exists(inEvent.index)) {
						// if there are nested indices, store all of them in an array
						// but leave the innermost one in the index property
						inEvent.indices = inEvent.indices || [inEvent.index];
						inEvent.indices.push(this.index);
					} else {
						// for a single level, just decorate the index property
						inEvent.index = this.index;
					}
					// update delegate during bubbling to account for proxy
					// by moving the delegate up to the repeater level
					if (inEvent.delegate && inEvent.delegate.owner === this) {
						inEvent.delegate = this.owner;
					}
				}
				sup.apply(this, arguments);
			};
		})
	});

})(enyo, this);
<commit_msg>ENYO-1: Correct the parent kind for enyo.OwnerProxy.<commit_after>(function (enyo, scope) {
	/**
	* The extended {@glossary event} [object]{@glossary Object} that is provided
	* when the [onSetupItem]{@link enyo.Repeater#onSetupItem} event is fired.
	*
	* @typedef {Object} enyo.Repeater~SetupItemEvent
	* @property {Number} index - The item's index.
	* @property {Object} item - The item control, for decoration.
	*/

	/**
	* Fires when each item is created.
	*
	* @event enyo.Repeater#onSetupItem
	* @type {Object}
	* @property {Object} sender - The [component]{@link enyo.Component} that most recently
	*	propagated the {@glossary event}.
	* @property {enyo.Repeater~SetupItemEvent} event - An [object]{@glossary Object} containing
	*	event information.
	* @public
	*/

	/**
	* {@link enyo.Repeater} is a simple [control]{@link enyo.Control} for making lists of items.
	*
	* The [components]{@link enyo.Component} of a repeater are copied for each item created,
	* and are wrapped in a control that keeps the state of the item index.
	*
	* ```javascript
	* {kind: 'Repeater', count: 2, onSetupItem: 'setImageSource', components: [
	*	{kind: 'Image'}
	* ]}
	*
	* setImageSource: function(inSender, inEvent) {
	*	var index = inEvent.index;
	*	var item = inEvent.item;
	*	item.$.image.setSrc(this.imageSources[index]);
	*	return true;
	* }
	* ```
	*
	* Be sure to return `true` from your `onSetupItem` handler to avoid having other
	* {@glossary event} handlers further up the tree try to modify your item control.
	*
	* For more information, see the documentation on
	* [Lists](building-apps/layout/lists.html) in the Enyo Developer Guide.
	*
	* @class enyo.Repeater
	* @extends enyo.Control
	* @ui
	* @public
	*/
	enyo.kind(
		/** @lends enyo.Repeater.prototype */ {

		/**
		* @private
		*/
		name: 'enyo.Repeater',

		/**
		* @private
		*/
		kind: 'enyo.Control',

		/**
		* @private
		*/
		published:
			/** @lends enyo.Repeater.prototype */ {

			/**
			* The number of items to be repeated.
			*
			* @type {Number}
			* @default 0
			* @public
			*/
			count: 0
		},

		/**
		* @private
		*/
		events: {
			onSetupItem: ''
		},

		/**
		* @method
		* @private
		*/
		create: enyo.inherit(function (sup) {
			return function() {
				sup.apply(this, arguments);
				this.countChanged();
			};
		}),

		/**
		* @method
		* @private
		*/
		initComponents: enyo.inherit(function (sup) {
			return function() {
				this.itemComponents = this.components || this.kindComponents;
				this.components = this.kindComponents = null;
				sup.apply(this, arguments);
			};
		}),

		/**
		* @private
		*/
		countChanged: function () {
			this.build();
		},

		/**
		* @private
		*/
		itemAtIndex: function (idx) {
			return this.controlAtIndex(idx);
		},

		/**
		* Renders the [collection]{@link enyo.Collection} of items. This will delete any
		* existing items and recreate the [repeater]{@link enyo.Repeater} if called after
		* the repeater has been rendered. This is called automatically when the
		* [count]{@link enyo.Repeater#count} property changes. To set the `count` property
		* and force a re-render, such as when a [data model]{@link enyo.Model} changes,
		* use `set('count', newCount, true)`, where the last parameter forces the change
		* handler to be called, even if the `count` remains the same.
		*
		* @fires enyo.Repeater#onSetupItem
		* @public
		*/
		build: function () {
			this.destroyClientControls();
			for (var i=0, c; i<this.count; i++) {
				c = this.createComponent({kind: 'enyo.OwnerProxy', index: i});
				// do this as a second step so 'c' is the owner of the created components
				c.createComponents(this.itemComponents);
				// invoke user's setup code
				this.doSetupItem({index: i, item: c});
			}
			this.render();
		},
		/**
		* Renders a specific item in the [collection]{@link enyo.Collection}. This does not
		* destroy the item, but just calls the `onSetupItem` {@glossary event} handler again
		* for it, so any state stored in the item is preserved.
		*
		* @param {Number} idx - The index of the item to render.
		* @fires enyo.Repeater#onSetupItem
		* @public
		*/
		renderRow: function (idx) {
			var c = this.itemAtIndex(idx);
			this.doSetupItem({index: idx, item: c});
		}
	});

	/**
	* Sometimes client [controls]{@link enyo.Control} are intermediated with null-controls.
	* These overrides reroute [events]{@glossary event} from such controls to the nominal
	* [delegate]{@glossary delegate}, as would happen in the absence of intermediation.
	*
	* @class enyo.OwnerProxy
	* @extends enyo.Control
	* @private
	*/
	enyo.kind(
		/** @lends enyo.OwnerProxy.prototype */ {

		/**
		* @private
		*/
		name: 'enyo.OwnerProxy',

		/**
		* @private
		*/
		kind: 'enyo.Control',

		/**
		* @private
		*/
		tag: null,

		/**
		* @method
		* @private
		*/
		decorateEvent: enyo.inherit(function (sup) {
			return function(inEventName, inEvent, inSender) {
				if (inEvent) {
					// preserve an existing index property.
					if (enyo.exists(inEvent.index)) {
						// if there are nested indices, store all of them in an array
						// but leave the innermost one in the index property
						inEvent.indices = inEvent.indices || [inEvent.index];
						inEvent.indices.push(this.index);
					} else {
						// for a single level, just decorate the index property
						inEvent.index = this.index;
					}
					// update delegate during bubbling to account for proxy
					// by moving the delegate up to the repeater level
					if (inEvent.delegate && inEvent.delegate.owner === this) {
						inEvent.delegate = this.owner;
					}
				}
				sup.apply(this, arguments);
			};
		})
	});

})(enyo, this);
<|endoftext|>


<commit_before>var _ = require('underscore');
var $ = require('jquery');
var log = require('cdb.log');
var util = require('cdb.core.util');
var View = require('../core/view');
var StackedLegend = require('../geo/ui/legend/stacked-legend');
var MapViewFactory = require('../geo/map-view-factory');
var LegendModel = require('../geo/ui/legend-model');
var Legend = require('../geo/ui/legend');
var InfowindowModel = require('../geo/ui/infowindow-model');
var Infowindow = require('../geo/ui/infowindow-view');
var Template = require('../core/template');
var Layers = require('./vis/layers');
var Overlay = require('./vis/overlay');
var INFOWINDOW_TEMPLATE = require('./vis/infowindow-template');
var InfowindowManager = require('./infowindow-manager');
var TooltipManager = require('./tooltip-manager');

/**
 * Visualization creation
 */
var Vis = View.extend({
  initialize: function () {
    this.model.bind('change:loading', function () {
      if (this.loader) {
        if (this.model.get('loading')) {
          this.loader.show();
        } else {
          this.loader.hide();
        }
      }
    }, this);

    this.model.once('load', this.render, this);
    this.model.on('invalidateSize', this._invalidateSize, this);
    this.model.overlaysCollection.on('add remove', this._resetOverlays, this);

    this.overlays = [];

    _.bindAll(this, '_onResize');
  },

  render: function () {
    // TODO: Get this from this.model
    var options = {};

    // Create the MapView

    var div = $('<div>').css({
      position: 'relative',
      width: '100%',
      height: '100%'
    });

    this.container = div;

    // Another div to prevent leaflet grabbing the div
    var div_hack = $('<div>')
      .addClass('cartodb-map-wrapper')
      .css({
        position: 'absolute',
        top: 0,
        left: 0,
        right: 0,
        bottom: 0,
        width: '100%'
      });

    div.append(div_hack);

    this.$el.append(div);

    var mapViewFactory = new MapViewFactory();

    this.mapView = mapViewFactory.createMapView(this.model.map.get('provider'), this.model.map, div_hack, this.model.layerGroupModel);

    // Infowindows && Tooltips
    var infowindowManager = new InfowindowManager(this, {
      showEmptyFields: this.model.get('showEmptyInfowindowFields')
    });
    infowindowManager.manage(this.mapView, this.model.map);

    var tooltipManager = new TooltipManager(this);
    tooltipManager.manage(this.mapView, this.model.map);

    this.mapView.bind('newLayerView', this._bindLayerViewToLoader, this);

    // Bindings
    if (this.model.get('showLegends')) {
      this.addLegends();
    }

    this._resetOverlays(options);

    // If a CartoDB embed map is hidden by default, its
    // height is 0 and it will need to recalculate its size
    // and re-center again.
    // We will wait until it is resized and then apply
    // the center provided in the parameters and the
    // correct size.
    var map_h = this.$el.outerHeight();
    if (map_h === 0) {
      $(window).bind('resize', this._onResize);
    }
  },

  _bindLayerViewToLoader: function (layerView) {
    layerView.bind('load', function () {
      this.model.untrackLoadingObject(layerView);
    }, this);
    layerView.bind('loading', function () {
      this.model.trackLoadingObject(layerView);
    }, this);
  },

  addLegends: function (layers) {
    this._addLegends(this.createLegendView(this.model.map.layers));
  },

  _addLegends: function (legends) {
    if (this.legends) {
      this.legends.remove();
    }

    this.legends = new StackedLegend({
      legends: legends
    });

    this.mapView.addOverlay(this.legends);
  },

  createLegendView: function (layers) {
    var legends = [];
    for (var i = layers.length - 1; i >= 0; --i) {
      var cid = layers.at(i).cid;
      var layer = layers.at(i).attributes;
      if (layer.visible) {
        var layerView = this.mapView.getLayerViewByLayerCid(cid);
        if (layerView) {
          legends.push(this._createLayerLegendView(layer, layerView));
        }
      }
    }
    return _.flatten(legends);
  },

  _createLegendView: function (layer, layerView) {
    if (layer.legend) {
      layer.legend.data = layer.legend.items;
      var legend = layer.legend;

      if ((legend.items && legend.items.length) || legend.template) {
        var legendAttrs = _.extend(layer.legend, {
          visible: layer.visible
        });
        var legendModel = new LegendModel(legendAttrs);
        var legendView = new Legend({ model: legendModel });
        layerView.bind('change:visibility', function (layer, hidden) {
          legendView[hidden ? 'hide' : 'show']();
        });
        layerView.legend = legendModel;
        return legendView;
      }
    }
    return null;
  },

  _createLayerLegendView: function (layer, layerView) {
    var self = this;
    var legends = [];
    var sublayers;
    if (layer.options && layer.options.layer_definition) {
      sublayers = layer.options.layer_definition.layers;
      _(sublayers).each(function (sub, i) {
        legends.push(self._createLegendView(sub, layerView.getSubLayer(i)));
      });
    } else if (layer.options && layer.options.named_map && layer.options.named_map.layers) {
      sublayers = layer.options.named_map.layers;
      _(sublayers).each(function (sub, i) {
        legends.push(self._createLegendView(sub, layerView.getSubLayer(i)));
      });
    } else {
      legends.push(this._createLegendView(layer, layerView));
    }
    return _.compact(legends).reverse();
  },

  _setLayerOptions: function (options) {
    var layers = [];

    // flatten layers (except baselayer)
    layers = _.map(this.getLayerViews().slice(1), function (layer) {
      if (layer.getSubLayers) {
        return layer.getSubLayers();
      }
      return layer;
    });

    layers = _.flatten(layers);

    for (var i = 0; i < Math.min(options.sublayer_options.length, layers.length); ++i) {
      var o = options.sublayer_options[i];
      var subLayer = layers[i];
      var legend = this.legends && this.legends.getLegendByIndex(i);

      if (legend) {
        legend[o.visible ? 'show' : 'hide']();
      }

      // HACK
      if (subLayer.model && subLayer.model.get('type') === 'torque') {
        if (o.visible === false) {
          subLayer.model.set('visible', false);
        }
      }
    }
  },

  _resetOverlays: function (options) {
    var overlays = this.model.overlaysCollection.toJSON();
    // Sort the overlays by its internal order
    overlays = _.sortBy(overlays, function (overlay) {
      return overlay.order === null ? Number.MAX_VALUE : overlay.order;
    });

    // if logo is not included, let add it :)
    var overlayNames = _.pluck(overlays, 'type');
    if (!_.contains(overlayNames, 'logo')) {
      overlays.push({
        type: 'logo'
      });
    }

    // clean current overlays
    while (this.overlays.length !== 0) {
      this.overlays.pop().clean();
    }

    this._createOverlays(overlays, options);
  },

  _createOverlays: function (overlays, options) {
    _(overlays).each(function (data) {
      var type = data.type;

      // IE<10 doesn't support the Fullscreen API
      if (type === 'fullscreen' && util.browser.ie && util.browser.ie.version <= 10) return;

      // Decide to create or not the custom overlays
      if (type === 'image' || type === 'text' || type === 'annotation') {
        var isDevice = (data.options.device === 'mobile');
        if (this.mobile !== isDevice) return;
        if (!options[type] && options[type] !== undefined) {
          return;
        }
      }

      // We add the header overlay
      var overlay;
      if (type === 'header') {
        overlay = this._addHeader(data);
      } else {
        overlay = this.addOverlay(data);
      }

      // We show/hide the overlays
      if (overlay && (type in options) && options[type] === false) overlay.hide();

      var opt = data.options;

      if (type === 'layer_selector' && options[type] || type === 'layer_selector' && overlay.model.get('display') && options[type] === undefined) {
        overlay.show();
      }

      if (type === 'fullscreen' && options[type] || type === 'fullscreen' && opt.display && options[type] === undefined) {
        overlay.show();
      }

      if (type === 'search' && options[type] || type === 'search' && opt.display && options[type] === undefined) {
        overlay.show();
      }

      if (type === 'logo' && (options[type] !== undefined || options[type] === true)) {
        overlay.show();
      }

      if (type === 'header') {
        var m = overlay.model;

        var title = this.model.get('title');
        if (title) {
          m.set('show_title', title);
        }
        var description = this.model.get('description');
        if (description) {
          m.set('show_description', description);
        }

        if (m.get('show_title') || m.get('show_description')) {
          $('.cartodb-map-wrapper').addClass('with_header');
        }

        overlay.render();
      }
    }, this);
  },

  _setupSublayers: function (layers, options) {
    options.sublayer_options = [];

    _.each(layers.slice(1), function (lyr) {
      if (lyr.type === 'layergroup') {
        _.each(lyr.options.layer_definition.layers, function (l) {
          options.sublayer_options.push({ visible: (l.visible !== undefined ? l.visible : true) });
        });
      } else if (lyr.type === 'namedmap') {
        _.each(lyr.options.named_map.layers, function (l) {
          options.sublayer_options.push({ visible: (l.visible !== undefined ? l.visible : true) });
        });
      } else if (lyr.type === 'torque') {
        options.sublayer_options.push({ visible: (lyr.options.visible !== undefined ? lyr.options.visible : true) });
      }
    });
  },

  _invalidateSize: function () {
    this.mapView.invalidateSize();
  },

  _addHeader: function (data) {
    return this.addOverlay({
      type: 'header',
      options: data.options
    });
  },

  addOverlay: function (overlay) {
    overlay.map = this.model.map;

    var v = Overlay.create(overlay.type, this, overlay);

    if (v) {
      // Save tiles loader view for later
      if (overlay.type === 'loader') {
        this.loader = v;
      }

      this.mapView.addOverlay(v);

      this.overlays.push(v);

      v.bind('clean', function () {
        for (var i in this.overlays) {
          var o = this.overlays[i];
          if (v.cid === o.cid) {
            this.overlays.splice(i, 1);
            return;
          }
        }
      }, this);
    }
    return v;
  },

  createLayer: function (layerData) {
    var layerModel = Layers.create(layerData.type || layerData.kind, this, layerData);
    return this.mapView.createLayer(layerModel);
  },

  throwError: function (msg, lyr) {
    log.error(msg);
    var self = this;
    _.defer(function () {
      self.trigger('error', msg, lyr);
    });
  },

  // returns an array of layers
  getLayerViews: function () {
    var self = this;
    return _.compact(this.model.map.layers.map(function (layer) {
      return self.mapView.getLayerViewByLayerCid(layer.cid);
    }));
  },

  getOverlays: function () {
    return this.overlays;
  },

  getOverlay: function (type) {
    return _(this.overlays).find(function (v) {
      return v.type === type;
    });
  },

  getOverlaysByType: function (type) {
    return _(this.overlays).filter(function (v) {
      return v.type === type;
    });
  },

  _onResize: function () {
    $(window).unbind('resize', this._onResize);

    var self = this;
    // This timeout is necessary due to GMaps needs time
    // to load tiles and recalculate its bounds :S
    setTimeout(function () {
      self.model.centerMapToOrigin();
    }, 150);
  }
}, {
  /**
   * adds an infowindow to the map controlled by layer events.
   * it enables interaction and overrides the layer interacivity
   * ``fields`` array of column names
   * ``map`` native map object, leaflet of gmaps
   * ``layer`` cartodb layer (or sublayer)
   */
  addInfowindow: function (map, layer, fields, opts) {
    var options = _.defaults(opts || {}, {
      infowindowTemplate: INFOWINDOW_TEMPLATE.light,
      templateType: 'mustache',
      triggerEvent: 'featureClick',
      templateName: 'light',
      extraFields: [],
      cursorInteraction: true
    });

    if (!map) throw new Error('map is not valid');
    if (!layer) throw new Error('layer is not valid');
    if (!fields && fields.length === undefined) throw new Error('fields should be a list of strings');

    var f = [];
    fields = fields.concat(options.extraFields);
    for (var i = 0; i < fields.length; ++i) {
      f.push({ name: fields, order: i });
    }

    var infowindowModel = new InfowindowModel({
      fields: f,
      template_name: options.templateName
    });

    var infowindow = new Infowindow({
      model: infowindowModel,
      mapView: map.viz.mapView,
      template: new Template({
        template: options.infowindowTemplate,
        type: options.templateType
      }).asFunction()
    });

    map.viz.mapView.addInfowindow(infowindow);

    layer.bind(options.triggerEvent, function (e, latlng, pos, data, layer) {
      var render_fields = [];
      for (var f = 0; f < fields.length; ++f) {
        var field = fields[f];
        var d = data[field];
        if (d) {
          render_fields.push({
            title: field,
            value: d,
            index: 0
          });
        }
      }

      infowindow.model.set({
        content: {
          fields: render_fields,
          data: data
        }
      });

      infowindow
        .setLatLng(latlng)
        .showInfowindow();
      infowindow.adjustPan();
    }, infowindow);

    // remove the callback on clean
    infowindow.bind('clean', function () {
      layer.unbind(options.triggerEvent, null, infowindow);
    });

    if (options.cursorInteraction) {
      Vis.addCursorInteraction(map, layer);
    }

    return infowindow;
  },

  addCursorInteraction: function (map, layer) {
    var mapView = map.viz.mapView;
    layer.bind('mouseover', function () {
      mapView.setCursor('pointer');
    });

    layer.bind('mouseout', function (m, layer) {
      mapView.setCursor('auto');
    });
  },

  removeCursorInteraction: function (map, layer) {
    var mapView = map.viz.mapView;
    layer.unbind(null, null, mapView);
  }
});

module.exports = Vis;
<commit_msg>Better checking for logo overlay<commit_after>var _ = require('underscore');
var $ = require('jquery');
var log = require('cdb.log');
var util = require('cdb.core.util');
var View = require('../core/view');
var StackedLegend = require('../geo/ui/legend/stacked-legend');
var MapViewFactory = require('../geo/map-view-factory');
var LegendModel = require('../geo/ui/legend-model');
var Legend = require('../geo/ui/legend');
var InfowindowModel = require('../geo/ui/infowindow-model');
var Infowindow = require('../geo/ui/infowindow-view');
var Template = require('../core/template');
var Layers = require('./vis/layers');
var Overlay = require('./vis/overlay');
var INFOWINDOW_TEMPLATE = require('./vis/infowindow-template');
var InfowindowManager = require('./infowindow-manager');
var TooltipManager = require('./tooltip-manager');

/**
 * Visualization creation
 */
var Vis = View.extend({
  initialize: function () {
    this.model.bind('change:loading', function () {
      if (this.loader) {
        if (this.model.get('loading')) {
          this.loader.show();
        } else {
          this.loader.hide();
        }
      }
    }, this);

    this.model.once('load', this.render, this);
    this.model.on('invalidateSize', this._invalidateSize, this);
    this.model.overlaysCollection.on('add remove', this._resetOverlays, this);

    this.overlays = [];

    _.bindAll(this, '_onResize');
  },

  render: function () {
    // TODO: Get this from this.model
    var options = {};

    // Create the MapView

    var div = $('<div>').css({
      position: 'relative',
      width: '100%',
      height: '100%'
    });

    this.container = div;

    // Another div to prevent leaflet grabbing the div
    var div_hack = $('<div>')
      .addClass('cartodb-map-wrapper')
      .css({
        position: 'absolute',
        top: 0,
        left: 0,
        right: 0,
        bottom: 0,
        width: '100%'
      });

    div.append(div_hack);

    this.$el.append(div);

    var mapViewFactory = new MapViewFactory();

    this.mapView = mapViewFactory.createMapView(this.model.map.get('provider'), this.model.map, div_hack, this.model.layerGroupModel);

    // Infowindows && Tooltips
    var infowindowManager = new InfowindowManager(this, {
      showEmptyFields: this.model.get('showEmptyInfowindowFields')
    });
    infowindowManager.manage(this.mapView, this.model.map);

    var tooltipManager = new TooltipManager(this);
    tooltipManager.manage(this.mapView, this.model.map);

    this.mapView.bind('newLayerView', this._bindLayerViewToLoader, this);

    // Bindings
    if (this.model.get('showLegends')) {
      this.addLegends();
    }

    this._resetOverlays(options);

    // If a CartoDB embed map is hidden by default, its
    // height is 0 and it will need to recalculate its size
    // and re-center again.
    // We will wait until it is resized and then apply
    // the center provided in the parameters and the
    // correct size.
    var map_h = this.$el.outerHeight();
    if (map_h === 0) {
      $(window).bind('resize', this._onResize);
    }
  },

  _bindLayerViewToLoader: function (layerView) {
    layerView.bind('load', function () {
      this.model.untrackLoadingObject(layerView);
    }, this);
    layerView.bind('loading', function () {
      this.model.trackLoadingObject(layerView);
    }, this);
  },

  addLegends: function (layers) {
    this._addLegends(this.createLegendView(this.model.map.layers));
  },

  _addLegends: function (legends) {
    if (this.legends) {
      this.legends.remove();
    }

    this.legends = new StackedLegend({
      legends: legends
    });

    this.mapView.addOverlay(this.legends);
  },

  createLegendView: function (layers) {
    var legends = [];
    for (var i = layers.length - 1; i >= 0; --i) {
      var cid = layers.at(i).cid;
      var layer = layers.at(i).attributes;
      if (layer.visible) {
        var layerView = this.mapView.getLayerViewByLayerCid(cid);
        if (layerView) {
          legends.push(this._createLayerLegendView(layer, layerView));
        }
      }
    }
    return _.flatten(legends);
  },

  _createLegendView: function (layer, layerView) {
    if (layer.legend) {
      layer.legend.data = layer.legend.items;
      var legend = layer.legend;

      if ((legend.items && legend.items.length) || legend.template) {
        var legendAttrs = _.extend(layer.legend, {
          visible: layer.visible
        });
        var legendModel = new LegendModel(legendAttrs);
        var legendView = new Legend({ model: legendModel });
        layerView.bind('change:visibility', function (layer, hidden) {
          legendView[hidden ? 'hide' : 'show']();
        });
        layerView.legend = legendModel;
        return legendView;
      }
    }
    return null;
  },

  _createLayerLegendView: function (layer, layerView) {
    var self = this;
    var legends = [];
    var sublayers;
    if (layer.options && layer.options.layer_definition) {
      sublayers = layer.options.layer_definition.layers;
      _(sublayers).each(function (sub, i) {
        legends.push(self._createLegendView(sub, layerView.getSubLayer(i)));
      });
    } else if (layer.options && layer.options.named_map && layer.options.named_map.layers) {
      sublayers = layer.options.named_map.layers;
      _(sublayers).each(function (sub, i) {
        legends.push(self._createLegendView(sub, layerView.getSubLayer(i)));
      });
    } else {
      legends.push(this._createLegendView(layer, layerView));
    }
    return _.compact(legends).reverse();
  },

  _setLayerOptions: function (options) {
    var layers = [];

    // flatten layers (except baselayer)
    layers = _.map(this.getLayerViews().slice(1), function (layer) {
      if (layer.getSubLayers) {
        return layer.getSubLayers();
      }
      return layer;
    });

    layers = _.flatten(layers);

    for (var i = 0; i < Math.min(options.sublayer_options.length, layers.length); ++i) {
      var o = options.sublayer_options[i];
      var subLayer = layers[i];
      var legend = this.legends && this.legends.getLegendByIndex(i);

      if (legend) {
        legend[o.visible ? 'show' : 'hide']();
      }

      // HACK
      if (subLayer.model && subLayer.model.get('type') === 'torque') {
        if (o.visible === false) {
          subLayer.model.set('visible', false);
        }
      }
    }
  },

  _resetOverlays: function (options) {
    var overlays = this.model.overlaysCollection.toJSON();
    // Sort the overlays by its internal order
    overlays = _.sortBy(overlays, function (overlay) {
      return overlay.order === null ? Number.MAX_VALUE : overlay.order;
    });

    // if logo is not included, let add it :)
    var overlayNames = _.pluck(overlays, 'type');
    if (!_.contains(overlayNames, 'logo')) {
      overlays.push({
        type: 'logo'
      });
    }

    // clean current overlays
    while (this.overlays.length !== 0) {
      this.overlays.pop().clean();
    }

    this._createOverlays(overlays, options);
  },

  _createOverlays: function (overlays, options) {
    _(overlays).each(function (data) {
      var type = data.type;

      // IE<10 doesn't support the Fullscreen API
      if (type === 'fullscreen' && util.browser.ie && util.browser.ie.version <= 10) return;

      // Decide to create or not the custom overlays
      if (type === 'image' || type === 'text' || type === 'annotation') {
        var isDevice = (data.options.device === 'mobile');
        if (this.mobile !== isDevice) return;
        if (!options[type] && options[type] !== undefined) {
          return;
        }
      }

      // We add the header overlay
      var overlay;
      if (type === 'header') {
        overlay = this._addHeader(data);
      } else {
        overlay = this.addOverlay(data);
      }

      // We show/hide the overlays
      if (overlay && (type in options) && options[type] === false) overlay.hide();

      var opt = data.options;

      if (type === 'layer_selector' && options[type] || type === 'layer_selector' && overlay.model.get('display') && options[type] === undefined) {
        overlay.show();
      }

      if (type === 'fullscreen' && options[type] || type === 'fullscreen' && opt.display && options[type] === undefined) {
        overlay.show();
      }

      if (type === 'search' && options[type] || type === 'search' && opt.display && options[type] === undefined) {
        overlay.show();
      }

      if (type === 'logo' && (options[type] === undefined || options[type] === true)) {
        overlay.show();
      }

      if (type === 'header') {
        var m = overlay.model;

        var title = this.model.get('title');
        if (title) {
          m.set('show_title', title);
        }
        var description = this.model.get('description');
        if (description) {
          m.set('show_description', description);
        }

        if (m.get('show_title') || m.get('show_description')) {
          $('.cartodb-map-wrapper').addClass('with_header');
        }

        overlay.render();
      }
    }, this);
  },

  _setupSublayers: function (layers, options) {
    options.sublayer_options = [];

    _.each(layers.slice(1), function (lyr) {
      if (lyr.type === 'layergroup') {
        _.each(lyr.options.layer_definition.layers, function (l) {
          options.sublayer_options.push({ visible: (l.visible !== undefined ? l.visible : true) });
        });
      } else if (lyr.type === 'namedmap') {
        _.each(lyr.options.named_map.layers, function (l) {
          options.sublayer_options.push({ visible: (l.visible !== undefined ? l.visible : true) });
        });
      } else if (lyr.type === 'torque') {
        options.sublayer_options.push({ visible: (lyr.options.visible !== undefined ? lyr.options.visible : true) });
      }
    });
  },

  _invalidateSize: function () {
    this.mapView.invalidateSize();
  },

  _addHeader: function (data) {
    return this.addOverlay({
      type: 'header',
      options: data.options
    });
  },

  addOverlay: function (overlay) {
    overlay.map = this.model.map;

    var v = Overlay.create(overlay.type, this, overlay);

    if (v) {
      // Save tiles loader view for later
      if (overlay.type === 'loader') {
        this.loader = v;
      }

      this.mapView.addOverlay(v);

      this.overlays.push(v);

      v.bind('clean', function () {
        for (var i in this.overlays) {
          var o = this.overlays[i];
          if (v.cid === o.cid) {
            this.overlays.splice(i, 1);
            return;
          }
        }
      }, this);
    }
    return v;
  },

  createLayer: function (layerData) {
    var layerModel = Layers.create(layerData.type || layerData.kind, this, layerData);
    return this.mapView.createLayer(layerModel);
  },

  throwError: function (msg, lyr) {
    log.error(msg);
    var self = this;
    _.defer(function () {
      self.trigger('error', msg, lyr);
    });
  },

  // returns an array of layers
  getLayerViews: function () {
    var self = this;
    return _.compact(this.model.map.layers.map(function (layer) {
      return self.mapView.getLayerViewByLayerCid(layer.cid);
    }));
  },

  getOverlays: function () {
    return this.overlays;
  },

  getOverlay: function (type) {
    return _(this.overlays).find(function (v) {
      return v.type === type;
    });
  },

  getOverlaysByType: function (type) {
    return _(this.overlays).filter(function (v) {
      return v.type === type;
    });
  },

  _onResize: function () {
    $(window).unbind('resize', this._onResize);

    var self = this;
    // This timeout is necessary due to GMaps needs time
    // to load tiles and recalculate its bounds :S
    setTimeout(function () {
      self.model.centerMapToOrigin();
    }, 150);
  }
}, {
  /**
   * adds an infowindow to the map controlled by layer events.
   * it enables interaction and overrides the layer interacivity
   * ``fields`` array of column names
   * ``map`` native map object, leaflet of gmaps
   * ``layer`` cartodb layer (or sublayer)
   */
  addInfowindow: function (map, layer, fields, opts) {
    var options = _.defaults(opts || {}, {
      infowindowTemplate: INFOWINDOW_TEMPLATE.light,
      templateType: 'mustache',
      triggerEvent: 'featureClick',
      templateName: 'light',
      extraFields: [],
      cursorInteraction: true
    });

    if (!map) throw new Error('map is not valid');
    if (!layer) throw new Error('layer is not valid');
    if (!fields && fields.length === undefined) throw new Error('fields should be a list of strings');

    var f = [];
    fields = fields.concat(options.extraFields);
    for (var i = 0; i < fields.length; ++i) {
      f.push({ name: fields, order: i });
    }

    var infowindowModel = new InfowindowModel({
      fields: f,
      template_name: options.templateName
    });

    var infowindow = new Infowindow({
      model: infowindowModel,
      mapView: map.viz.mapView,
      template: new Template({
        template: options.infowindowTemplate,
        type: options.templateType
      }).asFunction()
    });

    map.viz.mapView.addInfowindow(infowindow);

    layer.bind(options.triggerEvent, function (e, latlng, pos, data, layer) {
      var render_fields = [];
      for (var f = 0; f < fields.length; ++f) {
        var field = fields[f];
        var d = data[field];
        if (d) {
          render_fields.push({
            title: field,
            value: d,
            index: 0
          });
        }
      }

      infowindow.model.set({
        content: {
          fields: render_fields,
          data: data
        }
      });

      infowindow
        .setLatLng(latlng)
        .showInfowindow();
      infowindow.adjustPan();
    }, infowindow);

    // remove the callback on clean
    infowindow.bind('clean', function () {
      layer.unbind(options.triggerEvent, null, infowindow);
    });

    if (options.cursorInteraction) {
      Vis.addCursorInteraction(map, layer);
    }

    return infowindow;
  },

  addCursorInteraction: function (map, layer) {
    var mapView = map.viz.mapView;
    layer.bind('mouseover', function () {
      mapView.setCursor('pointer');
    });

    layer.bind('mouseout', function (m, layer) {
      mapView.setCursor('auto');
    });
  },

  removeCursorInteraction: function (map, layer) {
    var mapView = map.viz.mapView;
    layer.unbind(null, null, mapView);
  }
});

module.exports = Vis;
<|endoftext|>


<commit_before>'use strict';

/**
 * This class manages App Generator prompts.
 */
module.exports = class Prompt {

  /**
   * Ask the user to answer some questions.
   */
  static questions (generator) {
    return generator.prompt([{
      type: 'checkbox',
      name: 'ngModules',
      message: 'What Angular modules do you want?',
      store: true,
      choices: [
        {
          name: 'ngAnimate',
          value: 'ngAnimate'
        },
        {
          name: 'ngCookies',
          value: 'ngCookies'
        },
        {
          name: 'ngTouch',
          value: 'ngTouch'
        },
        {
          name: 'ngSanitize',
          value: 'ngSanitize'
        },
        {
          name: 'ngMessages',
          value: 'ngMessages'
        }
      ]
    }, {
      type: 'list',
      name: 'cssPreprocessor',
      message: 'Which CSS preprocessor do you want?',
      default: 'sass',
      store: true,
      choices: [
        {
          name: 'Sass (with Node-sass)',
          value: 'sass'
        },
        {
          name: 'Less',
          value: 'less'
        },
        {
          name: 'Plain CSS',
          value: 'css'
        }
      ]
    }]).then(answers => {
      // Angular modules
      generator.props.ngAnimate = answers.ngModules && answers.ngModules.includes('ngAnimate');
      generator.props.ngCookies = answers.ngModules && answers.ngModules.includes('ngCookies');
      generator.props.ngTouch = answers.ngModules && answers.ngModules.includes('ngTouch');
      generator.props.ngSanitize = answers.ngModules && answers.ngModules.includes('ngSanitize');
      generator.props.ngMessages = answers.ngModules && answers.ngModules.includes('ngMessages');

      // CSS preprocessor
      generator.props.cssPreprocessor = answers.cssPreprocessor;
    });
  }
};
<commit_msg>feat(prompt): add resource related questions<commit_after>'use strict';

/**
 * This class manages App Generator prompts.
 */
module.exports = class Prompt {

  /**
   * Ask the user to answer some questions.
   */
  static questions (generator) {
    return generator.prompt([{
      type: 'checkbox',
      name: 'ngModules',
      message: 'What Angular modules do you want?',
      store: true,
      choices: [
        {
          name: 'ngAnimate',
          value: 'ngAnimate'
        },
        {
          name: 'ngCookies',
          value: 'ngCookies'
        },
        {
          name: 'ngTouch',
          value: 'ngTouch'
        },
        {
          name: 'ngSanitize',
          value: 'ngSanitize'
        },
        {
          name: 'ngMessages',
          value: 'ngMessages'
        }
      ]
    }, {
      type: 'list',
      name: 'resource',
      message: 'Would you like to use a REST resource library?',
      default: 'restangular',
      store: true,
      choices: [
        {
          name: 'Restangular, an alternative service to handles RESTful requests',
          value: 'restangular'
        },
        {
          name: 'ngResource, the official support for RESTful services',
          value: 'ngResource'
        },
        {
          name: 'None, $http is enough!',
          value: '$http'
        }
      ]
    }, {
      type: 'list',
      name: 'cssPreprocessor',
      message: 'Which CSS preprocessor do you want?',
      default: 'sass',
      store: true,
      choices: [
        {
          name: 'Sass (with Node-sass)',
          value: 'sass'
        },
        {
          name: 'Less',
          value: 'less'
        },
        {
          name: 'Plain CSS',
          value: 'css'
        }
      ]
    }]).then(answers => {

      // Angular modules
      generator.props.ngAnimate = answers.ngModules && answers.ngModules.includes('ngAnimate');
      generator.props.ngCookies = answers.ngModules && answers.ngModules.includes('ngCookies');
      generator.props.ngTouch = answers.ngModules && answers.ngModules.includes('ngTouch');
      generator.props.ngSanitize = answers.ngModules && answers.ngModules.includes('ngSanitize');
      generator.props.ngMessages = answers.ngModules && answers.ngModules.includes('ngMessages');

      // Resource
      generator.props.resource = answers.resource;

      // CSS preprocessor
      generator.props.cssPreprocessor = answers.cssPreprocessor;
    });
  }
};
<|endoftext|>


<commit_before>/*
 * benc2json
 * https://github.com/pwmckenna/benc2json
 *
 * Copyright (c) 2013 Patrick Williams
 * Licensed under the MIT license.
 */
var express = require('express');
var Firebase = require('./firebase-node');
var http = require('http');
var URL = require('url');
var q = require('q');
var request = require('request');
var URL = require('url');

var MAGNET_LINK_IDENTIFIER = 'magnet:?xt=urn:btih:';
var firebase = new Firebase('https://todium.firebaseio.com/');

var addFirebaseTracker = function(firebase, id, info_hash, src) {
    console.log('addFirebaseTracker', id, info_hash, src);

    var campaignTracker = firebase.child('campaigns').child(id).child('trackers').push();
    var globalTracker = firebase.child('trackers').push();
    globalTracker.set({
        campaign: id,
        info_hash: info_hash,
        stats: {
            completed: 0,
            transferred: 0,
        },
        src: src,
        time: new Date().getTime()
    });
    campaignTracker.set(globalTracker.name());
    return globalTracker;
}

var addFirebaseTorrent = function(firebase, id, tracker, torrent) {
    var file = URL.parse(torrent).pathname;
    file = file.substring(file.lastIndexOf('/'));
    var shortened = firebase.child('torrent').push({
        tracker: tracker,
        torrent: torrent,
        campaign: id
    });
    return 'http://torrent.todium.com/' + shortened.name() + '.torrent';
}

var addLinkRequest = function(firebase, id, src) {
    console.log('addLinkRequest', id, src);

    var ret = new q.defer();
    if(src.match(/[0-9A-Fa-f]{40}/)) {
        var info_hash = src;
        var tracker = addFirebaseTracker(firebase, id, info_hash, MAGNET_LINK_IDENTIFIER + info_hash);
        var url = 'http://tracker.todium.com/' + tracker.name() + '/announce';
        var trackable = MAGNET_LINK_IDENTIFIER + info_hash + '&tr=' + url;
        tracker.child('trackable').set(trackable);
        ret.resolve(url);
    } else if(src.indexOf(MAGNET_LINK_IDENTIFIER) === 0) {
        console.log('replacing magnet link');
        var info_hash = src.substr(MAGNET_LINK_IDENTIFIER.length, 40);
        var tracker = addFirebaseTracker(firebase, id, info_hash, src);
        var url = 'http://tracker.todium.com/' + tracker.name() + '/announce';
        var trackable = src + '&tr=' + url;
        tracker.child('trackable').set(trackable);
        ret.resolve(trackable);
    } else {
        console.log('replacing torrent file', src);
        request.get({
            url: 'http://hasher.todium.com',
            qs: {
                torrent: src
            },
            json: true
        }, function (error, response, info_hash) {
            console.log(error, response.statusCode, info_hash);
            if(error || typeof info_hash !== 'string' || info_hash.length !== 40) {
                ret.reject(error);
                return;
            } else {
                var tracker = addFirebaseTracker(firebase, id, info_hash, src);
                var url = 'http://tracker.todium.com/' + tracker.name() + '/announce';
                var trackable = addFirebaseTorrent(firebase, id, url, src);
                tracker.child('trackable').set(trackable);
                ret.resolve(trackable);
            }
        });
    }
    return ret.promise;
};

var authenticationRequest = function(id, secret) {
    console.log('authenticationRequest', id, secret);
    var ret = new q.defer();
    firebase.child('campaigns').child(id).child('secret').once('value', function(secretSnapshot) {
        if(secretSnapshot.val() !== secret) {
            var error = 'invalid campaign secret';
            console.error(error);
            return ret.reject(error);
        } else {
            return ret.resolve();
        }
    });
    return ret.promise;
};

var port = process.env.PORT || 5051;
console.log('port', port);
var app = express();
app.use(express.bodyParser());
app.post('/', function(req, res) {
    var abort = function(err) {
        res.send(400, err);
    };

    // if we don't have a url argument, then lets bail
    if(!req.body.hasOwnProperty('id') ||
        !req.body.hasOwnProperty('secret') ||
        !req.body.hasOwnProperty('src')
    ) {
        abort('src/id/secret mandatory');
        return;
    }
    var id = req.body['id'];
    var secret = req.body['secret'];
    var src = req.body['src'];

    console.log('id', id);
    console.log('secret', secret);
    console.log('src', src);

    authenticationRequest(id, secret).then(function() {
        addLinkRequest(firebase, id, src).then(function(link) {
            res.send(200, link);
        }, abort);
    }, abort);
}).listen(5051, function() {
    console.log('Listening on ' + port);
});<commit_msg>adding a tracker endpoint and fixing the hardcoded port issue<commit_after>/*
 * benc2json
 * https://github.com/pwmckenna/benc2json
 *
 * Copyright (c) 2013 Patrick Williams
 * Licensed under the MIT license.
 */
var express = require('express');
var Firebase = require('./firebase-node');
var http = require('http');
var URL = require('url');
var q = require('q');
var request = require('request');
var URL = require('url');

var MAGNET_LINK_IDENTIFIER = 'magnet:?xt=urn:btih:';
var firebase = new Firebase('https://todium.firebaseio.com/');

var addFirebaseTracker = function(firebase, id, info_hash, src) {
    console.log('addFirebaseTracker', id, info_hash, src);

    var campaignTracker = firebase.child('campaigns').child(id).child('trackers').push();
    var globalTracker = firebase.child('trackers').push();
    globalTracker.set({
        campaign: id,
        info_hash: info_hash,
        stats: {
            completed: 0,
            transferred: 0,
        },
        src: src,
        time: new Date().getTime()
    });
    campaignTracker.set(globalTracker.name());
    return globalTracker;
}

var addFirebaseTorrent = function(firebase, id, tracker, torrent) {
    var file = URL.parse(torrent).pathname;
    file = file.substring(file.lastIndexOf('/'));
    var shortened = firebase.child('torrent').push({
        tracker: tracker,
        torrent: torrent,
        campaign: id
    });
    return 'http://torrent.todium.com/' + shortened.name() + '.torrent';
}

var addLinkRequest = function(firebase, id, src) {
    console.log('addLinkRequest', id, src);

    var ret = new q.defer();
    if(src.match(/[0-9A-Fa-f]{40}/)) {
        var info_hash = src;
        var tracker = addFirebaseTracker(firebase, id, info_hash, MAGNET_LINK_IDENTIFIER + info_hash);
        var url = 'http://tracker.todium.com/' + tracker.name() + '/announce';
        var trackable = MAGNET_LINK_IDENTIFIER + info_hash + '&tr=' + url;
        tracker.child('trackable').set(trackable);
        ret.resolve(url);
    } else if(src.indexOf(MAGNET_LINK_IDENTIFIER) === 0) {
        console.log('replacing magnet link');
        var info_hash = src.substr(MAGNET_LINK_IDENTIFIER.length, 40);
        var tracker = addFirebaseTracker(firebase, id, info_hash, src);
        var url = 'http://tracker.todium.com/' + tracker.name() + '/announce';
        var trackable = src + '&tr=' + url;
        tracker.child('trackable').set(trackable);
        ret.resolve(trackable);
    } else {
        console.log('replacing torrent file', src);
        request.get({
            url: 'http://hasher.todium.com',
            qs: {
                torrent: src
            },
            json: true
        }, function (error, response, info_hash) {
            console.log(error, response.statusCode, info_hash);
            if(error || typeof info_hash !== 'string' || info_hash.length !== 40) {
                ret.reject(error);
                return;
            } else {
                var tracker = addFirebaseTracker(firebase, id, info_hash, src);
                var url = 'http://tracker.todium.com/' + tracker.name() + '/announce';
                var trackable = addFirebaseTorrent(firebase, id, url, src);
                tracker.child('trackable').set(trackable);
                ret.resolve(trackable);
            }
        });
    }
    return ret.promise;
};

var authenticationRequest = function(id, secret) {
    console.log('authenticationRequest', id, secret);
    var ret = new q.defer();
    firebase.child('campaigns').child(id).child('secret').once('value', function(secretSnapshot) {
        if(secretSnapshot.val() !== secret) {
            var error = 'invalid campaign secret';
            console.error(error);
            return ret.reject(error);
        } else {
            return ret.resolve();
        }
    });
    return ret.promise;
};

var port = process.env.PORT || 5051;
console.log('port', port);
var app = express();
app.use(express.bodyParser());
var handlePost = function(req, res) {
    var abort = function(err) {
        res.send(400, err);
    };

    // if we don't have a url argument, then lets bail
    if(!req.body.hasOwnProperty('id') ||
        !req.body.hasOwnProperty('secret') ||
        !req.body.hasOwnProperty('src')
    ) {
        abort('src/id/secret mandatory');
        return;
    }
    var id = req.body['id'];
    var secret = req.body['secret'];
    var src = req.body['src'];

    console.log('id', id);
    console.log('secret', secret);
    console.log('src', src);

    authenticationRequest(id, secret).then(function() {
        addLinkRequest(firebase, id, src).then(function(link) {
            res.send(200, link);
        }, abort);
    }, abort);
};
app.post('/', handlePost).post('/tracker').listen(port, function() {
    console.log('Listening on ' + port);
});<|endoftext|>