<commit_before>
/**
 * Bichromatic dominating pairs using the divide and conquer chainsaw.
 *
 * see F. P. Preparata and M. I. Shamos, Computational Geometry, NY, 1985, p. 366.
 *
 * Here the algorithm handles non-strict ( >= ) dominance.
 *
 * select( f, a, i, j, k ) where
 *   f is a comparator
 *   a the array of points
 *   [i, j[ the range to search in the array
 *   k the index to select
 *
 * select(...) partitions the array in tree regions
 *  -----------------------------
 * |    <= h    | h |    >= h    |
 *  -----------------------------
 * i    ....    k  k+1   ....   j-1
 *
 * __eq__( x ) template for a function eq( y )
 *   returns true iff coordinate x equals coordinate y
 *
 * color( point )
 *   = 0 if point is blue
 *   = 1 if point is red
 *
 */

var __bdpdc__ = function ( select, __eq__, __ne__, color, split, swap ) {

	/**
	 * a is an array of points
	 *
	 *     note that we only consider points starting
	 *     at index i and ending at index j-1 in a
	 *
	 * points are arrays of coordinates
	 *
	 *     d = dj - di is the number of coordinates of each point
	 *
	 *
	 * __f__ is a template for a function {coordinates^2} -> {<0, =0, >0} named f
	 *
	 *     i.e. for coordinates a and b
	 *
	 *       f( a, b ) < 0 means a < b;
	 *       f( a, b ) = 0 means a = b;
	 *       f( a, b ) > 0 means a > b.
	 *
	 * out is the output array
	 *
	 */

	var bdpdc = function ( __f__, a, i, j, di, dj, out ) {

		var h, m, k, x, y, tmp, p, q, _m, _n;

		// empty or one element array case

		if ( i >= j - 1 ) {
			return out;
		}

		// base case : dj - di = d = 0
		// enumerate all red / blue pairs
		// [i, p[ contains only blue points
		// [p, j[ contains only red points
		// p = index of first red point

		if ( di === dj ) {

			// move all blue points left and all red points right
			// (arbitrary choice)

			p = split( color, a, i, j );

			// for each red point

			for ( x = p ; x < j ; ++x ) {

				// for each blue point

				for ( y = i ; y < p ; ++y ) {

					out.push( [ a[x], a[y] ] );

				}
			}

			return out;

		}

		/**
		 * recursion fairy
		 *
		 * we compute m such that h is the median of
		 * the ith coordinate of all points
		 *
		 */

		else {

			//  -------------------------------------------------------
			// |                     b&r scrambled                     |
			//  -------------------------------------------------------
			// i                                                       j

			m = ( i + j ) / 2 | 0;

			//  -------------------------------------------------------
			// |                     b&r scrambled                     |
			//  -------------------------------------------------------
			// i                         m                             j

			// select median element
			// O(n)

			select( __f__( di ), a, i, j, m );

			h = a[m][di];

			//  -------------------------------------------------------
			// |         b&r <= h        | h |         b&r >= h        |
			//  -------------------------------------------------------
			// i                         m                             j

			// we do 3 recursive calls
			//
			// first: for red and blue points with di < h in R^d
			// we do not consider points with di = h because either
			//
			// 1. red = h, blue < h --> handled by last call
			// 2. red < h, blue = h --> red cannot dominate blue
			// 3. red = h, blue = h --> handled by last call
			//    (would be "red cannot dominate blue" for strict dominance
			//    in this 3rd case)
			//
			// second: for red and blue points with di > h in R^d
			// we do not consider points with di = h for similar reasons as above
			//
			// last: for red points with di >= h and blue points with di <= h in R^{d-1}
			// (would be > and < for strict dominance)
			//
			// note that we do not need to handle the case where red < h and blue >= h
			// or red <= h and blue > h since red cannot domminate blue in those cases

			// first recursive call
			// we only consider points that have di < h
			// since all points that have di === h will be handled later

			// move median elements from a_i to a_m - 1
			// in the [ p, m [ interval
			// O(n)

			x = split( __eq__( h ), a, i, m );

			//  -------------------------------------------------------
			// |    b&r < h    | b&r = h | h |         b&r > h         |
			//  -------------------------------------------------------
			// i               x         m                             j

			bdpdc( __f__, a, i, x, di, dj, out );

			// move median elements from a_m to a_j - 1
			// in the ] m, y - 1 ] interval
			// O(n)

			y = split( __ne__( h ), a, m + 1, j );

			//  -------------------------------------------------------
			// |    b&r < h    | b&r = h | h | b&r = h |    b&r > h    |
			//  -------------------------------------------------------
			// i               x         m             y               j

			bdpdc( __f__, a, y, j, di, dj, out );

			// since we do not touch median elements in the first two
			// recursive calls they are still at the correct place

			// Now we want to
			//   - move red points such that di < h to the right
			//   - move red points such that di >= h to the left
			//
			// /!\ Note that we also might think that we should
			//   - move blue points such that di > h to the right
			//   - move blue points such that di <= h to the left
			// but after the selection algorithm this is already the case

			//  -------------------------------------------------------
			// |    b&r < h    | b&r = h | h | b&r = h |    b&r > h    |
			//  -------------------------------------------------------
			// i               x         m             y               j

			p = split( color, a, i, x );

			//  -------------------------------------------------------
			// | b < h | r < h | b&r = h | h | b&r = h |    b&r > h    |
			//  -------------------------------------------------------
			// i       p       x         m             y               j

			q = split( color, a, y, j );

			//  -------------------------------------------------------
			// | b < h | r < h | b&r = h | h | b&r = h | b > h | r > h |
			//  -------------------------------------------------------
			// i       p       x         m             y       q       j

			// we now want to swap r < h elements with r > h elements
			// we have 3 cases
			//   1. x - p = j - q
			//   2. x - p < j - q
			//   3. x - p > j - q

			m = x - p;
			n = j - q;

			//   1. x - p = j - q
			if ( m === n ) {
				swap( a, q, q + m, a, p );

				//  -------------------------------------------------------
				// | b < h | r > h | b&r = h | h | b&r = h | b > h | r < h |
				//  -------------------------------------------------------
				//  i      p       x         m             y       q       j

			}
			//   2. x - p < j - q
			else if ( m < n ) {

				swap( a, q, q + m, a, p );

				//  ---------------------------------------------------------------
				// | b < h | r > h | b&r = h | h | b&r = h | b > h | r < h | r > h |
				//  ---------------------------------------------------------------
				//  i      p       x         m             y       q      q+m      j

				// we now want to swap r < h and b > h elements with r > h elements
				// we have 2 cases
				//   1. (q + m) - y >= j - (q + m) [OR  >]
				//   2. (q + m) - y  < j - (q + m) [OR <=]

				_m = (q + m) - y;
				_n = j - (q + m);

				//   1. (q + m) - y >= j - (q + m)
				if ( _m >= _n ) {
					swap( a, q + m, j, a, y );
				}
				//   2. (q + m) - y  < j - (q + m)
				else {
					swap( a, j - _m, j, a, y );
				}

				//  ---------------------------------------------------------------
				// | b < h | r > h | b&r = h | h | b&r = h | r > h |   b>h & r<h   |
				//  ---------------------------------------------------------------
				//  i      p       x         m             y      y+_n             j
			}
			//   3. x - p > j - q
			else {

				swap( a, q, j, a, p );

				//  ---------------------------------------------------------------
				// | b < h | r > h | r < h | b&r = h | h | b&r = h | b > h | r < h |
				//  ---------------------------------------------------------------
				// i       p       x                 m             y       q       j

				swap( a, y - m + n, y, a, p + n );
			}

			//  -----------------------------------------------------------------------
			// | b < h | r > h | b&r = h | h | b&r = h | r > h | r < h | b > h | r < h |
			//  -----------------------------------------------------------------------
			//  i      p     x-m+n         m           y       q      j

			bdpdc( __f__, a, i, m + k, di + 1, dj, out );

			return out;
		}

	};

};

exports.__bdpdc__ = __bdpdc__;
<commit_msg>progress on bdp<commit_after>
/**
 * Bichromatic dominating pairs using the divide and conquer chainsaw.
 *
 * see F. P. Preparata and M. I. Shamos, Computational Geometry, NY, 1985, p. 366.
 *
 * Here the algorithm handles non-strict ( >= ) dominance.
 *
 * select( f, a, i, j, k ) where
 *   f is a comparator
 *   a the array of points
 *   [i, j[ the range to search in the array
 *   k the index to select
 *
 * select(...) partitions the array in tree regions
 *  -----------------------------
 * |    <= h    | h |    >= h    |
 *  -----------------------------
 * i    ....    k  k+1   ....   j-1
 *
 * __eq__( x ) template for a function eq( y )
 *   returns true iff coordinate x equals coordinate y
 *
 * color( point )
 *   = 0 if point is blue
 *   = 1 if point is red
 *
 */

