<commit_before>/**
 * @requires javelin-install
 *           javelin-event
 * @provides javelin-vector
 *
 * @javelin-installs JX.$V
 *
 * @javelin
 */

/**
 * Convenience function that returns a @{class:JX.Vector} instance. This allows
 * you to concisely write things like:
 *
 *  JX.$V(x, y).add(10, 10);                // Explicit coordinates.
 *  JX.$V(node).add(50, 50).setDim(node);   // Position of a node.
 *
 * @param number|Node         If a node, returns the node's position vector.
 *                            If numeric, the x-coordinate for the new vector.
 * @param number?             The y-coordinate for the new vector.
 * @return @{class:JX.Vector} New vector.
 *
 * @group dom
 */
JX.$V = function(x, y) {
  return new JX.Vector(x, y);
};

/**
 * Query and update positions and dimensions of nodes (and other things) within
 * within a document. Each vector has two elements, 'x' and 'y', which usually
 * represent width/height ('dimension vector') or left/top ('position vector').
 *
 * Vectors are used to manage the sizes and positions of elements, events,
 * the document, and the viewport (the visible section of the document, i.e.
 * how much of the page the user can actually see in their browser window).
 * Unlike most Javelin classes, @{class:JX.Vector} exposes two bare properties,
 * 'x' and 'y'. You can read and manipulate these directly:
 *
 *   // Give the user information about elements when they click on them.
 *   JX.Stratcom.listen(
 *     'click',
 *     null,
 *     function(e) {
 *       var p = new JX.Vector(e);
 *       var d = JX.Vector.getDim(e.getTarget());
 *
 *       alert('You clicked at <' + p.x + ',' + p.y + '> and the element ' +
 *             'you clicked is ' + d.x + 'px wide and ' + d.y + 'px high.');
 *     });
 *
 * You can also update positions and dimensions using vectors:
 *
 *   // When the user clicks on something, make it 10px wider and 10px taller.
 *   JX.Stratcom.listen(
 *     'click',
 *     null,
 *     function(e) {
 *       var target = e.getTarget();
 *       JX.$V(target).add(10, 10).setDim(target);
 *     });
 *
 * Additionally, vectors can be used to query document and viewport information:
 *
 *   var v = JX.Vector.getViewport(); // Viewport (window) width and height.
 *   var d = JX.Vector.getDocument(); // Document width and height.
 *   var visible_area = parseInt(100 * (v.x * v.y) / (d.x * d.y), 10);
 *   alert('You can currently see ' + visible_area + ' % of the document.');
 *
 * The function @{function:JX.$V} provides convenience construction of common
 * vectors.
 *
 * @task query  Querying Positions and Dimensions
 * @task update Changing Positions and Dimensions
 * @task manip  Manipulating Vectors
 *
 * @group dom
 */
JX.install('Vector', {

  /**
   * Construct a vector, either from explicit coordinates or from a node
   * or event. You can pass two Numbers to construct an explicit vector:
   *
   *   var p = new JX.Vector(35, 42);
   *
   * Otherwise, you can pass a @{class:JX.Event} or a Node to implicitly
   * construct a vector:
   *
   *   var q = new JX.Vector(some_event);
   *   var r = new JX.Vector(some_node);
   *
   * These are just like calling JX.Vector.getPos() on the @{class:JX.Event} or
   * Node.
   *
   * For convenience, @{function:JX.$V} constructs a new vector so you don't
   * need to use the 'new' keyword. That is, these are equivalent:
   *
   *   var s = new JX.Vector(x, y);
   *   var t = JX.$V(x, y);
   *
   * Methods like @{method:getScroll}, @{method:getViewport} and
   * @{method:getDocument} also create new vectors.
   *
   * Once you have a vector, you can manipulate it with add():
   *
   *   var u = JX.$V(35, 42);
   *   var v = u.add(5, -12); // v = <40, 30>
   *
   * @param wild      'x' component of the vector, or a @{class:JX.Event}, or a
   *                  Node.
   * @param Number?   If providing an 'x' component, the 'y' component of the
   *                  vector.
   * @return @{class:JX.Vector} Specified vector.
   * @task query
   */
  construct : function(x, y) {
    if (typeof y == 'undefined') {
      return JX.Vector.getPos(x);
    }

    this.x = (x === null) ? null : parseFloat(x);
    this.y = (y === null) ? null : parseFloat(y);
  },

  members : {
    x : null,
    y : null,

    /**
     * Move a node around by setting the position of a Node to the vector's
     * coordinates. For instance, if you want to move an element to the top left
     * corner of the document, you could do this (assuming it has 'position:
     * absolute'):
     *
     *   JX.$V(0, 0).setPos(node);
     *
     * @param Node Node to move.
     * @return this
     * @task update
     */
    setPos : function(node) {
      node.style.left = (this.x === null) ? '' : (parseInt(this.x, 10) + 'px');
      node.style.top  = (this.y === null) ? '' : (parseInt(this.y, 10) + 'px');
      return this;
    },

    /**
     * Change the size of a node by setting its dimensions to the vector's
     * coordinates. For instance, if you want to change an element to be 100px
     * by 100px:
     *
     *   JX.$V(100, 100).setDim(node);
     *
     * Or if you want to expand a node's dimensions by 50px:
     *
     *   JX.$V(node).add(50, 50).setDim(node);
     *
     * @param Node Node to resize.
     * @return this
     * @task update
     */
    setDim : function(node) {
      node.style.width =
        (this.x === null)
          ? ''
          : (parseInt(this.x, 10) + 'px');
      node.style.height =
        (this.y === null)
          ? ''
          : (parseInt(this.y, 10) + 'px');
      return this;
    },

    /**
     * Change a vector's x and y coordinates by adding numbers to them, or
     * adding the coordinates of another vector. For example:
     *
     *   var u = JX.$V(3, 4).add(100, 200); // u = <103, 204>
     *
     * You can also add another vector:
     *
     *   var q = JX.$V(777, 999);
     *   var r = JX.$V(1000, 2000);
     *   var s = q.add(r); // s = <1777, 2999>
     *
     * Note that this method returns a new vector. It does not modify the
     * 'this' vector.
     *
     * @param wild      Value to add to the vector's x component, or another
     *                  vector.
     * @param Number?   Value to add to the vector's y component.
     * @return @{class:JX.Vector} New vector, with summed components.
     * @task manip
     */
    add : function(x, y) {
      if (x instanceof JX.Vector) {
        y = x.y;
        x = x.x;
      }
      return new JX.Vector(this.x + parseFloat(x), this.y + parseFloat(y));
    }
  },

  statics : {
    _viewport: null,

    /**
     * Determine where in a document an element is (or where an event, like
     * a click, occurred) by building a new vector containing the position of a
     * Node or @{class:JX.Event}. The 'x' component of the vector will
     * correspond to the pixel offset of the argument relative to the left edge
     * of the document, and the 'y' component will correspond to the pixel
     * offset of the argument relative to the top edge of the document. Note
     * that all vectors are generated in document coordinates, so the scroll
     * position does not affect them.
     *
     * See also @{method:getDim}, used to determine an element's dimensions.
     *
     * @param  Node|@{class:JX.Event}  Node or event to determine the position
     *                                 of.
     * @return @{class:JX.Vector}      New vector with the argument's position.
     * @task query
     */
    getPos : function(node) {
      JX.Event && (node instanceof JX.Event) && (node = node.getRawEvent());

      if (('pageX' in node) || ('clientX' in node)) {
        var c = JX.Vector._viewport;
        return new JX.Vector(
          node.pageX || (node.clientX + c.scrollLeft),
          node.pageY || (node.clientY + c.scrollTop)
        );
      }

      var x = 0;
      var y = 0;
      do {
        x += node.offsetLeft;
        y += node.offsetTop;
        node = node.offsetParent;
      } while (node && node != document.body);

      return new JX.Vector(x, y);
    },

    /**
     * Determine the width and height of a node by building a new vector with
     * dimension information. The 'x' component of the vector will correspond
     * to the element's width in pixels, and the 'y' component will correspond
     * to its height in pixels.
     *
     * See also @{method:getPos}, used to determine an element's position.
     *
     * @param  Node      Node to determine the display size of.
     * @return @{JX.$V}  New vector with the node's dimensions.
     * @task query
     */
    getDim : function(node) {
      return new JX.Vector(node.offsetWidth, node.offsetHeight);
    },

    /**
     * Determine the current scroll position by building a new vector where
     * the 'x' component corresponds to how many pixels the user has scrolled
     * from the left edge of the document, and the 'y' component corresponds to
     * how many pixels the user has scrolled from the top edge of the document.
     *
     * See also @{method:getViewport}, used to determine the size of the
     * viewport.
     *
     * @return @{JX.$V}  New vector with the document scroll position.
     * @task query
     */
    getScroll : function() {
      // We can't use JX.Vector._viewport here because there's diversity between
      // browsers with respect to where position/dimension and scroll position
      // information is stored.
      var b = document.body;
      var e = document.documentElement;
      return new JX.Vector(
        window.pageXOffset || b.scrollLeft || e.scrollLeft,
        window.pageYOffset || b.scrollTop || e.scrollTop
      );
    },

    /**
     * Determine the size of the viewport (basically, the browser window) by
     * building a new vector where the 'x' component corresponds to the width
     * of the viewport in pixels and the 'y' component corresponds to the height
     * of the viewport in pixels.
     *
     * See also @{method:getScroll}, used to determine the position of the
     * viewport, and @{method:getDocument}, used to determine the size of the
     * entire document.
     *
     * @return @{class:JX.Vector}  New vector with the viewport dimensions.
     * @task query
     */
    getViewport : function() {
      var c = JX.Vector._viewport;
      return new JX.Vector(
        window.innerWidth || c.clientWidth || 0,
        window.innerHeight || c.clientHeight || 0
      );
    },

    /**
     * Determine the size of the document, including any area outside the
     * current viewport which the user would need to scroll in order to see, by
     * building a new vector where the 'x' component corresponds to the document
     * width in pixels and the 'y' component corresponds to the document height
     * in pixels.
     *
     * @return @{class:JX.Vector} New vector with the document dimensions.
     * @task query
     */
    getDocument : function() {
      var c = JX.Vector._viewport;
      return new JX.Vector(c.scrollWidth || 0, c.scrollHeight || 0);
    }
  },

  /**
   * On initialization, the browser-dependent viewport root is determined and
   * stored.
   *
   * In ##__DEV__##, @{class:JX.Vector} installs a toString() method so
   * vectors print in a debuggable way:
   *
   *   <23, 92>
   *
   * This string representation of vectors is not available in a production
   * context.
   *
   * @return void
   */
  initialize : function() {
    JX.Vector._viewport = document.documentElement || document.body;

    if (__DEV__) {
      JX.Vector.prototype.toString = function() {
        return '<' + this.x + ', ' + this.y + '>';
      };
    }
  }

});
<commit_msg>Vector - fix Vector.getPos() to handle crazier elements<commit_after>/**
 * @requires javelin-install
 *           javelin-event
 * @provides javelin-vector
 *
 * @javelin-installs JX.$V
 *
 * @javelin
 */

