<commit_before>/**
 * @ngdoc directive
 * @name patternfly.filters.component:pfFilterPanel
 * @restrict E
 *
 * @description
 *   The Filter Panel is opened and closed by clicking on the Filter button.  It can contain any HTML desired by
 *   the application developer.  As such, the application developer is repsonsible for constructing the appliedFilters array which is passed to
 *   the filter results tags.
 *   The application developer is responsible for filtering items based on the appliedFilters array; both when a
 *   filter is changed on the panel, or when a filter tag is 'cleared' (by user clicking on 'x' in the filter results tag).
 *

 *
 * @param {object} config configuration settings for the filters:

 *

 *
.appliedFilters - (Array) List of the currently applied filters. Used to render the filter results tags and returned
 * in the onFilterChange function where it can be used to filter a set of items.
 *

 *
.id          - (String) Id for the filter, useful for comparisons
 *
.title       - (String) The title to display for the filter results tag
 *
.values      - (Array) The value(s) to display for the filter results tag. Ie. [title: [value1 x] [value2 x]]
 *

 *
.resultsCount   - (int) The number of results returned after the current applied filters have been applied
 *
.totalCount     - (int) The total number of items before any filters have been applied. The 'm' in the label: 'n' of 'm'
 *
.resultsLabel   - (String) Optional label for the result units.  Default is "Results".  Ex: "'n' of 'm' Results"
 *
.onFilterChange - ( function(appliedFilters, changedFilterId, changedFilterValue) ) Function to call when the applied
 * filters list changes.  Triggered by user clicking on 'x' or 'Clear All Filters' in the filter result tags.
 * changedFilterId and changedFilterValue are returned after user clicks on an 'x' in a tag to
 * denote which filter and filter value was cleared.  changedFilterId and changedFilterValue are
 * not returned when 'Clear All Filters' link is clicked.
 *

 *
 * @example
<example module="patternfly.filters">
  <file name="index.html">

        <pf-filter-panel id="exampleFilter" config="filterConfig">

            <input type="text" ng-model="filterPanelModel[0].value"
                   class="keyword-filter"
                   placeholder="{{filterPanelModel[0].placeholder}}"
                   ng-keypress="onKeywordKeyPress($event)"/>

{{filter.title}}

                  <input type="checkbox" ng-model="value.selected" ng-change="filterChanged()"/>
                  {{value.title}}

        </pf-filter-panel>

        Valid Items:

ID

Keyword

Category One

Category Two

               {{item.id}}

              {{item.keyword}}

              {{item.categoryOne}}

              {{item.categoryTwo}}

  </file>

  <file name="script.js">
    angular.module('patternfly.filters').controller('ViewCtrl', ['$scope',
      function ($scope) {
        $scope.filterPanelModel = [
          {
            id: 'keyword',
            title:  'Keyword',
            placeholder: 'Filter by Keyword',
            filterType: 'input',
            values: []
          },
          {
            id: 'category1',
            title:  'Category One',
            filterType: 'checkbox',
            values: [
              {
                id: 'val1',
                title: 'Value 1',
                value: 'Value 1',
                selected: false
              },
              {
                id: 'val2',
                title: 'Value 2',
                value: 'Value 2',
                selected: false
              },
              {
                id: 'val3',
                title: 'Value 3',
                value: 'Value 3',
                selected: false
              }
            ]
          },
          {
            id: 'category2',
            title:  'Category Two',
            filterType: 'checkbox',
            values: [
              {
                id: 'val1',
                title: 'Value 1',
                value: 'Value 1',
                selected: false
              },
              {
                id: 'val2',
                title: 'Value 2',
                value: 'Value 2',
                selected: false
              },
              {
                id: 'val3',
                title: 'Value 3',
                value: 'Value 3',
                selected: false
              }
            ]
          }
        ];

        $scope.filtersText = '';

        $scope.allItems = [
          {
            id: "1",
            keyword: "Foo",
            categoryOne: "Value 1",
            categoryTwo: "Value 1"
          },
          {
            id: "2",
            keyword: "Bar",
            categoryOne: "Value 2",
            categoryTwo: "Value 1"
          },
          {
            id: "3",
            keyword: "Foo",
            categoryOne: "Value 3",
            categoryTwo: "Value 1"
          },
          {
            id: "4",
            keyword: "Bar",
            categoryOne: "Value 1",
            categoryTwo: "Value 2"
          },
          {
            id: "5",
            keyword: "Foo",
            categoryOne: "Value 2",
            categoryTwo: "Value 2"
          },
          {
            id: "6",
            keyword: "Bar",
            categoryOne: "Value 3",
            categoryTwo: "Value 2"
          },
          {
            id: "7",
            keyword: "Foo",
            categoryOne: "Value 1",
            categoryTwo: "Value 3"
          },
          {
            id: "8",
            keyword: "Bar",
            categoryOne: "Value 2",
            categoryTwo: "Value 3"
          },
          {
            id: "9",
            keyword: "Foo",
            categoryOne: "Value 3",
            categoryTwo: "Value 3"
          }
        ];
        $scope.items = $scope.allItems;

        // called when filter cleared by hitting 'x' in filter results tag, or 'Clear All Filters' link
        var onFilterChange = function (appliedFilters, changedFilterId, changedFilterValue) {
          if (angular.isDefined(changedFilterId) && angular.isDefined(changedFilterValue)) {
            updateFilterPanelModel(changedFilterId, changedFilterValue);
          } else {
            // the 'Clear All Filters' link was clicked
            resetFilterPanelModel();
          }
          // filter items
          filterItems(appliedFilters);
        };

        $scope.filterConfig = {
          resultsLabel: "Items",
          resultsCount: $scope.items.length,
          totalCount: $scope.allItems.length,
          appliedFilters: [],
          onFilterChange: onFilterChange
        };

        // called when filter is changed in the filter panel
        $scope.filterChanged = function() {
          applyFilters();
        };

        $scope.onKeywordKeyPress = function(keyEvent) {
          if (keyEvent.which === 13 && $scope.filterPanelModel[0].value.length > 0) {
            // store new keywoard filter value in values array
            $scope.filterPanelModel[0].values.push($scope.filterPanelModel[0].value);
            // remove the keyword value to show placeholder text
            delete $scope.filterPanelModel[0].value;
            applyFilters();
          }
        };

        var applyFilters = function () {
          var newAppliedFilters = [];
          _.forEach($scope.filterPanelModel, function(filter) {
            var filterValues = [];
            if (angular.isDefined(filter.values) && filter.values.length > 0) {
              if(filter.filterType === "checkbox") {
                // the values of the selected checkboxes are stored in a single new appliedFilter
                _.forEach(filter.values, function(value) {
                  if(value.selected) {
                    filterValues.push(value.value)
                  }
                });
                if (filterValues.length > 0) {
                  newAppliedFilters.push( createAppliedFilter (filter, filterValues) );
                }
              } else {
                // each keyword value gets a new appliedFilter
                _.forEach(filter.values, function(value) {
                  filterValues = [value];
                  newAppliedFilters.push( createAppliedFilter (filter, filterValues) );
                });
              }
            }
          });

          // sets the filter result tags
          $scope.filterConfig.appliedFilters = newAppliedFilters;
          // filter items
          filterItems($scope.filterConfig.appliedFilters);
        };

        var createAppliedFilter = function(filter, values) {
          return {
                    id: filter.id,
                    title: filter.title,
                    values: values
                 };
        };

        var updateFilterPanelModel = function (changedFilterId, changedFilterValue) {
          var changedFilter = _.find($scope.filterPanelModel, function(f) { return f.id === changedFilterId});
          if(changedFilter.filterType === "checkbox") {
             // unselect the checkbox
             _.find(changedFilter.values, function(v) {return v.value == changedFilterValue}).selected = false;
          } else {
             // remove keyword from values array
             _.remove(changedFilter.values, function(v) {return v == changedFilterValue});
          }
        };

        var resetFilterPanelModel = function () {
          _.forEach($scope.filterPanelModel, function(filter) {
            if (angular.isDefined(filter.values) && filter.values.length > 0) {
              if(filter.filterType === "checkbox") {
                // unselect all checkboxes
                filter.values.forEach(function (value) {
                  value.selected = false;
                });
              } else {
                // clear all keyword filter values
                filter.values = [];
              }
            }
          });
        };

        var filterItems = function (filters) {
          $scope.items = [];
          if (filters && filters.length > 0) {
           _.forEach($scope.allItems, function(item) {
              if (matchesFilters(item, filters)) {
                $scope.items.push(item);
              }
            });
          } else {
            $scope.items = $scope.allItems;
          }
          $scope.filterConfig.resultsCount = $scope.items.length;
        };

        var matchesFilters = function (item, filters) {
          var matches = true;

          _.forEach(filters, function(filter) {
              if (!matchesFilter(item, filter)) {
                matches = false;
                return false;
              }
          });
          return matches;
        };

        var matchesFilter = function (item, filter) {
          var match = true;

          if (filter.id === 'keyword') {
            var re = new RegExp(filter.values[0], 'i');
            match = item.keyword.match(re) !== null;
          } else if (filter.id === 'category1') {
            // values are OR'ed
            _.forEach(filter.values, function(value) {
              match = item.categoryOne === value;
              return !match;
            });
          } else if (filter.id === 'category2') {
            // values are OR'ed
            _.forEach(filter.values, function(value) {
              match = item.categoryTwo === value;
              return !match;
            });
          }
          return match;
        };
      }
    ]);
  </file>
</example>
*/
<commit_msg>chore(pfFilterPanel): Update ngDoc to show how to prevent duplicate keywords in filter<commit_after>/**
 * @ngdoc directive
 * @name patternfly.filters.component:pfFilterPanel
 * @restrict E
 *
 * @description
 *   The Filter Panel is opened and closed by clicking on the Filter button.  It can contain any HTML desired by
 *   the application developer.  As such, the application developer is repsonsible for constructing the appliedFilters array which is passed to
 *   the filter results tags.
 *   The application developer is responsible for filtering items based on the appliedFilters array; both when a
 *   filter is changed on the panel, or when a filter tag is 'cleared' (by user clicking on 'x' in the filter results tag).
 *

 *
 * @param {object} config configuration settings for the filters:

 *

 *
.appliedFilters - (Array) List of the currently applied filters. Used to render the filter results tags and returned
 * in the onFilterChange function where it can be used to filter a set of items.
 *

 *
.id          - (String) Id for the filter, useful for comparisons
 *
.title       - (String) The title to display for the filter results tag
 *
.values      - (Array) The value(s) to display for the filter results tag. Ie. [title: [value1 x] [value2 x]]
 *

 *
.resultsCount   - (int) The number of results returned after the current applied filters have been applied
 *
.totalCount     - (int) The total number of items before any filters have been applied. The 'm' in the label: 'n' of 'm'
 *
.resultsLabel   - (String) Optional label for the result units.  Default is "Results".  Ex: "'n' of 'm' Results"
 *
.onFilterChange - ( function(appliedFilters, changedFilterId, changedFilterValue) ) Function to call when the applied
 * filters list changes.  Triggered by user clicking on 'x' or 'Clear All Filters' in the filter result tags.
 * changedFilterId and changedFilterValue are returned after user clicks on an 'x' in a tag to
 * denote which filter and filter value was cleared.  changedFilterId and changedFilterValue are
 * not returned when 'Clear All Filters' link is clicked.
 *

 *
 * @example
<example module="patternfly.filters">
  <file name="index.html">

        <pf-filter-panel id="exampleFilter" config="filterConfig">

            <input type="text" ng-model="filterPanelModel[0].value"
                   class="keyword-filter"
                   placeholder="{{filterPanelModel[0].placeholder}}"
                   ng-keypress="onKeywordKeyPress($event)"/>

{{filter.title}}

                  <input type="checkbox" ng-model="value.selected" ng-change="filterChanged()"/>
                  {{value.title}}

        </pf-filter-panel>

        Valid Items:

ID

Keyword

Category One

Category Two

               {{item.id}}

              {{item.keyword}}

              {{item.categoryOne}}

              {{item.categoryTwo}}

  </file>

  <file name="script.js">
    angular.module('patternfly.filters').controller('ViewCtrl', ['$scope',
      function ($scope) {
        $scope.filterPanelModel = [
          {
            id: 'keyword',
            title:  'Keyword',
            placeholder: 'Filter by Keyword',
            filterType: 'input',
            values: []
          },
          {
            id: 'category1',
            title:  'Category One',
            filterType: 'checkbox',
            values: [
              {
                id: 'val1',
                title: 'Value 1',
                value: 'Value 1',
                selected: false
              },
              {
                id: 'val2',
                title: 'Value 2',
                value: 'Value 2',
                selected: false
              },
              {
                id: 'val3',
                title: 'Value 3',
                value: 'Value 3',
                selected: false
              }
            ]
          },
          {
            id: 'category2',
            title:  'Category Two',
            filterType: 'checkbox',
            values: [
              {
                id: 'val1',
                title: 'Value 1',
                value: 'Value 1',
                selected: false
              },
              {
                id: 'val2',
                title: 'Value 2',
                value: 'Value 2',
                selected: false
              },
              {
                id: 'val3',
                title: 'Value 3',
                value: 'Value 3',
                selected: false
              }
            ]
          }
        ];

        $scope.filtersText = '';

        $scope.allItems = [
          {
            id: "1",
            keyword: "Foo",
            categoryOne: "Value 1",
            categoryTwo: "Value 1"
          },
          {
            id: "2",
            keyword: "Bar",
            categoryOne: "Value 2",
            categoryTwo: "Value 1"
          },
          {
            id: "3",
            keyword: "Foo",
            categoryOne: "Value 3",
            categoryTwo: "Value 1"
          },
          {
            id: "4",
            keyword: "Bar",
            categoryOne: "Value 1",
            categoryTwo: "Value 2"
          },
          {
            id: "5",
            keyword: "Foo",
            categoryOne: "Value 2",
            categoryTwo: "Value 2"
          },
          {
            id: "6",
            keyword: "Bar",
            categoryOne: "Value 3",
            categoryTwo: "Value 2"
          },
          {
            id: "7",
            keyword: "Foo",
            categoryOne: "Value 1",
            categoryTwo: "Value 3"
          },
          {
            id: "8",
            keyword: "Bar",
            categoryOne: "Value 2",
            categoryTwo: "Value 3"
          },
          {
            id: "9",
            keyword: "Foo",
            categoryOne: "Value 3",
            categoryTwo: "Value 3"
          }
        ];
        $scope.items = $scope.allItems;

        // called when filter cleared by hitting 'x' in filter results tag, or 'Clear All Filters' link
        var onFilterChange = function (appliedFilters, changedFilterId, changedFilterValue) {
          if (angular.isDefined(changedFilterId) && angular.isDefined(changedFilterValue)) {
            updateFilterPanelModel(changedFilterId, changedFilterValue);
          } else {
            // the 'Clear All Filters' link was clicked
            resetFilterPanelModel();
          }
          // filter items
          filterItems(appliedFilters);
        };

        $scope.filterConfig = {
          resultsLabel: "Items",
          resultsCount: $scope.items.length,
          totalCount: $scope.allItems.length,
          appliedFilters: [],
          onFilterChange: onFilterChange
        };

        // called when filter is changed in the filter panel
        $scope.filterChanged = function() {
          applyFilters();
        };

        $scope.onKeywordKeyPress = function(keyEvent) {
          if (keyEvent.which === 13 && $scope.filterPanelModel[0].value.length > 0) {
            var currentKeyword = $scope.filterPanelModel[0].value;
            if(!keywordFilterExists(currentKeyword)) {
              // store new keywoard filter value in values array
              $scope.filterPanelModel[0].values.push(currentKeyword);
              applyFilters();
            }
            // remove the keyword value to show placeholder text
            delete $scope.filterPanelModel[0].value;
          }
        };

        var keywordFilterExists = function (keyword) {
          return _.some( $scope.filterPanelModel[0].values, function(existingKeyword) {
            // case sensitive
            //   return keyword === existingKeyword;
            // case insensitive:
            return keyword.toLowerCase() === existingKeyword.toLowerCase();
          });
        }

        var applyFilters = function () {
          var newAppliedFilters = [];
          _.forEach($scope.filterPanelModel, function(filter) {
            var filterValues = [];
            if (angular.isDefined(filter.values) && filter.values.length > 0) {
              if(filter.filterType === "checkbox") {
                // the values of the selected checkboxes are stored in a single new appliedFilter
                _.forEach(filter.values, function(value) {
                  if(value.selected) {
                    filterValues.push(value.value)
                  }
                });
                if (filterValues.length > 0) {
                  newAppliedFilters.push( createAppliedFilter (filter, filterValues) );
                }
              } else {
                // each keyword value gets a new appliedFilter
                _.forEach(filter.values, function(value) {
                  filterValues = [value];
                  newAppliedFilters.push( createAppliedFilter (filter, filterValues) );
                });
              }
            }
          });

          // sets the filter result tags
          $scope.filterConfig.appliedFilters = newAppliedFilters;
          // filter items
          filterItems($scope.filterConfig.appliedFilters);
        };

        var createAppliedFilter = function(filter, values) {
          return {
                    id: filter.id,
                    title: filter.title,
                    values: values
                 };
        };

        var updateFilterPanelModel = function (changedFilterId, changedFilterValue) {
          var changedFilter = _.find($scope.filterPanelModel, function(f) { return f.id === changedFilterId});
          if(changedFilter.filterType === "checkbox") {
             // unselect the checkbox
             _.find(changedFilter.values, function(v) {return v.value == changedFilterValue}).selected = false;
          } else {
             // remove keyword from values array
             _.remove(changedFilter.values, function(v) {return v == changedFilterValue});
          }
        };

        var resetFilterPanelModel = function () {
          _.forEach($scope.filterPanelModel, function(filter) {
            if (angular.isDefined(filter.values) && filter.values.length > 0) {
              if(filter.filterType === "checkbox") {
                // unselect all checkboxes
                filter.values.forEach(function (value) {
                  value.selected = false;
                });
              } else {
                // clear all keyword filter values
                filter.values = [];
              }
            }
          });
        };

        var filterItems = function (filters) {
          $scope.items = [];
          if (filters && filters.length > 0) {
           _.forEach($scope.allItems, function(item) {
              if (matchesFilters(item, filters)) {
                $scope.items.push(item);
              }
            });
          } else {
            $scope.items = $scope.allItems;
          }
          $scope.filterConfig.resultsCount = $scope.items.length;
        };

        var matchesFilters = function (item, filters) {
          var matches = true;

          _.forEach(filters, function(filter) {
              if (!matchesFilter(item, filter)) {
                matches = false;
                return false;
              }
          });
          return matches;
        };

        var matchesFilter = function (item, filter) {
          var match = true;

          if (filter.id === 'keyword') {
            var re = new RegExp(filter.values[0], 'i');
            match = item.keyword.match(re) !== null;
          } else if (filter.id === 'category1') {
            // values are OR'ed
            _.forEach(filter.values, function(value) {
              match = item.categoryOne === value;
              return !match;
            });
          } else if (filter.id === 'category2') {
            // values are OR'ed
            _.forEach(filter.values, function(value) {
              match = item.categoryTwo === value;
              return !match;
            });
          }
          return match;
        };
      }
    ]);
  </file>
</example>
*/
<|endoftext|>


