<commit_before>// Licensed under the Apache License, Version 2.0 (the “License”); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an “AS IS” BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.

// http://eslint.org/docs/rules/#best-practices
module.exports = {
  rules: {
    // This rule enforces a style where it requires to have a getter for every
    // property which has a setter defined.
    'accessor-pairs': 'off',

    // This rule enforces usage of return statement in callbacks of array’s
    // methods.
    'array-callback-return': 'warn',

    // This rule aims to reduce the usage of variables outside of their binding
    // context and emulate traditional block scope from other languages. This is
    // to help newcomers to the language avoid difficult bugs with variable
    // hoisting.
    'block-scoped-var': 'error',

    // If a class method does not use this, it can safely be made a static
    // function.
    'class-methods-use-this': 'warn',

    // This rule is aimed at reducing code complexity by capping the amount of
    // cyclomatic complexity allowed in a program
    complexity: ['warn', 10],

    // This rule requires return statements to either always or never specify
    // values. It ignores function definitions where the name begins with an
    // uppercase letter because constructors, when invoked with the new
    // operator, return the instantiated object implicitly if they do not return
    // another object explicitly.
    'consistent-return': 'error',

    // This rule is aimed at preventing bugs and increasing code clarity by
    // ensuring that block statements are wrapped in curly braces. It will warn
    // when it encounters blocks that omit curly braces.
    curly: [
      'error',
      // Forces brace-less “if”, “else if”, “else”, “for”, “while” or “do” if
      // their body contains only one single-line statement. And forces braces
      // in all other cases.
      'multi-or-nest',
    ],

    // This rule aims to require default case in switch statements.
    'default-case': 'off',

    // This rule aims to enforce newline consistency in member expressions.
    'dot-location': ['error', 'property'],

    // This rule is aimed at maintaining code consistency and improving code
    // readability by encouraging use of the dot notation style whenever
    // possible.
    'dot-notation': 'error',

    // This rule is aimed at eliminating the type-unsafe equality operators.
    eqeqeq: ['error', 'always'],

    // This rule is aimed at preventing unexpected behavior that could arise
    // from using a “for in” loop without filtering the results in the loop. As
    // such, it will warn when “for in” loops do not filter their results with
    // an if statement.
    'guard-for-in': 'off',

    // This rule is aimed at catching debugging code that should be removed and
    // popup UI elements that should be replaced with less obtrusive custom UIs.
    // As such, it will warn when it encounters “alert”, “prompt”, and “confirm”
    // function calls which are not shadowed.
    'no-alert': 'warn',

    // This rule is aimed at discouraging the use of deprecated and sub-optimal
    // code, disallowing the use of arguments.caller and arguments.callee. As
    // such, it will warn when “arguments.caller” and “arguments.callee” are
    // used.
    'no-caller': 'error',

    // This rule aims to prevent access to uninitialized lexical bindings as
    // well as accessing hoisted functions across case clauses.
    'no-case-declarations': 'error',

    // This is used to disambiguate the division operator to not confuse users.
    // Note: https://github.com/borela/naomi can color the regex literals and
    // make it clear it is not a division operator.
    'no-div-regex': 'off',

    // This rule is aimed at highlighting an unnecessary block of code following
    // an if containing a return statement. As such, it will warn when it
    // encounters an “else” following a chain of “if”s, all of them containing a
    // return statement.
    'no-else-return': 'error',

    // This rule is aimed at eliminating empty functions. A function will not be
    // considered a problem if it contains a comment.
    'no-empty-function': 'error',

    // This rule aims to flag any empty patterns in destructured objects and
    // arrays, and as such, will report a problem whenever one is encountered.
    'no-empty-pattern': 'error',

    // This rule aims reduce potential bug and unwanted behavior by ensuring
    // that comparisons to “null” only match “null”, and not also “undefined”.
    // As such it will flag comparisons when using == and !=.
    'no-eq-null': 'error',

    // This rule is aimed at preventing potentially dangerous, unnecessary, and
    // slow code by disallowing the use of the “eval” function.
    'no-eval': 'warn',

    // Disallows directly modifying the prototype of builtin objects.
    'no-extend-native': 'warn',

    // This rule is aimed at avoiding the unnecessary use of “bind()”.
    'no-extra-bind': 'error',

    // This rule is aimed at eliminating unnecessary labels.
    'no-extra-label': 'error',

    // This rule is aimed at eliminating unintentional fallthrough of one case
    // to the other. Note: Some heretics might try to convince you that
    // fallthrough is bad but don’t listen to them, it is awesome!
    'no-fallthrough': 'off',

    // This rule is aimed at eliminating floating decimal points and will warn
    // whenever a numeric value has a decimal point but is missing a number
    // either before or after it. Note: The ambiguity can be solved by using a
    // good syntax highlighter and color scheme: https://github.com/borela/naomi
    'no-floating-decimal': 'off',

    // This rule disallows modifications to read-only global variables.
    'no-global-assign': 'error',

    // This rule is aimed to flag shorter notations for the type conversion,
    // then suggest a more self-explanatory notation.
    'no-implicit-coercion': 'off',

    // This rule disallows var and named function declarations at the top-level
    // script scope. This does not apply to ES and CommonJS modules since they
    // have a module scope.
    'no-implicit-globals': 'off',

    // This rule aims to eliminate implied “eval()” through the use of
    // “setTimeout()”, “setInterval()” or “execScript()”. As such, it will warn
    // when either function is used with a string as the first argument.
    'no-implied-eval': 'warn',

    // This rule aims to flag usage of this keywords outside of classes or
    // class-like objects.
    // 'no-invalid-this': 'off',
    'babel/no-invalid-this': 'off',

    // This rule is aimed at preventing errors that may arise from using the
    // __iterator__ property, which is not implemented in several browsers. As
    // such, it will warn whenever it encounters the __iterator__ property.
    'no-iterator': 'error',

    // This rule aims to eliminate the use of labeled statements in JavaScript.
    // It will warn whenever a labeled statement is encountered and whenever
    // break or continue are used with a label.
    'no-labels': 'off',

    // This rule aims to eliminate unnecessary and potentially confusing blocks
    // at the top level of a script or within other blocks.
    'no-lone-blocks': 'error',

    // This error is raised to highlight a piece of code that may not work as
    // you expect it to and could also indicate a misunderstanding of how the
    // language works. Your code may run without any problems if you do not fix
    // this error, but in some situations it could behave unexpectedly.
    'no-loop-func': 'off',

    // This rule aims to make code more readable and refactoring easier by
    // ensuring that special numbers are declared as constants to make their
    // meaning explicit.
    'no-magic-numbers': 'off',

    // This rule aims to disallow multiple whitespace around logical
    // expressions, conditional expressions, declarations, array elements,
    // object properties, sequences and function parameters.
    'no-multi-spaces': 'error',

    // This rule is aimed at preventing the use of multiline strings.
    'no-multi-str': 'off',

    // This rule is aimed at maintaining consistency and convention by
    // disallowing constructor calls using the new keyword that do not assign
    // the resulting object to a variable.
    'no-new': 'error',

    // This error is raised to highlight the use of a bad practice. By passing a
    // string to the Function constructor, you are requiring the engine to parse
    // that string much in the way it has to when you call the eval function.
    'no-new-func': 'off',

    // This rule aims to eliminate the use of “String”, “Number”, and “Boolean”
    // with the “new” operator. As such, it warns whenever it sees “new String”,
    // “new Number” or “new Boolean”.
    'no-new-wrappers': 'error',

    // Because the leading zero which identifies an octal literal has been a
    // source of confusion and error in JavaScript code, ECMAScript 5 deprecates
    // the use of octal numeric literals. In ES6 a new less confusing prefix
    // “0o” was added.
    'no-octal': 'error',

    // This rule disallows octal escape sequences in string literals.
    'no-octal-escape': 'error',

    // This rule aims to prevent unintended behavior caused by modification or
    // reassignment of function parameters.
    'no-param-reassign': 'off',

    // This rule aims to disallow the use of the __proto__ property as it has
    // been deprecated as of ECMAScript 3.1.
    'no-proto': 'error',

    // This rule is aimed at eliminating variables that have multiple
    // declarations in the same scope.
    'no-redeclare': 'error',

    // This rule looks for accessing a given property key on a given object
    // name, either when reading the property’s value or invoking it as a
    // function. You may specify an optional message to indicate an alternative
    // API or a reason for the restriction.
    'no-restricted-properties': 'off',

    // This rule aims to eliminate assignments from return statements. As such,
    // it will warn whenever an assignment is found as part of return.
    'no-return-assign': 'off',

    // This rule aims to prevent a likely common performance hazard due to a
    // lack of understanding of the semantics of async function.
    'no-return-await': 'error',

    // Using “javascript:” URLs is considered by some as a form of eval
    'no-script-url': 'warn',

    // This rule is aimed at eliminating self assignments.
    'no-self-assign': 'error',

    // This error is raised to highlight a potentially confusing and potentially
    // pointless piece of code.
    'no-self-compare': 'error',

    // This rule forbids the use of the comma operator, with the following
    // exceptions:
    //   * In the initialization or update portions of a for statement.
    //   * If the expression sequence is explicitly wrapped in parentheses.
    'no-sequences': 'off',

    // This rule is aimed at maintaining consistency when throwing exception by
    // disallowing to throw literals and other expressions which cannot possibly
    // be an Error object.
    'no-throw-literal': 'off',

    // This rule finds references which are inside of loop conditions, then
    // checks the variables of those references are modified in the loop.
    'no-unmodified-loop-condition': 'off',

    // This rule aims to eliminate unused expressions which have no effect on
    // the state of the program.
    // 'no-unused-expressions': 'error',
    'babel/no-unused-expressions': 'error',

    // This rule is aimed at eliminating unused labels.
    'no-unused-labels': 'error',

    // This rule is aimed to flag usage of “Function.prototype.call()” and
    // “Function.prototype.apply()” that can be replaced with the normal
    // function invocation.
    'no-useless-call': 'error',

    // This rule aims to flag the concatenation of 2 literals when they could be
    // combined into a single literal.
    'no-useless-concat': 'off',

    // This rule flags escapes that can be safely removed without changing
    // behavior.
    'no-useless-escape': 'error',

    // This rule aims to report redundant return statements.
    'no-useless-return': 'error',

    // This rule aims to eliminate use of void operator.
    'no-void': 'off',

    // This rule reports comments that include any of the predefined terms
    // specified in its configuration.
    'no-warning-comments': [
      'warn', {
        location: 'start',
        terms: ['FIXME', 'TODO', 'WARNING', 'XXX'],
      },
    ],

    // This rule disallows “with” statements. This statement is forbidden in
    // ECMAScript 5 strict mode.
    'no-with': 'error',

    // This rule aims to ensure that Promises are only rejected with Error
    // objects.
    'prefer-promise-reject-errors': ['error', {allowEmptyReject: true}],

    // This rule is aimed at preventing the unintended conversion of a string to
    // a number of a different base than the intended.
    radix: 'error',

    // This rule warns async functions which have no await expression.
    'require-await': 'off',

    // This rule aims to keep all variable declarations in the leading series of
    // statements. Allowing multiple declarations helps promote maintainability
    // and is thus allowed.
    'vars-on-top': 'error',

    // This rule requires all immediately-invoked function expressions to be
    // wrapped in parentheses.
    'wrap-iife': 'error',

    // This rule aims to enforce consistent style of conditions which compare a
    // variable to a literal value.
    yoda: ['error', 'never'],
  },
}
<commit_msg>Relax rules<commit_after>// Licensed under the Apache License, Version 2.0 (the “License”); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an “AS IS” BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.