/**
 * Convenience function that returns a @{class:JX.Vector} instance. This allows
 * you to concisely write things like:
 *
 *  JX.$V(x, y).add(10, 10);                // Explicit coordinates.
 *  JX.$V(node).add(50, 50).setDim(node);   // Position of a node.
 *
 * @param number|Node         If a node, returns the node's position vector.
 *                            If numeric, the x-coordinate for the new vector.
 * @param number?             The y-coordinate for the new vector.
 * @return @{class:JX.Vector} New vector.
 *
 * @group dom
 */
JX.$V = function(x, y) {
  return new JX.Vector(x, y);
};

/**
 * Query and update positions and dimensions of nodes (and other things) within
 * within a document. Each vector has two elements, 'x' and 'y', which usually
 * represent width/height ('dimension vector') or left/top ('position vector').
 *
 * Vectors are used to manage the sizes and positions of elements, events,
 * the document, and the viewport (the visible section of the document, i.e.
 * how much of the page the user can actually see in their browser window).
 * Unlike most Javelin classes, @{class:JX.Vector} exposes two bare properties,
 * 'x' and 'y'. You can read and manipulate these directly:
 *
 *   // Give the user information about elements when they click on them.
 *   JX.Stratcom.listen(
 *     'click',
 *     null,
 *     function(e) {
 *       var p = new JX.Vector(e);
 *       var d = JX.Vector.getDim(e.getTarget());
 *
 *       alert('You clicked at <' + p.x + ',' + p.y + '> and the element ' +
 *             'you clicked is ' + d.x + 'px wide and ' + d.y + 'px high.');
 *     });
 *
 * You can also update positions and dimensions using vectors:
 *
 *   // When the user clicks on something, make it 10px wider and 10px taller.
 *   JX.Stratcom.listen(
 *     'click',
 *     null,
 *     function(e) {
 *       var target = e.getTarget();
 *       JX.$V(target).add(10, 10).setDim(target);
 *     });
 *
 * Additionally, vectors can be used to query document and viewport information:
 *
 *   var v = JX.Vector.getViewport(); // Viewport (window) width and height.
 *   var d = JX.Vector.getDocument(); // Document width and height.
 *   var visible_area = parseInt(100 * (v.x * v.y) / (d.x * d.y), 10);
 *   alert('You can currently see ' + visible_area + ' % of the document.');
 *
 * The function @{function:JX.$V} provides convenience construction of common
 * vectors.
 *
 * @task query  Querying Positions and Dimensions
 * @task update Changing Positions and Dimensions
 * @task manip  Manipulating Vectors
 *
 * @group dom
 */