<commit_before>/**
 *
 */
var _serviceUrl = document.getElementsByTagName("base")[0].href+"service/";
var formCommit = false;
/**
 * iframe弹出框
 * 依赖：bootstrap.js
 * @param url url路径
 * @param height 高度，由于宽度需要自适应，所以这里只给了高度参数
 * 例：openModalForm("http://www.baidu.com",500);
 */
function openModalForm(url,height){
	if(height || height === undefined){
		height=500;
	}
	var contentFrame = $("

  ");
	$("body").append(contentFrame);
	$("#contentFrame").attr("src",url);
	$('#remoteModal').modal({backdrop:"static"});
}

/**
 * 关闭modal,如果页面中存在打开的modal,则关闭之
 *
此关闭方法会清空浏览器缓存，防止iframe内存泄露问题

 */
function closeModal(){
	var contentFrame = $("#contentFrame");
	if(contentFrame){
		contentFrame.attr("src","about:blank");
		var contentModal = $('#remoteModal');
		if(contentModal){
			contentModal.modal("hide");
		}
	}
}

/**
 * web中的通知方式
 * 依赖：bootstrap.js、SmartNotification.js
 * @param mesTitle 标题
 * @param mes 信息内容
 * @param type 类型：error,info,warning
 * 例：showMesssage("提示","保存成功","info")
 */
function showMessage(mesTitle,mes,type){
	if(mesTitle || mesTitle === undefined){
		mesTitle ="提示";
	}
	$.smallBox({
		title : mesTitle,
		content : mes,
		color : "#C46A69",
		icon : "fa fa-warning shake animated",
		timeout : 2000
	});
}

/**
 * 截取url中的parameter
 * 依赖：无
 * @param paras
 * @return
 * 例：url http://localhost:8080/index.jsp?username=admin
 * requestUrlParam("username");
 * 则return admin
 */
function requestUrlParam(paras) {
	var url = location.href;
	var paraString = url.substring(url.indexOf("?") + 1, url.length).split("&");
	var paraObj = {};
	for (var i = 0; j = paraString[i]; i++) {
		paraObj[j.substring(0, j.indexOf("=")).toLowerCase()] = j.substring(j
				.indexOf("=") + 1, j.length);
	}
	var returnValue = paraObj[paras.toLowerCase()];
	if (typeof (returnValue) == "undefined") {
		return "";
	} else {
		return returnValue;
	}
}<commit_msg>调整弹出框默认大小<commit_after>/**
 *
 */
var _serviceUrl = document.getElementsByTagName("base")[0].href+"service/";
var formCommit = false;
/**
 * iframe弹出框
 * 依赖：bootstrap.js
 * @param url url路径
 * @param height 高度，由于宽度需要自适应，所以这里只给了高度参数
 * 例：openModalForm("http://www.baidu.com",500);
 */
function openModalForm(url,height){
	if(height || height === undefined){
		height=550;
	}
	var contentFrame = $("

  ");
	$("body").append(contentFrame);
	$("#contentFrame").attr("src",url);
	$('#remoteModal').modal({backdrop:"static"});
}

/**
 * 关闭modal,如果页面中存在打开的modal,则关闭之
 *
此关闭方法会清空浏览器缓存，防止iframe内存泄露问题

 */
function closeModal(){
	var contentFrame = $("#contentFrame");
	if(contentFrame){
		contentFrame.attr("src","about:blank");
		var contentModal = $('#remoteModal');
		if(contentModal){
			contentModal.modal("hide");
		}
	}
}

/**
 * web中的通知方式
 * 依赖：bootstrap.js、SmartNotification.js
 * @param mesTitle 标题
 * @param mes 信息内容
 * @param type 类型：error,info,warning
 * 例：showMesssage("提示","保存成功","info")
 */
function showMessage(mesTitle,mes,type){
	if(mesTitle || mesTitle === undefined){
		mesTitle ="提示";
	}
	$.smallBox({
		title : mesTitle,
		content : mes,
		color : "#C46A69",
		icon : "fa fa-warning shake animated",
		timeout : 2000
	});
}

/**
 * 截取url中的parameter
 * 依赖：无
 * @param paras
 * @return
 * 例：url http://localhost:8080/index.jsp?username=admin
 * requestUrlParam("username");
 * 则return admin
 */
function requestUrlParam(paras) {
	var url = location.href;
	var paraString = url.substring(url.indexOf("?") + 1, url.length).split("&");
	var paraObj = {};
	for (var i = 0; j = paraString[i]; i++) {
		paraObj[j.substring(0, j.indexOf("=")).toLowerCase()] = j.substring(j
				.indexOf("=") + 1, j.length);
	}
	var returnValue = paraObj[paras.toLowerCase()];
	if (typeof (returnValue) == "undefined") {
		return "";
	} else {
		return returnValue;
	}
}<|endoftext|>


<commit_before>function Board(tiles) {
  var self = this;
  this._board = [];
  this._gameOver = false;

  [].map.call(tiles, function (tile) {
    var ids = /^tile-(\d)(\d)$/.exec(tile.id);
    tile.gameData = {
      filled : false,
      x : parseInt(ids[2]),
      y : parseInt(ids[1]),
      id : tile.id,
      value : 0
    };
    self._board[ids[2]] = self._board[ids[2]] || [];
    self._board[ids[2]][ids[1]] = tile;
  });
}

Board.prototype.getEmptyCells = function () {
  var emptyCells = [];
  var x, y;

  for (x = 0; x < this._board.length; x++) {
    for (y = 0; y < this._board[x].length; y++) {
      if (!this._board[x][y].gameData.filled) {
        emptyCells.push(this._board[x][y]);
      }
    }
  }

  return emptyCells;
};

Board.prototype.nextRound = function () {
  var emptyCells = this.getEmptyCells();
  if (emptyCells.length === 0) {
    this._gameOver = true;
    return alert('game over');
  }

  var randomCellIdx = Math.floor(Math.random() * emptyCells.length);
  var twoOrFour = Math.random() >= 0.75 ? 4 : 2;
  this.fillTile(emptyCells[randomCellIdx], twoOrFour);
};

Board.prototype.fillTile = function (tile, number) {
  tile.gameData.filled = true;
  tile.gameData.value = number;

  tile.classList.remove();
  tile.classList.add('number-' + number);
  tile.innerHTML = number;

  this._gameOver = checkGameOver(this);
};

Board.prototype.getTile = function (id) {
  return this._board[id % 4][Math.floor(id / 4)];
};

Board.prototype.isMoveXPossible = function () {
  var data, i, oldData, tile;
  var oldTile = this.getTile(0);
  for (i = 1; i < 16; i++) {
    tile = this.getTile(i);
    oldData = oldTile.gameData;
    data = tile.gameData;
    if (data.y === oldData.y &&
      ((data.value !== 0 && data.value === oldData.value) ||
      (data.value === 0 && oldData.value !== 0) ||
      (data.value !== 0 && oldData.value === 0))) {
      return true;
    }
    oldTile = tile;
  }
  return false;
};

Board.prototype.isMoveYPossible = function () {
  var data, i, oldData, tile;
  var oldTile = this.getTile(0);
  for (i = 1; i < 16; i++) {
    tile = i === 15 ? this.getTile(15) : this.getTile((i * 4) % 15);
    oldData = oldTile.gameData;
    data = tile.gameData;
    if (data.x === oldData.x &&
      ((data.value !== 0 && data.value === oldData.value) ||
      (data.value === 0 && oldData.value !== 0) ||
      (data.value !== 0 && oldData.value === 0))) {
      return true;
    }
    oldTile = tile;
  }
  return false;
};

Board.prototype.moveRight = function () {
  var mostRightX, tile, x, y;
  for (y = 0; y < 4; y++) {
    for (mostRightX = 3; mostRightX >= 0; mostRightX--) {
      tile = this._board[mostRightX][y];
      for (x = mostRightX - 1; x >= 0; x--) {
        if (isEmptyTile(this._board[x][y])) {
          continue;
        }

        // current tile not empty
        if (isEmptyTile(tile)) {
          this.moveTile(this._board[x][y], tile);
        } else if (tile.gameData.value === this._board[x][y].gameData.value) {
          this.mergeTile(this._board[x][y], tile);
          tile = this._board[x][y];
        } else {
          break;
        }
      }
    }
  }
};

Board.prototype.moveLeft = function () {
  var mostLeftX, tile, x, y;
  for (y = 0; y < 4; y++) {
    for (mostLeftX = 0; mostLeftX <= 3; mostLeftX++) {
      tile = this._board[mostLeftX][y];
      for (x = mostLeftX + 1; x <= 3; x++) {
        if (isEmptyTile(this._board[x][y])) {
          continue;
        }

        // current tile not empty
        if (isEmptyTile(tile)) {
          this.moveTile(this._board[x][y], tile);
        } else if (tile.gameData.value === this._board[x][y].gameData.value) {
          this.mergeTile(this._board[x][y], tile);
          tile = this._board[x][y];
        } else {
          break;
        }
      }
    }
  }
};

Board.prototype.moveUp = function () {
  var topY, tile, x, y;
  for (x = 0; x < 4; x++) {
    for (topY = 0; topY <= 3; topY++) {
      tile = this._board[x][topY];
      for (y = topY + 1; y <= 3; y++) {
        if (isEmptyTile(this._board[x][y])) {
          continue;
        }

        // current tile not empty
        if (isEmptyTile(tile)) {
          this.moveTile(this._board[x][y], tile);
        } else if (tile.gameData.value === this._board[x][y].gameData.value) {
          this.mergeTile(this._board[x][y], tile);
          tile = this._board[x][y];
        } else {
          break;
        }
      }
    }
  }
};

Board.prototype.moveDown = function () {
  var bottomY, tile, x, y;
  for (x = 0; x < 4; x++) {
    for (bottomY = 3; bottomY >= 0; bottomY--) {
      tile = this._board[x][bottomY];
      for (y = bottomY - 1; y >= 0; y--) {
        if (isEmptyTile(this._board[x][y])) {
          continue;
        }

        // current tile not empty
        if (isEmptyTile(tile)) {
          this.moveTile(this._board[x][y], tile);
        } else if (tile.gameData.value === this._board[x][y].gameData.value) {
          this.mergeTile(this._board[x][y], tile);
          tile = this._board[x][y];
        } else {
          break;
        }
      }
    }
  }
};

Board.prototype.isGameOver = function () {
  return this._gameOver;
};

Board.prototype.mergeTile = function (a, b) {
  this.fillTile(b, b.gameData.value + a.gameData.value);
  emptyTile(a);
};

Board.prototype.moveTile = function (a, b) {
  this.fillTile(b, a.gameData.value);
  emptyTile(a);
};

function isEmptyTile(tile) {
  return tile.gameData.value === 0;
}

function emptyTile(tile) {
  tile.innerHTML = '';
  tile.gameData.filled = false;
  tile.gameData.value = 0;
}

function checkGameOver(board) {
  return !(board.isMoveXPossible() || board.isMoveYPossible());
}

module.exports = Board;
<commit_msg>Remove old number classes<commit_after>function Board(tiles) {
  var self = this;
  this._board = [];
  this._gameOver = false;

  [].map.call(tiles, function (tile) {
    var ids = /^tile-(\d)(\d)$/.exec(tile.id);
    tile.gameData = {
      filled : false,
      x : parseInt(ids[2]),
      y : parseInt(ids[1]),
      id : tile.id,
      value : 0
    };
    self._board[ids[2]] = self._board[ids[2]] || [];
    self._board[ids[2]][ids[1]] = tile;
  });
}

Board.prototype.getEmptyCells = function () {
  var emptyCells = [];
  var x, y;

  for (x = 0; x < this._board.length; x++) {
    for (y = 0; y < this._board[x].length; y++) {
      if (!this._board[x][y].gameData.filled) {
        emptyCells.push(this._board[x][y]);
      }
    }
  }

  return emptyCells;
};

Board.prototype.nextRound = function () {
  var emptyCells = this.getEmptyCells();
  if (emptyCells.length === 0) {
    this._gameOver = true;
    return alert('game over');
  }

  var randomCellIdx = Math.floor(Math.random() * emptyCells.length);
  var twoOrFour = Math.random() >= 0.75 ? 4 : 2;
  this.fillTile(emptyCells[randomCellIdx], twoOrFour);
};

Board.prototype.fillTile = function (tile, number) {
  tile.gameData.filled = true;
  tile.gameData.value = number;

  tile.classList.remove();
  tile.classList.add('number-' + number);
  tile.innerHTML = number;

  this._gameOver = checkGameOver(this);
};

Board.prototype.getTile = function (id) {
  return this._board[id % 4][Math.floor(id / 4)];
};

Board.prototype.isMoveXPossible = function () {
  var data, i, oldData, tile;
  var oldTile = this.getTile(0);
  for (i = 1; i < 16; i++) {
    tile = this.getTile(i);
    oldData = oldTile.gameData;
    data = tile.gameData;
    if (data.y === oldData.y &&
      ((data.value !== 0 && data.value === oldData.value) ||
      (data.value === 0 && oldData.value !== 0) ||
      (data.value !== 0 && oldData.value === 0))) {
      return true;
    }
    oldTile = tile;
  }
  return false;
};

Board.prototype.isMoveYPossible = function () {
  var data, i, oldData, tile;
  var oldTile = this.getTile(0);
  for (i = 1; i < 16; i++) {
    tile = i === 15 ? this.getTile(15) : this.getTile((i * 4) % 15);
    oldData = oldTile.gameData;
    data = tile.gameData;
    if (data.x === oldData.x &&
      ((data.value !== 0 && data.value === oldData.value) ||
      (data.value === 0 && oldData.value !== 0) ||
      (data.value !== 0 && oldData.value === 0))) {
      return true;
    }
    oldTile = tile;
  }
  return false;
};

Board.prototype.moveRight = function () {
  var mostRightX, tile, x, y;
  for (y = 0; y < 4; y++) {
    for (mostRightX = 3; mostRightX >= 0; mostRightX--) {
      tile = this._board[mostRightX][y];
      for (x = mostRightX - 1; x >= 0; x--) {
        if (isEmptyTile(this._board[x][y])) {
          continue;
        }

        // current tile not empty
        if (isEmptyTile(tile)) {
          this.moveTile(this._board[x][y], tile);
        } else if (tile.gameData.value === this._board[x][y].gameData.value) {
          this.mergeTile(this._board[x][y], tile);
          tile = this._board[x][y];
        } else {
          break;
        }
      }
    }
  }
};

Board.prototype.moveLeft = function () {
  var mostLeftX, tile, x, y;
  for (y = 0; y < 4; y++) {
    for (mostLeftX = 0; mostLeftX <= 3; mostLeftX++) {
      tile = this._board[mostLeftX][y];
      for (x = mostLeftX + 1; x <= 3; x++) {
        if (isEmptyTile(this._board[x][y])) {
          continue;
        }

        // current tile not empty
        if (isEmptyTile(tile)) {
          this.moveTile(this._board[x][y], tile);
        } else if (tile.gameData.value === this._board[x][y].gameData.value) {
          this.mergeTile(this._board[x][y], tile);
          tile = this._board[x][y];
        } else {
          break;
        }
      }
    }
  }
};

Board.prototype.moveUp = function () {
  var topY, tile, x, y;
  for (x = 0; x < 4; x++) {
    for (topY = 0; topY <= 3; topY++) {
      tile = this._board[x][topY];
      for (y = topY + 1; y <= 3; y++) {
        if (isEmptyTile(this._board[x][y])) {
          continue;
        }

        // current tile not empty
        if (isEmptyTile(tile)) {
          this.moveTile(this._board[x][y], tile);
        } else if (tile.gameData.value === this._board[x][y].gameData.value) {
          this.mergeTile(this._board[x][y], tile);
          tile = this._board[x][y];
        } else {
          break;
        }
      }
    }
  }
};

Board.prototype.moveDown = function () {
  var bottomY, tile, x, y;
  for (x = 0; x < 4; x++) {
    for (bottomY = 3; bottomY >= 0; bottomY--) {
      tile = this._board[x][bottomY];
      for (y = bottomY - 1; y >= 0; y--) {
        if (isEmptyTile(this._board[x][y])) {
          continue;
        }

        // current tile not empty
        if (isEmptyTile(tile)) {
          this.moveTile(this._board[x][y], tile);
        } else if (tile.gameData.value === this._board[x][y].gameData.value) {
          this.mergeTile(this._board[x][y], tile);
          tile = this._board[x][y];
        } else {
          break;
        }
      }
    }
  }
};

Board.prototype.isGameOver = function () {
  return this._gameOver;
};

Board.prototype.mergeTile = function (a, b) {
  this.fillTile(b, b.gameData.value + a.gameData.value);
  emptyTile(a);
};

Board.prototype.moveTile = function (a, b) {
  this.fillTile(b, a.gameData.value);
  emptyTile(a);
};

function isEmptyTile(tile) {
  return tile.gameData.value === 0;
}

function emptyTile(tile) {
  tile.classList.remove('number-' + tile.gameData.value);
  tile.innerHTML = '';
  tile.gameData.filled = false;
  tile.gameData.value = 0;
}

function checkGameOver(board) {
  return !(board.isMoveXPossible() || board.isMoveYPossible());
}

module.exports = Board;
<|endoftext|>


<commit_before>/*
1) "inbox:1"
2) "mailalias:ankri.dk:ahk"
3) "inboxnextid"
*/

var redis = require("redis"),
client = redis.createClient();

var validator = require("validator");
var fs = require("fs")

var MailModule = function () {
	this.authorizedDownloadHashes = [];
};

MailModule.prototype.init = function(fw, onFinished) {
    this.fw = fw;
	onFinished.call(this);
}

MailModule.prototype.onMessage = function (req, callback, res) {
	var t = this;
	if(typeof(req.body.type) !== "string"){
		callback({error: "Invalid request"});
		return;
	}

	/* Callbacks not requiring login */
	switch(req.body.type){
		case "GetAttachment" :
			if(typeof req.body.file !== "string" || req.body.file.length < 1){
				callback({error: "No file"});
				return;
			}
			if(req.body.file in this.authorizedDownloadHashes){
				client.hgetall(this.authorizedDownloadHashes[req.body.file], function(err, attr){
					if(attr && attr.file){
						//fs.readFile('../haraka_attachments/' + attr.file, function (err, data) {
							if (err)
								throw err;

							res.writeHead(200, {
								'Content-Type':attr.contentType,
								'Content-Disposition': 'attachment; filename=' + attr.filename
							});
							//res.end(data);
							fs.createReadStream('../haraka_attachments/' + attr.file).pipe(res);
						//});
					} else {
						callback({error: "Invalid file"})
					}
				});
			} else {
				callback({error: "Invalid download link"});
			}
			return;
	}

	if(!this.fw.modules["user"].loggedIn(req.body.sessionId)){
		callback({error: "You are not logged in"});
		return;
	}

	var session = fw.modules["user"].sessionId2Session(req.body.sessionId);

	switch(req.body.type){
		case "GetRecentMailList" :
			if(req.body.isSpam == true){
				this.fw.modules["user"].hasPermission(session.sessionId, "admin", function(hasPermission){
					if(hasPermission){
						t.getMails("inbox:spam", 0, 9, function(mails){
							callback(mails);
						})
					} else {
						callback({error: "You do not have permission to access the spam folder"});
					}
				});
			} else {
				this.getUserInbox.call(this, session.userId, function(inboxId){
					var inbox = "inbox:" + inboxId + ":mails";
					t.getMails(inbox, 0, 9, function(mails){
						callback(mails);
					})
				});
			}
			break;
		case "DeleteMail" :
			this.fw.modules["user"].hasPermission(session.sessionId, "admin", function(hasPermission){
				if(hasPermission){
					t.deleteMail(req.body.mailId, function(){
						callback({success: true})
					})
				}
			});
			break;
		case "GetAliases" :
			client.smembers("user:" + session.userId + ":mailaliases", function(err, res){
				if(err)
					callback({error: err});
				else {
					var aliases = [];
					for(i in res){
						aliases.push({
							alias: res[i]
						});
					}
					callback(aliases);
				}
			});
			break;
		case "AddAlias" :
			if(typeof(req.body.alias) !== "string" || !validator.isEmail(req.body.alias)){
				callback({error: "Invalid e-mail address"});
				break;
			}
			this.getUserInbox(session.userId, function(inboxId){
				if(inboxId > 0){
					client.setnx("mailalias:" + req.body.alias, inboxId, function(err, success){
						if(err)
							callback({error: err});
						else if(success != 1)
							callback({success: false, message: "Alias already exists"});
						else {
							//sadd user:1:mailaliases test@ankri.dk
							client.sadd("user:" + session.userId + ":mailaliases", req.body.alias, function(){
								callback({success: true});
							});
						}
					});
				} else {
					callback({error: "You do not have an inbox"});
				}
			});
			break;
		case "RemoveAlias" :
			client.del("mailalias:" + req.body.alias, function(){});
			client.srem("user:" + session.userId + ":mailaliases", req.body.alias, function(){});
			callback({success: true});
			break;
	}
};

MailModule.prototype.getMails = function (inbox, start, end, callback) {
	var t = this;
	client.zrevrange(inbox, start, end, function(err, mailIds){
		var multi = client.multi();

		mailIds.forEach(function (id, i) {
            multi.hgetall("mail:" + id);
        });

		multi.exec(function(err, mails){

			/* Attachments */
			var multi = client.multi();
			for(i in mails){
				if(mails[i] === undefined)
					continue;

				mails[i].attachments = [];

				if(!isNaN(mails[i].numAttachments)){
					for(var a = 1; a <= mails[i].numAttachments; a++){
						multi.hgetall("mail:" + mails[i].id + ":attachment:" + a);
					}

				}
			}
			multi.exec(function(err, attachments){
				for(i in attachments){
					var a = attachments[i];
					if(a == null || isNaN(a.mailId) || isNaN(a.num))
						continue;

					a.downloadHash = Math.random().toString(36).substr(2, 12);
					t.authorizedDownloadHashes[a.downloadHash] = "mail:" + a.mailId + ":attachment:" + a.num;

					var mailIdx = mailIds.indexOf(a.mailId);
					mails[mailIdx].attachments.push(a);
				}

				callback(mails);
			})
		});
	});
}

MailModule.prototype.getUserInbox = function (userId, callback) {
	client.get("user:" + userId + ":inbox", function(err, inboxId){
		if(inboxId > 0){
			callback(inboxId);
		} else {
			client.incr("inboxnextid", function(err, id){
				client.set("user:" + userId + ":inbox", id, function(){
					callback(id);
				})
			})
		}
	});
}

MailModule.prototype.deleteMail = function (mailId, callback) {
	if(isNaN(mailId)){
		callback("Not a number")
		return;
	}
	/*
	client.hgetall("mail:" + mailId, function(err, mail){
		if(mail === undefined)
			return;

		client.del("mail:" + mailId);
		client.del("mail:" + mailId + ":raw");
		if(!isNaN(mail.numAttachments)){
		for(var i = 1; i <= mail.numAttachments; i++)
			client.del("mail:" + mailId + ":attachment:" + i)
		}
	})
	*/

	client.smembers("mail:" + mailId + ":inboxes", function(err, inboxes){
		var multi = client.multi();

		if(inboxes && inboxes.length > 0){
			for(var i in inboxes)
				multi.zrem("inbox:" + inboxes[i] + ":mails", mailId);
		} else {
			console.log("no inboxes")
			client.keys("inbox:*:mails", function(err, inboxKeys){
				console.log(inboxKeys)
				for(var i in inboxKeys)
					client.zrem(inboxKeys[i], mailId);
			})
		}

		multi.del("mail:" + mailId)
		multi.eval("return redis.call('del', unpack(redis.call('keys', ARGV[1])))", 0, "mail:" + mailId + ":*")
		multi.exec(function(){
			callback();
		});
	})
	client.zrem("inbox:spam", mailId);
}

module.exports = MailModule;<commit_msg>misc fixes<commit_after>/*
1) "inbox:1"
2) "mailalias:ankri.dk:ahk"
3) "inboxnextid"
*/

var redis = require("redis"),
client = redis.createClient();

var validator = require("validator");
var fs = require("fs")

var MailModule = function () {
	this.authorizedDownloadHashes = [];
};

MailModule.prototype.init = function(fw, onFinished) {
    this.fw = fw;
	onFinished.call(this);
}

MailModule.prototype.onMessage = function (req, callback, res) {
	var t = this;
	if(typeof(req.body.type) !== "string"){
		callback({error: "Invalid request"});
		return;
	}

	/* Callbacks not requiring login */
	switch(req.body.type){
		case "GetAttachment" :
			if(typeof req.body.file !== "string" || req.body.file.length < 1){
				callback({error: "No file"});
				return;
			}
			if(req.body.file in this.authorizedDownloadHashes){
				client.hgetall(this.authorizedDownloadHashes[req.body.file], function(err, attr){
					if(attr && attr.file){
						//fs.readFile('../haraka_attachments/' + attr.file, function (err, data) {
							if (err)
								throw err;

							res.writeHead(200, {
								'Content-Type':attr.contentType,
								'Content-Disposition': 'attachment; filename=' + attr.filename
							});
							//res.end(data);
							fs.createReadStream('../haraka_attachments/' + attr.file).pipe(res);
						//});
					} else {
						callback({error: "Invalid file"})
					}
				});
			} else {
				callback({error: "Invalid download link"});
			}
			return;
	}

	if(!this.fw.modules["user"].loggedIn(req.body.sessionId)){
		callback({error: "You are not logged in"});
		return;
	}

	var session = fw.modules["user"].sessionId2Session(req.body.sessionId);

	switch(req.body.type){
		case "GetRecentMailList" :
			if(req.body.isSpam == true){
				this.fw.modules["user"].hasPermission(session.sessionId, "admin", function(hasPermission){
					if(hasPermission){
						t.getMails("inbox:spam", 0, 9, function(mails){
							callback(mails);
						})
					} else {
						callback({error: "You do not have permission to access the spam folder"});
					}
				});
			} else {
				this.getUserInbox.call(this, session.userId, function(inboxId){
					var inbox = "inbox:" + inboxId + ":mails";
					t.getMails(inbox, 0, 9, function(mails){
						callback(mails);
					})
				});
			}
			break;
		case "DeleteMail" :
			this.getUserInbox.call(this, session.userId, function(inboxId){
				client.zscore("inbox:" + inboxId + ":mails", req.body.mailId, function(err, score){
					if(score){
						t.deleteMail(req.body.mailId, function(){
							callback({success: true})
						})
					} else {
						this.fw.modules["user"].hasPermission(session.sessionId, "admin", function(hasPermission){
							if(hasPermission){
								t.deleteMail(req.body.mailId, function(){
									callback({success: true})
								})
							}
						});
					}
				})
			})
			break;
		case "GetAliases" :
			client.smembers("user:" + session.userId + ":mailaliases", function(err, res){
				if(err)
					callback({error: err});
				else {
					var aliases = [];
					for(i in res){
						aliases.push({
							alias: res[i]
						});
					}
					callback(aliases);
				}
			});
			break;
		case "AddAlias" :
			if(typeof(req.body.alias) !== "string" || !validator.isEmail(req.body.alias)){
				callback({error: "Invalid e-mail address"});
				break;
			}
			this.getUserInbox(session.userId, function(inboxId){
				if(inboxId > 0){
					client.setnx("mailalias:" + req.body.alias, inboxId, function(err, success){
						if(err)
							callback({error: err});
						else if(success != 1)
							callback({success: false, message: "Alias already exists"});
						else {
							//sadd user:1:mailaliases test@ankri.dk
							client.sadd("user:" + session.userId + ":mailaliases", req.body.alias, function(){
								callback({success: true});
							});
						}
					});
				} else {
					callback({error: "You do not have an inbox"});
				}
			});
			break;
		case "RemoveAlias" :
			client.del("mailalias:" + req.body.alias, function(){});
			client.srem("user:" + session.userId + ":mailaliases", req.body.alias, function(){});
			callback({success: true});
			break;
	}
};

MailModule.prototype.getMails = function (inbox, start, end, callback) {
	var t = this;
	client.zrevrange(inbox, start, end, function(err, mailIds){
		var multi = client.multi();

		mailIds.forEach(function (id, i) {
            multi.hgetall("mail:" + id);
        });

		multi.exec(function(err, mails){

			/* Attachments */
			var multi = client.multi();
			for(i in mails){
				if(mails[i] === undefined)
					continue;

				mails[i].attachments = [];

				if(!isNaN(mails[i].numAttachments)){
					for(var a = 1; a <= mails[i].numAttachments; a++){
						multi.hgetall("mail:" + mails[i].id + ":attachment:" + a);
					}

				}
			}
			multi.exec(function(err, attachments){
				for(i in attachments){
					var a = attachments[i];
					if(a == null || isNaN(a.mailId) || isNaN(a.num))
						continue;

					a.downloadHash = Math.random().toString(36).substr(2, 12);
					t.authorizedDownloadHashes[a.downloadHash] = "mail:" + a.mailId + ":attachment:" + a.num;

					var mailIdx = mailIds.indexOf(a.mailId);
					mails[mailIdx].attachments.push(a);
				}

				callback(mails);
			})
		});
	});
}

MailModule.prototype.getUserInbox = function (userId, callback) {
	client.get("user:" + userId + ":inbox", function(err, inboxId){
		if(inboxId > 0){
			callback(inboxId);
		} else {
			client.incr("inboxnextid", function(err, id){
				client.set("user:" + userId + ":inbox", id, function(){
					callback(id);
				})
			})
		}
	});
}

MailModule.prototype.deleteMail = function (mailId, callback) {
	if(isNaN(mailId)){
		callback("Not a number")
		return;
	}

	client.smembers("mail:" + mailId + ":inboxes", function(err, inboxes){
		var multi = client.multi();

		if(inboxes && inboxes.length > 0){
			for(var i in inboxes)
				multi.zrem("inbox:" + inboxes[i] + ":mails", mailId);
		} else {
			console.log("no inboxes")
			client.keys("inbox:*:mails", function(err, inboxKeys){
				console.log(inboxKeys)
				for(var i in inboxKeys)
					client.zrem(inboxKeys[i], mailId);
			})
		}

		multi.del("mail:" + mailId)
		multi.eval("return redis.call('del', unpack(redis.call('keys', ARGV[1])))", 0, "mail:" + mailId + ":*")
		multi.exec(function(){
			callback();
		});
	})
	client.zrem("inbox:spam", mailId);
}

module.exports = MailModule;<|endoftext|>


<commit_before>import {Autofit} from 'widget/autofit'
import {ContentPadding} from 'widget/sectionLayout'
import {compose} from 'compose'
import {connect} from 'store'
import {forkJoin, timer} from 'rxjs'
import {msg} from 'translate'
import {runApp$} from 'apps'
import Icon from 'widget/icon'
import Notifications from 'widget/notifications'
import PropTypes from 'prop-types'
import React from 'react'
import styles from './appInstance.module.css'

class AppInstance extends React.Component {
    state = {
        appState: 'REQUESTED'
    }

    constructor(props) {
        super(props)
        this.runApp(props.app)
    }

    runApp(app) {
        this.props.stream('RUN_APP',
            forkJoin(
                runApp$(app.path),
                timer(500)
            ),
            () => this.setState({appState: 'INITIALIZED'}),
            () => Notifications.error({message: msg('apps.run.error', {label: app.label || app.alt})})
        )
    }

    render() {
        const {app: {path, label, alt}} = this.props
        const {appState} = this.state
        return (
            <ContentPadding
                menuPadding
                className={styles.appInstance}>

                    <Autofit className={styles.spinner}>
                        <Icon name='circle-notch' size='10x' spin/>
                    </Autofit>
                    <Autofit className={styles.backdrop}>
                        {label || alt}
                    </Autofit>

                        {this.renderStatus()}

                     this.setState({appState: 'READY'})}
                    />

            </ContentPadding>
        )
    }

    renderStatus() {
        const {appState} = this.state
        return appState === 'REQUESTED'
            ? msg('apps.initializing')
            : appState !== 'READY'
                ? msg('apps.loading.progress')
                : null
    }
}

AppInstance.propTypes = {
    app: PropTypes.shape({
        alt: PropTypes.string,
        label: PropTypes.string,
        path: PropTypes.string
    })
}

AppInstance.contextTypes = {
    active: PropTypes.bool,
    focus: PropTypes.func
}

export default compose(
    AppInstance,
    connect()
)
<commit_msg>Restored real app path.<commit_after>import {Autofit} from 'widget/autofit'
import {ContentPadding} from 'widget/sectionLayout'
import {compose} from 'compose'
import {connect} from 'store'
import {forkJoin, timer} from 'rxjs'
import {msg} from 'translate'
import {runApp$} from 'apps'
import Icon from 'widget/icon'
import Notifications from 'widget/notifications'
import PropTypes from 'prop-types'
import React from 'react'
import styles from './appInstance.module.css'

class AppInstance extends React.Component {
    state = {
        appState: 'REQUESTED'
    }

    constructor(props) {
        super(props)
        this.runApp(props.app)
    }

    runApp(app) {
        this.props.stream('RUN_APP',
            forkJoin(
                runApp$(app.path),
                timer(500)
            ),
            () => this.setState({appState: 'INITIALIZED'}),
            () => Notifications.error({message: msg('apps.run.error', {label: app.label || app.alt})})
        )
    }

    render() {
        const {app: {path, label, alt}} = this.props
        const {appState} = this.state
        return (
            <ContentPadding
                menuPadding
                className={styles.appInstance}>

                    <Autofit className={styles.spinner}>
                        <Icon name='circle-notch' size='10x' spin/>
                    </Autofit>
                    <Autofit className={styles.backdrop}>
                        {label || alt}
                    </Autofit>

                        {this.renderStatus()}

                     this.setState({appState: 'READY'})}
                    />

            </ContentPadding>
        )
    }

    renderStatus() {
        const {appState} = this.state
        return appState === 'REQUESTED'
            ? msg('apps.initializing')
            : appState !== 'READY'
                ? msg('apps.loading.progress')
                : null
    }
}