// http://eslint.org/docs/rules/#best-practices
module.exports = {
  rules: {
    // This rule enforces a style where it requires to have a getter for every
    // property which has a setter defined.
    'accessor-pairs': 'off',

    // This rule enforces usage of return statement in callbacks of array’s
    // methods.
    'array-callback-return': 'warn',

    // This rule aims to reduce the usage of variables outside of their binding
    // context and emulate traditional block scope from other languages. This is
    // to help newcomers to the language avoid difficult bugs with variable
    // hoisting.
    'block-scoped-var': 'error',

    // If a class method does not use this, it can safely be made a static
    // function.
    'class-methods-use-this': 'warn',

    // This rule is aimed at reducing code complexity by capping the amount of
    // cyclomatic complexity allowed in a program
    complexity: ['warn', 10],

    // This rule requires return statements to either always or never specify
    // values. It ignores function definitions where the name begins with an
    // uppercase letter because constructors, when invoked with the new
    // operator, return the instantiated object implicitly if they do not return
    // another object explicitly.
    'consistent-return': 'error',

    // This rule is aimed at preventing bugs and increasing code clarity by
    // ensuring that block statements are wrapped in curly braces. It will warn
    // when it encounters blocks that omit curly braces.
    curly: [
      'error',
      // Forces brace-less “if”, “else if”, “else”, “for”, “while” or “do” if
      // their body contains only one single-line statement. And forces braces
      // in all other cases.
      'multi-or-nest',
    ],

    // This rule aims to require default case in switch statements.
    'default-case': 'off',

    // This rule aims to enforce newline consistency in member expressions.
    'dot-location': ['error', 'property'],

    // This rule is aimed at maintaining code consistency and improving code
    // readability by encouraging use of the dot notation style whenever
    // possible.
    'dot-notation': 'error',

    // This rule is aimed at eliminating the type-unsafe equality operators.
    eqeqeq: ['error', 'always'],

    // This rule is aimed at preventing unexpected behavior that could arise
    // from using a “for in” loop without filtering the results in the loop. As
    // such, it will warn when “for in” loops do not filter their results with
    // an if statement.
    'guard-for-in': 'off',

    // This rule is aimed at catching debugging code that should be removed and
    // popup UI elements that should be replaced with less obtrusive custom UIs.
    // As such, it will warn when it encounters “alert”, “prompt”, and “confirm”
    // function calls which are not shadowed.
    'no-alert': 'warn',

    // This rule is aimed at discouraging the use of deprecated and sub-optimal
    // code, disallowing the use of arguments.caller and arguments.callee. As
    // such, it will warn when “arguments.caller” and “arguments.callee” are
    // used.
    'no-caller': 'error',

    // This rule aims to prevent access to uninitialized lexical bindings as
    // well as accessing hoisted functions across case clauses.
    'no-case-declarations': 'error',

    // This is used to disambiguate the division operator to not confuse users.
    // Note: https://github.com/borela/naomi can color the regex literals and
    // make it clear it is not a division operator.
    'no-div-regex': 'off',

    // This rule is aimed at highlighting an unnecessary block of code following
    // an if containing a return statement. As such, it will warn when it
    // encounters an “else” following a chain of “if”s, all of them containing a
    // return statement.
    'no-else-return': 'error',

    // This rule is aimed at eliminating empty functions. A function will not be
    // considered a problem if it contains a comment.
    'no-empty-function': 'error',

    // This rule aims to flag any empty patterns in destructured objects and
    // arrays, and as such, will report a problem whenever one is encountered.
    'no-empty-pattern': 'error',

    // This rule aims reduce potential bug and unwanted behavior by ensuring
    // that comparisons to “null” only match “null”, and not also “undefined”.
    // As such it will flag comparisons when using == and !=.
    'no-eq-null': 'error',

    // This rule is aimed at preventing potentially dangerous, unnecessary, and
    // slow code by disallowing the use of the “eval” function.
    'no-eval': 'warn',

    // Disallows directly modifying the prototype of builtin objects.
    'no-extend-native': 'warn',

    // This rule is aimed at avoiding the unnecessary use of “bind()”.
    'no-extra-bind': 'error',

    // This rule is aimed at eliminating unnecessary labels.
    'no-extra-label': 'error',

    // This rule is aimed at eliminating unintentional fallthrough of one case
    // to the other. Note: Some heretics might try to convince you that
    // fallthrough is bad but don’t listen to them, it is awesome!
    'no-fallthrough': 'off',

    // This rule is aimed at eliminating floating decimal points and will warn
    // whenever a numeric value has a decimal point but is missing a number
    // either before or after it. Note: The ambiguity can be solved by using a
    // good syntax highlighter and color scheme: https://github.com/borela/naomi
    'no-floating-decimal': 'off',

    // This rule disallows modifications to read-only global variables.
    'no-global-assign': 'error',

    // This rule is aimed to flag shorter notations for the type conversion,
    // then suggest a more self-explanatory notation.
    'no-implicit-coercion': 'off',

    // This rule disallows var and named function declarations at the top-level
    // script scope. This does not apply to ES and CommonJS modules since they
    // have a module scope.
    'no-implicit-globals': 'off',

    // This rule aims to eliminate implied “eval()” through the use of
    // “setTimeout()”, “setInterval()” or “execScript()”. As such, it will warn
    // when either function is used with a string as the first argument.
    'no-implied-eval': 'warn',

    // This rule aims to flag usage of this keywords outside of classes or
    // class-like objects.
    // 'no-invalid-this': 'off',
    'babel/no-invalid-this': 'off',

    // This rule is aimed at preventing errors that may arise from using the
    // __iterator__ property, which is not implemented in several browsers. As
    // such, it will warn whenever it encounters the __iterator__ property.
    'no-iterator': 'error',

    // This rule aims to eliminate the use of labeled statements in JavaScript.
    // It will warn whenever a labeled statement is encountered and whenever
    // break or continue are used with a label.
    'no-labels': 'off',

    // This rule aims to eliminate unnecessary and potentially confusing blocks
    // at the top level of a script or within other blocks.
    'no-lone-blocks': 'error',

    // This error is raised to highlight a piece of code that may not work as
    // you expect it to and could also indicate a misunderstanding of how the
    // language works. Your code may run without any problems if you do not fix
    // this error, but in some situations it could behave unexpectedly.
    'no-loop-func': 'off',

    // This rule aims to make code more readable and refactoring easier by
    // ensuring that special numbers are declared as constants to make their
    // meaning explicit.
    'no-magic-numbers': 'off',

    // This rule aims to disallow multiple whitespace around logical
    // expressions, conditional expressions, declarations, array elements,
    // object properties, sequences and function parameters.
    'no-multi-spaces': 'error',

    // This rule is aimed at preventing the use of multiline strings.
    'no-multi-str': 'off',

    // This rule is aimed at maintaining consistency and convention by
    // disallowing constructor calls using the new keyword that do not assign
    // the resulting object to a variable.
    'no-new': 'error',

    // This error is raised to highlight the use of a bad practice. By passing a
    // string to the Function constructor, you are requiring the engine to parse
    // that string much in the way it has to when you call the eval function.
    'no-new-func': 'off',

    // This rule aims to eliminate the use of “String”, “Number”, and “Boolean”
    // with the “new” operator. As such, it warns whenever it sees “new String”,
    // “new Number” or “new Boolean”.
    'no-new-wrappers': 'error',

    // Because the leading zero which identifies an octal literal has been a
    // source of confusion and error in JavaScript code, ECMAScript 5 deprecates
    // the use of octal numeric literals. In ES6 a new less confusing prefix
    // “0o” was added.
    'no-octal': 'error',

    // This rule disallows octal escape sequences in string literals.
    'no-octal-escape': 'error',

    // This rule aims to prevent unintended behavior caused by modification or
    // reassignment of function parameters.
    'no-param-reassign': 'off',

    // This rule aims to disallow the use of the __proto__ property as it has
    // been deprecated as of ECMAScript 3.1.
    'no-proto': 'error',

    // This rule is aimed at eliminating variables that have multiple
    // declarations in the same scope.
    'no-redeclare': 'error',

    // This rule looks for accessing a given property key on a given object
    // name, either when reading the property’s value or invoking it as a
    // function. You may specify an optional message to indicate an alternative
    // API or a reason for the restriction.
    'no-restricted-properties': 'off',

    // This rule aims to eliminate assignments from return statements. As such,
    // it will warn whenever an assignment is found as part of return.
    'no-return-assign': 'off',

    // This rule aims to prevent a likely common performance hazard due to a
    // lack of understanding of the semantics of async function.
    'no-return-await': 'error',

    // Using “javascript:” URLs is considered by some as a form of eval
    'no-script-url': 'warn',

    // This rule is aimed at eliminating self assignments.
    'no-self-assign': 'error',

    // This error is raised to highlight a potentially confusing and potentially
    // pointless piece of code.
    'no-self-compare': 'error',

    // This rule forbids the use of the comma operator, with the following
    // exceptions:
    //   * In the initialization or update portions of a for statement.
    //   * If the expression sequence is explicitly wrapped in parentheses.
    'no-sequences': 'off',

    // This rule is aimed at maintaining consistency when throwing exception by
    // disallowing to throw literals and other expressions which cannot possibly
    // be an Error object.
    'no-throw-literal': 'off',

    // This rule finds references which are inside of loop conditions, then
    // checks the variables of those references are modified in the loop.
    'no-unmodified-loop-condition': 'off',

    // This rule aims to eliminate unused expressions which have no effect on
    // the state of the program.
    // 'no-unused-expressions': 'error',
    'babel/no-unused-expressions': 'warn',

    // This rule is aimed at eliminating unused labels.
    'no-unused-labels': 'error',

    // This rule is aimed to flag usage of “Function.prototype.call()” and
    // “Function.prototype.apply()” that can be replaced with the normal
    // function invocation.
    'no-useless-call': 'error',

    // This rule aims to flag the concatenation of 2 literals when they could be
    // combined into a single literal.
    'no-useless-concat': 'off',

    // This rule flags escapes that can be safely removed without changing
    // behavior.
    'no-useless-escape': 'error',

    // This rule aims to report redundant return statements.
    'no-useless-return': 'error',

    // This rule aims to eliminate use of void operator.
    'no-void': 'off',

    // This rule reports comments that include any of the predefined terms
    // specified in its configuration.
    'no-warning-comments': [
      'warn', {
        location: 'start',
        terms: ['FIXME', 'TODO', 'XXX'],
      },
    ],

    // This rule disallows “with” statements. This statement is forbidden in
    // ECMAScript 5 strict mode.
    'no-with': 'error',

    // This rule aims to ensure that Promises are only rejected with Error
    // objects.
    'prefer-promise-reject-errors': ['error', {allowEmptyReject: true}],

    // This rule is aimed at preventing the unintended conversion of a string to
    // a number of a different base than the intended.
    radix: 'error',

    // This rule warns async functions which have no await expression.
    'require-await': 'off',

    // This rule aims to keep all variable declarations in the leading series of
    // statements. Allowing multiple declarations helps promote maintainability
    // and is thus allowed.
    'vars-on-top': 'error',

    // This rule requires all immediately-invoked function expressions to be
    // wrapped in parentheses.
    'wrap-iife': 'error',

    // This rule aims to enforce consistent style of conditions which compare a
    // variable to a literal value.
    yoda: ['error', 'never'],
  },
}
<|endoftext|>


<commit_before>import React, {Component, PropTypes} from 'react';
import {connect} from 'react-redux';
import c from 'classnames';
import FlatButton from 'material-ui/FlatButton';
import Dialog from 'material-ui/Dialog';
import {Table, TableBody, TableHeader, TableHeaderColumn, TableRow, TableRowColumn} from 'material-ui/Table';
import {range} from 'ramda';
import {Link} from 'react-router';
import {get} from 'lodash';
import {hashHistory} from 'react-router';