var __bdpdc__ = function ( select, __eq__, __ne__, color, split, swap ) {

	/**
	 * a is an array of points
	 *
	 *     note that we only consider points starting
	 *     at index i and ending at index j-1 in a
	 *
	 * points are arrays of coordinates
	 *
	 *     d = dj - di is the number of coordinates of each point
	 *
	 *
	 * __f__ is a template for a function {coordinates^2} -> {<0, =0, >0} named f
	 *
	 *     i.e. for coordinates a and b
	 *
	 *       f( a, b ) < 0 means a < b;
	 *       f( a, b ) = 0 means a = b;
	 *       f( a, b ) > 0 means a > b.
	 *
	 * out is the output array
	 *
	 */

	var bdpdc = function ( __f__, a, i, j, di, dj, out ) {

		var k, h, x, y, p, q, m, n, _m, _n;

		// empty or one element array case

		if ( i >= j - 1 ) {
			return out;
		}

		// base case : dj - di = d = 0
		// enumerate all red / blue pairs
		// [i, p[ contains only blue points
		// [p, j[ contains only red points
		// p = index of first red point

		if ( di === dj ) {

			// move all blue points left and all red points right
			// (arbitrary choice)

			p = split( color, a, i, j );

			// for each red point

			for ( x = p ; x < j ; ++x ) {

				// for each blue point

				for ( y = i ; y < p ; ++y ) {

					out.push( [ a[x], a[y] ] );

				}
			}

			return out;

		}

		/**
		 * recursion fairy
		 *
		 * we compute m such that h is the median of
		 * the ith coordinate of all points
		 *
		 */

		else {

			//  -------------------------------------------------------
			// |                     b&r scrambled                     |
			//  -------------------------------------------------------
			// i                                                       j

			k = ( i + j ) / 2 | 0;

			//  -------------------------------------------------------
			// |                     b&r scrambled                     |
			//  -------------------------------------------------------
			// i                         k                             j

			// select median element
			// O(n)

			select( __f__( di ), a, i, j, k );

			h = a[k][di];

			//  -------------------------------------------------------
			// |         b&r <= h        | h |         b&r >= h        |
			//  -------------------------------------------------------
			// i                         k                             j

			// we do 3 recursive calls
			//
			// first: for red and blue points with di < h in R^d
			// we do not consider points with di = h because either
			//
			// 1. red = h, blue < h --> handled by last call
			// 2. red < h, blue = h --> red cannot dominate blue
			// 3. red = h, blue = h --> handled by last call
			//    (would be "red cannot dominate blue" for strict dominance
			//    in this 3rd case)
			//
			// second: for red and blue points with di > h in R^d
			// we do not consider points with di = h for similar reasons as above
			//
			// last: for red points with di >= h and blue points with di <= h in R^{d-1}
			// (would be > and < for strict dominance)
			//
			// note that we do not need to handle the case where red < h and blue >= h
			// or red <= h and blue > h since red cannot dominate blue in those cases

			// first recursive call
			// we only consider points that have di < h
			// since all points that have di = h will be handled later

			// move median elements from [ i, k [ in the [ x, k [ interval, x <= i
			// O(n)

			x = split( __eq__( h ), a, i, k );

			//  -------------------------------------------------------
			// |    b&r < h    | b&r = h | h |         b&r > h         |
			//  -------------------------------------------------------
			// i               x         k                             j

			bdpdc( __f__, a, i, x, di, dj, out );

			// move median elements from [ k + 1, j [ in the [ y, j [ interval, y <= k + 1
			// O(n)

			y = split( __ne__( h ), a, k + 1, j );

			//  -------------------------------------------------------
			// |    b&r < h    | b&r = h | h | b&r = h |    b&r > h    |
			//  -------------------------------------------------------
			// i               x         k             y               j

			bdpdc( __f__, a, y, j, di, dj, out );

			// since we do not touch median elements in the first two
			// recursive calls they are still at the correct place

			// Now we want to
			//   - move red points such that di < h to the right
			//   - move red points such that di >= h to the left
			//
			// /!\ Note that we also might think that we should
			//   - move blue points such that di > h to the right
			//   - move blue points such that di <= h to the left
			// but after the selection algorithm this is already the case

			//  -------------------------------------------------------
			// |    b&r < h    | b&r = h | h | b&r = h |    b&r > h    |
			//  -------------------------------------------------------
			// i               x         k             y               j

			p = split( color, a, i, x );

			//  -------------------------------------------------------
			// | b < h | r < h | b&r = h | h | b&r = h |    b&r > h    |
			//  -------------------------------------------------------
			// i       p       x         k             y               j

			q = split( color, a, y, j );

			//  -------------------------------------------------------
			// | b < h | r < h | b&r = h | h | b&r = h | b > h | r > h |
			//  -------------------------------------------------------
			// i       p       x         k             y       q       j

			// we now want to swap r < h elements with r > h elements
			// we have 3 cases
			//   1. x - p = j - q
			//   2. x - p < j - q
			//   3. x - p > j - q

			m = x - p;
			n = j - q;

			//   1. x - p = j - q

			if ( m === n ) {
				swap( a, q, j, a, p );

				//  -------------------------------------------------------
				// | b < h | r > h | b&r = h | h | b&r = h | b > h | r < h |
				//  -------------------------------------------------------
				// i       p       x         k             y       q       j

				j = y;

				//  -------------------------------------------------------
				// | b < h | r > h | b&r = h | h | b&r = h | b > h | r < h |
				//  -------------------------------------------------------
				// i       p       x         k             j      ...

			}

			//   2. x - p < j - q

			else if ( m < n ) {

				swap( a, p, x, a, q );

				//  ---------------------------------------------------------------
				// | b < h | r > h | b&r = h | h | b&r = h | b > h | r < h | r > h |
				//  ---------------------------------------------------------------
				// i       p       x         k             y       q      q+m      j

				// we now want to swap b > h and r < h elements with r > h elements
				//                     [y,q[    [q,q+m[             [q+m,j[
				// we have 2 cases
				//   1. (q + m) - y >= j - (q + m) [OR  >]
				//   2. (q + m) - y  < j - (q + m) [OR <=]

				_m = (q + m) - y;
				_n = j - (q + m);

				//   1. (q + m) - y >= j - (q + m)
				if ( _m >= _n ) {
					swap( a, q + m, j, a, y );
				}
				//   2. (q + m) - y  < j - (q + m)
				else {
					swap( a, j - _m, j, a, y );
				}

				//  ---------------------------------------------------------------
				// | b < h | r > h | b&r = h | h | b&r = h | r > h |   b>h & r<h   |
				//  ---------------------------------------------------------------
				// i       p       x         k             y      y+_n             j

				j = y + _n;

				//  ---------------------------------------------------------------
				// | b < h | r > h | b&r = h | h | b&r = h | r > h |   b>h & r<h   |
				//  ---------------------------------------------------------------
				// i       p       x         k             y       j      ...

			}

			//   3. x - p > j - q

			else {

				swap( a, q, j, a, p );

				//  ---------------------------------------------------------------
				// | b < h | r > h | r < h | b&r = h | h | b&r = h | b > h | r < h |
				//  ---------------------------------------------------------------
				// i       p      p+n      x         k             y       q       j

				// we now want to swap r < h with b&r = h elements
				// we have 2 cases
				//   1. x - (p + n) >= y - x
				//   2. x - (p + n)  < y - x

				_m = x - (p + n);
				_n = y - x;

				//   1. x - (p + n) >= y - x
				if ( _m >= _n ) {
					swap( a, x, y, a, p + n );
				}
				//   2. x - (p + n)  < y - x
				else {
					swap( a, y - _m, y, a, p + n );
				}

				//  -----------------------------------------------------------
				// | b < h | r > h |     b&r = h     | h | b&r = h | b>h & r<h |
				//  -----------------------------------------------------------
				// i       p      p+n                k            y-_m         j

				j = y - _m;

				//  -----------------------------------------------------------
				// | b < h | r > h |     b&r = h     | h | b&r = h | b>h & r<h |
				//  -----------------------------------------------------------
				// i       p      p+n                k             j    ...

			}

			// [i, j[ now contains only b <= h and r >= h points
			// in this new interval, all r points dominate b points
			// for the ith coordinate
			// we can thus ask the recursion fairy to take care of the other
			// dj - di - 1 dimensions left

			bdpdc( __f__, a, i, j, di + 1, dj, out );

			return out;
		}

	};

};

exports.__bdpdc__ = __bdpdc__;
<|endoftext|>


<commit_before>/*
 * This file is part of the Sonatra package.
 *
 * (c) François Pluchino <francois.pluchino@sonatra.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

+function ($) {
    'use strict';

    // STICKY HEADER CLASS DEFINITION
    // ==============================

    var StickyHeader = function (element, options) {
        this.guid       = jQuery.guid;
        this.options    = $.extend({}, StickyHeader.DEFAULTS, options);
        this.$element   = $(element);

        this.$element.on('scroll.st.stickyheader', $.proxy(StickyHeader.prototype.checkPosition, this));
        this.checkPosition();
    };

    StickyHeader.DEFAULTS = {
        classSticky: 'sticky-header'
    };

    StickyHeader.prototype.checkPosition = function () {
        this.$element.find('> ul > li > span').each($.proxy(function(index, element) {
            var $group = $(element);
            var top = $group.position()['top'];

            if (top > 0) {
                this.$element.parent().find('> [data-sticky-index="' + index + '"]').remove();
                $group.removeAttr('data-sticky-ref');

                return;
            }

            var $headerFind = this.$element.parent().find('> [data-sticky-index="' + index + '"]');
            var $nextItemFind = $group.parent().next().find('> span, > a');

            if (0 == $headerFind.size()) {
                var $sticky = $('

');
                $sticky.append($group.clone());
                $group.attr('data-sticky-ref', index);
                this.$element.parent().prepend($sticky);

            } else if ($nextItemFind.eq(0).position()['top'] <= 0) {
                $headerFind.eq(0).css('display', 'none');

            } else {
                $headerFind.eq(0).css('display', '');
            }
        }, this));
    };

    StickyHeader.prototype.destroy = function () {
        this.$element.off('scroll.st.stickyheader', $.proxy(StickyHeader.prototype.checkPosition, this));
        this.$element.parent().find('> .' + this.options.classSticky).remove();
    };

    // STICKY HEADER PLUGIN DEFINITION
    // ===============================

    var old = $.fn.stickyheader;

    $.fn.stickyheader = function (option, _relatedTarget) {
        return this.each(function () {
            var $this   = $(this);
            var data    = $this.data('st.stickyheader');
            var options = typeof option == 'object' && option;

            if (!data && option == 'destroy') {
                return;
            }

            if (!data) {
                $this.data('st.stickyheader', (data = new StickyHeader(this, options)));
            }

            if (typeof option == 'string') {
                data[option]();
            }
        });
    };

    $.fn.stickyheader.Constructor = StickyHeader;

    // STICKY HEADER NO CONFLICT
    // =========================

    $.fn.stickyheader.noConflict = function () {
        $.fn.stickyheader = old;

        return this;
    };

    // STICKY HEADER DATA-API
    // ======================

    $(window).on('load', function () {
        $('[data-sticky-header="true"]').each(function () {
            var $this = $(this);
            $this.stickyheader($this.data());
        });
    });

}(jQuery);
<commit_msg>Fix undefined next sticky header<commit_after>/*
 * This file is part of the Sonatra package.
 *
 * (c) François Pluchino <francois.pluchino@sonatra.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

+function ($) {
    'use strict';

    // STICKY HEADER CLASS DEFINITION
    // ==============================

    var StickyHeader = function (element, options) {
        this.guid       = jQuery.guid;
        this.options    = $.extend({}, StickyHeader.DEFAULTS, options);
        this.$element   = $(element);

        this.$element.on('scroll.st.stickyheader', $.proxy(StickyHeader.prototype.checkPosition, this));
        this.checkPosition();
    };

    StickyHeader.DEFAULTS = {
        classSticky: 'sticky-header'
    };

    StickyHeader.prototype.checkPosition = function () {
        this.$element.find('> ul > li > span').each($.proxy(function(index, element) {
            var $group = $(element);
            var top = $group.position()['top'];

            if (top > 0) {
                this.$element.parent().find('> [data-sticky-index="' + index + '"]').remove();
                $group.removeAttr('data-sticky-ref');

                return;
            }

            var $headerFind = this.$element.parent().find('> [data-sticky-index="' + index + '"]');
            var $nextItemFind = $group.parent().next().find('> span, > a');

            if (0 == $headerFind.size()) {
                var $sticky = $('

');
                $sticky.append($group.clone());
                $group.attr('data-sticky-ref', index);
                this.$element.parent().prepend($sticky);

            } else if ($nextItemFind.eq(0).size() > 0 && $nextItemFind.eq(0).position()['top'] <= 0) {
                $headerFind.eq(0).css('display', 'none');

            } else {
                $headerFind.eq(0).css('display', '');
            }
        }, this));
    };

    StickyHeader.prototype.destroy = function () {
        this.$element.off('scroll.st.stickyheader', $.proxy(StickyHeader.prototype.checkPosition, this));
        this.$element.parent().find('> .' + this.options.classSticky).remove();
    };

    // STICKY HEADER PLUGIN DEFINITION
    // ===============================

    var old = $.fn.stickyheader;

    $.fn.stickyheader = function (option, _relatedTarget) {
        return this.each(function () {
            var $this   = $(this);
            var data    = $this.data('st.stickyheader');
            var options = typeof option == 'object' && option;

            if (!data && option == 'destroy') {
                return;
            }

            if (!data) {
                $this.data('st.stickyheader', (data = new StickyHeader(this, options)));
            }

            if (typeof option == 'string') {
                data[option]();
            }
        });
    };

    $.fn.stickyheader.Constructor = StickyHeader;

    // STICKY HEADER NO CONFLICT
    // =========================

    $.fn.stickyheader.noConflict = function () {
        $.fn.stickyheader = old;

        return this;
    };

    // STICKY HEADER DATA-API
    // ======================

    $(window).on('load', function () {
        $('[data-sticky-header="true"]').each(function () {
            var $this = $(this);
            $this.stickyheader($this.data());
        });
    });

}(jQuery);
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/**
 * tabiframedeck.js
 *
 * This is the central piece of Firefox.html.
 * TabIframeDeck controls the list of <tab-iframe>s.
 *
 * TabIframeDeck object is used to manipulate the list
 * of tabs (add, remove, select, …) and be notified
 * on tab changes (events like 'select', 'add', …).
 *
 */