AppInstance.propTypes = {
    app: PropTypes.shape({
        alt: PropTypes.string,
        label: PropTypes.string,
        path: PropTypes.string
    })
}

AppInstance.contextTypes = {
    active: PropTypes.bool,
    focus: PropTypes.func
}

export default compose(
    AppInstance,
    connect()
)
<|endoftext|>


<commit_before>var  cmd = require('../../util/process')
   , path = require('path')
   , fs = require('graceful-fs')

var internal = {};

exports.launch = function(flow) {

    var launch_wait = flow.config.build.launch_wait;
    var flag_launch_wait = flow.flags['launch-wait'];

    if(flag_launch_wait !== undefined) {
        launch_wait = parseFloat(flag_launch_wait);
    }

    if(launch_wait) {

        flow.log(2, 'launch waiting %ds', launch_wait);

        setTimeout(function(){
            internal.launch(flow);
        }, launch_wait*1000);

    } else {

        internal.launch(flow);

    }

} //launch

internal.launch = function(flow) {

    if(flow.target_desktop) {

        var abs_binary = path.resolve(flow.project.root, flow.project.paths.binary.full);
        var abs_outpath = path.resolve(flow.project.root, flow.project.paths.output);

        cmd.exec(flow, abs_binary, [], { cwd: abs_outpath });

    } else if(flow.target_mobile) {

        switch(flow.target) {
            case 'android':
                internal.android_launch_init(flow);

                internal.install_android(flow, function(code) {
                    if(!code) {
                        internal.launch_android(flow);
                    } else {
                        flow.log(1, 'launch - stopping due to failure in install step');
                    }
                });

            case 'ios':
                internal.launch_ios(flow);
        }

    } //target mobile

} //launch