import TopBar from './../../components/TopBar/TopBar';
import Breadcrumb from './../Breadcrumb/Breadcrumb';
import {setSnackBarParams} from './../../redux/modules/ui';
import {getFolder} from './../../redux/modules/folder';
import {listFolderEntries, setSelectedFolderEntryIds, clearSelectedFolderEntryIds,
  deleteEntries} from './../../redux/modules/entry';
import {setCachePageEntries} from './../../redux/modules/cache';

import injectF from './../../helpers/injectF';
import injectPush from './../../helpers/injectPush';
import resolve from './../../helpers/resolve';
import Pagination from './../../components/Pagination/Pagination';
import PageJumper from './../../components/PageJumper/PageJumper';
import SearchBar from './../../components/SearchBar/SearchBar';
import hasValue from './../../helpers/hasValue';
import getFontSize from './../../helpers/getFontSize';
import SEARCH_TYPES from './../../constants/searchTypes';

const styles = require('./PageEntries.scss');

const SORT_METHODS = ['', 'asc', 'desc'];
const SORT_CLASSNAME_MAP = {
  ['']: 'fa fa-sort',
  asc: 'fa fa-sort-asc',
  desc: 'fa fa-sort-desc'
};

// how to access params outside of react component
// https://github.com/reactjs/react-router-redux#how-do-i-access-router-state-in-a-container-component
@connect(({main, folder, entry, cache, routing}, {params}) => {
  const folderId = parseInt(params.id, 10);
  return {
    cache: get(cache.get('cachePageEntriesDataSet'), folderId, {}),
    perpage: entry.get('perpage'),
    folder: folder.get('folder'),
    importingFolderId: folder.get('importingFolderId'),
    interfaceFontSizeScalingFactor: main.get('interfaceFontSizeScalingFactor'),
    folderEntries: entry.get('folderEntries'),
    folderEntryCount: entry.get('folderEntryCount'),
    selectedFolderEntryMap: entry.get('selectedFolderEntryMap')
  };
}, {listFolderEntries, setSnackBarParams, setSelectedFolderEntryIds, setCachePageEntries,
  deleteEntries, clearSelectedFolderEntryIds})
@injectPush
@injectF
@resolve(({dispatch}, {params, page, perpage, cache}) => {

  const promises = [];
  const folderId = params.id;

  promises.push(dispatch(getFolder({id: folderId})));
  promises.push(dispatch(setSelectedFolderEntryIds([])));

  const searchParams = Object.assign({
    folderId,
    page,
    perpage,
    pageNumSortMethod: ''
  }, cache);
  promises.push(dispatch(listFolderEntries(searchParams)));

  return Promise.all(promises);
})
export default class PageEntries extends Component {

  static propTypes = {
    cache: PropTypes.object.isRequired,
    push: PropTypes.func.isRequired,
    perpage: PropTypes.number.isRequired,
    f: PropTypes.func.isRequired,
    folder: PropTypes.object.isRequired,
    folderEntries: PropTypes.array.isRequired,
    interfaceFontSizeScalingFactor: PropTypes.number.isRequired,
    folderEntryCount: PropTypes.number.isRequired,
    params: PropTypes.object.isRequired,
    listFolderEntries: PropTypes.func.isRequired,
    setCachePageEntries: PropTypes.func.isRequired,
    setSelectedFolderEntryIds: PropTypes.func.isRequired,
    clearSelectedFolderEntryIds: PropTypes.func.isRequired,
    selectedFolderEntryMap: PropTypes.object.isRequired,
    deleteEntries: PropTypes.func.isRequired,
    importingFolderId: PropTypes.number,
    setSnackBarParams: PropTypes.func.isRequired
  };

  constructor(props) {
    super(props);
    const [pageNumSortMethod] = SORT_METHODS;
    this.state = Object.assign({
      page: 1,
      tableKey: 0,
      searchKeyword: '',
      searchType: 'source-entry',
      pageNumSortMethod,
      isConfirmEntryDeletionOpen: false
    }, props.cache);

    if (! this.hasPageNumField()) {
      this.state.pageNumSortMethod = '';
      this.state.searchType = 'source-entry';
    }
  }

  hasPageNumField() {
    return this.props.folder.data.contentFields.includes('page-num');
  }

  componentWillUpdate(nextProps, nextState) {
    const {page, searchKeyword, searchType, pageNumSortMethod} = this.state;
    const {perpage, listFolderEntries, setCachePageEntries} = this.props;
    const nextPage = nextState.page;
    const nextPerpage = nextProps.perpage;
    const nextSearchKeyword = nextState.searchKeyword;
    const nextSearchType = nextState.searchType;
    const folderId = nextProps.folder.id;
    const nextPageNumSortMethod = nextState.pageNumSortMethod;

    if ((page !== nextPage) || (perpage !== nextPerpage) ||
        (searchKeyword !== nextSearchKeyword) || (searchType !== nextSearchType) || (pageNumSortMethod !== nextPageNumSortMethod)) {

      setCachePageEntries(folderId, {
        page: nextPage,
        searchKeyword: nextSearchKeyword,
        searchType: nextSearchType,
        pageNumSortMethod: nextPageNumSortMethod
      });

      listFolderEntries({
        folderId,
        page: nextPage,
        perpage: nextPerpage,
        searchKeyword: nextSearchKeyword.trim(),
        searchType: nextSearchType,
        pageNumSortMethod: nextPageNumSortMethod
      });
    }
  }

  handleRowSelection = (res) => {

    const {setSelectedFolderEntryIds, perpage, folderEntries} = this.props;

    let indices = res;

    if ('all' === res) {
      indices = range(0, perpage);
    }
    else if ('none' === res) {
      indices = [];
    }

    const ids = folderEntries.filter((row, index) => (-1 !== indices.indexOf(index)))
      .map((row) => row.id);

    setSelectedFolderEntryIds(ids);
  };

  goToSingleFolderEntryPage = (entryId) => {
    const {folder, push} = this.props;
    return () => push(`/folders/${folder.id}/entries/${entryId}`);
  };

  changePageNumberSortMethod = () => {
    const {pageNumSortMethod} = this.state;
    const index = SORT_METHODS.indexOf(pageNumSortMethod);
    const nextIndex = (index + 1) % 3;
    this.setState({pageNumSortMethod: SORT_METHODS[nextIndex]});
  };

  renderFolderEntries() {

    const fontSize = 20;
    const colStyle = {fontSize, width: 'initial'};
    const {tableKey, pageNumSortMethod} = this.state;
    const {folderEntries, f} = this.props;

    const isPageNumVisible = this.hasPageNumField();

    const tableRows = folderEntries.map((entry) => {
      return (
        <TableRow key={`table-row-${entry.id}`}>
          <TableRowColumn style={colStyle}>
            {entry.sourceEntry}
          </TableRowColumn>
          {isPageNumVisible && <TableRowColumn style={colStyle}>{get(entry, 'pageNum', '')}</TableRowColumn>}
        </TableRow>
      );
    });

    // https://github.com/callemall/material-ui/issues/6006#issuecomment-277669719
    const key = `table-workaround-${tableKey}`;

    return (

        <TableHeader>
          <TableRow>
            <TableHeaderColumn style={colStyle}>{f('source-entry')}</TableHeaderColumn>
            {isPageNumVisible && <TableHeaderColumn style={colStyle}>
              {f('page-num')}
              <button className={styles.btnSort} onClick={this.changePageNumberSortMethod}>

              </button>
            </TableHeaderColumn>}
          </TableRow>
        </TableHeader>
        <TableBody showRowHover deselectOnClickaway={false}>{tableRows}</TableBody>

    );
  }

  goToAddFolderEntryPage = () => {
    const {folder, push} = this.props;
    push(`/folders/${folder.id}/entries/add`);
  };

  updateTableKey = () => this.setState((prevState) => ({tableKey: prevState.tableKey + 1}));

  deleteSelectedFolderEntries = async () => {

    const {page, pageNumSortMethod} = this.state;
    const {selectedFolderEntryMap, f, deleteEntries, perpage, params, listFolderEntries,
      setSnackBarParams, folderEntryCount, clearSelectedFolderEntryIds} = this.props;

    const ids = Object.keys(selectedFolderEntryMap);
    await deleteEntries({ids});
    clearSelectedFolderEntryIds();
    const totalPages = Math.ceil((folderEntryCount - ids.length) / perpage);
    const nextPage = (page > totalPages) ? totalPages : page;

    await listFolderEntries({folderId: params.id, page: nextPage, perpage, pageNumSortMethod});
    this.setState({page: nextPage});
    setSnackBarParams(true, f('folder-entries-has-been-deleted', {count: ids.length}));
    this.updateTableKey();
    this.closeConfirmEntryDeletionDialog();
  };

  renderDeleteButton() {
    const {selectedFolderEntryMap, f, interfaceFontSizeScalingFactor} = this.props;
    if (Object.keys(selectedFolderEntryMap).length > 0) {
      const buttonFontSize = getFontSize(interfaceFontSizeScalingFactor, 0.9);
      return (
        <FlatButton label={f('delete')} labelStyle={{fontSize: buttonFontSize}}
          icon={} onTouchTap={this.openConfirmEntryDeletionDialog} />
      );
    }
  }

  handlePageButtonTouchTap = (page) => {
    this.updateTableKey();
    this.setState({page});
  };

  handleSearchInputChange = (searchKeyword) => {
    if (this.state.searchKeyword !== searchKeyword) {
      this.setState({page: 1});
    }
    this.setState({searchKeyword});
  };

  handleSearchTypeChange = (event, key, searchType) => this.setState({searchType});

  getMatchedCount() {

    const {searchKeyword} = this.state;
    const {folderEntryCount} = this.props;

    if (hasValue(searchKeyword)) {
      return folderEntryCount;
    }
    return 0;
  }

  handleClearSearchButtonTouchTap = () => {
    this.setState({searchKeyword: ''});
    this.refs.searchBar.getWrappedInstance().getWrappedInstance().clear();
  };

  isImporting() {
    const {folder, importingFolderId} = this.props;
    return folder.id === importingFolderId;
  }

  openConfirmEntryDeletionDialog = () => this.setState({isConfirmEntryDeletionOpen: true});

  closeConfirmEntryDeletionDialog = () => this.setState({isConfirmEntryDeletionOpen: false});

  renderConfirmEntryDeletionDialog() {
    const {isConfirmEntryDeletionOpen} = this.state;
    const {f, interfaceFontSizeScalingFactor} = this.props;
    const dialogContentFontSize = getFontSize(interfaceFontSizeScalingFactor, 1.2);
    const dialogContentLineHeight = getFontSize(interfaceFontSizeScalingFactor, 1.2);
    const pStyle = {
      fontSize: dialogContentFontSize,
      lineHeight: dialogContentLineHeight
    };
    const actions = [
      <FlatButton label={f('cancel')} primary onTouchTap={this.closeConfirmEntryDeletionDialog} />,
      <FlatButton label={f('delete')} primary keyboardFocused onTouchTap={this.deleteSelectedFolderEntries} />
    ];
    return (
      <Dialog title={f('title-confirm-delete-dialog')} actions={actions}
        modal={false} open={isConfirmEntryDeletionOpen} onRequestClose={this.closeConfirmEntryDeletionDialog}>

{f('content-confirm-delete-dialog1')}

{f('content-confirm-delete-dialog2')}

      </Dialog>
    );
  }