define(['js/tabiframe', 'js/eventemitter', 'js/keybindings'],
       function(TabIframe, EventEmitter, RegisterKeyBindings) {

  'use strict';

  const HOMEPAGE = '/landing-page/';

  let _tabIframeArray = [];
  let _selectIndex = -1;

  const TabIframeDeck = {

    // This is a poor man session storage. Just a temporary
    // thing (localStorage is bad).
    saveSession: function() {
      let session = _tabIframeArray.map(t => t.location);
      session = session.filter(t => t.location && !t.location.startsWith("browserui://"));
      window.localStorage.session = JSON.stringify(session);
    },

    restoreSession: function() {
      let session = [];
      try {
        session = JSON.parse(window.localStorage.session);
      } catch (e) {}

      if (Array.isArray(session) && session.length > 0) {
        for (let url of session) {
          TabIframeDeck.add({url});
        }
      } else {
        TabIframeDeck.add({url: HOMEPAGE});
      }
    },

    onMozBrowserOpenWindow: function(type, event) {
      TabIframeDeck.add({url: event.detail.url});
    },

    add: function(options={}) {
      let tabIframe = document.createElement('tab-iframe');
      tabIframe.setAttribute('flex', '1');

      let parent = document.querySelector('.iframes');
      parent.appendChild(tabIframe);
      _tabIframeArray.push(tabIframe);

      tabIframe.on('mozbrowseropenwindow', this.onMozBrowserOpenWindow);
      tabIframe.on('mozbrowserlocationchange', this.saveSession);

      this.emit('add', {tabIframe: tabIframe});

      if (options.url) {
        tabIframe.setLocation(options.url);
      }

      if (options.select || _selectIndex < 0) {
        this.select(tabIframe);
      } else {
        tabIframe.hide();
      }

      this.saveSession();

      return tabIframe;
    },

    remove: function(tabIframe) {
      let index = _tabIframeArray.indexOf(tabIframe);
      if (index < 0) {
        throw new Error('Unknown tabIframe');
      }

      if (_tabIframeArray.length == 1) {
        throw new Error('Deck has only one tabiframe');
      }

      if (index == _selectIndex) {
        let newSelectIndex;
        if (index == _tabIframeArray.length - 1) {
          newSelectIndex = index - 1;
        } else {
          newSelectIndex = index + 1;
        }
        this.select(_tabIframeArray[newSelectIndex]);
      }

      if (_selectIndex > index) {
        _selectIndex--;
      }

      _tabIframeArray.splice(index, 1);
      tabIframe.off('mozbrowseropenwindow', this.onMozBrowserOpenWindow);
      tabIframe.remove();

      this.saveSession();

      this.emit('remove', {tabIframe});
    },

    select: function(tabIframe) {
      let index = _tabIframeArray.indexOf(tabIframe);
      if (index < 0) {
        throw new Error('Unknown tabiframe');
      }

      if (index == _selectIndex) {
        // already selected
        return;
      }

      tabIframe.willBeVisibleSoon();

      let previouslySelectTabIframe = _tabIframeArray[_selectIndex];
      if (previouslySelectTabIframe) {
        this.emit('unselect', {tabIframe: previouslySelectTabIframe});
      }

      _selectIndex = index;

      this.emit('select', {tabIframe});

      // Do the actual switch
      window.requestAnimationFrame(() => {
        if (previouslySelectTabIframe) {
          previouslySelectTabIframe.hide();
        }
        tabIframe.show();
      });
    },

    selectNext: function() {
      let newSelectIndex = _selectIndex + 1;
      if (newSelectIndex == _tabIframeArray.length) {
        newSelectIndex = 0;
      }
      this.select(_tabIframeArray[newSelectIndex]);
    },

    selectPrevious: function() {
      let newSelectIndex = _selectIndex - 1;
      if (newSelectIndex < 0) {
        newSelectIndex = _tabIframeArray.length - 1;
      }
      this.select(_tabIframeArray[newSelectIndex]);
    },

    getSelected: function() {
      return _tabIframeArray[_selectIndex];
    },

    getCount: function() {
      return _tabIframeArray.length;
    },
  }

  TabIframeDeck[Symbol.iterator] = function*() {
    for (let tabIframe of _tabIframeArray) {
      yield tabIframe;
    }
  }

  EventEmitter.decorate(TabIframeDeck);

  TabIframeDeck.restoreSession();

  RegisterKeyBindings(
    ['',              'Esc',        () => TabIframeDeck.getSelected().stop()],
    ['Ctrl',          'Tab',        () => TabIframeDeck.selectNext()],
    ['Ctrl Shift',    'code:9',     () => TabIframeDeck.selectPrevious()],
    ['',              'F5',         () => TabIframeDeck.getSelected().reload()]
  );

  if (window.OS == 'linux' || window.OS == 'windows') {
    RegisterKeyBindings(
      ['Ctrl',          't',          () => TabIframeDeck.add({select: true})],
      ['Ctrl',          'r',          () => TabIframeDeck.getSelected().reload()],
      ['Alt',           'Left',       () => TabIframeDeck.getSelected().goBack()],
      ['Alt',           'Right',      () => TabIframeDeck.getSelected().goForward()],
      ['Ctrl',          'w',          () => TabIframeDeck.remove(TabIframeDeck.getSelected())],
      ['Ctrl Shift',    '+',          () => TabIframeDeck.getSelected().zoomIn()],
      ['Ctrl',          '=',          () => TabIframeDeck.getSelected().zoomIn()],
      ['Ctrl',          '-',          () => TabIframeDeck.getSelected().zoomOut()],
      ['Ctrl',          '0',          () => TabIframeDeck.getSelected().resetZoom()]
    );
  }

  if (window.OS == 'osx') {
    RegisterKeyBindings(
      ['Cmd',       't',          () => TabIframeDeck.add({select: true})],
      ['Cmd',       'r',          () => TabIframeDeck.getSelected().reload()],
      ['Cmd',       'Left',       () => TabIframeDeck.getSelected().goBack()],
      ['Cmd',       'Right',      () => TabIframeDeck.getSelected().goForward()],
      ['Cmd',       'w',          () => TabIframeDeck.remove(TabIframeDeck.getSelected())],
      ['Cmd Shift', '+',          () => TabIframeDeck.getSelected().zoomIn()],
      ['Cmd',       '=',          () => TabIframeDeck.getSelected().zoomIn()],
      ['Cmd',       '-',          () => TabIframeDeck.getSelected().zoomOut()],
      ['Cmd',       '0',          () => TabIframeDeck.getSelected().resetZoom()]
    );
  }

  return TabIframeDeck;
});
<commit_msg>Make more start page urls relative<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/**
 * tabiframedeck.js
 *
 * This is the central piece of Firefox.html.
 * TabIframeDeck controls the list of <tab-iframe>s.
 *
 * TabIframeDeck object is used to manipulate the list
 * of tabs (add, remove, select, …) and be notified
 * on tab changes (events like 'select', 'add', …).
 *
 */

define(['js/tabiframe', 'js/eventemitter', 'js/keybindings'],
       function(TabIframe, EventEmitter, RegisterKeyBindings) {

  'use strict';

  const HOMEPAGE = '../../landing-page/';

  let _tabIframeArray = [];
  let _selectIndex = -1;

  const TabIframeDeck = {

    // This is a poor man session storage. Just a temporary
    // thing (localStorage is bad).
    saveSession: function() {
      let session = _tabIframeArray.map(t => t.location);
      session = session.filter(t => t.location && !t.location.startsWith("browserui://"));
      window.localStorage.session = JSON.stringify(session);
    },

    restoreSession: function() {
      let session = [];
      try {
        session = JSON.parse(window.localStorage.session);
      } catch (e) {}

      if (Array.isArray(session) && session.length > 0) {
        for (let url of session) {
          TabIframeDeck.add({url});
        }
      } else {
        TabIframeDeck.add({url: HOMEPAGE});
      }
    },

    onMozBrowserOpenWindow: function(type, event) {
      TabIframeDeck.add({url: event.detail.url});
    },

    add: function(options={}) {
      let tabIframe = document.createElement('tab-iframe');
      tabIframe.setAttribute('flex', '1');

      let parent = document.querySelector('.iframes');
      parent.appendChild(tabIframe);
      _tabIframeArray.push(tabIframe);

      tabIframe.on('mozbrowseropenwindow', this.onMozBrowserOpenWindow);
      tabIframe.on('mozbrowserlocationchange', this.saveSession);

      this.emit('add', {tabIframe: tabIframe});

      if (options.url) {
        tabIframe.setLocation(options.url);
      }

      if (options.select || _selectIndex < 0) {
        this.select(tabIframe);
      } else {
        tabIframe.hide();
      }

      this.saveSession();

      return tabIframe;
    },

    remove: function(tabIframe) {
      let index = _tabIframeArray.indexOf(tabIframe);
      if (index < 0) {
        throw new Error('Unknown tabIframe');
      }

      if (_tabIframeArray.length == 1) {
        throw new Error('Deck has only one tabiframe');
      }

      if (index == _selectIndex) {
        let newSelectIndex;
        if (index == _tabIframeArray.length - 1) {
          newSelectIndex = index - 1;
        } else {
          newSelectIndex = index + 1;
        }
        this.select(_tabIframeArray[newSelectIndex]);
      }

      if (_selectIndex > index) {
        _selectIndex--;
      }

      _tabIframeArray.splice(index, 1);
      tabIframe.off('mozbrowseropenwindow', this.onMozBrowserOpenWindow);
      tabIframe.remove();

      this.saveSession();

      this.emit('remove', {tabIframe});
    },

    select: function(tabIframe) {
      let index = _tabIframeArray.indexOf(tabIframe);
      if (index < 0) {
        throw new Error('Unknown tabiframe');
      }

      if (index == _selectIndex) {
        // already selected
        return;
      }

      tabIframe.willBeVisibleSoon();

      let previouslySelectTabIframe = _tabIframeArray[_selectIndex];
      if (previouslySelectTabIframe) {
        this.emit('unselect', {tabIframe: previouslySelectTabIframe});
      }

      _selectIndex = index;

      this.emit('select', {tabIframe});

      // Do the actual switch
      window.requestAnimationFrame(() => {
        if (previouslySelectTabIframe) {
          previouslySelectTabIframe.hide();
        }
        tabIframe.show();
      });
    },

    selectNext: function() {
      let newSelectIndex = _selectIndex + 1;
      if (newSelectIndex == _tabIframeArray.length) {
        newSelectIndex = 0;
      }
      this.select(_tabIframeArray[newSelectIndex]);
    },

    selectPrevious: function() {
      let newSelectIndex = _selectIndex - 1;
      if (newSelectIndex < 0) {
        newSelectIndex = _tabIframeArray.length - 1;
      }
      this.select(_tabIframeArray[newSelectIndex]);
    },

    getSelected: function() {
      return _tabIframeArray[_selectIndex];
    },

    getCount: function() {
      return _tabIframeArray.length;
    },
  }

  TabIframeDeck[Symbol.iterator] = function*() {
    for (let tabIframe of _tabIframeArray) {
      yield tabIframe;
    }
  }

  EventEmitter.decorate(TabIframeDeck);

  TabIframeDeck.restoreSession();

  RegisterKeyBindings(
    ['',              'Esc',        () => TabIframeDeck.getSelected().stop()],
    ['Ctrl',          'Tab',        () => TabIframeDeck.selectNext()],
    ['Ctrl Shift',    'code:9',     () => TabIframeDeck.selectPrevious()],
    ['',              'F5',         () => TabIframeDeck.getSelected().reload()]
  );

  if (window.OS == 'linux' || window.OS == 'windows') {
    RegisterKeyBindings(
      ['Ctrl',          't',          () => TabIframeDeck.add({select: true})],
      ['Ctrl',          'r',          () => TabIframeDeck.getSelected().reload()],
      ['Alt',           'Left',       () => TabIframeDeck.getSelected().goBack()],
      ['Alt',           'Right',      () => TabIframeDeck.getSelected().goForward()],
      ['Ctrl',          'w',          () => TabIframeDeck.remove(TabIframeDeck.getSelected())],
      ['Ctrl Shift',    '+',          () => TabIframeDeck.getSelected().zoomIn()],
      ['Ctrl',          '=',          () => TabIframeDeck.getSelected().zoomIn()],
      ['Ctrl',          '-',          () => TabIframeDeck.getSelected().zoomOut()],
      ['Ctrl',          '0',          () => TabIframeDeck.getSelected().resetZoom()]
    );
  }

  if (window.OS == 'osx') {
    RegisterKeyBindings(
      ['Cmd',       't',          () => TabIframeDeck.add({select: true})],
      ['Cmd',       'r',          () => TabIframeDeck.getSelected().reload()],
      ['Cmd',       'Left',       () => TabIframeDeck.getSelected().goBack()],
      ['Cmd',       'Right',      () => TabIframeDeck.getSelected().goForward()],
      ['Cmd',       'w',          () => TabIframeDeck.remove(TabIframeDeck.getSelected())],
      ['Cmd Shift', '+',          () => TabIframeDeck.getSelected().zoomIn()],
      ['Cmd',       '=',          () => TabIframeDeck.getSelected().zoomIn()],
      ['Cmd',       '-',          () => TabIframeDeck.getSelected().zoomOut()],
      ['Cmd',       '0',          () => TabIframeDeck.getSelected().resetZoom()]
    );
  }

  return TabIframeDeck;
});
<|endoftext|>