internal.android_launch_init = function(flow) {

    var project = flow.project.prepared.source.project;

        //work out where adb should be
    internal.adb_path = path.join(flow.config.build.android.sdk,'platform-tools/adb');
        //find out if this is a signed or unsigned store build
    internal.build_type = project.app.mobile.android.build_type;
        //work out the name of the apk
    internal.apk_name = project.app.name + '-' + internal.build_type + '.apk';
        //work out the apk location
    internal.abs_outpath = path.resolve(flow.project.root, flow.project.paths.output);

} //android_launch_init

internal.launch_android = function(flow) {

    var project = flow.project.prepared.source.project;

    if(flow.flags.launch !== false) {

        flow.log(2, 'launch - starting app on device');

        var activity = project.app.package + '/' + project.app.package + '.' + flow.config.build.android.activity_name;
        var adb_args_run = [ "shell", "am", "start", "-a", "android.intent.action.MAIN", "-n", activity ];

        flow.log(2, 'launch - running adb %s', adb_args_run.join(' '));

            //start
        cmd.exec(flow, internal.adb_path, adb_args_run, { cwd: internal.abs_outpath });
            //logcat immediately
        internal.launch_android_logcat(flow, internal.adb_path, internal.abs_outpath);

    } else {//--no-launch

        flow.log(2, 'launch - not launching app due to --no-launch');

    }

} //launch_android