JX.install('Vector', {

  /**
   * Construct a vector, either from explicit coordinates or from a node
   * or event. You can pass two Numbers to construct an explicit vector:
   *
   *   var p = new JX.Vector(35, 42);
   *
   * Otherwise, you can pass a @{class:JX.Event} or a Node to implicitly
   * construct a vector:
   *
   *   var q = new JX.Vector(some_event);
   *   var r = new JX.Vector(some_node);
   *
   * These are just like calling JX.Vector.getPos() on the @{class:JX.Event} or
   * Node.
   *
   * For convenience, @{function:JX.$V} constructs a new vector so you don't
   * need to use the 'new' keyword. That is, these are equivalent:
   *
   *   var s = new JX.Vector(x, y);
   *   var t = JX.$V(x, y);
   *
   * Methods like @{method:getScroll}, @{method:getViewport} and
   * @{method:getDocument} also create new vectors.
   *
   * Once you have a vector, you can manipulate it with add():
   *
   *   var u = JX.$V(35, 42);
   *   var v = u.add(5, -12); // v = <40, 30>
   *
   * @param wild      'x' component of the vector, or a @{class:JX.Event}, or a
   *                  Node.
   * @param Number?   If providing an 'x' component, the 'y' component of the
   *                  vector.
   * @return @{class:JX.Vector} Specified vector.
   * @task query
   */
  construct : function(x, y) {
    if (typeof y == 'undefined') {
      return JX.Vector.getPos(x);
    }

    this.x = (x === null) ? null : parseFloat(x);
    this.y = (y === null) ? null : parseFloat(y);
  },

  members : {
    x : null,
    y : null,

    /**
     * Move a node around by setting the position of a Node to the vector's
     * coordinates. For instance, if you want to move an element to the top left
     * corner of the document, you could do this (assuming it has 'position:
     * absolute'):
     *
     *   JX.$V(0, 0).setPos(node);
     *
     * @param Node Node to move.
     * @return this
     * @task update
     */
    setPos : function(node) {
      node.style.left = (this.x === null) ? '' : (parseInt(this.x, 10) + 'px');
      node.style.top  = (this.y === null) ? '' : (parseInt(this.y, 10) + 'px');
      return this;
    },

    /**
     * Change the size of a node by setting its dimensions to the vector's
     * coordinates. For instance, if you want to change an element to be 100px
     * by 100px:
     *
     *   JX.$V(100, 100).setDim(node);
     *
     * Or if you want to expand a node's dimensions by 50px:
     *
     *   JX.$V(node).add(50, 50).setDim(node);
     *
     * @param Node Node to resize.
     * @return this
     * @task update
     */
    setDim : function(node) {
      node.style.width =
        (this.x === null)
          ? ''
          : (parseInt(this.x, 10) + 'px');
      node.style.height =
        (this.y === null)
          ? ''
          : (parseInt(this.y, 10) + 'px');
      return this;
    },

    /**
     * Change a vector's x and y coordinates by adding numbers to them, or
     * adding the coordinates of another vector. For example:
     *
     *   var u = JX.$V(3, 4).add(100, 200); // u = <103, 204>
     *
     * You can also add another vector:
     *
     *   var q = JX.$V(777, 999);
     *   var r = JX.$V(1000, 2000);
     *   var s = q.add(r); // s = <1777, 2999>
     *
     * Note that this method returns a new vector. It does not modify the
     * 'this' vector.
     *
     * @param wild      Value to add to the vector's x component, or another
     *                  vector.
     * @param Number?   Value to add to the vector's y component.
     * @return @{class:JX.Vector} New vector, with summed components.
     * @task manip
     */
    add : function(x, y) {
      if (x instanceof JX.Vector) {
        y = x.y;
        x = x.x;
      }
      return new JX.Vector(this.x + parseFloat(x), this.y + parseFloat(y));
    }
  },

  statics : {
    _viewport: null,

    /**
     * Determine where in a document an element is (or where an event, like
     * a click, occurred) by building a new vector containing the position of a
     * Node or @{class:JX.Event}. The 'x' component of the vector will
     * correspond to the pixel offset of the argument relative to the left edge
     * of the document, and the 'y' component will correspond to the pixel
     * offset of the argument relative to the top edge of the document. Note
     * that all vectors are generated in document coordinates, so the scroll
     * position does not affect them.
     *
     * See also @{method:getDim}, used to determine an element's dimensions.
     *
     * @param  Node|@{class:JX.Event}  Node or event to determine the position
     *                                 of.
     * @return @{class:JX.Vector}      New vector with the argument's position.
     * @task query
     */
    getPos : function(node) {
      JX.Event && (node instanceof JX.Event) && (node = node.getRawEvent());

      if (('pageX' in node) || ('clientX' in node)) {
        var c = JX.Vector._viewport;
        return new JX.Vector(
          node.pageX || (node.clientX + c.scrollLeft),
          node.pageY || (node.clientY + c.scrollTop)
        );
      }

      var x = 0;
      var y = 0;
      do {
        var offsetParent = node.offsetParent;
        var scrollLeft = 0;
        var scrollTop = 0;
        if (offsetParent && offsetParent != document.body) {
          scrollLeft = offsetParent.scrollLeft;
          scrollTop = offsetParent.scrollTop;
        }
        x += (node.offsetLeft - scrollLeft);
        y += (node.offsetTop - scrollTop);
        node = offsetParent;
      } while (node && node != document.body);

      return new JX.Vector(x, y);
    },

    /**
     * Determine the width and height of a node by building a new vector with
     * dimension information. The 'x' component of the vector will correspond
     * to the element's width in pixels, and the 'y' component will correspond
     * to its height in pixels.
     *
     * See also @{method:getPos}, used to determine an element's position.
     *
     * @param  Node      Node to determine the display size of.
     * @return @{JX.$V}  New vector with the node's dimensions.
     * @task query
     */
    getDim : function(node) {
      return new JX.Vector(node.offsetWidth, node.offsetHeight);
    },

    /**
     * Determine the current scroll position by building a new vector where
     * the 'x' component corresponds to how many pixels the user has scrolled
     * from the left edge of the document, and the 'y' component corresponds to
     * how many pixels the user has scrolled from the top edge of the document.
     *
     * See also @{method:getViewport}, used to determine the size of the
     * viewport.
     *
     * @return @{JX.$V}  New vector with the document scroll position.
     * @task query
     */
    getScroll : function() {
      // We can't use JX.Vector._viewport here because there's diversity between
      // browsers with respect to where position/dimension and scroll position
      // information is stored.
      var b = document.body;
      var e = document.documentElement;
      return new JX.Vector(
        window.pageXOffset || b.scrollLeft || e.scrollLeft,
        window.pageYOffset || b.scrollTop || e.scrollTop
      );
    },

    /**
     * Determine the size of the viewport (basically, the browser window) by
     * building a new vector where the 'x' component corresponds to the width
     * of the viewport in pixels and the 'y' component corresponds to the height
     * of the viewport in pixels.
     *
     * See also @{method:getScroll}, used to determine the position of the
     * viewport, and @{method:getDocument}, used to determine the size of the
     * entire document.
     *
     * @return @{class:JX.Vector}  New vector with the viewport dimensions.
     * @task query
     */
    getViewport : function() {
      var c = JX.Vector._viewport;
      return new JX.Vector(
        window.innerWidth || c.clientWidth || 0,
        window.innerHeight || c.clientHeight || 0
      );
    },

    /**
     * Determine the size of the document, including any area outside the
     * current viewport which the user would need to scroll in order to see, by
     * building a new vector where the 'x' component corresponds to the document
     * width in pixels and the 'y' component corresponds to the document height
     * in pixels.
     *
     * @return @{class:JX.Vector} New vector with the document dimensions.
     * @task query
     */
    getDocument : function() {
      var c = JX.Vector._viewport;
      return new JX.Vector(c.scrollWidth || 0, c.scrollHeight || 0);
    }
  },

  /**
   * On initialization, the browser-dependent viewport root is determined and
   * stored.
   *
   * In ##__DEV__##, @{class:JX.Vector} installs a toString() method so
   * vectors print in a debuggable way:
   *
   *   <23, 92>
   *
   * This string representation of vectors is not available in a production
   * context.
   *
   * @return void
   */
  initialize : function() {
    JX.Vector._viewport = document.documentElement || document.body;

    if (__DEV__) {
      JX.Vector.prototype.toString = function() {
        return '<' + this.x + ', ' + this.y + '>';
      };
    }
  }

});
<|endoftext|>


<commit_before>#!/usr/bin/env node

/*
       Licensed to the Apache Software Foundation (ASF) under one
       or more contributor license agreements.  See the NOTICE file
       distributed with this work for additional information
       regarding copyright ownership.  The ASF licenses this file
       to you 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.
*/

var shell   = require('shelljs'),
    spawn   = require('./spawn'),
    Q       = require('q'),
    path    = require('path'),
    fs      = require('fs'),
    ROOT    = path.join(__dirname, '..', '..');
var check_reqs = require('./check_reqs');

var LOCAL_PROPERTIES_TEMPLATE =
    '# This file is automatically generated.\n' +
    '# Do not modify this file -- YOUR CHANGES WILL BE ERASED!\n';

function find_files(directory, predicate) {
    if (fs.existsSync(directory)) {
        var candidates = fs.readdirSync(directory).filter(predicate).map(function(p) {
            p = path.join(directory, p);
            return { p: p, t: fs.statSync(p).mtime };
        }).sort(function(a,b) {
            return a.t > b.t ? -1 :
                   a.t < b.t ? 1 : 0;
        }).map(function(p) { return p.p; });
        return candidates;
    } else {
        console.error('ERROR : unable to find project ' + directory + ' directory, could not locate .apk');
        process.exit(2);
    }
}

function hasCustomRules() {
    return fs.existsSync(path.join(ROOT, 'custom_rules.xml'));
}

function extractProjectNameFromManifest(projectPath) {
    var manifestPath = path.join(projectPath, 'AndroidManifest.xml');
    var manifestData = fs.readFileSync(manifestPath, 'utf8');
    var m = /<activity[\s\S]*?android:name\s*=\s*"(.*?)"/i.exec(manifestData);
    if (!m) {
        throw new Error('Could not find activity name in ' + manifestPath);
    }
    return m[1];
}

function extractSubProjectPaths() {
    var data = fs.readFileSync(path.join(ROOT, 'project.properties'), 'utf8');
    var ret = {};
    var r = /^\s*android\.library\.reference\.\d+=(.*)(?:\s|$)/mg
    var m;
    while (m = r.exec(data)) {
        ret[m[1]] = 1;
    }
    return Object.keys(ret);
}