  render() {

    const {page, searchType, searchKeyword} = this.state;
    const {f, folder, folderEntryCount, perpage, interfaceFontSizeScalingFactor} = this.props;
    const matchedCount = this.getMatchedCount();
    const buttonFontSize = getFontSize(interfaceFontSizeScalingFactor, 0.9);

    let pageNumProps = {};

    if (this.hasPageNumField()) {
      pageNumProps = {
        searchTypes: SEARCH_TYPES,
        selectedSearchType: searchType,
        onSearchTypeChange: this.handleSearchTypeChange
      };
    }

    return (

        <TopBar>
          <Breadcrumb>
            <Link to="/">{f('folders')}</Link>
            {f('folder-entries', {folderName: folder.name})}
          </Breadcrumb>

            {this.renderDeleteButton()}
            <FlatButton icon={} labelStyle={{fontSize: buttonFontSize}}
              label={f('add-entry')} onTouchTap={this.goToAddFolderEntryPage} disabled={this.isImporting()} />
            <FlatButton icon={} labelStyle={{fontSize: buttonFontSize}}
              label={f('back')} onTouchTap={hashHistory.goBack} />

        </TopBar>

          <SearchBar ref="searchBar" onInputChange={this.handleSearchInputChange}
            searchKeyword={searchKeyword} matchedCount={matchedCount}
            onClearFilterButtonTouchTap={this.handleClearSearchButtonTouchTap} {...pageNumProps} />
          {this.renderFolderEntries()}

            {(folderEntryCount > perpage) && <Pagination current={page} total={Math.ceil(folderEntryCount / perpage)}
              onButtonTouchTap={this.handlePageButtonTouchTap} />}
            <PageJumper current={page} total={Math.ceil(folderEntryCount / perpage)}
              onButtonTouchTap={this.handlePageButtonTouchTap} />

        {this.renderConfirmEntryDeletionDialog()}

    );
  }
}
<commit_msg>declare total variable to store total page<commit_after>import React, {Component, PropTypes} from 'react';
import {connect} from 'react-redux';
import c from 'classnames';
import FlatButton from 'material-ui/FlatButton';
import Dialog from 'material-ui/Dialog';
import {Table, TableBody, TableHeader, TableHeaderColumn, TableRow, TableRowColumn} from 'material-ui/Table';
import {range} from 'ramda';
import {Link} from 'react-router';
import {get} from 'lodash';
import {hashHistory} from 'react-router';

import TopBar from './../../components/TopBar/TopBar';
import Breadcrumb from './../Breadcrumb/Breadcrumb';
import {setSnackBarParams} from './../../redux/modules/ui';
import {getFolder} from './../../redux/modules/folder';
import {listFolderEntries, setSelectedFolderEntryIds, clearSelectedFolderEntryIds,
  deleteEntries} from './../../redux/modules/entry';
import {setCachePageEntries} from './../../redux/modules/cache';

import injectF from './../../helpers/injectF';
import injectPush from './../../helpers/injectPush';
import resolve from './../../helpers/resolve';
import Pagination from './../../components/Pagination/Pagination';
import PageJumper from './../../components/PageJumper/PageJumper';
import SearchBar from './../../components/SearchBar/SearchBar';
import hasValue from './../../helpers/hasValue';
import getFontSize from './../../helpers/getFontSize';
import SEARCH_TYPES from './../../constants/searchTypes';

const styles = require('./PageEntries.scss');

const SORT_METHODS = ['', 'asc', 'desc'];
const SORT_CLASSNAME_MAP = {
  ['']: 'fa fa-sort',
  asc: 'fa fa-sort-asc',
  desc: 'fa fa-sort-desc'
};

// how to access params outside of react component
// https://github.com/reactjs/react-router-redux#how-do-i-access-router-state-in-a-container-component
@connect(({main, folder, entry, cache, routing}, {params}) => {
  const folderId = parseInt(params.id, 10);
  return {
    cache: get(cache.get('cachePageEntriesDataSet'), folderId, {}),
    perpage: entry.get('perpage'),
    folder: folder.get('folder'),
    importingFolderId: folder.get('importingFolderId'),
    interfaceFontSizeScalingFactor: main.get('interfaceFontSizeScalingFactor'),
    folderEntries: entry.get('folderEntries'),
    folderEntryCount: entry.get('folderEntryCount'),
    selectedFolderEntryMap: entry.get('selectedFolderEntryMap')
  };
}, {listFolderEntries, setSnackBarParams, setSelectedFolderEntryIds, setCachePageEntries,
  deleteEntries, clearSelectedFolderEntryIds})
@injectPush
@injectF
@resolve(({dispatch}, {params, page, perpage, cache}) => {

  const promises = [];
  const folderId = params.id;

  promises.push(dispatch(getFolder({id: folderId})));
  promises.push(dispatch(setSelectedFolderEntryIds([])));

  const searchParams = Object.assign({
    folderId,
    page,
    perpage,
    pageNumSortMethod: ''
  }, cache);
  promises.push(dispatch(listFolderEntries(searchParams)));

  return Promise.all(promises);
})
export default class PageEntries extends Component {

  static propTypes = {
    cache: PropTypes.object.isRequired,
    push: PropTypes.func.isRequired,
    perpage: PropTypes.number.isRequired,
    f: PropTypes.func.isRequired,
    folder: PropTypes.object.isRequired,
    folderEntries: PropTypes.array.isRequired,
    interfaceFontSizeScalingFactor: PropTypes.number.isRequired,
    folderEntryCount: PropTypes.number.isRequired,
    params: PropTypes.object.isRequired,
    listFolderEntries: PropTypes.func.isRequired,
    setCachePageEntries: PropTypes.func.isRequired,
    setSelectedFolderEntryIds: PropTypes.func.isRequired,
    clearSelectedFolderEntryIds: PropTypes.func.isRequired,
    selectedFolderEntryMap: PropTypes.object.isRequired,
    deleteEntries: PropTypes.func.isRequired,
    importingFolderId: PropTypes.number,
    setSnackBarParams: PropTypes.func.isRequired
  };

  constructor(props) {
    super(props);
    const [pageNumSortMethod] = SORT_METHODS;
    this.state = Object.assign({
      page: 1,
      tableKey: 0,
      searchKeyword: '',
      searchType: 'source-entry',
      pageNumSortMethod,
      isConfirmEntryDeletionOpen: false
    }, props.cache);

    if (! this.hasPageNumField()) {
      this.state.pageNumSortMethod = '';
      this.state.searchType = 'source-entry';
    }
  }

  hasPageNumField() {
    return this.props.folder.data.contentFields.includes('page-num');
  }

  componentWillUpdate(nextProps, nextState) {
    const {page, searchKeyword, searchType, pageNumSortMethod} = this.state;
    const {perpage, listFolderEntries, setCachePageEntries} = this.props;
    const nextPage = nextState.page;
    const nextPerpage = nextProps.perpage;
    const nextSearchKeyword = nextState.searchKeyword;
    const nextSearchType = nextState.searchType;
    const folderId = nextProps.folder.id;
    const nextPageNumSortMethod = nextState.pageNumSortMethod;

    if ((page !== nextPage) || (perpage !== nextPerpage) ||
        (searchKeyword !== nextSearchKeyword) || (searchType !== nextSearchType) || (pageNumSortMethod !== nextPageNumSortMethod)) {

      setCachePageEntries(folderId, {
        page: nextPage,
        searchKeyword: nextSearchKeyword,
        searchType: nextSearchType,
        pageNumSortMethod: nextPageNumSortMethod
      });

      listFolderEntries({
        folderId,
        page: nextPage,
        perpage: nextPerpage,
        searchKeyword: nextSearchKeyword.trim(),
        searchType: nextSearchType,
        pageNumSortMethod: nextPageNumSortMethod
      });
    }
  }

  handleRowSelection = (res) => {

    const {setSelectedFolderEntryIds, perpage, folderEntries} = this.props;

    let indices = res;

    if ('all' === res) {
      indices = range(0, perpage);
    }
    else if ('none' === res) {
      indices = [];
    }

    const ids = folderEntries.filter((row, index) => (-1 !== indices.indexOf(index)))
      .map((row) => row.id);

    setSelectedFolderEntryIds(ids);
  };

  goToSingleFolderEntryPage = (entryId) => {
    const {folder, push} = this.props;
    return () => push(`/folders/${folder.id}/entries/${entryId}`);
  };

  changePageNumberSortMethod = () => {
    const {pageNumSortMethod} = this.state;
    const index = SORT_METHODS.indexOf(pageNumSortMethod);
    const nextIndex = (index + 1) % 3;
    this.setState({pageNumSortMethod: SORT_METHODS[nextIndex]});
  };

  renderFolderEntries() {

    const fontSize = 20;
    const colStyle = {fontSize, width: 'initial'};
    const {tableKey, pageNumSortMethod} = this.state;
    const {folderEntries, f} = this.props;

    const isPageNumVisible = this.hasPageNumField();

    const tableRows = folderEntries.map((entry) => {
      return (
        <TableRow key={`table-row-${entry.id}`}>
          <TableRowColumn style={colStyle}>
            {entry.sourceEntry}
          </TableRowColumn>
          {isPageNumVisible && <TableRowColumn style={colStyle}>{get(entry, 'pageNum', '')}</TableRowColumn>}
        </TableRow>
      );
    });

    // https://github.com/callemall/material-ui/issues/6006#issuecomment-277669719
    const key = `table-workaround-${tableKey}`;

    return (

        <TableHeader>
          <TableRow>
            <TableHeaderColumn style={colStyle}>{f('source-entry')}</TableHeaderColumn>
            {isPageNumVisible && <TableHeaderColumn style={colStyle}>
              {f('page-num')}
              <button className={styles.btnSort} onClick={this.changePageNumberSortMethod}>

              </button>
            </TableHeaderColumn>}
          </TableRow>
        </TableHeader>
        <TableBody showRowHover deselectOnClickaway={false}>{tableRows}</TableBody>

    );
  }

  goToAddFolderEntryPage = () => {
    const {folder, push} = this.props;
    push(`/folders/${folder.id}/entries/add`);
  };

  updateTableKey = () => this.setState((prevState) => ({tableKey: prevState.tableKey + 1}));

  deleteSelectedFolderEntries = async () => {

    const {page, pageNumSortMethod} = this.state;
    const {selectedFolderEntryMap, f, deleteEntries, perpage, params, listFolderEntries,
      setSnackBarParams, folderEntryCount, clearSelectedFolderEntryIds} = this.props;

    const ids = Object.keys(selectedFolderEntryMap);
    await deleteEntries({ids});
    clearSelectedFolderEntryIds();
    const totalPages = Math.ceil((folderEntryCount - ids.length) / perpage);
    const nextPage = (page > totalPages) ? totalPages : page;

    await listFolderEntries({folderId: params.id, page: nextPage, perpage, pageNumSortMethod});
    this.setState({page: nextPage});
    setSnackBarParams(true, f('folder-entries-has-been-deleted', {count: ids.length}));
    this.updateTableKey();
    this.closeConfirmEntryDeletionDialog();
  };

  renderDeleteButton() {
    const {selectedFolderEntryMap, f, interfaceFontSizeScalingFactor} = this.props;
    if (Object.keys(selectedFolderEntryMap).length > 0) {
      const buttonFontSize = getFontSize(interfaceFontSizeScalingFactor, 0.9);
      return (
        <FlatButton label={f('delete')} labelStyle={{fontSize: buttonFontSize}}
          icon={} onTouchTap={this.openConfirmEntryDeletionDialog} />
      );
    }
  }

  handlePageButtonTouchTap = (page) => {
    this.updateTableKey();
    this.setState({page});
  };

  handleSearchInputChange = (searchKeyword) => {
    if (this.state.searchKeyword !== searchKeyword) {
      this.setState({page: 1});
    }
    this.setState({searchKeyword});
  };