internal.install_android = function(flow, done) {

    if(flow.flags.install !== false) {

        flow.log(2, 'launch - installing apk to device ...');

        var adb_args = ["install", "-r", internal.apk_name];

        flow.log(2, 'launch - adb will run in', internal.abs_outpath);
        flow.log(2, 'launch - adb %s', adb_args.join(' '));

        cmd.exec(flow, internal.adb_path, adb_args, { cwd: internal.abs_outpath }, function(code,out,err){

            flow.log(2, 'launch - installed');
            if(done) {
                done(code, out, err);
            }

        }); //exec adb install -r

    } else {

        flow.log(2, 'launch - not installing apk to device due to --no-install');

        if(done) {
            done();
        }

    }

} //launch_android

internal.launch_android_logcat = function(flow) {

    var logcat_filter = '';

    for(name in flow.config.build.android.logcat_include) {
        var include = flow.config.build.android.logcat_include[name];
        logcat_filter += ' ' + include + ':E';
        logcat_filter += ' ' + include + ':I';
        logcat_filter += ' ' + include + ':V';
    }

    logcat_filter = logcat_filter.trim();

    var adb_args_clear = ["logcat", "-c"];
    var adb_args = ["logcat", "-s"].concat(logcat_filter.split(' '));

    flow.log(2, 'launch - starting logcat %s', adb_args.join(' '));

    cmd.exec(flow, internal.adb_path, adb_args_clear, { cwd: internal.abs_outpath }, function(code,out,err){
        cmd.exec(flow, internal.adb_path, adb_args, { cwd: internal.abs_outpath });
    });

} //launch_android_logcat