var builders = {
    ant: {
        getArgs: function(cmd) {
            var args = [cmd, '-f', path.join(ROOT, 'build.xml')];
            // custom_rules.xml is required for incremental builds.
            if (hasCustomRules()) {
                args.push('-Dout.dir=ant-build', '-Dgen.absolute.dir=ant-gen');
            }
            return args;
        },

        prepEnv: function() {
            return check_reqs.check_ant()
            .then(function() {
                // Copy in build.xml on each build so that:
                // A) we don't require the Android SDK at project creation time, and
                // B) we always use the SDK's latest version of it.
                var sdkDir = process.env['ANDROID_HOME'];
                var buildTemplate = fs.readFileSync(path.join(sdkDir, 'tools', 'lib', 'build.template'), 'utf8');
                function writeBuildXml(projectPath) {
                    var newData = buildTemplate.replace('PROJECT_NAME', extractProjectNameFromManifest(ROOT));
                    fs.writeFileSync(path.join(projectPath, 'build.xml'), newData);
                    if (!fs.existsSync(path.join(projectPath, 'local.properties'))) {
                        fs.writeFileSync(path.join(projectPath, 'local.properties'), LOCAL_PROPERTIES_TEMPLATE);
                    }
                }
                var subProjects = extractSubProjectPaths();
                writeBuildXml(ROOT);
                for (var i = 0; i < subProjects.length; ++i) {
                    writeBuildXml(path.join(ROOT, subProjects[i]));
                }
            });
        },

        /*
         * Builds the project with ant.
         * Returns a promise.
         */
        build: function(build_type) {
            // Without our custom_rules.xml, we need to clean before building.
            var ret = Q();
            if (!hasCustomRules()) {
                // clean will call check_ant() for us.
                ret = this.clean();
            }

            var builder = this;
            var args = this.getArgs(build_type == 'debug' ? 'debug' : 'release');
            return check_reqs.check_ant()
            .then(function() {
                return spawn('ant', args);
            }).then(function() {
                return builder.getOutputFiles();
            });
        },

        clean: function() {
            var args = this.getArgs('clean');
            return check_reqs.check_ant()
            .then(function() {
                return spawn('ant', args);
            });
        },

        // Find the recently-generated output APK files
        // Ant only generates one output file; return it.
        getOutputFiles: function() {
            var binDir;
            if(hasCustomRules()) {
                binDir = path.join(ROOT, 'ant-build');
            } else {
                binDir = path.join(ROOT, 'bin');
            }
            var candidates = find_files(binDir, function(candidate) { return path.extname(candidate) == '.apk'; });
            if (candidates.length === 0) {
                console.error('ERROR : No .apk found in ' + binDir + ' directory');
                process.exit(2);
            }
            console.log('Using apk: ' + candidates[0]);
            return [candidates[0]];
        }
    },
    gradle: {
        getArgs: function(cmd) {
            var lintSteps = [
                'lint',
                'lintVitalRelease',
                'compileLint',
                'copyReleaseLint',
                'copyDebugLint'
            ];
            var args = [cmd, '-b', path.join(ROOT, 'build.gradle')];
            // 10 seconds -> 6 seconds
            args.push('-Dorg.gradle.daemon=true');
            // Excluding lint: 6s-> 1.6s
            for (var i = 0; i < lintSteps.length; ++i) {
                args.push('-x', lintSteps[i]);
            }
            // Shaves another 100ms, but produces a "try at own risk" warning. Not worth it (yet):
            // args.push('-Dorg.gradle.parallel=true');
            return args;
        },

        prepEnv: function() {
            return check_reqs.check_gradle()
            .then(function() {
                // Copy the gradle wrapper on each build so that:
                // A) we don't require the Android SDK at project creation time, and
                // B) we always use the SDK's latest version of it.
                var projectPath = ROOT;
                // check_reqs ensures that this is set.
                var sdkDir = process.env['ANDROID_HOME'];
                var wrapperDir = path.join(sdkDir, 'tools', 'templates', 'gradle', 'wrapper');
                if (process.platform == 'win32') {
                    shell.cp('-f', path.join(wrapperDir, 'gradlew.bat'), projectPath);
                } else {
                    shell.cp('-f', path.join(wrapperDir, 'gradlew'), projectPath);
                }
                shell.rm('-rf', path.join(projectPath, 'gradle', 'wrapper'));
                shell.mkdir('-p', path.join(projectPath, 'gradle'));
                shell.cp('-r', path.join(wrapperDir, 'gradle', 'wrapper'), path.join(projectPath, 'gradle'));
            });
        },

        /*
         * Builds the project with gradle.
         * Returns a promise.
         */
        build: function(build_type) {
            var builder = this;
            var wrapper = path.join(ROOT, 'gradlew');
            var args = builder.getArgs('build');
            copyGradleWrapper();
            return Q().then(function() {
                return spawn(wrapper, args);
            }).then(function() {
                return builder.getOutputFiles(build_type);
            });
        },

        clean: function() {
            var builder = this;
            var wrapper = path.join(ROOT, 'gradlew');
            var args = builder.getArgs('clean');
            copyGradleWrapper();
            return Q().then(function() {
                return spawn(wrapper, args);
            });
        },

        // Find the recently-generated output APK files
        // Gradle can generate multiple output files; return all of them.
        getOutputFiles: function(build_type) {
            var binDir = path.join(ROOT, 'build', 'apk');
            var candidates = find_files(binDir, function(candidate) {
                // Need to choose between release and debug .apk.
                if (build_type === 'debug') {
                    return (path.extname(candidate) == '.apk' && candidate.indexOf('-debug-') >= 0);
                }
                if (build_type === 'release') {
                    return (path.extname(candidate) == '.apk' && candidate.indexOf('-release-') >= 0);
                }
                return path.extname(candidate) == '.apk';
            });
            return candidates;
        }
    },

    none: {
        prepEnv: function() {
            return Q();
        },
        build: function() {
            console.log('Skipping build...');
            return Q();
        },
        clean: function() {
            return Q();
        },
    }
};

function parseOpts(options) {
    // Backwards-compatibility: Allow a single string argument
    if (typeof options == "string") options = [options];

    var ret = {
        buildType: 'debug',
        buildMethod: process.env['ANDROID_BUILD'] || 'ant'
    };

    // Iterate through command line options
    for (var i=0; options && (i < options.length); ++i) {
        if (options[i].substring && options[i].substring(0,2) == "--") {
            var option = options[i].substring(2);
            switch(option) {
                case 'debug':
                case 'release':
                    ret.buildType = option;
                    break;
                case 'ant':
                case 'gradle':
                    ret.buildMethod = option;
                    break;
                case 'nobuild' :
                    ret.buildMethod = 'none';
                    break;
                default :
                    return Q.reject('Build option \'' + options[i] + '\' not recognized.');
            }
        } else {
            return Q.reject('Build option \'' + options[i] + '\' not recognized.');
        }
    }
    return ret;
}

/*
 * Builds the project with the specifed options
 * Returns a promise.
 */
module.exports.runClean = function(options) {
    var opts = parseOpts(options);
    var builder = builders[opts.buildMethod];
    return builder.prepEnv()
    .then(function() {
        return builder.clean();
    }).then(function() {
        shell.rm('-rf', path.join(ROOT, 'out'));
    });
};

/*
 * Builds the project with the specifed options
 * Returns a promise.
 */
module.exports.run = function(options) {
    var opts = parseOpts(options);

    var builder = builders[opts.buildMethod];
    return builder.prepEnv()
    .then(function() {
        return builder.build(opts.buildType);
    }).then(function(apkFiles) {
        // TODO: Rather than copy apks to out, it might be better to
        // just write out what the last .apk build was. These files
        // are used by get_apk().
        var outputDir = path.join(ROOT, 'out');
        shell.mkdir('-p', outputDir);
        for (var i=0; i < apkFiles.length; ++i) {
            shell.cp('-f', apkFiles[i], path.join(outputDir, path.basename(apkFiles[i])));
        }
    });
};

/*
 * Gets the path to the apk file, if not such file exists then
 * the script will error out. (should we error or just return undefined?)
 * This is called by the run script to install the apk to the device
 */