<commit_before>//JavaScript for hacking around the default behavior of Shiny

$("#top-nav a[data-value]").each(function() {
	// this references the DOM element and we change its 'href' attribute.
	if(this.getAttribute('data-value').substring(0,4) == 'java') {
		this.setAttribute('onClick', this.getAttribute('data-value'))
		this.setAttribute('data-toggle', null);
	} else {
		this.setAttribute('href', this.getAttribute('data-value'));
		this.setAttribute('target', '_blank');
		this.setAttribute('data-toggle', null);
	}
});

$('.fa-code').parent().parent().toggleClass('active');

<commit_msg>リファクタ<commit_after>//JavaScript for hacking around the default behavior of Shiny

$("#top-nav a[data-value]").each(function() {
    // this references the DOM element and we change its 'href' attribute.
    if(this.getAttribute('data-value').substring(0,4) == 'java') {
        this.setAttribute('onClick', this.getAttribute('data-value'))
            this.setAttribute('data-toggle', null);
    } else {
        this.setAttribute('href', this.getAttribute('data-value'));
        this.setAttribute('target', '_blank');
        this.setAttribute('data-toggle', null);
    }
});

<|endoftext|>


<commit_before>/*global $,app*/

app.views.results = function (parsedQuery) {
  // Breadcrumbs
  app.hooks.resultsCrumb.find('b').text(parsedQuery.label);
  app.hooks.crumbs.update(app.hooks.resultsCrumb);

  // Search
  app.hooks.searchRight.html(' ');
  app.hooks.searchLeft.removeClass('medium-14').addClass('medium-4').html(' ');
  app.hooks.searchBox.removeClass('medium-10 right').addClass('medium-16');

  // Empty both content areas
  app.hooks.content.children().detach();
  app.hooks.belowContent.children().detach();

  var opaEndpoint = parsedQuery.type + '/',
      isOwnerSearch = false;

  switch (parsedQuery.type) {
    case 'account':
      opaEndpoint += encodeURI(parsedQuery.account);
      break;
    case 'intersection':
      opaEndpoint += encodeURI(parsedQuery.street1 + '/' + parsedQuery.street2);
      break;
    case 'block':
      opaEndpoint += encodeURI(parsedQuery.address + '/');
      break;
    case 'address':
      opaEndpoint += encodeURI(parsedQuery.address + '/');

      if (parsedQuery.unit) {
        opaEndpoint += encodeURI(parsedQuery.unit);
      }
      break;
    case 'owner':
      isOwnerSearch = true;
      opaEndpoint += encodeURI(parsedQuery.owner);
      break;
  }

  app.hooks.content.empty(); // Remove loading message
  app.hooks.ownerSearchDisclaimer.addClass('hide');

  if (history.state) {
    render();
  } else {
    app.hooks.content.append(app.hooks.loading);

    $.ajax('https://api.phila.gov/opa/v1.1/' + opaEndpoint + '?format=json',
      {dataType: app.settings.ajaxType})
      .done(function (data) {
        var property, accountNumber, href, withUnit;

        // For business reasons, owner searches need to always show on the
        // results page for the disclaimer.
        if (!isOwnerSearch && (data.data.property || data.data.properties.length === 1)) {

          // If only one property go straight to property view
          property = data.data.property || data.data.properties[0];
          accountNumber = property.account_number;
          href = '?' + $.param({p: accountNumber});
          withUnit = app.util.addressWithUnit(property);

          history.replaceState({
            opa: property,
            address: withUnit
          }, withUnit, href);

          app.views.property(accountNumber);
        } else {
          // Used for rendering a special owner search disclaimer
          if (isOwnerSearch) {
            data = $.extend({isOwnerSearch: true}, data);
          }

          history.replaceState(data, ''); // Second param not optional in IE10
          render();
        }
      })
      .fail(function () {
        history.replaceState({error: 'Failed to retrieve results. Please try another search.'}, '');
        render();
      });
  }

  function render () {
    var state = history.state;
    if (state.error) return app.hooks.content.text(state.error);

    app.hooks.content.empty(); // Remove loading message

    if (state.total === 0) {
      return app.hooks.content.append(app.hooks.noResults);
    }

    if (state.isOwnerSearch) {
      renderOwnerSearchDisclaimer();
    }

    // TODO find a place for count
    //app.hooks.count.find('#total').text(state.total);
    //app.hooks.content.append(app.hooks.count);
    app.hooks.resultRows.empty(); // TODO reuse existing result nodes
    state.data.properties.forEach(addRow);
    if (state.total > state.data.properties.length) {
      var seeMoreA = app.hooks.seeMore.find('a');
      seeMoreA.off('click'); // Drop previously created click events
      seeMoreA.on('click', function (e) {
        $.ajax('https://api.phila.gov/opa/v1.1/' + opaEndpoint + '?format=json'+
          '&skip=' + state.data.properties.length, {dataType: app.settings.ajaxType})
          .done(function (data) {
            state.data.properties = state.data.properties.concat(data.data.properties);
            history.replaceState(state, ''); // Second param not optional in IE10
            data.data.properties.forEach(addRow);
            if (state.total === state.data.properties.length) app.hooks.seeMore.hide();
          });
      });
      app.hooks.seeMore.show();
    } else {
      app.hooks.seeMore.hide();
    }
    app.hooks.content.append(app.hooks.results);
  }

  function renderOwnerSearchDisclaimer() {
    var now = new Date(),
        mins = now.getMinutes(),
        prettyMins = mins < 10 ? ('0' + mins) : mins,
        prettyNow = (now.getMonth() + 1) + '/' + now.getDate() + '/' +  now.getFullYear() +
                    ' ' + now.getHours() + ':' + prettyMins;

    app.hooks.ownerSearchDisclaimer.removeClass('hide');
    app.hooks.ownerSearchDisclaimerDatetime.text(prettyNow);
    app.hooks.ownerSearchDisclaimerQuery.text(parsedQuery.label);

    // Fetch IP & show it
    $.getJSON('https://api.ipify.org?format=json')
      .done(function(response) {
        if(response.ip) {
          app.hooks.ownerSearchDisclaimerIp.text(response.ip);
        }
      })
      .fail(function() {
        app.hooks.ownerSearchDisclaimerIp.text(app.hooks.ownerSearchDisclaimerIp.attr('data-default'));
      });
  }

  function addRow (property) {
    var row = app.hooks.resultRow.clone();
    var accountNumber = property.account_number;
    var withUnit = app.util.addressWithUnit(property);
    var href = '?' + $.param({p: accountNumber});
    row.append($('').append($('').text(withUnit)));
    row.append($('').text(property.valuation_history && accounting.formatMoney(property.valuation_history[0].market_value)));
    row.append($('').text(app.util.formatSalesDate(property.sales_information.sales_date)
      + ', ' + accounting.formatMoney(property.sales_information.sales_price)));
    row.append($('').text(property.ownership.owners.join(', ')));
    row.append($('').html(''));
    row.on('click', function (e) {
        if (e.ctrlKey || e.altKey || e.shiftKey) return;
        e.preventDefault();
        history.pushState({opa: property, address: withUnit}, withUnit, href);
        window.scroll(0, 0);
        app.views.property(accountNumber);
      });
    app.hooks.resultRows.append(row);
  }
};
<commit_msg>Handle strange 200 http response that is actually a 400 error.<commit_after>/*global $,app*/

app.views.results = function (parsedQuery) {
  // Breadcrumbs
  app.hooks.resultsCrumb.find('b').text(parsedQuery.label);
  app.hooks.crumbs.update(app.hooks.resultsCrumb);

  // Search
  app.hooks.searchRight.html(' ');
  app.hooks.searchLeft.removeClass('medium-14').addClass('medium-4').html(' ');
  app.hooks.searchBox.removeClass('medium-10 right').addClass('medium-16');

  // Empty both content areas
  app.hooks.content.children().detach();
  app.hooks.belowContent.children().detach();

  var opaEndpoint = parsedQuery.type + '/',
      isOwnerSearch = false;

  switch (parsedQuery.type) {
    case 'account':
      opaEndpoint += encodeURI(parsedQuery.account);
      break;
    case 'intersection':
      opaEndpoint += encodeURI(parsedQuery.street1 + '/' + parsedQuery.street2);
      break;
    case 'block':
      opaEndpoint += encodeURI(parsedQuery.address + '/');
      break;
    case 'address':
      opaEndpoint += encodeURI(parsedQuery.address + '/');

      if (parsedQuery.unit) {
        opaEndpoint += encodeURI(parsedQuery.unit);
      }
      break;
    case 'owner':
      isOwnerSearch = true;
      opaEndpoint += encodeURI(parsedQuery.owner);
      break;
  }

  app.hooks.content.empty(); // Remove loading message
  app.hooks.ownerSearchDisclaimer.addClass('hide');

  if (history.state) {
    render();
  } else {
    app.hooks.content.append(app.hooks.loading);

    $.ajax('https://api.phila.gov/opa/v1.1/' + opaEndpoint + '?format=json',
      {dataType: app.settings.ajaxType})
      .done(function (data) {
        var property, accountNumber, href, withUnit;

        // If we get a 200 response but an 400 error code (ummmmm), treat it like a fail.
        if (!data.data) {
          history.replaceState({error: 'Failed to retrieve results. Please try another search.'}, '');
          render();
          return;
        }

        // For business reasons, owner searches need to always show on the
        // results page for the disclaimer.
        if (!isOwnerSearch && (data.data.property || data.data.properties.length === 1)) {

          // If only one property go straight to property view
          property = data.data.property || data.data.properties[0];
          accountNumber = property.account_number;
          href = '?' + $.param({p: accountNumber});
          withUnit = app.util.addressWithUnit(property);

          history.replaceState({
            opa: property,
            address: withUnit
          }, withUnit, href);

          app.views.property(accountNumber);
        } else {
          // Used for rendering a special owner search disclaimer
          if (isOwnerSearch) {
            data = $.extend({isOwnerSearch: true}, data);
          }

          history.replaceState(data, ''); // Second param not optional in IE10
          render();
        }
      })
      .fail(function () {
        history.replaceState({error: 'Failed to retrieve results. Please try another search.'}, '');
        render();
      });
  }

  function render () {
    var state = history.state;
    if (state.error) return app.hooks.content.text(state.error);

    app.hooks.content.empty(); // Remove loading message

    if (state.total === 0) {
      return app.hooks.content.append(app.hooks.noResults);
    }

    if (state.isOwnerSearch) {
      renderOwnerSearchDisclaimer();
    }

    // TODO find a place for count
    //app.hooks.count.find('#total').text(state.total);
    //app.hooks.content.append(app.hooks.count);
    app.hooks.resultRows.empty(); // TODO reuse existing result nodes
    state.data.properties.forEach(addRow);
    if (state.total > state.data.properties.length) {
      var seeMoreA = app.hooks.seeMore.find('a');
      seeMoreA.off('click'); // Drop previously created click events
      seeMoreA.on('click', function (e) {
        $.ajax('https://api.phila.gov/opa/v1.1/' + opaEndpoint + '?format=json'+
          '&skip=' + state.data.properties.length, {dataType: app.settings.ajaxType})
          .done(function (data) {
            state.data.properties = state.data.properties.concat(data.data.properties);
            history.replaceState(state, ''); // Second param not optional in IE10
            data.data.properties.forEach(addRow);
            if (state.total === state.data.properties.length) app.hooks.seeMore.hide();
          });
      });
      app.hooks.seeMore.show();
    } else {
      app.hooks.seeMore.hide();
    }
    app.hooks.content.append(app.hooks.results);
  }

  function renderOwnerSearchDisclaimer() {
    var now = new Date(),
        mins = now.getMinutes(),
        prettyMins = mins < 10 ? ('0' + mins) : mins,
        prettyNow = (now.getMonth() + 1) + '/' + now.getDate() + '/' +  now.getFullYear() +
                    ' ' + now.getHours() + ':' + prettyMins;

    app.hooks.ownerSearchDisclaimer.removeClass('hide');
    app.hooks.ownerSearchDisclaimerDatetime.text(prettyNow);
    app.hooks.ownerSearchDisclaimerQuery.text(parsedQuery.label);

    // Fetch IP & show it
    $.getJSON('https://api.ipify.org?format=json')
      .done(function(response) {
        if(response.ip) {
          app.hooks.ownerSearchDisclaimerIp.text(response.ip);
        }
      })
      .fail(function() {
        app.hooks.ownerSearchDisclaimerIp.text(app.hooks.ownerSearchDisclaimerIp.attr('data-default'));
      });
  }

  function addRow (property) {
    var row = app.hooks.resultRow.clone();
    var accountNumber = property.account_number;
    var withUnit = app.util.addressWithUnit(property);
    var href = '?' + $.param({p: accountNumber});
    row.append($('').append($('').text(withUnit)));
    row.append($('').text(property.valuation_history && accounting.formatMoney(property.valuation_history[0].market_value)));
    row.append($('').text(app.util.formatSalesDate(property.sales_information.sales_date)
      + ', ' + accounting.formatMoney(property.sales_information.sales_price)));
    row.append($('').text(property.ownership.owners.join(', ')));
    row.append($('').html(''));
    row.on('click', function (e) {
        if (e.ctrlKey || e.altKey || e.shiftKey) return;
        e.preventDefault();
        history.pushState({opa: property, address: withUnit}, withUnit, href);
        window.scroll(0, 0);
        app.views.property(accountNumber);
      });
    app.hooks.resultRows.append(row);
  }
};
<|endoftext|>