internal.launch_ios = function(flow) {

        //open the xcode project path
    var ios_project_path = path.resolve( flow.project.root, flow.config.build.ios.project );
    var ios_project_file = path.join( ios_project_path, flow.project.prepared.source.project.app.name + '.xcodeproj' );

    if(fs.existsSync(ios_project_file)) {
        cmd.exec(flow, 'open', [ios_project_file]);
    } else {
        flow.log(1, 'seems the project is not located at expected location %s, can\'t focus it?', ios_project_file);
    }

}<commit_msg>launch; fix missing breaks (too much haxe) causing iOS launch to attempt after android launch starts<commit_after>var  cmd = require('../../util/process')
   , path = require('path')
   , fs = require('graceful-fs')

var internal = {};

exports.launch = function(flow) {

    var launch_wait = flow.config.build.launch_wait;
    var flag_launch_wait = flow.flags['launch-wait'];

    if(flag_launch_wait !== undefined) {
        launch_wait = parseFloat(flag_launch_wait);
    }

    if(launch_wait) {

        flow.log(2, 'launch waiting %ds', launch_wait);

        setTimeout(function(){
            internal.launch(flow);
        }, launch_wait*1000);

    } else {

        internal.launch(flow);

    }

} //launch

internal.launch = function(flow) {

    if(flow.target_desktop) {

        var abs_binary = path.resolve(flow.project.root, flow.project.paths.binary.full);
        var abs_outpath = path.resolve(flow.project.root, flow.project.paths.output);

        cmd.exec(flow, abs_binary, [], { cwd: abs_outpath });

    } else if(flow.target_mobile) {

        switch(flow.target) {

            case 'android':

                internal.android_launch_init(flow);

                internal.install_android(flow, function(code) {
                    if(!code) {
                        internal.launch_android(flow);
                    } else {
                        flow.log(1, 'launch - stopping due to failure in install step');
                    }
                });

                break;

            case 'ios':

                internal.launch_ios(flow);

                break;

            default:
                flow.log(1, 'launch - stopping due to unknown target');

        }

    } //target mobile

} //launch

internal.android_launch_init = function(flow) {

    var project = flow.project.prepared.source.project;

        //work out where adb should be
    internal.adb_path = path.join(flow.config.build.android.sdk,'platform-tools/adb');
        //find out if this is a signed or unsigned store build
    internal.build_type = project.app.mobile.android.build_type;
        //work out the name of the apk
    internal.apk_name = project.app.name + '-' + internal.build_type + '.apk';
        //work out the apk location
    internal.abs_outpath = path.resolve(flow.project.root, flow.project.paths.output);

} //android_launch_init

internal.launch_android = function(flow) {

    var project = flow.project.prepared.source.project;

    if(flow.flags.launch !== false) {

        flow.log(2, 'launch - starting app on device');

        var activity = project.app.package + '/' + project.app.package + '.' + flow.config.build.android.activity_name;
        var adb_args_run = [ "shell", "am", "start", "-a", "android.intent.action.MAIN", "-n", activity ];

        flow.log(2, 'launch - running adb %s', adb_args_run.join(' '));

            //start
        cmd.exec(flow, internal.adb_path, adb_args_run, { cwd: internal.abs_outpath });
            //logcat immediately
        internal.launch_android_logcat(flow, internal.adb_path, internal.abs_outpath);

    } else {//--no-launch

        flow.log(2, 'launch - not launching app due to --no-launch');

    }

} //launch_android

internal.install_android = function(flow, done) {

    if(flow.flags.install !== false) {

        flow.log(2, 'launch - installing apk to device ...');

        var adb_args = ["install", "-r", internal.apk_name];

        flow.log(2, 'launch - adb will run in', internal.abs_outpath);
        flow.log(2, 'launch - adb %s', adb_args.join(' '));

        cmd.exec(flow, internal.adb_path, adb_args, { cwd: internal.abs_outpath }, function(code,out,err){

            flow.log(2, 'launch - installed');
            if(done) {
                done(code, out, err);
            }

        }); //exec adb install -r

    } else {

        flow.log(2, 'launch - not installing apk to device due to --no-install');

        if(done) {
            done();
        }

    }

} //launch_android

internal.launch_android_logcat = function(flow) {

    var logcat_filter = '';

    for(name in flow.config.build.android.logcat_include) {
        var include = flow.config.build.android.logcat_include[name];
        logcat_filter += ' ' + include + ':E';
        logcat_filter += ' ' + include + ':I';
        logcat_filter += ' ' + include + ':V';
    }

    logcat_filter = logcat_filter.trim();

    var adb_args_clear = ["logcat", "-c"];
    var adb_args = ["logcat", "-s"].concat(logcat_filter.split(' '));

    flow.log(2, 'launch - starting logcat %s', adb_args.join(' '));

    cmd.exec(flow, internal.adb_path, adb_args_clear, { cwd: internal.abs_outpath }, function(code,out,err){
        cmd.exec(flow, internal.adb_path, adb_args, { cwd: internal.abs_outpath });
    });

} //launch_android_logcat

internal.launch_ios = function(flow) {

        //open the xcode project path
    var ios_project_path = path.resolve( flow.project.root, flow.config.build.ios.project );
    var ios_project_file = path.join( ios_project_path, flow.project.prepared.source.project.app.name + '.xcodeproj' );

    if(fs.existsSync(ios_project_file)) {
        cmd.exec(flow, 'open', [ios_project_file]);
    } else {
        flow.log(1, 'seems the project is not located at expected location %s, can\'t focus it?', ios_project_file);
    }

}<|endoftext|>


<commit_before>/**
 * ProjectName: ArrowJS
 * Name: Petar Bojinov
 * Email: petar@conduit.com
 * Github: https: //github.com/chrisenytc/generator-library
 */