module.exports.get_apk = function(build_type) {
    var outputDir = path.join(ROOT, 'out');
    var candidates = find_files(outputDir, function() { return true; });
    if (candidates.length === 0) {
        console.error('ERROR : No .apk found in ' + outputDir + ' directory');
        process.exit(2);
    }
    // TODO: Use build_type here.
    console.log('Using apk: ' + candidates[0]);
    return candidates[0];
};

module.exports.help = function() {
    console.log('Usage: ' + path.relative(process.cwd(), path.join(ROOT, 'cordova', 'build')) + ' [build_type]');
    console.log('Build Types : ');
    console.log('    \'--debug\': Default build, will build project in debug mode');
    console.log('    \'--release\': will build project for release');
    console.log('    \'--ant\': Default build, will build project with ant');
    console.log('    \'--gradle\': will build project with gradle');
    console.log('    \'--nobuild\': will skip build process (can be used with run command)');
    process.exit(0);
};
<commit_msg>CB-7330 Fix dangling function call in last commit (broke gradle builds)<commit_after>#!/usr/bin/env node

/*
       Licensed to the Apache Software Foundation (ASF) under one
       or more contributor license agreements.  See the NOTICE file
       distributed with this work for additional information
       regarding copyright ownership.  The ASF licenses this file
       to you 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.
*/

var shell   = require('shelljs'),
    spawn   = require('./spawn'),
    Q       = require('q'),
    path    = require('path'),
    fs      = require('fs'),
    ROOT    = path.join(__dirname, '..', '..');
var check_reqs = require('./check_reqs');

var LOCAL_PROPERTIES_TEMPLATE =
    '# This file is automatically generated.\n' +
    '# Do not modify this file -- YOUR CHANGES WILL BE ERASED!\n';

function find_files(directory, predicate) {
    if (fs.existsSync(directory)) {
        var candidates = fs.readdirSync(directory).filter(predicate).map(function(p) {
            p = path.join(directory, p);
            return { p: p, t: fs.statSync(p).mtime };
        }).sort(function(a,b) {
            return a.t > b.t ? -1 :
                   a.t < b.t ? 1 : 0;
        }).map(function(p) { return p.p; });
        return candidates;
    } else {
        console.error('ERROR : unable to find project ' + directory + ' directory, could not locate .apk');
        process.exit(2);
    }
}

function hasCustomRules() {
    return fs.existsSync(path.join(ROOT, 'custom_rules.xml'));
}

function extractProjectNameFromManifest(projectPath) {
    var manifestPath = path.join(projectPath, 'AndroidManifest.xml');
    var manifestData = fs.readFileSync(manifestPath, 'utf8');
    var m = /<activity[\s\S]*?android:name\s*=\s*"(.*?)"/i.exec(manifestData);
    if (!m) {
        throw new Error('Could not find activity name in ' + manifestPath);
    }
    return m[1];
}

function extractSubProjectPaths() {
    var data = fs.readFileSync(path.join(ROOT, 'project.properties'), 'utf8');
    var ret = {};
    var r = /^\s*android\.library\.reference\.\d+=(.*)(?:\s|$)/mg
    var m;
    while (m = r.exec(data)) {
        ret[m[1]] = 1;
    }
    return Object.keys(ret);
}

var builders = {
    ant: {
        getArgs: function(cmd) {
            var args = [cmd, '-f', path.join(ROOT, 'build.xml')];
            // custom_rules.xml is required for incremental builds.
            if (hasCustomRules()) {
                args.push('-Dout.dir=ant-build', '-Dgen.absolute.dir=ant-gen');
            }
            return args;
        },

        prepEnv: function() {
            return check_reqs.check_ant()
            .then(function() {
                // Copy in build.xml on each build so that:
                // A) we don't require the Android SDK at project creation time, and
                // B) we always use the SDK's latest version of it.
                var sdkDir = process.env['ANDROID_HOME'];
                var buildTemplate = fs.readFileSync(path.join(sdkDir, 'tools', 'lib', 'build.template'), 'utf8');
                function writeBuildXml(projectPath) {
                    var newData = buildTemplate.replace('PROJECT_NAME', extractProjectNameFromManifest(ROOT));
                    fs.writeFileSync(path.join(projectPath, 'build.xml'), newData);
                    if (!fs.existsSync(path.join(projectPath, 'local.properties'))) {
                        fs.writeFileSync(path.join(projectPath, 'local.properties'), LOCAL_PROPERTIES_TEMPLATE);
                    }
                }
                var subProjects = extractSubProjectPaths();
                writeBuildXml(ROOT);
                for (var i = 0; i < subProjects.length; ++i) {
                    writeBuildXml(path.join(ROOT, subProjects[i]));
                }
            });
        },

        /*
         * Builds the project with ant.
         * Returns a promise.
         */
        build: function(build_type) {
            // Without our custom_rules.xml, we need to clean before building.
            var ret = Q();
            if (!hasCustomRules()) {
                // clean will call check_ant() for us.
                ret = this.clean();
            }

            var builder = this;
            var args = this.getArgs(build_type == 'debug' ? 'debug' : 'release');
            return check_reqs.check_ant()
            .then(function() {
                return spawn('ant', args);
            }).then(function() {
                return builder.getOutputFiles();
            });
        },

        clean: function() {
            var args = this.getArgs('clean');
            return check_reqs.check_ant()
            .then(function() {
                return spawn('ant', args);
            });
        },

        // Find the recently-generated output APK files
        // Ant only generates one output file; return it.
        getOutputFiles: function() {
            var binDir;
            if(hasCustomRules()) {
                binDir = path.join(ROOT, 'ant-build');
            } else {
                binDir = path.join(ROOT, 'bin');
            }
            var candidates = find_files(binDir, function(candidate) { return path.extname(candidate) == '.apk'; });
            if (candidates.length === 0) {
                console.error('ERROR : No .apk found in ' + binDir + ' directory');
                process.exit(2);
            }
            console.log('Using apk: ' + candidates[0]);
            return [candidates[0]];
        }
    },
    gradle: {
        getArgs: function(cmd) {
            var lintSteps = [
                'lint',
                'lintVitalRelease',
                'compileLint',
                'copyReleaseLint',
                'copyDebugLint'
            ];
            var args = [cmd, '-b', path.join(ROOT, 'build.gradle')];
            // 10 seconds -> 6 seconds
            args.push('-Dorg.gradle.daemon=true');
            // Excluding lint: 6s-> 1.6s
            for (var i = 0; i < lintSteps.length; ++i) {
                args.push('-x', lintSteps[i]);
            }
            // Shaves another 100ms, but produces a "try at own risk" warning. Not worth it (yet):
            // args.push('-Dorg.gradle.parallel=true');
            return args;
        },

        prepEnv: function() {
            return check_reqs.check_gradle()
            .then(function() {
                // Copy the gradle wrapper on each build so that:
                // A) we don't require the Android SDK at project creation time, and
                // B) we always use the SDK's latest version of it.
                var projectPath = ROOT;
                // check_reqs ensures that this is set.
                var sdkDir = process.env['ANDROID_HOME'];
                var wrapperDir = path.join(sdkDir, 'tools', 'templates', 'gradle', 'wrapper');
                if (process.platform == 'win32') {
                    shell.cp('-f', path.join(wrapperDir, 'gradlew.bat'), projectPath);
                } else {
                    shell.cp('-f', path.join(wrapperDir, 'gradlew'), projectPath);
                }
                shell.rm('-rf', path.join(projectPath, 'gradle', 'wrapper'));
                shell.mkdir('-p', path.join(projectPath, 'gradle'));
                shell.cp('-r', path.join(wrapperDir, 'gradle', 'wrapper'), path.join(projectPath, 'gradle'));
            });
        },

        /*
         * Builds the project with gradle.
         * Returns a promise.
         */
        build: function(build_type) {
            var builder = this;
            var wrapper = path.join(ROOT, 'gradlew');
            var args = builder.getArgs('build');
            return Q().then(function() {
                return spawn(wrapper, args);
            }).then(function() {
                return builder.getOutputFiles(build_type);
            });
        },

        clean: function() {
            var builder = this;
            var wrapper = path.join(ROOT, 'gradlew');
            var args = builder.getArgs('clean');
            return Q().then(function() {
                return spawn(wrapper, args);
            });
        },

        // Find the recently-generated output APK files
        // Gradle can generate multiple output files; return all of them.
        getOutputFiles: function(build_type) {
            var binDir = path.join(ROOT, 'build', 'apk');
            var candidates = find_files(binDir, function(candidate) {
                // Need to choose between release and debug .apk.
                if (build_type === 'debug') {
                    return (path.extname(candidate) == '.apk' && candidate.indexOf('-debug-') >= 0);
                }
                if (build_type === 'release') {
                    return (path.extname(candidate) == '.apk' && candidate.indexOf('-release-') >= 0);
                }
                return path.extname(candidate) == '.apk';
            });
            return candidates;
        }
    },

    none: {
        prepEnv: function() {
            return Q();
        },
        build: function() {
            console.log('Skipping build...');
            return Q();
        },
        clean: function() {
            return Q();
        },
    }
};