<commit_before>/*global $,app*/

app.views.results = function (parsedQuery) {
  // Breadcrumbs
  app.hooks.resultsCrumb.find('b').text(parsedQuery.label);
  app.hooks.crumbs.update(app.hooks.resultsCrumb);

  // Search
  app.hooks.searchRight.html(' ');
  app.hooks.searchLeft.removeClass('medium-14').addClass('medium-4').html(' ');
  app.hooks.searchBox.removeClass('medium-10 float-right').addClass('medium-16');

  // Empty both content areas
  app.hooks.content.children().detach();
  app.hooks.belowContent.children().detach();
  app.hooks.aboveContent.children().detach();

  var endpointMap = {
    'address': 'addresses',
    'account': 'account', //TODO
    'intersection': 'intersection',
    'block': 'block',
    'owner': 'owner',
  };

  var endpoint = endpointMap[parsedQuery.type] + '/',
      isOwnerSearch = false;

  switch (parsedQuery.type) {
    case 'account':
      endpoint += encodeURI(parsedQuery.account);
      break;
    // TODO there's currently no intersection endpoint
    case 'intersection':
      endpoint += encodeURI(parsedQuery.street1 + '/' + parsedQuery.street2);
      break;
    case 'block':
      endpoint += encodeURI(parsedQuery.address);
      break;
    case 'address':
      endpoint += encodeURI(parsedQuery.address);

      if (parsedQuery.unit) {
        endpoint += encodeURI(parsedQuery.unit);
      }
      break;
    case 'owner':
      isOwnerSearch = true;
      endpoint += encodeURI(parsedQuery.owner);
      break;
  }

  app.hooks.content.empty(); // Remove loading message
  app.hooks.ownerSearchDisclaimer.addClass('hide');

  if (history.state && app.globals.historyState) {
    render();
  } else {
    app.hooks.content.append(app.hooks.loading);
    getData()
  }

  function getData () {
    var params = {
      gatekeeperKey: app.config['gatekeeperKey'],
      include_units: null,
      opa_only: null,
    };

    $.ajax( 'https://api.phila.gov/ais/v1/' + endpoint,
      {data: params, dataType: app.settings.ajaxType})
      .done(function (aisData) {
        var property, accountNumber, href, withUnit;

        if (!app.globals.historyState) history.state = {};

        // For business reasons, owner searches need to always show on the
        // results page for the disclaimer.
        if (!isOwnerSearch && (
            aisData.type == 'Feature' ||
            aisData.features.length === 1)) {
          // If only one property go straight to property view
          feature = (aisData.features ? aisData.features[0] : aisData);
          attrs = feature.properties;
          accountNumber = attrs.opa_account_num;
          href = '?' + $.param({p: accountNumber});
          withUnit = feature.properties.street_address;

          history.replaceState({
            ais: feature,
            address: withUnit,
          }, withUnit, href);

          app.views.property(accountNumber);
        } else {
          // Fetch market_value, sale data from OPA dataset
          var opaUrl = constructOpaUrl(aisData.features);
          $.ajax(opaUrl, {dataType: app.settings.ajaxType})
          .done(function (opaData) {
            var keyedOpaData = keyBy(opaData, 'parcel_number')
            $.each(aisData.features, function (index, feature) {
              $.extend(feature.properties, keyedOpaData[feature.properties.opa_account_num] || {})
            })

            var newState = $.extend({}, history.state);
            // Used for rendering a special owner search disclaimer
            if (isOwnerSearch) {
              aisData = $.extend({isOwnerSearch: true}, aisData);
            }
            newState = aisData

            if (!app.globals.historyState) {
              history.state = newState;
            } else {
              history.replaceState(newState, ''); // Second param not optional in IE10
            }
            render();
          });
        }
      })
      .fail(function () {
        history.replaceState({error: 'Failed to retrieve results. Please try another search.'}, '');
        render();
      });
  }

  function constructOpaUrl (features) {
    var accountNumbers = $.map(features, function (feature) {
      return feature.properties.opa_account_num
    })

    var params = {
      $select: [
        'parcel_number',
        'market_value',
        'sale_date',
        'sale_price'
      ].join(','),
      $where: 'parcel_number in ("' + accountNumbers.join('","') + '")'
    };
    return '//data.phila.gov/resource/tqtk-pmbv.json?' + $.param(params)
  }

  function keyBy (items, key) {
    var hash = {}
    for (var i = 0; i < items.length; i++) {
      hash[items[i][key]] = items[i]
    }
    return hash
  }

  function render () {
    var state = history.state;
    if (state.error) return app.hooks.content.text(state.error);
    var features = state.features;
    var total_size = state.total_size;
    var isOwnerSearch = state.isOwnerSearch;

    app.hooks.content.empty(); // Remove loading message

    if (total_size === 0) {
      return app.hooks.content.append(app.hooks.noResults);
    }

    if (isOwnerSearch) {
      renderOwnerSearchDisclaimer();
    }

    // TODO find a place for count
    //app.hooks.count.find('#total').text(state.total);
    //app.hooks.content.append(app.hooks.count);
    app.hooks.resultRows.empty(); // TODO reuse existing result nodes
    if (features) {
      $.each(features, addRow);

      if (total_size > features.length) {
        var seeMoreA = app.hooks.seeMore.find('a');
        seeMoreA.off('click'); // Drop previously created click events
        seeMoreA.on('click', function (e) {

          var params = {
            gatekeeperKey: app.config['gatekeeperKey'],
            include_units: null,
            opa_only: null,
            page: state.page + 1
          };

          $.ajax('https://api.phila.gov/ais/v1/' + endpoint,
                 {data: params, dataType: app.settings.ajaxType})
            .done(function (aisData) {
              // Fetch market_value, sale data from OPA dataset
              var opaUrl = constructOpaUrl(aisData.features);
              $.ajax(opaUrl, {dataType: app.settings.ajaxType})
              .done(function (opaData) {
                var keyedOpaData = keyBy(opaData, 'parcel_number')
                $.each(aisData.features, function (index, feature) {
                  $.extend(feature.properties, keyedOpaData[feature.properties.opa_account_num] || {})
                })

                state.features = state.features.concat(aisData.features);
                state.page = aisData.page;
                history.replaceState(state, ''); // Second param not optional in IE10

                $.each(aisData.features, addRow);
                if (state.total_size === state.features.length) app.hooks.seeMore.hide();

                // Update the Tablesaw responsive tables
                $(document).trigger('enhance.tablesaw');
              });
            });
        });
        app.hooks.seeMore.show();
      } else {
        app.hooks.seeMore.hide();
      }
      app.hooks.content.append(app.hooks.results);

      // Update the Tablesaw responsive tables
      $(document).trigger('enhance.tablesaw');
    }
  }

  function renderOwnerSearchDisclaimer() {
    var now = new Date(),
        mins = now.getMinutes(),
        prettyMins = mins < 10 ? ('0' + mins) : mins,
        prettyNow = (now.getMonth() + 1) + '/' + now.getDate() + '/' +  now.getFullYear() +
                    ' ' + now.getHours() + ':' + prettyMins;

    app.hooks.ownerSearchDisclaimer.removeClass('hide');
    app.hooks.ownerSearchDisclaimerDatetime.text(prettyNow);
    app.hooks.ownerSearchDisclaimerQuery.text(parsedQuery.label);

    // Fetch IP & show it
    $.getJSON('https://api.ipify.org?format=json')
      .done(function(response) {
        if(response.ip) {
          app.hooks.ownerSearchDisclaimerIp.text(response.ip);
        }
      })
      .fail(function() {
        app.hooks.ownerSearchDisclaimerIp.text(app.hooks.ownerSearchDisclaimerIp.attr('data-default'));
      });
  }

  function addRow (index, property) {
    var row = app.hooks.resultRow.clone(),
        attrs = property.properties,
        accountNumber = attrs.opa_account_num,
        withUnit = attrs.street_address,
        href = '?' + $.param({p: accountNumber});

    row.append($('').append($('').text(withUnit)));
    row.append($('').text(accounting.formatMoney(attrs.market_value)));
    row.append($('').text(moment(attrs.sale_date).format('M/D/YYYY') + ', ' + accounting.formatMoney(attrs.sale_price)));
    row.append($('').text(attrs.opa_owners && attrs.opa_owners.join(', ')));
    row.append($('').html(''));

    row.on('click', function (e) {
        if (e.ctrlKey || e.altKey || e.shiftKey) return;
        e.preventDefault();
        history.pushState({ais: property, address: withUnit}, withUnit, href);
        window.scroll(0, 0);
        app.views.property(accountNumber);
      });
    app.hooks.resultRows.append(row);
  }
};
<commit_msg>Add unit designator when including unit in address search<commit_after>/*global $,app*/