window.Arrow = (function (window, document, undefined) {

    "use strict";

    var version = '0.1.0',
        Arrow = {},
        arrowNode,
        browser = '',
        browserVersion = '',
        visibleHeight = 0,
        visibleWidth = 0;

    //http://storage.conduit.com/arrowjs/arrow_orange.png
    //http://storage.conduit.com/arrowjs/arrow_orange.gif
    //http://storage.conduit.com/arrowjs/arrow_green.png
    //http://storage.conduit.com/arrowjs/arrow_green.gif

    //determine browser type and browser version
    (function () {
        var N = navigator.appName, ua = navigator.userAgent, tem;
        var M = ua.match(/(opera|chrome|safari|firefox|msie)\/?\s*(\.?\d+(\.\d+)*)/i);
        if (M && (tem = ua.match(/version\/([\.\d]+)/i)) != null) M[2] = tem[1];
        M = M ? [M[1], M[2]] : [N, navigator.appVersion, '-?'];
        browser = M[0].toLowerCase();
        browserVersion = M[1];
    })();

    /**
     * Apply modern browser style then browser specific styles to arrow
     */

    function _increaseOpacity() {
        var arrow = document.getElementById('arrow-' + browser);
        arrow.style.display = 'block';
        var i = 0.0,
            ieI = 0; //need to use whole numbers for IE filter
        var x = setInterval(function () {
            i += 0.1;
            ieI += 10;
            if ((browser === 'msie') && (browserVersion <= 8)) {
                if (arrow.filters) {
                    arrow.filters.item("DXImageTransform.Microsoft.Alpha").opacity = ieI;
                }
            } else {
                arrow.style.opacity = i;
            }
        }, 50);
        setTimeout(function () {
            clearInterval(x);
        }, 600);
        // TODO use requestAnimationFrame instead
        // see http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
    }

    function _decreaseOpacity() {
        var arrow = document.getElementById('arrow-' + browser);
        var i = 1.0,
            ieI = 100; //need to use whole numbers for IE filter
        var x = setInterval(function () {
            i -= 0.1;
            ieI -= 10;
            if ((browser === 'msie') && (browserVersion <= 8)) {
                if (arrow.filters) {
                    arrow.filters.item("DXImageTransform.Microsoft.Alpha").opacity = ieI;
                }
            } else {
                arrow.style.opacity = i;
            }
        }, 50);
        setTimeout(function () {
            clearInterval(x);
            arrow.style.display = 'none';
        }, 600);
        // TODO use requestAnimationFrame instead
        // see http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
    }

    /*
     node.style.webkitTransform = "";
     node.style.MozTransform = "";
     node.style.msTransform = "";
     node.style.OTransform = "";
     node.style.transform = "";
     */

    //Always apply all modern browsers first, then vendor specific

    function _applyStyleModern(node) {
        node.style.position = 'absolute';
        //TODO
        //top, left, right, bottom position?
        node.style.zIndex = 999;
        node.style.display = 'none';
        node.style.height = '309px';
        node.style.width = '186px';
        node.style.opacity = 0;
        node.style.backgroundImage = 'url(http://storage.conduit.com/arrowjs/arrow_orange.png)';
        node.style.backgroundRepeat = 'no-repeat';
        node.style.backgroundPositionX = '0';
        node.style.backgroundPositionY = '0';
    }

    //IE 8 styles

    function _applyStyleIE8(node) {
        node.style.bottom = '0px';
        node.style.left = '20px';
        node.filters.item('DXImageTransform.Microsoft.AlphaImageLoader').src = 'http://storage.conduit.com/arrowjs/arrow_orange.png';
        node.filters.item('DXImageTransform.Microsoft.AlphaImageLoader').sizingMethod = 'scale';
        node.filters.item("DXImageTransform.Microsoft.Alpha").opacity = 0;
        node.filters.item("DXImageTransform.Microsoft.Matrix").M11 = 1;
        node.filters.item("DXImageTransform.Microsoft.Matrix").M12 = 1.2246063538223773e-16;
        node.filters.item("DXImageTransform.Microsoft.Matrix").M21 = -1.2246063538223773e-16;
        node.filters.item("DXImageTransform.Microsoft.Matrix").M22 = -1;
        node.filters.item("DXImageTransform.Microsoft.Matrix").SizingMethod = 'auto expand';
    }

    //IE 9 styles

    function _applyStyleMs(node) {
        node.style.bottom = '50px';
        node.style.left = '67%';
    }

    //Firefox 20+

    function _applyStyleMoz(node) {
        node.style.top = '0px';
        node.style.right = '40px';
        node.style.transform = 'rotateX(180deg) rotateY(180deg)';
        node.style.MozTransform = 'rotateX(180deg) rotateY(180deg)';
    }

    //Chrome

    function _applyStyleWebkit(node) {
        node.style.bottom = '0px';
        node.style.left = '20px';
    }

    function _setStyleType(node) {

        var versionNum = parseInt(browserVersion, 10) || 0;
        _applyStyleModern(node); //add our basic styles then do vendor prefixes

        if (browser === 'msie') {
            if (versionNum === 8) {
                _applyStyleIE8(node);
            } else if ((versionNum === 9) || (versionNum === 10)) {
                _applyStyleMs(node);
            }
        } else if (browser === 'chrome') {
            _applyStyleWebkit(node);
        } else if (browser === 'firefox') {
            //New download manager with arrow introducted in version 20
            if (versionNum >= 20) {
                _applyStyleMoz(node);
            }
        }
        //TODO windows safari download arrow?
    }

    //Create div container for arrow and set its id to the browser type

    function _buildArrow() {
        var div = document.createElement('div');
        div.setAttribute('id', 'arrow-' + browser);
        arrowNode = div; //only used in resizing ie9
        return div;
    }

    //Add node to the page, in this case our arrow

    function _injectNode(node) {
        document.body.appendChild(node);
    }

    function _isExist() {
        return !!(document.getElementById('arrow-' + browser));
    }

    //should only be run once per Arrow instance
    //in the future would be nice to manage multiple arrows

    function _initArrow() {
        var arrow = _buildArrow();
        _setStyleType(arrow);
        _calculateArrowPosition();
        _injectNode(arrow);
    }

    /**
     * Public API
     */

    function show() {
        if (_isExist()) {
            _increaseOpacity();
        } else {
            throw 'Invalid usage: arrow does not exist';
        }
    }

    function hide() {
        if (_isExist()) {
            _decreaseOpacity();
        } else {
            throw 'Invalid usage: There are no arrows on the page.';
        }
    }

    _initArrow(); //fired when library loads

    window.addEventListener('resize', _calculateArrowPosition);

    //Make sure IE9 arrow is in the right place
    function _calculateArrowPosition () {
        if (typeof( window.innerWidth ) === 'number') {
            //Non-IE
            visibleWidth = window.innerWidth;
            visibleHeight = window.innerHeight;
        } else if (document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight )) {
            //IE 6+ in 'standards compliant mode'
            visibleWidth = document.documentElement.clientWidth;
            visibleHeight = document.documentElement.clientHeight;
        }

        if ((browser === 'msie') && (browserVersion === '9.0')) {
            if (visibleWidth < 1005) {
                arrowNode.style.bottom = '85px';
            } else if (visibleWidth > 1006) {
                arrowNode.style.bottom = '50px';
            }
        }
    }

    /**
     * Expose Public Data and Functions
     */

    Arrow._version = version;
    Arrow._browser = browser;
    Arrow._browserVersion = browserVersion;
    Arrow.show = show;
    Arrow.hide = hide;

    return Arrow;

})(window, window.document);
<commit_msg>need to add filters, not just modify them<commit_after>/**
 * ProjectName: ArrowJS
 * Name: Petar Bojinov
 * Email: petar@conduit.com
 * Github: https: //github.com/chrisenytc/generator-library
 */