function parseOpts(options) {
    // Backwards-compatibility: Allow a single string argument
    if (typeof options == "string") options = [options];

    var ret = {
        buildType: 'debug',
        buildMethod: process.env['ANDROID_BUILD'] || 'ant'
    };

    // Iterate through command line options
    for (var i=0; options && (i < options.length); ++i) {
        if (options[i].substring && options[i].substring(0,2) == "--") {
            var option = options[i].substring(2);
            switch(option) {
                case 'debug':
                case 'release':
                    ret.buildType = option;
                    break;
                case 'ant':
                case 'gradle':
                    ret.buildMethod = option;
                    break;
                case 'nobuild' :
                    ret.buildMethod = 'none';
                    break;
                default :
                    return Q.reject('Build option \'' + options[i] + '\' not recognized.');
            }
        } else {
            return Q.reject('Build option \'' + options[i] + '\' not recognized.');
        }
    }
    return ret;
}

/*
 * Builds the project with the specifed options
 * Returns a promise.
 */
module.exports.runClean = function(options) {
    var opts = parseOpts(options);
    var builder = builders[opts.buildMethod];
    return builder.prepEnv()
    .then(function() {
        return builder.clean();
    }).then(function() {
        shell.rm('-rf', path.join(ROOT, 'out'));
    });
};

/*
 * Builds the project with the specifed options
 * Returns a promise.
 */
module.exports.run = function(options) {
    var opts = parseOpts(options);

    var builder = builders[opts.buildMethod];
    return builder.prepEnv()
    .then(function() {
        return builder.build(opts.buildType);
    }).then(function(apkFiles) {
        // TODO: Rather than copy apks to out, it might be better to
        // just write out what the last .apk build was. These files
        // are used by get_apk().
        var outputDir = path.join(ROOT, 'out');
        shell.mkdir('-p', outputDir);
        for (var i=0; i < apkFiles.length; ++i) {
            shell.cp('-f', apkFiles[i], path.join(outputDir, path.basename(apkFiles[i])));
        }
    });
};

/*
 * Gets the path to the apk file, if not such file exists then
 * the script will error out. (should we error or just return undefined?)
 * This is called by the run script to install the apk to the device
 */
module.exports.get_apk = function(build_type) {
    var outputDir = path.join(ROOT, 'out');
    var candidates = find_files(outputDir, function() { return true; });
    if (candidates.length === 0) {
        console.error('ERROR : No .apk found in ' + outputDir + ' directory');
        process.exit(2);
    }
    // TODO: Use build_type here.
    console.log('Using apk: ' + candidates[0]);
    return candidates[0];
};

module.exports.help = function() {
    console.log('Usage: ' + path.relative(process.cwd(), path.join(ROOT, 'cordova', 'build')) + ' [build_type]');
    console.log('Build Types : ');
    console.log('    \'--debug\': Default build, will build project in debug mode');
    console.log('    \'--release\': will build project for release');
    console.log('    \'--ant\': Default build, will build project with ant');
    console.log('    \'--gradle\': will build project with gradle');
    console.log('    \'--nobuild\': will skip build process (can be used with run command)');
    process.exit(0);
};
<|endoftext|>


<commit_before>/*
Copyright 2016 First People's Cultural Council

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.
*/
import React, { PropTypes } from 'react'
import Immutable, { Map } from 'immutable'
import provide from 'react-redux-provide'
import selectn from 'selectn'

import PromiseWrapper from 'views/components/Document/PromiseWrapper'

import ProviderHelpers from 'common/ProviderHelpers'
import NavigationHelpers from 'common/NavigationHelpers'
import UIHelpers from 'common/UIHelpers'

import DocumentListView from 'views/components/Document/DocumentListView'

import DataListView from 'views/pages/explore/dialect/learn/base/data-list-view'

import Preview from 'views/components/Editor/Preview'
import IntlService from 'views/services/intl'

const intl = IntlService.instance

/**
 * List view for words
 */
@provide
export default class ListView extends DataListView {
  static defaultProps = {
    DISABLED_SORT_COLS: ['state', 'fv-word:categories', 'related_audio', 'related_pictures'],
    DEFAULT_PAGE: 1,
    DEFAULT_PAGE_SIZE: 10,
    DEFAULT_LANGUAGE: 'english',
    DEFAULT_SORT_COL: 'fv:custom_order',
    DEFAULT_SORT_TYPE: 'asc',
    dialect: null,
    filter: new Map(),
    gridListView: false,
    controlViaURL: false,
    renderSimpleTable: false,
    disablePageSize: false,
  }

  static propTypes = {
    properties: PropTypes.object.isRequired,
    windowPath: PropTypes.string.isRequired,
    splitWindowPath: PropTypes.array.isRequired,
    pushWindowPath: PropTypes.func.isRequired,
    computeLogin: PropTypes.object.isRequired,
    fetchDialect2: PropTypes.func.isRequired,
    computeDialect2: PropTypes.object.isRequired,
    dialect: PropTypes.object,
    fetchWords: PropTypes.func.isRequired,
    computeWords: PropTypes.object.isRequired,
    routeParams: PropTypes.object.isRequired,
    pageProperties: PropTypes.object,
    filter: PropTypes.object,
    data: PropTypes.string,
    gridListView: PropTypes.bool,
    controlViaURL: PropTypes.bool,
    parentID: PropTypes.string,
    renderSimpleTable: PropTypes.bool,
    disablePageSize: PropTypes.bool,
    onPaginationReset: PropTypes.func,
    onPagePropertiesChange: PropTypes.func,
    action: PropTypes.func,

    DISABLED_SORT_COLS: PropTypes.array,
    DEFAULT_PAGE: PropTypes.number,
    DEFAULT_PAGE_SIZE: PropTypes.number,
    DEFAULT_SORT_COL: PropTypes.string,
    DEFAULT_SORT_TYPE: PropTypes.string,
  }