  handleSearchTypeChange = (event, key, searchType) => this.setState({searchType});

  getMatchedCount() {

    const {searchKeyword} = this.state;
    const {folderEntryCount} = this.props;

    if (hasValue(searchKeyword)) {
      return folderEntryCount;
    }
    return 0;
  }

  handleClearSearchButtonTouchTap = () => {
    this.setState({searchKeyword: ''});
    this.refs.searchBar.getWrappedInstance().getWrappedInstance().clear();
  };

  isImporting() {
    const {folder, importingFolderId} = this.props;
    return folder.id === importingFolderId;
  }

  openConfirmEntryDeletionDialog = () => this.setState({isConfirmEntryDeletionOpen: true});

  closeConfirmEntryDeletionDialog = () => this.setState({isConfirmEntryDeletionOpen: false});

  renderConfirmEntryDeletionDialog() {
    const {isConfirmEntryDeletionOpen} = this.state;
    const {f, interfaceFontSizeScalingFactor} = this.props;
    const dialogContentFontSize = getFontSize(interfaceFontSizeScalingFactor, 1.2);
    const dialogContentLineHeight = getFontSize(interfaceFontSizeScalingFactor, 1.2);
    const pStyle = {
      fontSize: dialogContentFontSize,
      lineHeight: dialogContentLineHeight
    };
    const actions = [
      <FlatButton label={f('cancel')} primary onTouchTap={this.closeConfirmEntryDeletionDialog} />,
      <FlatButton label={f('delete')} primary keyboardFocused onTouchTap={this.deleteSelectedFolderEntries} />
    ];
    return (
      <Dialog title={f('title-confirm-delete-dialog')} actions={actions}
        modal={false} open={isConfirmEntryDeletionOpen} onRequestClose={this.closeConfirmEntryDeletionDialog}>

{f('content-confirm-delete-dialog1')}

{f('content-confirm-delete-dialog2')}

      </Dialog>
    );
  }

  render() {

    const {page, searchType, searchKeyword} = this.state;
    const {f, folder, folderEntryCount, perpage, interfaceFontSizeScalingFactor} = this.props;
    const matchedCount = this.getMatchedCount();
    const buttonFontSize = getFontSize(interfaceFontSizeScalingFactor, 0.9);
    const total = Math.ceil(folderEntryCount / perpage);

    let pageNumProps = {};

    if (this.hasPageNumField()) {
      pageNumProps = {
        searchTypes: SEARCH_TYPES,
        selectedSearchType: searchType,
        onSearchTypeChange: this.handleSearchTypeChange
      };
    }

    return (

        <TopBar>
          <Breadcrumb>
            <Link to="/">{f('folders')}</Link>
            {f('folder-entries', {folderName: folder.name})}
          </Breadcrumb>

            {this.renderDeleteButton()}
            <FlatButton icon={} labelStyle={{fontSize: buttonFontSize}}
              label={f('add-entry')} onTouchTap={this.goToAddFolderEntryPage} disabled={this.isImporting()} />
            <FlatButton icon={} labelStyle={{fontSize: buttonFontSize}}
              label={f('back')} onTouchTap={hashHistory.goBack} />

        </TopBar>

          <SearchBar ref="searchBar" onInputChange={this.handleSearchInputChange}
            searchKeyword={searchKeyword} matchedCount={matchedCount}
            onClearFilterButtonTouchTap={this.handleClearSearchButtonTouchTap} {...pageNumProps} />
          {this.renderFolderEntries()}

            {(folderEntryCount > perpage) && <Pagination current={page} total={total}
              onButtonTouchTap={this.handlePageButtonTouchTap} />}
            <PageJumper current={page} total={total}
              onButtonTouchTap={this.handlePageButtonTouchTap} />

        {this.renderConfirmEntryDeletionDialog()}

    );
  }
}
<|endoftext|>


<commit_before>var _; //globals

/* This section uses a functional extension known as Underscore.js - http://documentcloud.github.com/underscore/
     "Underscore is a utility-belt library for JavaScript that provides a lot of the functional programming support
      that you would expect in Prototype.js (or Ruby), but without extending any of the built-in JavaScript objects.
      It's the tie to go along with jQuery's tux."
 */
describe("About Higher Order Functions", function () {

  it("should use filter to return array items that meet a criteria", function () {
    var numbers = [1,2,3];
    var odd = _(numbers).filter(function (x) { return x % 2 !== 0 });

    expect(odd).toEqual([1,3]);
    expect(odd.length).toBe(2);
    expect(numbers.length).toBe(3);
  });

  it("should use 'map' to transform each element", function () {
    var numbers = [1, 2, 3];
    var numbersPlus1 = _(numbers).map(function(x) { return x + 1 });

    expect(numbersPlus1).toEqual([2,3,4]);
    expect(numbers).toEqual([1,2,3]);
  });

  it("should use 'reduce' to update the same result on each iteration", function () {
    var numbers = [1, 2, 3];
    var reduction = _(numbers).reduce(
            function(/* result from last call */ memo, /* current */ x) { return memo + x }, /* initial */ 0);

    expect(reduction).toBe(6);
    expect(numbers).toEqual([1,2,3]);
  });

  it("should use 'forEach' for simple iteration", function () {
    var numbers = [1,2,3];
    var msg = "";
    var isEven = function (item) {
      msg += (item % 2) === 0;
    };

    _(numbers).forEach(isEven);

    expect(msg).toEqual('falsetruefalse');
    expect(numbers).toEqual([1,2,3]);
  });

  it("should use 'all' to test whether all items pass condition", function () {
    var onlyEven = [2,4,6];
    var mixedBag = [2,4,5,6];

    var isEven = function(x) { return x % 2 === 0 };

    expect(_(onlyEven).all(isEven)).toBe(true);
    expect(_(mixedBag).all(isEven)).toBe(false);
  });

  it("should use 'any' to test if any items passes condition" , function () {
    var onlyEven = [2,4,6];
    var mixedBag = [2,4,5,6];

    var isEven = function(x) { return x % 2 === 0 };

    expect(_(onlyEven).any(isEven)).toBe(true);
    expect(_(mixedBag).any(isEven)).toBe(true);
  });

  it("should use range to generate an array", function() {
      expect(_.range(3)).toEqual([0,1,2]);
      expect(_.range(1, 4)).toEqual([1,2,3]);
      expect(_.range(0, -4, -1)).toEqual([0,-1,-2,-3]);
  });

  it("should use flatten to make nested arrays easy to work with", function() {
      expect(_([ [1, 2], [3, 4] ]).flatten()).toEqual([1,2,3,4]);
  });

  it("should use chain() ... .value() to use multiple higher order functions", function() {
      var result = _([ [0, 1], 2 ]).chain()
                       .flatten()
                       .map(function(x) { return x+1 } )
                       .reduce(function (sum, x) { return sum + x })
                       .value();

      expect(result).toEqual(6);
  });

});

<commit_msg>Section 1 complete<commit_after>var _; //globals

/* This section uses a functional extension known as Underscore.js - http://documentcloud.github.com/underscore/
     "Underscore is a utility-belt library for JavaScript that provides a lot of the functional programming support
      that you would expect in Prototype.js (or Ruby), but without extending any of the built-in JavaScript objects.
      It's the tie to go along with jQuery's tux."
 */
describe("About Higher Order Functions", function () {

  it("should use filter to return array items that meet a criteria", function () {
    var numbers = [1,2,3];
    var odd = _.filter(numbers, function(x) {
      return x % 2 !== 0;
    });

    expect(odd).toEqual([1,3]);
    expect(odd.length).toBe(2);
    expect(numbers.length).toBe(3);
  });

  it("should use 'map' to transform each element", function () {
    var numbers = [1, 2, 3];
    var numbersPlus1 = _(numbers).map(function(x) { return x + 1 });

    expect(numbersPlus1).toEqual([2,3,4]);
    expect(numbers).toEqual([1,2,3]);
  });

  it("should use 'reduce' to update the same result on each iteration", function () {
    var numbers = [1, 2, 3];
    var reduction = _(numbers).reduce(
            function(/* result from last call */ memo, /* current */ x) { return memo + x }, /* initial */ 0);

    expect(reduction).toBe(6);
    expect(numbers).toEqual([1,2,3]);
  });

  it("should use 'forEach' for simple iteration", function () {
    var numbers = [1,2,3];
    var msg = "";
    var isEven = function (item) {
      msg += (item % 2) === 0;
    };

    _(numbers).forEach(isEven);

    expect(msg).toEqual('falsetruefalse');
    expect(numbers).toEqual([1,2,3]);
  });

  it("should use 'all' to test whether all items pass condition", function () {
    var onlyEven = [2,4,6];
    var mixedBag = [2,4,5,6];

    var isEven = function(x) { return x % 2 === 0 };

    expect(_(onlyEven).all(isEven)).toBe(true);
    expect(_(mixedBag).all(isEven)).toBe(false);
  });

  it("should use 'any' to test if any items passes condition" , function () {
    var onlyEven = [2,4,6];
    var mixedBag = [2,4,5,6];

    var isEven = function(x) { return x % 2 === 0 };

    expect(_(onlyEven).any(isEven)).toBe(true);
    expect(_(mixedBag).any(isEven)).toBe(true);
  });

  it("should use range to generate an array", function() {
      expect(_.range(3)).toEqual([0,1,2]);
      expect(_.range(1, 4)).toEqual([1,2,3]);
      expect(_.range(0, -4, -1)).toEqual([0,-1,-2,-3]);
  });

  it("should use flatten to make nested arrays easy to work with", function() {
      expect(_([ [1, 2], [3, 4] ]).flatten()).toEqual([1,2,3,4]);
  });

  it("should use chain() ... .value() to use multiple higher order functions", function() {
      var result = _([ [0, 1], 2 ]).chain()
                       .flatten()
                       .map(function(x) { return x+1 } )
                       .reduce(function (sum, x) { return sum + x })
                       .value();

      expect(result).toEqual(6);
  });

});

<|endoftext|>


<commit_before>import { Mirroring, TVSystem }         from "../common/types";
import { INESLoader }                  from "../loaders/ines-loader";
import { NES2Loader }                  from "../loaders/nes2-loader";
import { ArrayBufferReader }           from "../readers/array-buffer-reader";
import { LocalFileReader }             from "../readers/local-file-reader";
import { readableSize, readableBytes } from "../utils/format";
import { logger }                      from "../utils/logger";

var loaders = [
    new INESLoader, // Must be processed before iNES
    new NES2Loader
]

//=========================================================
// Factory for cartridge creation
//=========================================================

export class CartridgeFactory {

    fromArrayBuffer(buffer) {
        logger.info("Loading cartridge from array buffer");
        return this.fromReader(new ArrayBufferReader(buffer));
    }

    fromLocalFile(path) {
        logger.info(`Loading cartridge from '${path}'`);
        return this.fromReader(new LocalFileReader(path));
    }

    fromReader(reader) {
        for (var loader of loaders) {
            if (loader.supports(reader)) {
                logger.info(`Using '${loader.name}' loader`);
                var cartridge = loader.load(reader);
                this.printCartridgeInfo(cartridge);
                return cartridge;
            }
        }
        throw new Error("Unsupported input data format.");
    }