app.views.results = function (parsedQuery) {
  // Breadcrumbs
  app.hooks.resultsCrumb.find('b').text(parsedQuery.label);
  app.hooks.crumbs.update(app.hooks.resultsCrumb);

  // Search
  app.hooks.searchRight.html(' ');
  app.hooks.searchLeft.removeClass('medium-14').addClass('medium-4').html(' ');
  app.hooks.searchBox.removeClass('medium-10 float-right').addClass('medium-16');

  // Empty both content areas
  app.hooks.content.children().detach();
  app.hooks.belowContent.children().detach();
  app.hooks.aboveContent.children().detach();

  var endpointMap = {
    'address': 'addresses',
    'account': 'account', //TODO
    'intersection': 'intersection',
    'block': 'block',
    'owner': 'owner',
  };

  var endpoint = endpointMap[parsedQuery.type] + '/',
      isOwnerSearch = false;

  switch (parsedQuery.type) {
    case 'account':
      endpoint += encodeURI(parsedQuery.account);
      break;
    // TODO there's currently no intersection endpoint
    case 'intersection':
      endpoint += encodeURI(parsedQuery.street1 + '/' + parsedQuery.street2);
      break;
    case 'block':
      endpoint += encodeURI(parsedQuery.address);
      break;
    case 'address':
      endpoint += encodeURI(parsedQuery.address);

      if (parsedQuery.unit) {
        endpoint += encodeURI(' UNIT ' + parsedQuery.unit);
      }
      break;
    case 'owner':
      isOwnerSearch = true;
      endpoint += encodeURI(parsedQuery.owner);
      break;
  }

  app.hooks.content.empty(); // Remove loading message
  app.hooks.ownerSearchDisclaimer.addClass('hide');

  if (history.state && app.globals.historyState) {
    render();
  } else {
    app.hooks.content.append(app.hooks.loading);
    getData()
  }

  function getData () {
    var params = {
      gatekeeperKey: app.config['gatekeeperKey'],
      include_units: null,
      opa_only: null,
    };

    $.ajax( 'https://api.phila.gov/ais/v1/' + endpoint,
      {data: params, dataType: app.settings.ajaxType})
      .done(function (aisData) {
        var property, accountNumber, href, withUnit;

        if (!app.globals.historyState) history.state = {};

        // For business reasons, owner searches need to always show on the
        // results page for the disclaimer.
        if (!isOwnerSearch && (
            aisData.type == 'Feature' ||
            aisData.features.length === 1)) {
          // If only one property go straight to property view
          feature = (aisData.features ? aisData.features[0] : aisData);
          attrs = feature.properties;
          accountNumber = attrs.opa_account_num;
          href = '?' + $.param({p: accountNumber});
          withUnit = feature.properties.street_address;

          history.replaceState({
            ais: feature,
            address: withUnit,
          }, withUnit, href);

          app.views.property(accountNumber);
        } else {
          // Fetch market_value, sale data from OPA dataset
          var opaUrl = constructOpaUrl(aisData.features);
          $.ajax(opaUrl, {dataType: app.settings.ajaxType})
          .done(function (opaData) {
            var keyedOpaData = keyBy(opaData, 'parcel_number')
            $.each(aisData.features, function (index, feature) {
              $.extend(feature.properties, keyedOpaData[feature.properties.opa_account_num] || {})
            })

            var newState = $.extend({}, history.state);
            // Used for rendering a special owner search disclaimer
            if (isOwnerSearch) {
              aisData = $.extend({isOwnerSearch: true}, aisData);
            }
            newState = aisData

            if (!app.globals.historyState) {
              history.state = newState;
            } else {
              history.replaceState(newState, ''); // Second param not optional in IE10
            }
            render();
          });
        }
      })
      .fail(function () {
        history.replaceState({error: 'Failed to retrieve results. Please try another search.'}, '');
        render();
      });
  }

  function constructOpaUrl (features) {
    var accountNumbers = $.map(features, function (feature) {
      return feature.properties.opa_account_num
    })

    var params = {
      $select: [
        'parcel_number',
        'market_value',
        'sale_date',
        'sale_price'
      ].join(','),
      $where: 'parcel_number in ("' + accountNumbers.join('","') + '")'
    };
    return '//data.phila.gov/resource/tqtk-pmbv.json?' + $.param(params)
  }

  function keyBy (items, key) {
    var hash = {}
    for (var i = 0; i < items.length; i++) {
      hash[items[i][key]] = items[i]
    }
    return hash
  }

  function render () {
    var state = history.state;
    if (state.error) return app.hooks.content.text(state.error);
    var features = state.features;
    var total_size = state.total_size;
    var isOwnerSearch = state.isOwnerSearch;

    app.hooks.content.empty(); // Remove loading message

    if (total_size === 0) {
      return app.hooks.content.append(app.hooks.noResults);
    }

    if (isOwnerSearch) {
      renderOwnerSearchDisclaimer();
    }

    // TODO find a place for count
    //app.hooks.count.find('#total').text(state.total);
    //app.hooks.content.append(app.hooks.count);
    app.hooks.resultRows.empty(); // TODO reuse existing result nodes
    if (features) {
      $.each(features, addRow);

      if (total_size > features.length) {
        var seeMoreA = app.hooks.seeMore.find('a');
        seeMoreA.off('click'); // Drop previously created click events
        seeMoreA.on('click', function (e) {

          var params = {
            gatekeeperKey: app.config['gatekeeperKey'],
            include_units: null,
            opa_only: null,
            page: state.page + 1
          };

          $.ajax('https://api.phila.gov/ais/v1/' + endpoint,
                 {data: params, dataType: app.settings.ajaxType})
            .done(function (aisData) {
              // Fetch market_value, sale data from OPA dataset
              var opaUrl = constructOpaUrl(aisData.features);
              $.ajax(opaUrl, {dataType: app.settings.ajaxType})
              .done(function (opaData) {
                var keyedOpaData = keyBy(opaData, 'parcel_number')
                $.each(aisData.features, function (index, feature) {
                  $.extend(feature.properties, keyedOpaData[feature.properties.opa_account_num] || {})
                })

                state.features = state.features.concat(aisData.features);
                state.page = aisData.page;
                history.replaceState(state, ''); // Second param not optional in IE10

                $.each(aisData.features, addRow);
                if (state.total_size === state.features.length) app.hooks.seeMore.hide();

                // Update the Tablesaw responsive tables
                $(document).trigger('enhance.tablesaw');
              });
            });
        });
        app.hooks.seeMore.show();
      } else {
        app.hooks.seeMore.hide();
      }
      app.hooks.content.append(app.hooks.results);

      // Update the Tablesaw responsive tables
      $(document).trigger('enhance.tablesaw');
    }
  }

  function renderOwnerSearchDisclaimer() {
    var now = new Date(),
        mins = now.getMinutes(),
        prettyMins = mins < 10 ? ('0' + mins) : mins,
        prettyNow = (now.getMonth() + 1) + '/' + now.getDate() + '/' +  now.getFullYear() +
                    ' ' + now.getHours() + ':' + prettyMins;

    app.hooks.ownerSearchDisclaimer.removeClass('hide');
    app.hooks.ownerSearchDisclaimerDatetime.text(prettyNow);
    app.hooks.ownerSearchDisclaimerQuery.text(parsedQuery.label);

    // Fetch IP & show it
    $.getJSON('https://api.ipify.org?format=json')
      .done(function(response) {
        if(response.ip) {
          app.hooks.ownerSearchDisclaimerIp.text(response.ip);
        }
      })
      .fail(function() {
        app.hooks.ownerSearchDisclaimerIp.text(app.hooks.ownerSearchDisclaimerIp.attr('data-default'));
      });
  }

  function addRow (index, property) {
    var row = app.hooks.resultRow.clone(),
        attrs = property.properties,
        accountNumber = attrs.opa_account_num,
        withUnit = attrs.street_address,
        href = '?' + $.param({p: accountNumber});

    row.append($('').append($('').text(withUnit)));
    row.append($('').text(accounting.formatMoney(attrs.market_value)));
    row.append($('').text(moment(attrs.sale_date).format('M/D/YYYY') + ', ' + accounting.formatMoney(attrs.sale_price)));
    row.append($('').text(attrs.opa_owners && attrs.opa_owners.join(', ')));
    row.append($('').html(''));

    row.on('click', function (e) {
        if (e.ctrlKey || e.altKey || e.shiftKey) return;
        e.preventDefault();
        history.pushState({ais: property, address: withUnit}, withUnit, href);
        window.scroll(0, 0);
        app.views.property(accountNumber);
      });
    app.hooks.resultRows.append(row);
  }
};
<|endoftext|>


<commit_before>import {
  Promise as EmberPromise,
} from 'rsvp';
import $ from 'jquery';
import { A } from '@ember/array';
import ExpandableRecordArray from 'travis/utils/expandable-record-array';
import Model from 'ember-data/model';
import attr from 'ember-data/attr';
import { hasMany, belongsTo } from 'ember-data/relationships';
import { service } from 'ember-decorators/service';
import { computed } from 'ember-decorators/object';
import { oneWay } from 'ember-decorators/object/computed';
import { task } from 'ember-concurrency';

const Repo = Model.extend({
  @service api: null,
  @service auth: null,
  permissions: attr(),
  slug: attr(),
  description: attr(),
  'private': attr('boolean'),
  githubId: attr(),
  githubLanguage: attr(),
  active: attr(),
  owner: attr(),
  name: attr(),
  starred: attr('boolean'),
  active_on_org: attr('boolean'),
  emailSubscribed: attr('boolean'),

  @oneWay('owner.@type') ownerType: null,

  @oneWay('currentBuild.finishedAt') currentBuildFinishedAt: null,
  @oneWay('currentBuild.state') currentBuildState: null,
  @oneWay('currentBuild.id') currentBuildId: null,

  defaultBranch: belongsTo('branch', {
    async: false
  }),
  currentBuild: belongsTo('build', {
    async: true, inverse: 'repoCurrentBuild'
  }),
  _branches: hasMany('branch'),

  // TODO: this is a hack, we should remove it once @is_collaborator property is
  // added to a response with the repo
  @computed('auth.currentUser.permissions.[]')
  isCurrentUserACollaborator(permissions) {
    if (permissions) {
      let id = parseInt(this.get('id'));

      return permissions.includes(id);
    }
  },

  @computed('owner.login', 'name')
  formattedSlug(login, name) {
    return `${login} / ${name}`;
  },

  sshKey: function () {
    this.store.find('ssh_key', this.get('id'));
    return this.store.recordForId('ssh_key', this.get('id'));
  },

  @computed('id')
  envVars(id) {
    return this.store.filter('env_var', {
      repository_id: id
    }, (v) => v.get('repo.id') === id);
  },

  _buildRepoMatches(build, id) {
    // TODO: I don't understand why we need to compare string id's here
    return `${build.get('repo.id')}` === `${id}`;
  },

  _buildObservableArray(builds) {
    const array = ExpandableRecordArray.create({
      type: 'build',
      content: A([])
    });
    array.load(builds);
    array.observe(builds);
    return array;
  },

  @computed('id')
  builds(id) {
    const builds = this.store.filter('build', {
      event_type: ['push', 'api', 'cron'],
      repository_id: id
    }, (b) => {
      let eventTypes = ['push', 'api', 'cron'];
      return this._buildRepoMatches(b, id) && eventTypes.includes(b.get('eventType'));
    });
    return this._buildObservableArray(builds);
  },

  @computed('id')
  pullRequests(id) {
    const builds = this.store.filter('build', {
      event_type: 'pull_request',
      repository_id: id
    }, (b) => {
      const isPullRequest = b.get('eventType') === 'pull_request';
      return this._buildRepoMatches(b, id) && isPullRequest;
    });
    return this._buildObservableArray(builds);
  },

  @computed('id')
  branches(id) {
    return this.store.filter('branch', {
      repository_id: id
    }, (b) => b.get('repoId') === id);
  },

  @computed('id')
  cronJobs(id) {
    return this.store.filter('cron', {
      repository_id: id
    }, (cron) => cron.get('branch.repoId') === id);
  },

  // TODO: Stop performing a `set` as part of the cp!
  // TODO: Is this even used?
  @computed('slug', '_stats')
  stats(slug, stats) {
    if (slug) {
      return stats || $.get(`https://api.github.com/repos/${slug}`, (data) => {
        this.set('_stats', data);
        return this.notifyPropertyChange('stats');
      }) && {};
    }
  },

  updateTimes() {
    let currentBuild = this.get('currentBuild');
    if (currentBuild) {
      return currentBuild.updateTimes();
    }
  },

  fetchSettings() {
    const url = `/repo/${this.get('id')}/settings`;
    return this.get('api').get(url).
      then(data => this._convertV3SettingsToV2(data['settings']));
  },

  saveSetting(name, value) {
    return this.get('api').patch(`/repo/${this.get('id')}/setting/${name}`, {
      data: {
        'setting.value': value
      }
    });
  },

  _convertV3SettingsToV2(v3Settings) {
    return v3Settings.reduce((v2Settings, v3Setting) => {
      if (v3Setting) {
        v2Settings[v3Setting.name] = v3Setting.value;
      }
      return v2Settings;
    }, {});
  },

  toggle() {
    const adapter = this.store.adapterFor('repo');
    const id = this.get('id');
    let promise;
    if (this.get('active')) {
      promise = adapter.deactivate(id);
    } else {
      promise = adapter.activate(id);
    }

    return promise;
  },

  @computed('id')
  emailSubscriptionUrl(id) {
    return `/repo/${id}/email_subscription`;
  },

  subscribe: task(function* () {
    yield this.api.post(this.emailSubscriptionUrl);
    yield this.reload();
  }).drop(),

  unsubscribe: task(function* () {
    yield this.api.delete(this.emailSubscriptionUrl);
    yield this.reload();
  }).drop()
});