  constructor(props, context) {
    super(props, context)
    // TODO: Remove `let language` below?
    /*
      let language

      switch (intl.locale) {
      case 'en':
        language = 'english'
        break

          case 'fr':
        language = 'french'
        break
      }
      */

    this.state = {
      columns: [
        {
          name: 'title',
          title: intl.trans('word', 'Word', 'first'),
          render: (v, data) => (

              {v}

          ),
          sortName: 'fv:custom_order',
        },
        {
          name: 'fv:definitions',
          title: intl.trans('definitions', 'Definitions', 'first'),
          render: (v, data, cellProps) => {
            return UIHelpers.renderComplexArrayRow(selectn(`properties.${cellProps.name}`, data), (entry, i) => {
              if (entry.language === this.props.DEFAULT_LANGUAGE && i < 2) {
                return
{entry.translation}

              }
            })
          },
          sortName: 'fv:definitions/0/translation',
        },
        {
          name: 'related_audio',
          title: intl.trans('audio', 'Audio', 'first'),
          render: (v, data, cellProps) => {
            const firstAudio = selectn('contextParameters.word.' + cellProps.name + '[0]', data)
            if (firstAudio) {
              return (
                <Preview
                  key={selectn('uid', firstAudio)}
                  minimal
                  tagProps={{ preload: 'none' }}
                  tagStyles={{ width: '250px', maxWidth: '100%' }}
                  expandedValue={firstAudio}
                  type="FVAudio"
                />
              )
            }
          },
        },
        {
          name: 'related_pictures',
          width: 72,
          textAlign: 'center',
          title: intl.trans('picture', 'Picture', 'first'),
          render: (v, data, cellProps) => {
            const firstPicture = selectn('contextParameters.word.' + cellProps.name + '[0]', data)
            if (firstPicture) {
              return (

              )
            }
          },
        },
        {
          name: 'fv-word:part_of_speech',
          title: intl.trans('part_of_speech', 'Part of Speech', 'first'),
          render: (v, data) => selectn('contextParameters.word.part_of_speech', data),
        },
        {
          name: 'fv-word:categories',
          title: intl.trans('categories', 'Categories', 'first'),
          render: (v, data) =>
            UIHelpers.renderComplexArrayRow(selectn('contextParameters.word.categories', data), (entry, i) => (

{selectn('dc:title', entry)}

            )),
        },
      ],
      sortInfo: {
        uiSortOrder: [],
        currentSortCols: this.props.DEFAULT_SORT_COL,
        currentSortType: this.props.DEFAULT_SORT_TYPE,
      },
      pageInfo: {
        page: this.props.DEFAULT_PAGE,
        pageSize: this.props.DEFAULT_PAGE_SIZE,
      },
    }

    // Reduce the number of columns displayed for mobile
    if (UIHelpers.isViewSize('xs')) {
      this.state.columns = this.state.columns.filter((v) => ['title', 'fv:literal_translation'].indexOf(v.name) !== -1)
      this.state.hideStateColumn = true
    }

    // Bind methods to 'this'
    [
      '_onNavigateRequest',
      '_onEntryNavigateRequest',
      '_handleRefetch',
      '_handleSortChange',
      '_handleColumnOrderChange',
      '_resetColumns',
      '_fetchData2',
      '_getPathOrParentID',
    ].forEach((method) => (this[method] = this[method].bind(this))) // eslint-disable-line
  }

  _getPathOrParentID(newProps) {
    return newProps.parentID ? newProps.parentID : `${newProps.routeParams.dialect_path}/Dictionary`
  }

  // NOTE: DataListView calls `fetchData`
  fetchData(newProps) {
    if (newProps.dialect === null && !this.getDialect(newProps)) {
      newProps.fetchDialect2(newProps.routeParams.dialect_path)
    }

    this._fetchListViewData(
      newProps,
      newProps.DEFAULT_PAGE,
      newProps.DEFAULT_PAGE_SIZE,
      newProps.DEFAULT_SORT_TYPE,
      newProps.DEFAULT_SORT_COL
    )
  }

  _onEntryNavigateRequest(item) {
    if (this.props.action) {
      this.props.action(item)
    } else {
      NavigationHelpers.navigate(
        NavigationHelpers.generateUIDPath(this.props.routeParams.theme, item, 'words'),
        this.props.pushWindowPath,
        true
      )
    }
  }

  // NOTE: DataListView calls `_fetchListViewData`
  _fetchListViewData(props, pageIndex, pageSize, sortOrder, sortBy) {
    let currentAppliedFilter = ''

    if (props.filter.has('currentAppliedFilter')) {
      currentAppliedFilter = Object.values(props.filter.get('currentAppliedFilter').toJS()).join('')
    }
    const nql = `${currentAppliedFilter}&currentPageIndex=${pageIndex -
      1}&pageSize=${pageSize}&sortOrder=${sortOrder}&sortBy=${sortBy}`
    props.fetchWords(this._getPathOrParentID(props), nql)
  }

  getDialect(props = this.props) {
    return ProviderHelpers.getEntry(props.computeDialect2, props.routeParams.dialect_path)
  }

  _fetchData2(fetcherParams, props = this.props) {
    this.setState({
      fetcherParams: fetcherParams,
    })

    this._handleRefetch()

    /*props.fetchWords(props.routeParams.dialect_path + '/Dictionary',
          ProviderHelpers.filtersToNXQL(fetcherParams.filters)  +
          '&currentPageIndex=' + (fetcherParams.currentPageIndex - 1) +
          '&pageSize=' + fetcherParams.pageSize +
          '&sortOrder=' + fetcherParams.sortOrder +
          '&sortBy=' + fetcherParams.sortBy
      );*/

    //this._fetchListViewData(props, fetcherParams.currentPageIndex, fetcherParams.pageSize, fetcherParams.sortOrder, fetcherParams.sortBy);
  }

  render() {
    const computeEntities = Immutable.fromJS([
      {
        id: this._getPathOrParentID(this.props),
        entity: this.props.computeWords,
      },
    ])

    // If dialect not supplied, promise wrapper will need to wait for compute dialect
    if (!this.props.dialect) {
      computeEntities.push(
        new Map({
          id: this.props.routeParams.dialect_path,
          entity: this.props.computeDialect2,
        })
      )
    }

    const computeWords = ProviderHelpers.getEntry(this.props.computeWords, this._getPathOrParentID(this.props))
    const computeDialect2 = this.props.dialect || this.getDialect()

    const listViewProps = {
      className: 'browseDataGrid',
      columns: this.state.columns,
      data: computeWords,
      dialect: selectn('response', computeDialect2),
      disablePageSize: this.props.disablePageSize,
      gridListView: this.props.gridListView,
      objectDescriptions: 'words',
      onColumnOrderChange: this._handleColumnOrderChange,
      onSelectionChange: this._onEntryNavigateRequest,
      onSortChange: this._handleSortChange,
      page: this.state.pageInfo.page,
      pageSize: this.state.pageInfo.pageSize,
      refetcher: this._handleRefetch,
      refetcher2: this._handleRefetch,
      renderSimpleTable: this.props.renderSimpleTable,
      sortInfo: this.state.sortInfo.uiSortOrder,
      type: 'FVWord',
    }
    return (
      <PromiseWrapper renderOnError computeEntities={computeEntities}>
        {selectn('response.entries', computeWords) && <DocumentListView {...listViewProps} />}
      </PromiseWrapper>
    )
  }
}
<commit_msg>Enabling the  in word lists<commit_after>/*
Copyright 2016 First People's Cultural Council

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.
*/
import React, { PropTypes } from 'react'
import Immutable, { Map } from 'immutable'
import provide from 'react-redux-provide'
import selectn from 'selectn'

import PromiseWrapper from 'views/components/Document/PromiseWrapper'

import ProviderHelpers from 'common/ProviderHelpers'
import NavigationHelpers from 'common/NavigationHelpers'
import UIHelpers from 'common/UIHelpers'

import DocumentListView from 'views/components/Document/DocumentListView'

import DataListView from 'views/pages/explore/dialect/learn/base/data-list-view'

import Preview from 'views/components/Editor/Preview'
import IntlService from 'views/services/intl'

const intl = IntlService.instance

/**
 * List view for words
 */
@provide
export default class ListView extends DataListView {
  static defaultProps = {
    disableWordClick: true,
    DISABLED_SORT_COLS: ['state', 'fv-word:categories', 'related_audio', 'related_pictures'],
    DEFAULT_PAGE: 1,
    DEFAULT_PAGE_SIZE: 10,
    DEFAULT_LANGUAGE: 'english',
    DEFAULT_SORT_COL: 'fv:custom_order',
    DEFAULT_SORT_TYPE: 'asc',
    dialect: null,
    filter: new Map(),
    gridListView: false,
    controlViaURL: false,
    renderSimpleTable: false,
    disablePageSize: false,
  }

  static propTypes = {
    action: PropTypes.func,
    computeDialect2: PropTypes.object.isRequired,
    computeLogin: PropTypes.object.isRequired,
    computeWords: PropTypes.object.isRequired,
    controlViaURL: PropTypes.bool,
    data: PropTypes.string,
    disableWordClick: PropTypes.bool,
    dialect: PropTypes.object,
    DISABLED_SORT_COLS: PropTypes.array,
    DEFAULT_PAGE: PropTypes.number,
    DEFAULT_PAGE_SIZE: PropTypes.number,
    DEFAULT_SORT_COL: PropTypes.string,
    DEFAULT_SORT_TYPE: PropTypes.string,
    disablePageSize: PropTypes.bool,
    fetchWords: PropTypes.func.isRequired,
    fetchDialect2: PropTypes.func.isRequired,
    filter: PropTypes.object,
    gridListView: PropTypes.bool,
    onPaginationReset: PropTypes.func,
    onPagePropertiesChange: PropTypes.func,
    pageProperties: PropTypes.object,
    parentID: PropTypes.string,
    properties: PropTypes.object.isRequired,
    pushWindowPath: PropTypes.func.isRequired,
    routeParams: PropTypes.object.isRequired,
    renderSimpleTable: PropTypes.bool,
    splitWindowPath: PropTypes.array.isRequired,
    windowPath: PropTypes.string.isRequired,
  }