    printCartridgeInfo(cartridge) {
        var ref, ref1;
        logger.info("==========[Cartridge Info - Start]==========");
        logger.info("Mapper ID             : " + cartridge.mapperId);
        logger.info("Sub-mapper ID         : " + cartridge.subMapperId);
        logger.info("has PRG RAM           : " + cartridge.hasPRGRAM);
        logger.info("has PRG RAM battery   : " + cartridge.hasPRGRAMBattery);
        logger.info("has CHR RAM           : " + cartridge.hasCHRRAM);
        logger.info("has CHR RAM battery   : " + cartridge.hasCHRRAMBattery);
        logger.info("has BUS conflicts     : " + cartridge.hasBUSConflicts);
        logger.info("has trainer           : " + cartridge.hasTrainer);
        logger.info("PRG ROM size          : " + readableSize(cartridge.prgROMSize || cartridge.prgROM.length));
        logger.info("PRG RAM size          : " + readableSize(cartridge.prgRAMSize));
        logger.info("PRG RAM size (battery): " + readableSize(cartridge.prgRAMSizeBattery));
        logger.info("CHR ROM size          : " + readableSize(cartridge.chrROMSize || cartridge.chrROM.length));
        logger.info("CHR RAM size          : " + readableSize(cartridge.chrRAMSize));
        logger.info("CHR RAM size (battery): " + readableSize(cartridge.chrRAMSizeBattery));
        logger.info("Mirroring             : " + Mirroring.toString(cartridge.mirroring));
        logger.info("TV system             : " + TVSystem.toString(cartridge.tvSystem));
        logger.info("is Vs Unisistem       : " + cartridge.isVsUnisistem);
        logger.info("is PlayChoice         : " + cartridge.isPlayChoice);
        logger.info("Trainer               : " + readableBytes(cartridge.trainer));
        logger.info("==========[Cartridge Info - End]==========");
    }

}
<commit_msg>NES 2.0 loader is processed first.<commit_after>import { Mirroring, TVSystem }         from "../common/types";
import { INESLoader }                  from "../loaders/ines-loader";
import { NES2Loader }                  from "../loaders/nes2-loader";
import { ArrayBufferReader }           from "../readers/array-buffer-reader";
import { LocalFileReader }             from "../readers/local-file-reader";
import { readableSize, readableBytes } from "../utils/format";
import { logger }                      from "../utils/logger";

var loaders = [
    new NES2Loader, // Must be processed before iNES
    new INESLoader
]

//=========================================================
// Factory for cartridge creation
//=========================================================

export class CartridgeFactory {

    fromArrayBuffer(buffer) {
        logger.info("Loading cartridge from array buffer");
        return this.fromReader(new ArrayBufferReader(buffer));
    }

    fromLocalFile(path) {
        logger.info(`Loading cartridge from '${path}'`);
        return this.fromReader(new LocalFileReader(path));
    }

    fromReader(reader) {
        for (var loader of loaders) {
            if (loader.supports(reader)) {
                logger.info(`Using '${loader.name}' loader`);
                var cartridge = loader.load(reader);
                this.printCartridgeInfo(cartridge);
                return cartridge;
            }
        }
        throw new Error("Unsupported input data format.");
    }

    printCartridgeInfo(cartridge) {
        var ref, ref1;
        logger.info("==========[Cartridge Info - Start]==========");
        logger.info("Mapper ID             : " + cartridge.mapperId);
        logger.info("Sub-mapper ID         : " + cartridge.subMapperId);
        logger.info("has PRG RAM           : " + cartridge.hasPRGRAM);
        logger.info("has PRG RAM battery   : " + cartridge.hasPRGRAMBattery);
        logger.info("has CHR RAM           : " + cartridge.hasCHRRAM);
        logger.info("has CHR RAM battery   : " + cartridge.hasCHRRAMBattery);
        logger.info("has BUS conflicts     : " + cartridge.hasBUSConflicts);
        logger.info("has trainer           : " + cartridge.hasTrainer);
        logger.info("PRG ROM size          : " + readableSize(cartridge.prgROMSize || cartridge.prgROM.length));
        logger.info("PRG RAM size          : " + readableSize(cartridge.prgRAMSize));
        logger.info("PRG RAM size (battery): " + readableSize(cartridge.prgRAMSizeBattery));
        logger.info("CHR ROM size          : " + readableSize(cartridge.chrROMSize || cartridge.chrROM.length));
        logger.info("CHR RAM size          : " + readableSize(cartridge.chrRAMSize));
        logger.info("CHR RAM size (battery): " + readableSize(cartridge.chrRAMSizeBattery));
        logger.info("Mirroring             : " + Mirroring.toString(cartridge.mirroring));
        logger.info("TV system             : " + TVSystem.toString(cartridge.tvSystem));
        logger.info("is Vs Unisistem       : " + cartridge.isVsUnisistem);
        logger.info("is PlayChoice         : " + cartridge.isPlayChoice);
        logger.info("Trainer               : " + readableBytes(cartridge.trainer));
        logger.info("==========[Cartridge Info - End]==========");
    }

}
<|endoftext|>


<commit_before>/*:
 * @plugindesc Basic plugin for manipulating important parameters..
 * @author RM CoreScript team
 *
 * @help
 * Basic plugin for manipulating important parameters..
 * There is no plugin command.
 *
 * @param cacheLimit
 * @desc The upper limit of images' cached size (MPixel)
 * @default 20
 *
 * @param screenWidth
 * @desc The resolution of screen width
 * @default 816
 *
 * @param screenHeight
 * @desc The resolution of screen height
 * @default 624
 *
 * @param windowScaleFactor
 * @desc Scale window to (screen size * value).
 * @default 1
 *
 * @param changeWindowWidthTo
 * @desc If set, change window width to this value
 *
 * @param changeWindowHeightTo
 * @desc If set, change window height to this value
 *
 * @param renderingMode
 * @desc The rendering mode (canvas/webgl/auto)
 * @default auto
 *
 * @param alwaysDash
 * @desc The initial value whether the player always dashes (on/off)
 * @default off
 */

/*:ja
 * @plugindesc 基本的なパラメーターを設定するプラグインです。
 * @author RM CoreScript team
 *
 * @help
 * 基本的なパラメーターを設定するプラグインです。
 * このプラグインにはプラグインコマンドはありません。
 *
 * @param cacheLimit
 * @desc 画像のメモリへのキャッシュの上限値 (MPix)
 * @default 20
 *
 * @param screenWidth
 * @desc 画面サイズの幅
 * @default 816
 *
 * @param screenHeight
 * @desc 画面サイズの高さ
 * @default 624
 *
 * @param windowScaleFactor
 * @desc ウインドウを、画面サイズの指定された値分拡大・縮小します
 * @default 1
 *
 * @param changeWindowWidthTo
 * @desc 値が設定された場合、ウインドウの幅を指定した値に変更
 *
 * @param changeWindowHeightTo
 * @desc 値が設定された場合、ウインドウの高さを指定した値に変更
 *
 * @param renderingMode
 * @desc レンダリングモード (canvas/webgl/auto)
 * @default auto
 *
 * @param alwaysDash
 * @desc プレイヤーが常時ダッシュするかどうかの初期値 (on/off)
 * @default off
 */

(function() {
    function toNumber(str, def) {
        return isNaN(str) ? def : +(str || def);
    }

    var parameters = PluginManager.parameters('Community_Basic');
    var cacheLimit = toNumber(parameters['cacheLimit'], 20);
    var scaleFactor = toNumber(parameters['windowScaleFactor'], 1);
    var screenWidth = toNumber(parameters['screenWidth'], 816);
    var screenHeight = toNumber(parameters['screenHeight'], 624);
    var renderingMode = parameters['renderingMode'].toLowerCase();
    var alwaysDash = parameters['alwaysDash'].toLowerCase() === 'on';
    var windowWidthTo = toNumber(parameters['changeWindowWidthTo'], 0);
    var windowHeightTo = toNumber(parameters['changeWindowHeightTo'], 0);

    var windowWidth;
    var windowHeight;

    if(windowWidthTo){
        windowWidth = windowWidthTo;
    }else if(scaleFactor !== 1){
        windowWidth = screenWidth * scaleFactor;
    }else if(screenWidth !== SceneManager._screenWidth){
        windowWidth = screenWidth;
    }

    if(windowHeightTo){
        windowHeight = windowHeightTo;
    }else if(scaleFactor !== 1){
        windowHeight = screenHeight * scaleFactor;
    }else if(screenHeight !== SceneManager._screenHeight){
        windowHeight = screenHeight;
    }

    ImageCache.limit = cacheLimit * 1000 * 1000;
    SceneManager._screenWidth = screenWidth;
    SceneManager._screenHeight = screenHeight;
    SceneManager._boxWidth = screenWidth;
    SceneManager._boxHeight = screenHeight;

    SceneManager.preferableRendererType = function() {
        if (Utils.isOptionValid('canvas')) {
            return 'canvas';
        } else if (Utils.isOptionValid('webgl')) {
            return 'webgl';
        } else if (renderingMode === 'canvas') {
            return 'canvas';
        } else if (renderingMode === 'webgl') {
            return 'webgl';
        } else {
            return 'auto';
        }
    };

    var _ConfigManager_applyData = ConfigManager.applyData;
    ConfigManager.applyData = function(config) {
        _ConfigManager_applyData.apply(this, arguments);
        if (config['alwaysDash'] === undefined) {
            this.alwaysDash = alwaysDash;
        }
    };

    var _SceneManager_initNwjs = SceneManager.initNwjs;
    SceneManager.initNwjs = function() {
        _SceneManager_initNwjs.apply(this, arguments);

        if (Utils.isNwjs() && windowWidth && windowHeight) {
            var dw = windowWidth - window.innerWidth;
            var dh = windowHeight - window.innerHeight;
            window.moveBy(-dw / 2, -dh / 2);
            window.resizeBy(dw, dh);
        }
    };
})();<commit_msg>remove scaleFactor<commit_after>/*:
 * @plugindesc Basic plugin for manipulating important parameters..
 * @author RM CoreScript team
 *
 * @help
 * Basic plugin for manipulating important parameters..
 * There is no plugin command.
 *
 * @param cacheLimit
 * @desc The upper limit of images' cached size (MPixel)
 * @default 20
 *
 * @param screenWidth
 * @desc The resolution of screen width
 * @default 816
 *
 * @param screenHeight
 * @desc The resolution of screen height
 * @default 624
 *
 * @param changeWindowWidthTo
 * @desc If set, change window width to this value
 *
 * @param changeWindowHeightTo
 * @desc If set, change window height to this value
 *
 * @param renderingMode
 * @desc The rendering mode (canvas/webgl/auto)
 * @default auto
 *
 * @param alwaysDash
 * @desc The initial value whether the player always dashes (on/off)
 * @default off
 */

/*:ja
 * @plugindesc 基本的なパラメーターを設定するプラグインです。
 * @author RM CoreScript team
 *
 * @help
 * 基本的なパラメーターを設定するプラグインです。
 * このプラグインにはプラグインコマンドはありません。
 *
 * @param cacheLimit
 * @desc 画像のメモリへのキャッシュの上限値 (MPix)
 * @default 20
 *
 * @param screenWidth
 * @desc 画面サイズの幅
 * @default 816
 *
 * @param screenHeight
 * @desc 画面サイズの高さ
 * @default 624
 *
 * @param changeWindowWidthTo
 * @desc 値が設定された場合、ウインドウの幅を指定した値に変更
 *
 * @param changeWindowHeightTo
 * @desc 値が設定された場合、ウインドウの高さを指定した値に変更
 *
 * @param renderingMode
 * @desc レンダリングモード (canvas/webgl/auto)
 * @default auto
 *
 * @param alwaysDash
 * @desc プレイヤーが常時ダッシュするかどうかの初期値 (on/off)
 * @default off
 */