window.Arrow = (function (window, document, undefined) {

    "use strict";

    var version = '0.1.0',
        Arrow = {},
        arrowNode,
        browser = '',
        browserVersion = '',
        visibleHeight = 0,
        visibleWidth = 0;

    //http://storage.conduit.com/arrowjs/arrow_orange.png
    //http://storage.conduit.com/arrowjs/arrow_orange.gif
    //http://storage.conduit.com/arrowjs/arrow_green.png
    //http://storage.conduit.com/arrowjs/arrow_green.gif

    //determine browser type and browser version
    (function () {
        var N = navigator.appName, ua = navigator.userAgent, tem;
        var M = ua.match(/(opera|chrome|safari|firefox|msie)\/?\s*(\.?\d+(\.\d+)*)/i);
        if (M && (tem = ua.match(/version\/([\.\d]+)/i)) != null) M[2] = tem[1];
        M = M ? [M[1], M[2]] : [N, navigator.appVersion, '-?'];
        browser = M[0].toLowerCase();
        browserVersion = M[1];
    })();

    /**
     * Apply modern browser style then browser specific styles to arrow
     */

    function _increaseOpacity() {
        var arrow = document.getElementById('arrow-' + browser);
        arrow.style.display = 'block';
        var i = 0.0,
            ieI = 0; //need to use whole numbers for IE filter
        var x = setInterval(function () {
            i += 0.1;
            ieI += 10;
            if ((browser === 'msie') && (browserVersion <= 8)) {
                if (arrow.filters) {
                    arrow.filters.item("DXImageTransform.Microsoft.Alpha").opacity = ieI;
                }
            } else {
                arrow.style.opacity = i;
            }
        }, 50);
        setTimeout(function () {
            clearInterval(x);
        }, 600);
        // TODO use requestAnimationFrame instead
        // see http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
    }

    function _decreaseOpacity() {
        var arrow = document.getElementById('arrow-' + browser);
        var i = 1.0,
            ieI = 100; //need to use whole numbers for IE filter
        var x = setInterval(function () {
            i -= 0.1;
            ieI -= 10;
            if ((browser === 'msie') && (browserVersion <= 8)) {
                if (arrow.filters) {
                    arrow.filters.item("DXImageTransform.Microsoft.Alpha").opacity = ieI;
                }
            } else {
                arrow.style.opacity = i;
            }
        }, 50);
        setTimeout(function () {
            clearInterval(x);
            arrow.style.display = 'none';
        }, 600);
        // TODO use requestAnimationFrame instead
        // see http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
    }

    /*
     node.style.webkitTransform = "";
     node.style.MozTransform = "";
     node.style.msTransform = "";
     node.style.OTransform = "";
     node.style.transform = "";
     */

    //Always apply all modern browsers first, then vendor specific

    function _applyStyleModern(node) {
        node.style.position = 'absolute';
        //TODO
        //top, left, right, bottom position?
        node.style.zIndex = 999;
        node.style.display = 'none';
        node.style.height = '309px';
        node.style.width = '186px';
        node.style.opacity = 0;
        node.style.backgroundImage = 'url(http://storage.conduit.com/arrowjs/arrow_orange.png)';
        node.style.backgroundRepeat = 'no-repeat';
        node.style.backgroundPositionX = '0';
        node.style.backgroundPositionY = '0';
    }

    //IE 8 styles

    function _applyStyleIE8(node) {
        node.style.bottom = '0px';
        node.style.left = '20px';

        //we never set the filter in the first place...so IE cannot find it
        node.filters.item('DXImageTransform.Microsoft.AlphaImageLoader').src = 'http://storage.conduit.com/arrowjs/arrow_orange.png';
        node.filters.item('DXImageTransform.Microsoft.AlphaImageLoader').sizingMethod = 'scale';
        node.filters.item("DXImageTransform.Microsoft.Alpha").opacity = 0;
        node.filters.item("DXImageTransform.Microsoft.Matrix").M11 = 1;
        node.filters.item("DXImageTransform.Microsoft.Matrix").M12 = 1.2246063538223773e-16;
        node.filters.item("DXImageTransform.Microsoft.Matrix").M21 = -1.2246063538223773e-16;
        node.filters.item("DXImageTransform.Microsoft.Matrix").M22 = -1;
        node.filters.item("DXImageTransform.Microsoft.Matrix").SizingMethod = 'auto expand';
    }

    //IE 9 styles

    function _applyStyleMs(node) {
        node.style.bottom = '50px';
        node.style.left = '67%';
    }

    //Firefox 20+

    function _applyStyleMoz(node) {
        node.style.top = '0px';
        node.style.right = '40px';
        node.style.transform = 'rotateX(180deg) rotateY(180deg)';
        node.style.MozTransform = 'rotateX(180deg) rotateY(180deg)';
    }

    //Chrome

    function _applyStyleWebkit(node) {
        node.style.bottom = '0px';
        node.style.left = '20px';
    }

    function _setStyleType(node) {

        var versionNum = parseInt(browserVersion, 10) || 0;
        _applyStyleModern(node); //add our basic styles then do vendor prefixes

        if (browser === 'msie') {
            if (versionNum === 8) {
                _applyStyleIE8(node);
            } else if ((versionNum === 9) || (versionNum === 10)) {
                _applyStyleMs(node);
            }
        } else if (browser === 'chrome') {
            _applyStyleWebkit(node);
        } else if (browser === 'firefox') {
            //New download manager with arrow introducted in version 20
            if (versionNum >= 20) {
                _applyStyleMoz(node);
            }
        }
        //TODO windows safari download arrow?
    }

    //Create div container for arrow and set its id to the browser type

    function _buildArrow() {
        var div = document.createElement('div');
        div.setAttribute('id', 'arrow-' + browser);
        arrowNode = div; //only used in resizing ie9
        return div;
    }

    //Add node to the page, in this case our arrow

    function _injectNode(node) {
        document.body.appendChild(node);
    }

    function _isExist() {
        return !!(document.getElementById('arrow-' + browser));
    }

    //should only be run once per Arrow instance
    //in the future would be nice to manage multiple arrows

    function _initArrow() {
        var arrow = _buildArrow();
        _setStyleType(arrow);
        _calculateArrowPosition();
        _injectNode(arrow);
    }

    /**
     * Public API
     */

    function show() {
        if (_isExist()) {
            _increaseOpacity();
        } else {
            throw 'Invalid usage: arrow does not exist';
        }
    }

    function hide() {
        if (_isExist()) {
            _decreaseOpacity();
        } else {
            throw 'Invalid usage: There are no arrows on the page.';
        }
    }

    _initArrow(); //fired when library loads

    window.addEventListener('resize', _calculateArrowPosition);

    //Make sure IE9 arrow is in the right place
    function _calculateArrowPosition () {
        if (typeof( window.innerWidth ) === 'number') {
            //Non-IE
            visibleWidth = window.innerWidth;
            visibleHeight = window.innerHeight;
        } else if (document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight )) {
            //IE 6+ in 'standards compliant mode'
            visibleWidth = document.documentElement.clientWidth;
            visibleHeight = document.documentElement.clientHeight;
        }

        if ((browser === 'msie') && (browserVersion === '9.0')) {
            if (visibleWidth < 1005) {
                arrowNode.style.bottom = '85px';
            } else if (visibleWidth > 1006) {
                arrowNode.style.bottom = '50px';
            }
        }
    }

    /**
     * Expose Public Data and Functions
     */

    Arrow._version = version;
    Arrow._browser = browser;
    Arrow._browserVersion = browserVersion;
    Arrow.show = show;
    Arrow.hide = hide;

    return Arrow;

})(window, window.document);
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2019 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.comics.v2',
  name: 'DAOSummaryView',
  extends: 'foam.u2.View',

  documentation: `
    A configurable summary view for a specific instance
  `,

  topics: [
    'finished',
    'throwError'
  ],

  axioms: [
    foam.pattern.Faceted.create()
  ],

  css:`
    ^ {
      padding: 32px
    }

    ^ .foam-u2-ActionView-back {
      display: flex;
      align-items: center;
      width: 30%;
    }

    ^account-name {
      font-size: 36px;
      font-weight: 600;
    }

    ^actions-header .foam-u2-ActionView {
      margin-right: 24px;
      line-height: 1.5
    }

    ^view-container {
      margin: auto;
    }
  `,

  requires: [
    'foam.u2.layout.Cols',
    'foam.u2.layout.Rows',
    'foam.u2.ControllerMode',
    'foam.u2.dialog.NotificationMessage',
    'foam.u2.dialog.Popup',
  ],

  imports: [
    'stack'
  ],

  exports: [
    'controllerMode'
  ],

  properties: [
    {
      class: 'FObjectProperty',
      name: 'data'
    },
    {
      class: 'FObjectProperty',
      of: 'foam.comics.v2.DAOControllerConfig',
      name: 'config'
    },
    {
      name: 'controllerMode',
      factory: function() {
        return this.ControllerMode.VIEW;
      }
    },
    {
      name: 'primary',
      expression: function(config$of) {
        var allActions = config$of.getAxiomsByClass(foam.core.Action);
        var defaultAction = allActions.filter((a) => a.isDefault);
        return defaultAction.length >= 1 ? defaultAction[0] : allActions[0];
      }
    },
    {
      class: 'foam.u2.ViewSpecWithJava',
      name: 'viewView',
      expression: function() {
        return foam.u2.detail.SectionedDetailView;
      }
    }
  ],

  actions: [
    {
      name: 'edit',
      isAvailable: function(config$editEnabled) {
        return config$editEnabled;
      },
      code: function() {
        if ( ! this.stack ) return;
        this.stack.push({
          class: 'foam.comics.v2.DAOUpdateView',
          data: this.data,
          config: this.config,
          of: this.config.of
        });
      }
    },
    {
      name: 'delete',
      isAvailable: function(config$deleteEnabled) {
        return config$deleteEnabled;
      },
      code: function() {
        this.add(this.Popup.create().tag({
          class: 'foam.u2.DeleteModal',
          dao: this.config.dao,
          onDelete: () => {
            this.finished.pub();
            this.stack.back();
          },
          data: this.data
        }));
      }
    }
  ],

  methods: [
    function initE() {
      var self = this;
      this.SUPER();
      this
        .addClass(this.myClass())
        .add(self.slot(function(data, data$id, config$CRUDActionsAuth$update, config$CRUDActionsAuth$delete,config$browseTitle, config$viewBorder, viewView) {

          // iterate through permissions and replace % with data$id
          var editAction = self.EDIT;
          var deleteAction = self.DELETE;

          if ( config$CRUDActionsAuth$update ) {
            var editArray = config$CRUDActionsAuth$update;

            editArray = editArray.map(permission => {
              return permission.replace("%", data$id);
            });

            editAction = self.EDIT.clone().copyFrom({
              availablePermissions: self.EDIT.availablePermissions.concat(editArray)
            });
          }

          if ( config$CRUDActionsAuth$delete ) {
            var deleteArray = config$CRUDActionsAuth$delete;

            deleteArray = deleteArray.map(permission => {
              return permission.replace("%", data$id);
            });

            deleteAction = self.DELETE.copyFrom({
              availablePermissions: self.DELETE.availablePermissions.concat(deleteArray)
            });
          }

          return self.E()
            .start(self.Rows)
              .start(self.Rows)
                // we will handle this in the StackView instead
                .startContext({ data: self.stack })
                  .tag(self.stack.BACK, {
                    buttonStyle: foam.u2.ButtonStyle.TERTIARY,
                    icon: 'images/back-icon.svg',
                    label: `All ${config$browseTitle}`
                  })
                .endContext()
                .start(self.Cols).style({ 'align-items': 'center' })
                  .start()
                    .add(data.toSummary())
                    .addClass(this.myClass('account-name'))
                  .end()
                  .startContext({ data }).add(self.primary).endContext()
                .end()
              .end()

              .start(self.Cols)
                .start(self.Cols).addClass(this.myClass('actions-header'))
                  .startContext({ data: self })
                    .tag(editAction, {
                      buttonStyle: foam.u2.ButtonStyle.TERTIARY,
                      icon: 'images/edit-icon.svg'
                    })
                    .tag(deleteAction, {
                      buttonStyle: foam.u2.ButtonStyle.TERTIARY,
                      icon: 'images/delete-icon.svg'
                    })
                  .endContext()
                .end()
              .end()

              .start(config$viewBorder)
                .start(viewView, { data }).addClass(this.myClass('view-container')).end()
              .end()
            .end();
        }));
    }
  ]
});
<commit_msg>Update src/foam/comics/v2/DAOSummaryView.js<commit_after>/**
 * @license
 * Copyright 2019 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.comics.v2',
  name: 'DAOSummaryView',
  extends: 'foam.u2.View',

  documentation: `
    A configurable summary view for a specific instance
  `,

  topics: [
    'finished',
    'throwError'
  ],

  axioms: [
    foam.pattern.Faceted.create()
  ],

  css:`
    ^ {
      padding: 32px
    }

    ^ .foam-u2-ActionView-back {
      display: flex;
      align-items: center;
      width: 30%;
    }

    ^account-name {
      font-size: 36px;
      font-weight: 600;
    }

    ^actions-header .foam-u2-ActionView {
      margin-right: 24px;
      line-height: 1.5
    }

    ^view-container {
      margin: auto;
    }
  `,

  requires: [
    'foam.u2.layout.Cols',
    'foam.u2.layout.Rows',
    'foam.u2.ControllerMode',
    'foam.u2.dialog.NotificationMessage',
    'foam.u2.dialog.Popup',
  ],

  imports: [
    'stack'
  ],

  exports: [
    'controllerMode'
  ],

  properties: [
    {
      class: 'FObjectProperty',
      name: 'data'
    },
    {
      class: 'FObjectProperty',
      of: 'foam.comics.v2.DAOControllerConfig',
      name: 'config'
    },
    {
      name: 'controllerMode',
      factory: function() {
        return this.ControllerMode.VIEW;
      }
    },
    {
      name: 'primary',
      expression: function(config$of) {
        var allActions = config$of.getAxiomsByClass(foam.core.Action);
        var defaultAction = allActions.filter((a) => a.isDefault);
        return defaultAction.length >= 1 ? defaultAction[0] : allActions[0];
      }
    },
    {
      class: 'foam.u2.ViewSpecWithJava',
      name: 'viewView',
      expression: function() {
        return foam.u2.detail.SectionedDetailView;
      }
    }
  ],

  actions: [
    {
      name: 'edit',
      isAvailable: function(config$editEnabled) {
        return config$editEnabled;
      },
      code: function() {
        if ( ! this.stack ) return;
        this.stack.push({
          class: 'foam.comics.v2.DAOUpdateView',
          data: this.data,
          config: this.config,
          of: this.config.of
        });
      }
    },
    {
      name: 'delete',
      isAvailable: function(config$deleteEnabled) {
        return config$deleteEnabled;
      },
      code: function() {
        this.add(this.Popup.create().tag({
          class: 'foam.u2.DeleteModal',
          dao: this.config.dao,
          onDelete: () => {
            this.finished.pub();
            this.stack.back();
          },
          data: this.data
        }));
      }
    }
  ],

  methods: [
    function initE() {
      var self = this;
      this.SUPER();
      this
        .addClass(this.myClass())
        .add(self.slot(function(data, data$id, config$CRUDActionsAuth$update, config$CRUDActionsAuth$delete,config$browseTitle, config$viewBorder, viewView) {

          // iterate through permissions and replace % with data$id
          var editAction = self.EDIT;
          var deleteAction = self.DELETE;

          if ( config$CRUDActionsAuth$update ) {
            var editArray = config$CRUDActionsAuth$update;

            editArray = editArray.map(permission => {
              return permission.replace("%", data$id);
            });

            editAction = self.EDIT.clone().copyFrom({
              availablePermissions: self.EDIT.availablePermissions.concat(editArray)
            });
          }

          if ( config$CRUDActionsAuth$delete ) {
            var deleteArray = config$CRUDActionsAuth$delete;

            deleteArray = deleteArray.map(permission => {
              return permission.replace("%", data$id);
            });

            deleteAction = self.DELETE.clone().copyFrom({
              availablePermissions: self.DELETE.availablePermissions.concat(deleteArray)
            });
          }

          return self.E()
            .start(self.Rows)
              .start(self.Rows)
                // we will handle this in the StackView instead
                .startContext({ data: self.stack })
                  .tag(self.stack.BACK, {
                    buttonStyle: foam.u2.ButtonStyle.TERTIARY,
                    icon: 'images/back-icon.svg',
                    label: `All ${config$browseTitle}`
                  })
                .endContext()
                .start(self.Cols).style({ 'align-items': 'center' })
                  .start()
                    .add(data.toSummary())
                    .addClass(this.myClass('account-name'))
                  .end()
                  .startContext({ data }).add(self.primary).endContext()
                .end()
              .end()

              .start(self.Cols)
                .start(self.Cols).addClass(this.myClass('actions-header'))
                  .startContext({ data: self })
                    .tag(editAction, {
                      buttonStyle: foam.u2.ButtonStyle.TERTIARY,
                      icon: 'images/edit-icon.svg'
                    })
                    .tag(deleteAction, {
                      buttonStyle: foam.u2.ButtonStyle.TERTIARY,
                      icon: 'images/delete-icon.svg'
                    })
                  .endContext()
                .end()
              .end()

              .start(config$viewBorder)
                .start(viewView, { data }).addClass(this.myClass('view-container')).end()
              .end()
            .end();
        }));
    }
  ]
});
<|endoftext|>