  constructor(props, context) {
    super(props, context)
    // TODO: Remove `let language` below?
    /*
    let language

    switch (intl.locale) {
    case 'en':
      language = 'english'
      break

        case 'fr':
      language = 'french'
      break
    }
    */
    this.state = {
      columns: [
        {
          name: 'title',
          title: intl.trans('word', 'Word', 'first'),
          render: (v, data) => {
            const href = NavigationHelpers.generateUIDPath(this.props.routeParams.theme, data, 'words')
            const clickHandler = props.disableWordClick ? NavigationHelpers.disable : (e)=> {
              e.preventDefault()
              NavigationHelpers.navigate(href, this.props.pushWindowPath, false)
            }
            return (

                {v}

            )
          },
          sortName: 'fv:custom_order',
        },
        {
          name: 'fv:definitions',
          title: intl.trans('definitions', 'Definitions', 'first'),
          render: (v, data, cellProps) => {
            return UIHelpers.renderComplexArrayRow(selectn(`properties.${cellProps.name}`, data), (entry, i) => {
              if (entry.language === this.props.DEFAULT_LANGUAGE && i < 2) {
                return
{entry.translation}

              }
            })
          },
          sortName: 'fv:definitions/0/translation',
        },
        {
          name: 'related_audio',
          title: intl.trans('audio', 'Audio', 'first'),
          render: (v, data, cellProps) => {
            const firstAudio = selectn('contextParameters.word.' + cellProps.name + '[0]', data)
            if (firstAudio) {
              return (
                <Preview
                  key={selectn('uid', firstAudio)}
                  minimal
                  tagProps={{ preload: 'none' }}
                  tagStyles={{ width: '250px', maxWidth: '100%' }}
                  expandedValue={firstAudio}
                  type="FVAudio"
                />
              )
            }
          },
        },
        {
          name: 'related_pictures',
          width: 72,
          textAlign: 'center',
          title: intl.trans('picture', 'Picture', 'first'),
          render: (v, data, cellProps) => {
            const firstPicture = selectn('contextParameters.word.' + cellProps.name + '[0]', data)
            if (firstPicture) {
              return (

              )
            }
          },
        },
        {
          name: 'fv-word:part_of_speech',
          title: intl.trans('part_of_speech', 'Part of Speech', 'first'),
          render: (v, data) => selectn('contextParameters.word.part_of_speech', data),
        },
        {
          name: 'fv-word:categories',
          title: intl.trans('categories', 'Categories', 'first'),
          render: (v, data) =>
            UIHelpers.renderComplexArrayRow(selectn('contextParameters.word.categories', data), (entry, i) => (

{selectn('dc:title', entry)}

            )),
        },
      ],
      sortInfo: {
        uiSortOrder: [],
        currentSortCols: this.props.DEFAULT_SORT_COL,
        currentSortType: this.props.DEFAULT_SORT_TYPE,
      },
      pageInfo: {
        page: this.props.DEFAULT_PAGE,
        pageSize: this.props.DEFAULT_PAGE_SIZE,
      },
    }

    // Reduce the number of columns displayed for mobile
    if (UIHelpers.isViewSize('xs')) {
      this.state.columns = this.state.columns.filter((v) => ['title', 'fv:literal_translation'].indexOf(v.name) !== -1)
      this.state.hideStateColumn = true
    }

    // Bind methods to 'this'
    [
      '_onNavigateRequest',
      '_onEntryNavigateRequest',
      '_handleRefetch',
      '_handleSortChange',
      '_handleColumnOrderChange',
      '_resetColumns',
      '_fetchData2',
      '_getPathOrParentID',
    ].forEach((method) => (this[method] = this[method].bind(this))) // eslint-disable-line
  }

  _getPathOrParentID(newProps) {
    return newProps.parentID ? newProps.parentID : `${newProps.routeParams.dialect_path}/Dictionary`
  }

  // NOTE: DataListView calls `fetchData`
  fetchData(newProps) {
    if (newProps.dialect === null && !this.getDialect(newProps)) {
      newProps.fetchDialect2(newProps.routeParams.dialect_path)
    }

    this._fetchListViewData(
      newProps,
      newProps.DEFAULT_PAGE,
      newProps.DEFAULT_PAGE_SIZE,
      newProps.DEFAULT_SORT_TYPE,
      newProps.DEFAULT_SORT_COL
    )
  }

  _onEntryNavigateRequest(item) {
    if (this.props.action) {
      this.props.action(item)
    } else {
      NavigationHelpers.navigate(
        NavigationHelpers.generateUIDPath(this.props.routeParams.theme, item, 'words'),
        this.props.pushWindowPath,
        true
      )
    }
  }

  // NOTE: DataListView calls `_fetchListViewData`
  _fetchListViewData(props, pageIndex, pageSize, sortOrder, sortBy) {
    let currentAppliedFilter = ''

    if (props.filter.has('currentAppliedFilter')) {
      currentAppliedFilter = Object.values(props.filter.get('currentAppliedFilter').toJS()).join('')
    }
    const nql = `${currentAppliedFilter}&currentPageIndex=${pageIndex -
      1}&pageSize=${pageSize}&sortOrder=${sortOrder}&sortBy=${sortBy}`
    props.fetchWords(this._getPathOrParentID(props), nql)
  }

  getDialect(props = this.props) {
    return ProviderHelpers.getEntry(props.computeDialect2, props.routeParams.dialect_path)
  }

  _fetchData2(fetcherParams, props = this.props) {
    this.setState({
      fetcherParams: fetcherParams,
    })

    this._handleRefetch()

    /*props.fetchWords(props.routeParams.dialect_path + '/Dictionary',
          ProviderHelpers.filtersToNXQL(fetcherParams.filters)  +
          '&currentPageIndex=' + (fetcherParams.currentPageIndex - 1) +
          '&pageSize=' + fetcherParams.pageSize +
          '&sortOrder=' + fetcherParams.sortOrder +
          '&sortBy=' + fetcherParams.sortBy
      );*/

    //this._fetchListViewData(props, fetcherParams.currentPageIndex, fetcherParams.pageSize, fetcherParams.sortOrder, fetcherParams.sortBy);
  }

  render() {
    const computeEntities = Immutable.fromJS([
      {
        id: this._getPathOrParentID(this.props),
        entity: this.props.computeWords,
      },
    ])

    // If dialect not supplied, promise wrapper will need to wait for compute dialect
    if (!this.props.dialect) {
      computeEntities.push(
        new Map({
          id: this.props.routeParams.dialect_path,
          entity: this.props.computeDialect2,
        })
      )
    }

    const computeWords = ProviderHelpers.getEntry(this.props.computeWords, this._getPathOrParentID(this.props))
    const computeDialect2 = this.props.dialect || this.getDialect()

    const listViewProps = {
      className: 'browseDataGrid',
      columns: this.state.columns,
      data: computeWords,
      dialect: selectn('response', computeDialect2),
      disablePageSize: this.props.disablePageSize,
      gridListView: this.props.gridListView,
      objectDescriptions: 'words',
      onColumnOrderChange: this._handleColumnOrderChange,
      onSelectionChange: this._onEntryNavigateRequest,
      onSortChange: this._handleSortChange,
      page: this.state.pageInfo.page,
      pageSize: this.state.pageInfo.pageSize,
      refetcher: this._handleRefetch,
      refetcher2: this._handleRefetch,
      renderSimpleTable: this.props.renderSimpleTable,
      sortInfo: this.state.sortInfo.uiSortOrder,
      type: 'FVWord',
    }
    return (
      <PromiseWrapper renderOnError computeEntities={computeEntities}>
        {selectn('response.entries', computeWords) && <DocumentListView {...listViewProps} />}
      </PromiseWrapper>
    )
  }
}
<|endoftext|>