(function() {
    function toNumber(str, def) {
        return isNaN(str) ? def : +(str || def);
    }

    var parameters = PluginManager.parameters('Community_Basic');
    var cacheLimit = toNumber(parameters['cacheLimit'], 20);
    var screenWidth = toNumber(parameters['screenWidth'], 816);
    var screenHeight = toNumber(parameters['screenHeight'], 624);
    var renderingMode = parameters['renderingMode'].toLowerCase();
    var alwaysDash = parameters['alwaysDash'].toLowerCase() === 'on';
    var windowWidthTo = toNumber(parameters['changeWindowWidthTo'], 0);
    var windowHeightTo = toNumber(parameters['changeWindowHeightTo'], 0);

    var windowWidth;
    var windowHeight;

    if(windowWidthTo){
        windowWidth = windowWidthTo;
    }else if(screenWidth !== SceneManager._screenWidth){
        windowWidth = screenWidth;
    }

    if(windowHeightTo){
        windowHeight = windowHeightTo;
    }else if(screenHeight !== SceneManager._screenHeight){
        windowHeight = screenHeight;
    }

    ImageCache.limit = cacheLimit * 1000 * 1000;
    SceneManager._screenWidth = screenWidth;
    SceneManager._screenHeight = screenHeight;
    SceneManager._boxWidth = screenWidth;
    SceneManager._boxHeight = screenHeight;

    SceneManager.preferableRendererType = function() {
        if (Utils.isOptionValid('canvas')) {
            return 'canvas';
        } else if (Utils.isOptionValid('webgl')) {
            return 'webgl';
        } else if (renderingMode === 'canvas') {
            return 'canvas';
        } else if (renderingMode === 'webgl') {
            return 'webgl';
        } else {
            return 'auto';
        }
    };

    var _ConfigManager_applyData = ConfigManager.applyData;
    ConfigManager.applyData = function(config) {
        _ConfigManager_applyData.apply(this, arguments);
        if (config['alwaysDash'] === undefined) {
            this.alwaysDash = alwaysDash;
        }
    };

    var _SceneManager_initNwjs = SceneManager.initNwjs;
    SceneManager.initNwjs = function() {
        _SceneManager_initNwjs.apply(this, arguments);

        if (Utils.isNwjs() && windowWidth && windowHeight) {
            var dw = windowWidth - window.innerWidth;
            var dh = windowHeight - window.innerHeight;
            window.moveBy(-dw / 2, -dh / 2);
            window.resizeBy(dw, dh);
        }
    };
})();<|endoftext|>


<commit_before>// ==UserScript==
// @id             iitc-plugin-draw-tools@breunigs
// @name           iitc: draw tools
// @version        0.1
// @namespace      https://github.com/breunigs/ingress-intel-total-conversion
// @updateURL      https://raw.github.com/breunigs/ingress-intel-total-conversion/gh-pages/plugins/draw-tools.user.js
// @downloadURL    https://raw.github.com/breunigs/ingress-intel-total-conversion/gh-pages/plugins/draw-tools.user.js
// @description    Allows you to draw things into the current map so you may plan your next move
// @include        http://www.ingress.com/intel*
// @match          http://www.ingress.com/intel*
// ==/UserScript==

function wrapper() {
// ensure plugin framework is there, even if iitc is not yet loaded
if(typeof window.plugin !== 'function') window.plugin = function() {};

// PLUGIN START ////////////////////////////////////////////////////////

var DRAW_TOOLS_SHAPE_OPTIONS = {
  color: '#BB56FF',
  fill: false,
  opacity: 1,
  weight: 4,
  clickable: false
};

// use own namespace for plugin
window.plugin.drawTools = function() {};

window.plugin.drawTools.loadExternals = function() {
  var base = 'http://breunigs.github.com/ingress-intel-total-conversion/dist';
  //~ var base = 'http://0.0.0.0:8000/dist';
  $('head').append('<link rel="stylesheet" href="'+base+'/leaflet.draw.0.1.6.css" />');
  load(base+'/leaflet.draw.0.1.6.js').thenRun(window.plugin.drawTools.boot);
  // overwrite default Leaflet Marker icon.
  L.Icon.Default.imagePath = base + '/images';
}

window.plugin.drawTools.addStyles = function() {
  $('head').append('');
}

// renders buttons which are not originally part of Leaflet.draw, such
// as the clear-drawings button.
window.plugin.drawTools.addCustomButtons = function() {
  $('.leaflet-control-draw .leaflet-bar-part-bottom').removeClass('leaflet-bar-part-bottom');

  var undo = $('⎌')
    .click(function() {
      var last = null;
      window.plugin.drawTools.drawnItems.eachLayer(function(l) {
        last = l;
      });
      if(last) window.plugin.drawTools.drawnItems.removeLayer(last);
    }
  );

  var clear = $('✗')
    .click(function() {
      window.plugin.drawTools.drawnItems.clearLayers();
    }
  );

  $('.leaflet-control-draw').append(undo).append(clear);
}

// renders the draw control buttons in the top left corner
window.plugin.drawTools.addDrawControl = function() {
  var drawControl = new L.Control.Draw({
    rectangle: false,
    polygon: false,

    polyline: {
      shapeOptions: DRAW_TOOLS_SHAPE_OPTIONS
    },

    circle: {
      shapeOptions: DRAW_TOOLS_SHAPE_OPTIONS
    }
  });
  map.addControl(drawControl);
  plugin.drawTools.addCustomButtons();
}

// hacks into circle to render the radius of the circle while drawing
// and to allow the center of the circle to snap to a nearby portal.
window.plugin.drawTools.enhanceCircle = function() {
  // replace _onMouseMove function to also display the radius of the
  // circle
  L.Circle.Draw.prototype._onMouseMove = function (e) {
    var layerPoint = e.layerPoint,
        latlng = e.latlng;

    this._updateLabelPosition(layerPoint);
    if (this._isDrawing) {
      var dist = this._startLatLng.distanceTo(latlng);
      dist = dist  > 1000
              ? (dist  / 1000).toFixed(2) + ' km'
              : Math.ceil(dist) + ' m';
      this._updateLabelText({
        text: 'Release mouse to finish drawing. ',
        subtext: 'radius: ' +dist }
      );
      this._drawShape(latlng);
    }
  }

  // replace _onMouseDown to implement snapping
  L.Circle.Draw.prototype._onMouseDown = function (e) {
    this._isDrawing = true;

    var snapTo = window.plugin.drawTools.getSnapLatLng(e.containerPoint);
    this._startLatLng = snapTo || e.latlng;

    L.DomEvent
      .on(document, 'mouseup', this._onMouseUp, this)
      .preventDefault(e.originalEvent);
  }
}

// hacks into PolyLine to implement snapping and to remove the polyline
// markers when they are not required anymore for finishing the line.
// Otherwise they get in the way and make it harder to create a closed
// polyline.
window.plugin.drawTools.enhancePolyLine = function() {
  // hack in snapping
  L.Polyline.Draw.prototype._onClickOld = L.Polyline.Draw.prototype._onClick;
  L.Polyline.Draw.prototype._onClick = function(e) {
    var cp = map.latLngToContainerPoint(e.target.getLatLng());
    var snapTo = window.plugin.drawTools.getSnapLatLng(cp);
    if(snapTo) e.target._latlng = snapTo;
    return this._onClickOld(e);
  }

  // remove polyline markers because they get in the way
  L.Polyline.Draw.prototype._updateMarkerHandler = function() {
    if(!this._markers) return;

    if(this._markers.length >= 2)
      this._markerGroup.removeLayer(this._markers.shift());

    if(this._markers.length >= 1)
      this._markers[this._markers.length - 1].on('click', this._finishShape, this);
  }
}

// given a container point it tries to find the most suitable portal to
// snap to. It takes the CircleMarker’s radius and weight into account.
// Will return null if nothing to snap to or a LatLng instance.
window.plugin.drawTools.getSnapLatLng = function(containerPoint) {
  var candidates = [];
  $.each(window.portals, function(guid, portal) {
    var ll = portal.getLatLng();
    var pp = map.latLngToContainerPoint(ll);
    var size = portal.options.weight + portal.options.radius;
    var dist = pp.distanceTo(containerPoint);
    if(dist > size) return true;
    candidates.push([dist, ll]);
  });

  if(candidates.length === 0) return;
  candidates = candidates.sort(function(a, b) { return a[0]-b[0]; });
  return candidates[0][1];
}

window.plugin.drawTools.boot = function() {
  plugin.drawTools.enhanceCircle();
  plugin.drawTools.enhancePolyLine();
  plugin.drawTools.addStyles();
  plugin.drawTools.addDrawControl();

  window.plugin.drawTools.drawnItems = new L.LayerGroup();
  var drawnItems = window.plugin.drawTools.drawnItems;
  map.on('draw:poly-created', function (e) {
    drawnItems.addLayer(e.poly);
  });

  map.on('draw:circle-created', function (e) {
    drawnItems.addLayer(e.circ);
  });

  map.on('draw:marker-created', function (e) {
    drawnItems.addLayer(e.marker);
    //e.marker.dragging.enable();
  });

  window.layerChooser.addOverlay(drawnItems, 'Drawn Items');
  map.addLayer(drawnItems);
}

var setup =  window.plugin.drawTools.loadExternals;

// PLUGIN END //////////////////////////////////////////////////////////

if(window.iitcLoaded && typeof setup === 'function') {
  setup();
} else {
  if(window.bootPlugins)
    window.bootPlugins.push(setup);
  else
    window.bootPlugins = [setup];
}
} // wrapper end
// inject code into site context
var script = document.createElement('script');
script.appendChild(document.createTextNode('('+ wrapper +')();'));
(document.body || document.head || document.documentElement).appendChild(script);
<commit_msg>allow dragging of markers<commit_after>// ==UserScript==
// @id             iitc-plugin-draw-tools@breunigs
// @name           iitc: draw tools
// @version        0.1
// @namespace      https://github.com/breunigs/ingress-intel-total-conversion
// @updateURL      https://raw.github.com/breunigs/ingress-intel-total-conversion/gh-pages/plugins/draw-tools.user.js
// @downloadURL    https://raw.github.com/breunigs/ingress-intel-total-conversion/gh-pages/plugins/draw-tools.user.js
// @description    Allows you to draw things into the current map so you may plan your next move
// @include        http://www.ingress.com/intel*
// @match          http://www.ingress.com/intel*
// ==/UserScript==