Repo.reopenClass({
  recent() {
    return this.find();
  },

  accessibleBy(store, reposIdsOrlogin) {
    let repos, reposIds;
    reposIds = reposIdsOrlogin || [];
    repos = store.filter('repo', (repo) => {
      let repoId = parseInt(repo.get('id'));
      return reposIds.includes(repoId);
    });
    return new EmberPromise((resolve, reject) => {
      const params = {
        'repository.active': 'true',
        sort_by: 'current_build:desc',
        limit: 30,
      };
      return store.query('repo', params)
        .then(() => resolve(repos), () => reject());
    });
  },

  search(store, query) {
    return store.query('repo', {
      name_filter: query,
      sort_by: 'name_filter:desc',
      limit: 10
    });
  },

  fetchBySlug(store, slug) {
    let adapter, modelClass, promise, repos;
    repos = store.peekAll('repo').filterBy('slug', slug);
    if (repos.get('length') > 0) {
      return repos.get('firstObject');
    } else {
      promise = null;
      adapter = store.adapterFor('repo');
      modelClass = store.modelFor('repo');
      promise = adapter.findRecord(store, modelClass, slug).then((payload) => {
        let i, len, record, ref, repo, result, serializer;
        serializer = store.serializerFor('repo');
        modelClass = store.modelFor('repo');
        result = serializer.normalizeResponse(store, modelClass, payload, null, 'findRecord');
        repo = store.push({
          data: result.data
        });
        ref = result.included;
        for (i = 0, len = ref.length; i < len; i++) {
          record = ref[i];
          store.push({
            data: record
          });
        }
        return repo;
      });
      return promise['catch'](() => {
        let error;
        error = new Error('repo not found');
        error.slug = slug;
        throw error;
      });
    }
  },
});

export default Repo;
<commit_msg>Reduce builds request amount (#1816)<commit_after>import {
  Promise as EmberPromise,
} from 'rsvp';
import $ from 'jquery';
import { A } from '@ember/array';
import ExpandableRecordArray from 'travis/utils/expandable-record-array';
import Model from 'ember-data/model';
import attr from 'ember-data/attr';
import { hasMany, belongsTo } from 'ember-data/relationships';
import { service } from 'ember-decorators/service';
import { computed } from 'ember-decorators/object';
import { oneWay } from 'ember-decorators/object/computed';
import { task } from 'ember-concurrency';

const Repo = Model.extend({
  @service api: null,
  @service auth: null,
  permissions: attr(),
  slug: attr(),
  description: attr(),
  'private': attr('boolean'),
  githubId: attr(),
  githubLanguage: attr(),
  active: attr(),
  owner: attr(),
  name: attr(),
  starred: attr('boolean'),
  active_on_org: attr('boolean'),
  emailSubscribed: attr('boolean'),

  @oneWay('owner.@type') ownerType: null,

  @oneWay('currentBuild.finishedAt') currentBuildFinishedAt: null,
  @oneWay('currentBuild.state') currentBuildState: null,
  @oneWay('currentBuild.id') currentBuildId: null,

  defaultBranch: belongsTo('branch', {
    async: false
  }),
  currentBuild: belongsTo('build', {
    async: true, inverse: 'repoCurrentBuild'
  }),
  _branches: hasMany('branch'),

  // TODO: this is a hack, we should remove it once @is_collaborator property is
  // added to a response with the repo
  @computed('auth.currentUser.permissions.[]')
  isCurrentUserACollaborator(permissions) {
    if (permissions) {
      let id = parseInt(this.get('id'));

      return permissions.includes(id);
    }
  },

  @computed('owner.login', 'name')
  formattedSlug(login, name) {
    return `${login} / ${name}`;
  },

  sshKey: function () {
    this.store.find('ssh_key', this.get('id'));
    return this.store.recordForId('ssh_key', this.get('id'));
  },

  @computed('id')
  envVars(id) {
    return this.store.filter('env_var', {
      repository_id: id
    }, (v) => v.get('repo.id') === id);
  },

  _buildRepoMatches(build, id) {
    // TODO: I don't understand why we need to compare string id's here
    return `${build.get('repo.id')}` === `${id}`;
  },

  _buildObservableArray(builds) {
    const array = ExpandableRecordArray.create({
      type: 'build',
      content: A([])
    });
    array.load(builds);
    array.observe(builds);
    return array;
  },

  @computed('id')
  builds(id) {
    const builds = this.store.filter('build', {
      event_type: ['push', 'api', 'cron'],
      repository_id: id,
      limit: 10 // TODO Remove the limit after the API fix loading issue
    }, (b) => {
      let eventTypes = ['push', 'api', 'cron'];
      return this._buildRepoMatches(b, id) && eventTypes.includes(b.get('eventType'));
    });
    return this._buildObservableArray(builds);
  },

  @computed('id')
  pullRequests(id) {
    const builds = this.store.filter('build', {
      event_type: 'pull_request',
      repository_id: id,
      limit: 10 // TODO Remove the limit after the API fix loading issue
    }, (b) => {
      const isPullRequest = b.get('eventType') === 'pull_request';
      return this._buildRepoMatches(b, id) && isPullRequest;
    });
    return this._buildObservableArray(builds);
  },

  @computed('id')
  branches(id) {
    return this.store.filter('branch', {
      repository_id: id
    }, (b) => b.get('repoId') === id);
  },

  @computed('id')
  cronJobs(id) {
    return this.store.filter('cron', {
      repository_id: id
    }, (cron) => cron.get('branch.repoId') === id);
  },

  // TODO: Stop performing a `set` as part of the cp!
  // TODO: Is this even used?
  @computed('slug', '_stats')
  stats(slug, stats) {
    if (slug) {
      return stats || $.get(`https://api.github.com/repos/${slug}`, (data) => {
        this.set('_stats', data);
        return this.notifyPropertyChange('stats');
      }) && {};
    }
  },

  updateTimes() {
    let currentBuild = this.get('currentBuild');
    if (currentBuild) {
      return currentBuild.updateTimes();
    }
  },

  fetchSettings() {
    const url = `/repo/${this.get('id')}/settings`;
    return this.get('api').get(url).
      then(data => this._convertV3SettingsToV2(data['settings']));
  },

  saveSetting(name, value) {
    return this.get('api').patch(`/repo/${this.get('id')}/setting/${name}`, {
      data: {
        'setting.value': value
      }
    });
  },

  _convertV3SettingsToV2(v3Settings) {
    return v3Settings.reduce((v2Settings, v3Setting) => {
      if (v3Setting) {
        v2Settings[v3Setting.name] = v3Setting.value;
      }
      return v2Settings;
    }, {});
  },

  toggle() {
    const adapter = this.store.adapterFor('repo');
    const id = this.get('id');
    let promise;
    if (this.get('active')) {
      promise = adapter.deactivate(id);
    } else {
      promise = adapter.activate(id);
    }

    return promise;
  },

  @computed('id')
  emailSubscriptionUrl(id) {
    return `/repo/${id}/email_subscription`;
  },

  subscribe: task(function* () {
    yield this.api.post(this.emailSubscriptionUrl);
    yield this.reload();
  }).drop(),

  unsubscribe: task(function* () {
    yield this.api.delete(this.emailSubscriptionUrl);
    yield this.reload();
  }).drop()
});

Repo.reopenClass({
  recent() {
    return this.find();
  },

  accessibleBy(store, reposIdsOrlogin) {
    let repos, reposIds;
    reposIds = reposIdsOrlogin || [];
    repos = store.filter('repo', (repo) => {
      let repoId = parseInt(repo.get('id'));
      return reposIds.includes(repoId);
    });
    return new EmberPromise((resolve, reject) => {
      const params = {
        'repository.active': 'true',
        sort_by: 'current_build:desc',
        limit: 30,
      };
      return store.query('repo', params)
        .then(() => resolve(repos), () => reject());
    });
  },

  search(store, query) {
    return store.query('repo', {
      name_filter: query,
      sort_by: 'name_filter:desc',
      limit: 10
    });
  },

  fetchBySlug(store, slug) {
    let adapter, modelClass, promise, repos;
    repos = store.peekAll('repo').filterBy('slug', slug);
    if (repos.get('length') > 0) {
      return repos.get('firstObject');
    } else {
      promise = null;
      adapter = store.adapterFor('repo');
      modelClass = store.modelFor('repo');
      promise = adapter.findRecord(store, modelClass, slug).then((payload) => {
        let i, len, record, ref, repo, result, serializer;
        serializer = store.serializerFor('repo');
        modelClass = store.modelFor('repo');
        result = serializer.normalizeResponse(store, modelClass, payload, null, 'findRecord');
        repo = store.push({
          data: result.data
        });
        ref = result.included;
        for (i = 0, len = ref.length; i < len; i++) {
          record = ref[i];
          store.push({
            data: record
          });
        }
        return repo;
      });
      return promise['catch'](() => {
        let error;
        error = new Error('repo not found');
        error.slug = slug;
        throw error;
      });
    }
  },
});

export default Repo;
<|endoftext|>


<commit_before>angular.module('shop', [] )
    .service('Shop', ['$http','$q','Phones',function ($http,$q,Phones) {
        var orders=[];
        var items=[];
        function Shop() {
           console.log('constructor') ;
        }
        Shop.prototype.getOrders=function() {
            return orders;
        };
        Shop.prototype.addOrders=function(order) {
            return orders.push(order);
        };
        Shop.prototype.getAllItems=function () {
           if(items.length){
               return $q(function(resolve, reject) {
                   resolve(items);
               });
           }
            return $http({
               method:'Get',
                url:'..app/data/phones.json'
            }).then(function successCallback(response){
               //this callback will be called asynchronously
                //when the response is available
                items=response.data;
                return items.map(function (item) {
                    return new Phones(item.title,item.text, item.color, item.price)
                });
            },function errorCallback(response) {
               console.error(response);
            });
        };
        return new Shop();
    }])
    .factory('Phones', function() {
   function Phones(title,text, color, price) {
this.title=title;
this.text=text;
this.color=color;
this.price=price;
   }
   return Phones;
});<commit_msg>Initial commit<commit_after>angular.module('shop', [] )
    .service('Shop', ['$http','$q','Phones',function ($http,$q,Phones) {
        var orders=[];
        var items=[];
        function Shop() {
           console.log('constructor') ;
        }
        Shop.prototype.getOrders=function() {
            return orders;
        };
        Shop.prototype.addOrders=function(order) {
            return orders.push(order);
        };
        Shop.prototype.getAllItems=function () {
           if(items.length){
               return $q(function(resolve, reject) {
                   resolve(items);
               });
           }
            return $http({
               method:'Get',
                url:'..data/phones.json'
            }).then(function successCallback(response){
               //this callback will be called asynchronously
                //when the response is available
                items=response.data;
                return items.map(function (item) {
                    return new Phones(item.title,item.text, item.color, item.price)
                });
            },function errorCallback(response) {
               console.error(response);
            });
        };
        return new Shop();
    }])
    .factory('Phones', function() {
   function Phones(title,text, color, price) {
this.title=title;
this.text=text;
this.color=color;
this.price=price;
   }
   return Phones;
});<|endoftext|>