function wrapper() {
// ensure plugin framework is there, even if iitc is not yet loaded
if(typeof window.plugin !== 'function') window.plugin = function() {};

// PLUGIN START ////////////////////////////////////////////////////////

var DRAW_TOOLS_SHAPE_OPTIONS = {
  color: '#BB56FF',
  fill: false,
  opacity: 1,
  weight: 4,
  clickable: false
};

// use own namespace for plugin
window.plugin.drawTools = function() {};

window.plugin.drawTools.loadExternals = function() {
  var base = 'http://breunigs.github.com/ingress-intel-total-conversion/dist';
  //~ var base = 'http://0.0.0.0:8000/dist';
  $('head').append('<link rel="stylesheet" href="'+base+'/leaflet.draw.0.1.6.css" />');
  load(base+'/leaflet.draw.0.1.6.js').thenRun(window.plugin.drawTools.boot);
  // overwrite default Leaflet Marker icon.
  L.Icon.Default.imagePath = base + '/images';
}

window.plugin.drawTools.addStyles = function() {
  $('head').append('');
}

// renders buttons which are not originally part of Leaflet.draw, such
// as the clear-drawings button.
window.plugin.drawTools.addCustomButtons = function() {
  $('.leaflet-control-draw .leaflet-bar-part-bottom').removeClass('leaflet-bar-part-bottom');

  var undo = $('⎌')
    .click(function() {
      var last = null;
      window.plugin.drawTools.drawnItems.eachLayer(function(l) {
        last = l;
      });
      if(last) window.plugin.drawTools.drawnItems.removeLayer(last);
    }
  );

  var clear = $('✗')
    .click(function() {
      window.plugin.drawTools.drawnItems.clearLayers();
    }
  );

  $('.leaflet-control-draw').append(undo).append(clear);
}

// renders the draw control buttons in the top left corner
window.plugin.drawTools.addDrawControl = function() {
  var drawControl = new L.Control.Draw({
    rectangle: false,
    polygon: false,

    polyline: {
      shapeOptions: DRAW_TOOLS_SHAPE_OPTIONS
    },

    circle: {
      shapeOptions: DRAW_TOOLS_SHAPE_OPTIONS
    }
  });
  map.addControl(drawControl);
  plugin.drawTools.addCustomButtons();
}

// hacks into circle to render the radius of the circle while drawing
// and to allow the center of the circle to snap to a nearby portal.
window.plugin.drawTools.enhanceCircle = function() {
  // replace _onMouseMove function to also display the radius of the
  // circle
  L.Circle.Draw.prototype._onMouseMove = function (e) {
    var layerPoint = e.layerPoint,
        latlng = e.latlng;

    this._updateLabelPosition(layerPoint);
    if (this._isDrawing) {
      var dist = this._startLatLng.distanceTo(latlng);
      dist = dist  > 1000
              ? (dist  / 1000).toFixed(2) + ' km'
              : Math.ceil(dist) + ' m';
      this._updateLabelText({
        text: 'Release mouse to finish drawing. ',
        subtext: 'radius: ' +dist }
      );
      this._drawShape(latlng);
    }
  }

  // replace _onMouseDown to implement snapping
  L.Circle.Draw.prototype._onMouseDown = function (e) {
    this._isDrawing = true;

    var snapTo = window.plugin.drawTools.getSnapLatLng(e.containerPoint);
    this._startLatLng = snapTo || e.latlng;

    L.DomEvent
      .on(document, 'mouseup', this._onMouseUp, this)
      .preventDefault(e.originalEvent);
  }
}

// hacks into PolyLine to implement snapping and to remove the polyline
// markers when they are not required anymore for finishing the line.
// Otherwise they get in the way and make it harder to create a closed
// polyline.
window.plugin.drawTools.enhancePolyLine = function() {
  // hack in snapping
  L.Polyline.Draw.prototype._onClickOld = L.Polyline.Draw.prototype._onClick;
  L.Polyline.Draw.prototype._onClick = function(e) {
    var cp = map.latLngToContainerPoint(e.target.getLatLng());
    var snapTo = window.plugin.drawTools.getSnapLatLng(cp);
    if(snapTo) e.target._latlng = snapTo;
    return this._onClickOld(e);
  }

  // remove polyline markers because they get in the way
  L.Polyline.Draw.prototype._updateMarkerHandler = function() {
    if(!this._markers) return;

    if(this._markers.length >= 2)
      this._markerGroup.removeLayer(this._markers.shift());

    if(this._markers.length >= 1)
      this._markers[this._markers.length - 1].on('click', this._finishShape, this);
  }
}

// given a container point it tries to find the most suitable portal to
// snap to. It takes the CircleMarker’s radius and weight into account.
// Will return null if nothing to snap to or a LatLng instance.
window.plugin.drawTools.getSnapLatLng = function(containerPoint) {
  var candidates = [];
  $.each(window.portals, function(guid, portal) {
    var ll = portal.getLatLng();
    var pp = map.latLngToContainerPoint(ll);
    var size = portal.options.weight + portal.options.radius;
    var dist = pp.distanceTo(containerPoint);
    if(dist > size) return true;
    candidates.push([dist, ll]);
  });

  if(candidates.length === 0) return;
  candidates = candidates.sort(function(a, b) { return a[0]-b[0]; });
  return candidates[0][1];
}

window.plugin.drawTools.boot = function() {
  plugin.drawTools.enhanceCircle();
  plugin.drawTools.enhancePolyLine();
  plugin.drawTools.addStyles();
  plugin.drawTools.addDrawControl();

  window.plugin.drawTools.drawnItems = new L.LayerGroup();
  var drawnItems = window.plugin.drawTools.drawnItems;
  map.on('draw:poly-created', function (e) {
    drawnItems.addLayer(e.poly);
  });

  map.on('draw:circle-created', function (e) {
    drawnItems.addLayer(e.circ);
  });

  map.on('draw:marker-created', function (e) {
    drawnItems.addLayer(e.marker);
    e.marker.dragging.enable();
  });

  window.layerChooser.addOverlay(drawnItems, 'Drawn Items');
  map.addLayer(drawnItems);
}

var setup =  window.plugin.drawTools.loadExternals;

// PLUGIN END //////////////////////////////////////////////////////////

if(window.iitcLoaded && typeof setup === 'function') {
  setup();
} else {
  if(window.bootPlugins)
    window.bootPlugins.push(setup);
  else
    window.bootPlugins = [setup];
}
} // wrapper end
// inject code into site context
var script = document.createElement('script');
script.appendChild(document.createTextNode('('+ wrapper +')();'));
(document.body || document.head || document.documentElement).appendChild(script);
<|endoftext|>


<commit_before>/*
 * Copyright 2009 Matthew Eernisse (mde@fleegix.org)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
*/
if (typeof fleegix == 'undefined') { var fleegix = {}; }
if (typeof fleegix.event == 'undefined') {
  throw('fleegix.event.delegator depends on the base fleegix.event module in fleegix.js.'); }

fleegix.event.Delegator = function (containerNode, actions,
  options) {
  var _this = this;
  this.containerNode = containerNode;
  // List of CSS class names and their corresponding
  // click-actions
  this.actions = actions || {};
  // Optional execution context for called actions
  var opts = options || {};
  this.context = opts.context || null;
  // Assume we want to stop any further action
  this.stopPropagation =
    typeof opts.stopPropagation != 'undefined' ?
    opts.stopPropagation : true;
  this.preventDefault =
    typeof opts.preventDefault != 'undefined' ?
    opts.preventDefault : true;

  // Add another action
  this.addClickAction = function (className, act) {
    this.actions[className] = act;
  };
  this.getClickAction = function (e) {
    var actions = this.actions;
    var containerNode = this.containerNode;
    var node = e.target;
    var topNode = containerNode || document.body;
    // Look upward from the click target to the
    // a predefined parent -- if any of the parentNodes
    // have an action class attached, return the
    // class and bail out
    while (node.parentNode && node != topNode) {
      if (node.className) {
        for (var c in actions) {
          // Look through class name(s) set on the node
          var names = node.className.split(' ');
          for (var i = 0; i < names.length; i++) {
            if (names[i] == c) {
              return c;
            }
          }
        }
      }
      node = node.parentNode;
    }
    return null;
  };
  this.handleClick = function (e) {
    var context = this.context;
    var key = _this.getClickAction(e);
    // If there's a CSS class in the click chain with
    // a matching action, exec the action
    if (key) {
      var act = this.actions[key];
      if (context) {
        act.call(context, e);
      }
      else {
        act(e);
      }
      if (this.stopPropagation) {
        fleegix.event.stopPropagation(e);
      }
      if (this.preventDefault) {
        fleegix.event.preventDefault(e);
      }
    }
  };
  fleegix.event.listen(containerNode, 'onclick',
    this, 'handleClick');
};

<commit_msg>Allow delegation of various event-listener types, not just clicks.<commit_after>/*
 * Copyright 2009 Matthew Eernisse (mde@fleegix.org)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
*/
if (typeof fleegix == 'undefined') { var fleegix = {}; }
if (typeof fleegix.event == 'undefined') {
  throw('fleegix.event.delegator depends on the base fleegix.event module in fleegix.js.'); }

// Params:
// containerNode -- Container node for all the interactivity
//  One event listener for each specified event type is set
//  on this node, and events are dispatched according to the
//  desired class names
// actions -- defines the event listeners to set, and the
//  actions to take based on CSS class. Organized by
//  className/eventType, like so:
//  { classNameA: {
//      click: function () {},
//      mouseover: function () {},
//      mouseout: function () {}
//    },
//    classNameB: {
//      click: function () {}
//    }
// options -- stopPropagation/preventDefault, both default
//   to true, and idTransformer, a function to run on any
//   passed-back DOM-node id
fleegix.event.Delegator = function (containerNode, actions,
  options) {
  var _this = this; // Scope
  // Supported types of event handlers
  var _supportedEvents = {
    'click': true,
    'mousedown': true,
    'mouseup': true,
    'mouseover': true,
    'mouseout': true
  };
  // Set up a particular type of event listener
  // if it doesn't already exist
  var _initEventType = function (p) {
    if (typeof _this.actions[p] == 'undefined') {
      _this.actions[p] = {};
    }
  };

  // Container node for all the interactivity
  this.containerNode = containerNode;
  // Optional execution context for called actions
  var opts = options || {};
  this.context = opts.context || null;
  // Assume we want to stop any further action
  this.stopPropagation =
    typeof opts.stopPropagation != 'undefined' ?
    opts.stopPropagation : true;
  this.preventDefault =
    typeof opts.preventDefault != 'undefined' ?
    opts.preventDefault : true;
  this.idTransformer = opts.idTransformer || null;

  // List of CSS class names and their corresponding
  // event-actions -- organized like so:
  // click: {
  //  classNameA: function () {},
  //  classNameB: function () {}
  // },
  // mouseover: {
  //  classNameC: function () {}
  // }
  // Etc.
  this.actions = {};
  // Look for specific event types to listen for
  // Maps param organized as obj[className][eventType]
  // to internal structure of obj[eventType][className]
  // so we only have to recurse upward through the DOM
  // tree looking at class names for the desired event
  // types
  this.addAction = function (className, act) {
    var useDefault = true;
    for (var p in act) {
      if (typeof _supportedEvents[p] != 'undefined') {
        _initEventType(p);
        this.actions[p][className] = act[p];
        useDefault = false;
      }
    }
    if (useDefault) {
      _initEventType('click');
      this.actions.click[className] = act;
    }
    this.actions[className] = act;
  };
  this.getAction = function (e) {
    var actions = this.actions[e.type];
    var containerNode = this.containerNode;
    var node = e.target;
    var topNode = containerNode || document.body;
    // Look upward from the click target to the
    // a predefined parent -- if any of the parentNodes
    // have an action class attached, return the
    // class and bail out
    while (node.parentNode && node != topNode) {
      if (node.className) {
        for (var c in actions) {
          // Look through class name(s) set on the node
          var names = node.className.split(' ');
          for (var i = 0; i < names.length; i++) {
            if (names[i] == c) {
              return c;
            }
          }
        }
      }
      node = node.parentNode;
    }
    return null;
  };
  this.handleEvent = function (e) {
    var context = this.context;
    var key = _this.getAction(e);
    // If there's a CSS class in the click chain with
    // a matching action, exec the action, passing it
    // the event, and the id of the closest node above
    // the event target in the tree
    if (key) {
      var act = this.actions[e.type][key];
      var id = fleegix.event.getSrcElementId(e);
      if (id && typeof this.idTransformer == 'function') {
        id = this.idTransformer(id);
      }
      if (context) {
        act.call(context, e, id);
      }
      else {
        act(e, id);
      }
      if (this.stopPropagation) {
        fleegix.event.stopPropagation(e);
      }
      if (this.preventDefault) {
        fleegix.event.preventDefault(e);
      }
    }
  };

  for (var className in actions) {
    this.addAction(className, actions[className]);
  }

  for (var evt in this.actions) {
    fleegix.event.listen(containerNode, 'on' + evt,
        this, 'handleEvent');
  }
};

<|endoftext|>