<commit_before>import DS from 'ember-data';

var Site = DS.Model.extend({
  createdAt: DS.attr('date'),

  name: DS.attr(),
  tagline: DS.attr(),
  description: DS.attr(),
  moreDesc: DS.attr(),
  joinText: DS.attr(),

  email: DS.attr(),
  address: DS.attr(),

  facebook: DS.attr(),
  twitter: DS.attr(),
  googlePlus: DS.attr(),

  logo: DS.belongsTo('image', { inverse: 'logoSites', async: true }),
  cover: DS.belongsTo('image', { inverse: 'coverSites', async: true }),

  user: DS.belongsTo('user', { async: true }),

  posts: DS.hasMany('post', { async: true }),
  pages: DS.hasMany('page', { async: true }),
  activities: DS.hasMany('activity', { async: true }),
  images: DS.hasMany('image', { inverse: 'site', async: true }),

  pageSettings: DS.hasMany('sitePageSetting')
});

export default Site;
<commit_msg>Adds BuildMaster<commit_after>import DS from 'ember-data';

var Site = DS.Model.extend({
  createdAt: DS.attr('date'),
  updatedAt: DS.attr('date'),
  builtAt: DS.attr('date'),

  name: DS.attr(),
  tagline: DS.attr(),
  description: DS.attr(),
  moreDesc: DS.attr(),
  joinText: DS.attr(),

  email: DS.attr(),
  address: DS.attr(),

  facebook: DS.attr(),
  twitter: DS.attr(),
  googlePlus: DS.attr(),

  logo: DS.belongsTo('image', { inverse: 'logoSites', async: true }),
  cover: DS.belongsTo('image', { inverse: 'coverSites', async: true }),

  user: DS.belongsTo('user', { async: true }),

  posts: DS.hasMany('post', { async: true }),
  pages: DS.hasMany('page', { async: true }),
  activities: DS.hasMany('activity', { async: true }),
  images: DS.hasMany('image', { inverse: 'site', async: true }),

  pageSettings: DS.hasMany('sitePageSetting')
});

export default Site;
<|endoftext|>


<commit_before>var mongoose = require('mongoose');
var bycrypt = require('bycrypt-nodejs');

var Schema = mongoose.Schema;

var UserSchema = new Schema({
	local : {
		email : String,
		passpword : String
	},
	facebook : {
		id : String,
		token : String,
		email : String,
		name : String

	},
	google : {
		id : String,
		token : String,
		email : String,
		name : String
	},
	twitter : {
		id : String,
		token : String,
		displayName : String,
		userName : String
	}
});

UserSchema.methods.generateHash = function(passpword) {
	return bycrypt.hashSync(passpword, bycrypt.genSaltSync(8), null);
};

UserSchema.methods.validPassord = function(passpword) {
	return bycrypt.compareSync(passpword, this.local.password);
};

mongoose.exports = mongoose.model('User', UserSchema);<commit_msg>fixed the bug<commit_after>var mongoose = require('mongoose');
var bcrypt   = require('bcrypt-nodejs');

var Schema = mongoose.Schema;

var UserSchema = new Schema({
	local : {
		email : String,
		passpword : String
	},
	facebook : {
		id : String,
		token : String,
		email : String,
		name : String

	},
	google : {
		id : String,
		token : String,
		email : String,
		name : String
	},
	twitter : {
		id : String,
		token : String,
		displayName : String,
		userName : String
	}
});

UserSchema.methods.generateHash = function(passpword) {
	return bycrypt.hashSync(passpword, bycrypt.genSaltSync(8), null);
};

UserSchema.methods.validPassord = function(passpword) {
	return bycrypt.compareSync(passpword, this.local.password);
};

module.exports = mongoose.model('User', UserSchema);<|endoftext|>


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

/**
 * This is an explorer to help get the right element locators, and test out what
 * Protractor commands will do on your site without running a full test suite.
 *
 * This beta version only uses the Chrome browser.
 *
 * Usage:
 *
 * Expects a selenium standalone server to be running at http://localhost:4444
 * from protractor directory, run with:
 *
 *     ./bin/elementexplorer.js <urL>
 *
 * This will load up the URL on webdriver and put the terminal into a REPL loop.
 * You will see a > prompt. The `browser`, `element` and `protractor` variables
 * will be available. Enter a command such as:
 *
 *     > element(by.id('foobar')).getText()
 *
 * or
 *
 *     > browser.get('http://www.angularjs.org')
 *
 * try just
 *
 *     > browser
 *
 * to get a list of functions you can call.
 *
 * Typing tab at a blank prompt will fill in a suggestion for finding
 * elements.
 */

var webdriver = require('selenium-webdriver');
var protractor = require('../lib/protractor.js');
var repl = require('repl');
var util = require('util');
var vm = require('vm');

var driver, browser;

var INITIAL_SUGGESTIONS = [
  'element(by.id(\'\'))',
  'element(by.css(\'\'))',
  'element(by.name(\'\'))',
  'element(by.binding(\'\'))',
  'element(by.xpath(\'\'))',
  'element(by.tagName(\'\'))',
  'element(by.className(\'\'))'
];

var list = function(locator) {
  return browser.findElements(locator).then(function(arr) {
    var found = [];
    for (var i = 0; i < arr.length; ++i) {
      arr[i].getText().then(function(text) {
        found.push(text);
      });
    }
    return found;
  });
};

var flowEval = function(code, context, file, callback) {

  var vmErr,
      result,
      flow = webdriver.promise.controlFlow();

  flow.execute(function() {
    try {
      result = vm.runInThisContext(code, file);
    } catch (e) {
      vmErr = e;
      callback(vmErr, null);
    }
    if (vmErr && process.domain) {
      process.domain.emit('error', vmErr);
      process.domain.exit();
    }
    return webdriver.promise.fulfilled(result);
  }).then(function(res) {
    if (!vmErr) {
      callback(null, res);
    }
  }, function(err) {
    callback('There was a webdriver error: ' + err.name + ' ' + err.message,
        null);
  });
};

var startRepl = function() {
  var flowRepl = repl.start({
    'useGlobal': true,
    'eval': flowEval
  });

  var originalComplete = flowRepl.complete;

  flowRepl.complete = function(line, completeCallback) {
    if (line == '') {
      completeCallback(null, [INITIAL_SUGGESTIONS, '']);
    } else {
      originalComplete.apply(this, arguments);
    }
  };

  flowRepl.on('exit', function() {
    driver.quit();
    util.puts('Shutting down. Goodbye.');
  });
};

var startUp = function() {
  driver = new webdriver.Builder().
    usingServer('http://localhost:4444/wd/hub').
    withCapabilities({'browserName': 'chrome'}).build();

  driver.getSession().then(function(session) {
    driver.manage().timeouts().setScriptTimeout(11000);

    browser = protractor.wrapDriver(driver);

    // Set up globals to be available from the command line.
    global.driver = driver;
    global.protractor = protractor;
    global.browser = browser;
    global.$ = browser.$;
    global.$$ = browser.$$;
    global.element = browser.element;
    global.by = global.By = protractor.By;
    global.list = list;

    util.puts('Type <tab> to see a list of locator strategies.');
    util.puts('Use the `list` helper function to find elements by strategy:');
    util.puts('  e.g., list(by.binding(\'\')) gets all bindings.');
    util.puts('');

    var url = process.argv[2] || 'about:blank';
    util.puts('Getting page at: ' + url);
    driver.get(url);

    startRepl();
  });
};

startUp();
<commit_msg>fix(elementexplorer): elementexplorer hangs when returning ElementFinder<commit_after>#!/usr/bin/env node

/**
 * This is an explorer to help get the right element locators, and test out what
 * Protractor commands will do on your site without running a full test suite.
 *
 * This beta version only uses the Chrome browser.
 *
 * Usage:
 *
 * Expects a selenium standalone server to be running at http://localhost:4444
 * from protractor directory, run with:
 *
 *     ./bin/elementexplorer.js <urL>
 *
 * This will load up the URL on webdriver and put the terminal into a REPL loop.
 * You will see a > prompt. The `browser`, `element` and `protractor` variables
 * will be available. Enter a command such as:
 *
 *     > element(by.id('foobar')).getText()
 *
 * or
 *
 *     > browser.get('http://www.angularjs.org')
 *
 * try just
 *
 *     > browser
 *
 * to get a list of functions you can call.
 *
 * Typing tab at a blank prompt will fill in a suggestion for finding
 * elements.
 */

var webdriver = require('selenium-webdriver');
var protractor = require('../lib/protractor.js');
var repl = require('repl');
var util = require('util');
var vm = require('vm');

var driver, browser;

var INITIAL_SUGGESTIONS = [
  'element(by.id(\'\'))',
  'element(by.css(\'\'))',
  'element(by.name(\'\'))',
  'element(by.binding(\'\'))',
  'element(by.xpath(\'\'))',
  'element(by.tagName(\'\'))',
  'element(by.className(\'\'))'
];

var list = function(locator) {
  return browser.findElements(locator).then(function(arr) {
    var found = [];
    for (var i = 0; i < arr.length; ++i) {
      arr[i].getText().then(function(text) {
        found.push(text);
      });
    }
    return found;
  });
};

var flowEval = function(code, context, file, callback) {

  var vmErr,
      result,
      flow = webdriver.promise.controlFlow();

  flow.execute(function() {
    try {
      result = vm.runInThisContext(code, file);
    } catch (e) {
      vmErr = e;
      callback(vmErr, null);
    }
    if (vmErr && process.domain) {
      process.domain.emit('error', vmErr);
      process.domain.exit();
    }
    return result.then(function(val) {return val});
  }).then(function(res) {
    if (!vmErr) {
      callback(null, res);
    }
  }, function(err) {
    callback('There was a webdriver error: ' + err.name + ' ' + err.message,
        null);
  });
};

var startRepl = function() {
  var flowRepl = repl.start({
    'useGlobal': true,
    'eval': flowEval
  });

  var originalComplete = flowRepl.complete;

  flowRepl.complete = function(line, completeCallback) {
    if (line == '') {
      completeCallback(null, [INITIAL_SUGGESTIONS, '']);
    } else {
      originalComplete.apply(this, arguments);
    }
  };

  flowRepl.on('exit', function() {
    driver.quit();
    util.puts('Shutting down. Goodbye.');
  });
};

var startUp = function() {
  driver = new webdriver.Builder().
    usingServer('http://localhost:4444/wd/hub').
    withCapabilities({'browserName': 'chrome'}).build();

  driver.getSession().then(function(session) {
    driver.manage().timeouts().setScriptTimeout(11000);

    browser = protractor.wrapDriver(driver);

    // Set up globals to be available from the command line.
    global.driver = driver;
    global.protractor = protractor;
    global.browser = browser;
    global.$ = browser.$;
    global.$$ = browser.$$;
    global.element = browser.element;
    global.by = global.By = protractor.By;
    global.list = list;

    util.puts('Type <tab> to see a list of locator strategies.');
    util.puts('Use the `list` helper function to find elements by strategy:');
    util.puts('  e.g., list(by.binding(\'\')) gets all bindings.');
    util.puts('');

    var url = process.argv[2] || 'about:blank';
    util.puts('Getting page at: ' + url);
    driver.get(url);

    startRepl();
  });
};

startUp();
<|endoftext|>