<commit_before>/**
 * @overview ccm component for upload with XMLHttpRequest async
 * @author Manfred Kaul <manfred.kaul@h-brs.de> 2017
 * @license The MIT License (MIT)
 */

( function () {

  var ccm_version = '9.0.0';
  var ccm_url     = 'https://akless.github.io/ccm/ccm.js';

  var component_name = 'upload';
  var component_obj  = {

    name: component_name,

    config: {
      key:           'test',
      upload_server: 'https://kaul.inf.h-brs.de/data/upload.php',
      view_server:   'https://kaul.inf.h-brs.de/data/view.php',
      content_type:  'image/*',  // which types to accept by file chooser
      type_regex:    'image/.*', // check type via regex, or see next line:
      suffix_regex:  '\.jpeg$|\.jpg$|\.png$|\.pdf$', // or check via name suffix
            // both regex are alternatives
      html: {
        main: {
          tag: 'form', inner: [
            { tag: 'input', type: 'file', accept: '%accept%' },
            { tag: 'progress', min: '0', max: '100', value: '0', inner: '0% complete' },
            { tag: 'button', id: 'abort', inner: 'Abort' },
            { id: 'reports' },
            { id: 'failure'}
          ]
        },
        response: {
          tag: 'a', target: '_blank', inner: '%filename%'
        }
      },
      message: {
        abort:   'Upload abgebrochen: ',
        success: 'Fertig hochgeladen: '
      },
      css: [ 'ccm.load',  './resources/default.css' ]
      // css: [ 'ccm.load',  'https://mkaul.github.io/ccm-components/upload/resources/default.css' ],
      // user:   [ 'ccm.instance', 'https://akless.github.io/ccm-components/user/versions/ccm.user-1.0.0.min.js', { sign_on: "hbrsinfkaul" } ],
      // logger: [ 'ccm.instance', 'https://akless.github.io/ccm-components/log/versions/ccm.log-1.0.0.min.js', [ 'ccm.get', 'https://akless.github.io/ccm-components/log/resources/log_configs.min.js', 'greedy' ] ],
      // onfinish: function( instance, results ){ console.log( results ); }
    },

    Instance: function () {

      var self = this;

      this.start = function ( callback ) {

        // has logger instance? => log 'render' event
        if ( self.logger ) self.logger.log( 'render' );

        // prepare main HTML structure
        var main_elem = self.ccm.helper.html( self.html.main, { accept: self.content_type } );

        // select inner containers
        var input = main_elem.querySelector( 'input' );
        var progress_bar = main_elem.querySelector( 'progress' );
        var abort_button = main_elem.querySelector( '#abort' );
        var reports = main_elem.querySelector( '#reports' );
        var failure = main_elem.querySelector( '#failure' );

        // set content of own website area
        self.ccm.helper.setContent( self.element, main_elem );

        var xhr;
        var database_keys = { key: self.key, view_server: self.view_server };

        input.addEventListener('change', selectFile, false);
        abort_button.addEventListener('click', abort, false);

        // "select file" handler
        function selectFile() {

          // get and check file
          var file = this.files[0];
          database_keys.filename = file.name;

          function wrong_file_type(file){
            return ! file.type.match( self.type_regex )
              &&   ! file.name.match( self.suffix_regex );
          }

          if ( self.type_regex && self.suffix_regex && wrong_file_type(file) ){
            if ( ! file.type.match( self.type_regex ) ){
              reports.textContent = 'File type is ' + file.type
                + ', required is type ' + self.type_regex;
              return false;
            }
            if ( ! file.name.match( self.suffix_regex ) ){
              reports.textContent = 'File type is ' + file.type
                + ', required is suffix ' + self.suffix_regex;
              return false;
            }
          }

          // prepare next AJAX request
          xhr = new XMLHttpRequest();
          reports.textContent = ''; // clear old success reports
          failure.textContent = ''; // clear old error messages

          // prepare form data
          var formData = new FormData();
          formData.append('key', self.key);
          formData.append("file", file);

          // prepare AJAX call
          xhr.open('POST', self.upload_server, true);

          xhr.upload.onprogress = function (e) {
            if (e.lengthComputable) {
              progress_bar.value = (e.loaded / e.total) * 100;
              progress_bar.textContent = progress_bar.value; // Fallback for unsupported browsers.
            }
          };

          function error_message( event ){
            return ' Status: '+ xhr.status
              + ', Event: ' + JSON.stringify(event)
              + ', Response: ' + xhr.response + '.'
          }

          xhr.upload.onabort = function (event) {
            failure.textContent += 'Abort' + error_message( event );
          };

          xhr.upload.onerror = function (event) {
            failure.textContent += 'Error' + error_message( event );
          };

          xhr.upload.ontimeout = function (event) {
            failure.textContent += 'Timeout' + error_message( event );
          };

          xhr.onload = function () {
            if (this.status == 200) {
              reports.textContent = self.message.success;
              var element = self.ccm.helper.html( self.html.response, database_keys );
              element.href = self.view_server + '?'
                + 'user=' + database_keys.user
                + '&token=' + database_keys.token
                + '&key=' + self.key;
              reports.appendChild(element);
            }
          };

          // has user instance? => login user (if not already logged in)
          if (self.user) self.user.login(proceed); else proceed();

          function proceed() {
            database_keys.user = self.user.data().user;
            database_keys.token = self.user.data().token;
            formData.append('user', self.user.data().user);
            formData.append('token', self.user.data().token);
            xhr.send(formData);
          }

          function log_form(x) { // for debugging only
            console.log(x);
            var result = {};
            for (var entry of formData.entries()) {
              result[entry[0]] = entry[1];
            }
            result = JSON.stringify(result);
            console.log(result);
            return JSON.stringify(formData.entries());
          }
        }

        // interrupt current AJAX request without reloading the HTML page
        function abort(e){
          xhr.abort();

          // prevent reloading component and HTML page
          e.preventDefault();
          e.stopPropagation();
          return false;
        }

        if ( callback ) callback();
      };

    }

  };

  var namespace = window.ccm && ccm.components[ component_name ]; if ( namespace ) { if ( namespace.ccm_version ) ccm_version = namespace.ccm_version; if ( namespace.ccm_url ) ccm_url = namespace.ccm_url; }
  if ( !window.ccm || !ccm[ ccm_version ] ) { var tag = document.createElement( 'script' ); document.head.appendChild( tag ); tag.onload = register; tag.src = ccm_url; } else register();
  function register() { ccm[ ccm_version ].component( component_obj ); }
}() );<commit_msg>upload with type & suffix checks if exist, bugfix<commit_after>/**
 * @overview ccm component for upload with XMLHttpRequest async
 * @author Manfred Kaul <manfred.kaul@h-brs.de> 2017
 * @license The MIT License (MIT)
 */

( function () {

  var ccm_version = '9.0.0';
  var ccm_url     = 'https://akless.github.io/ccm/ccm.js';

  var component_name = 'upload';
  var component_obj  = {

    name: component_name,

    config: {
      key:           'test',
      upload_server: 'https://kaul.inf.h-brs.de/data/upload.php',
      view_server:   'https://kaul.inf.h-brs.de/data/view.php',
      content_type:  'image/*',  // which types to accept by file chooser
      type_regex:    'image/.*', // check type via regex, or see next line:
      suffix_regex:  '\.jpeg$|\.jpg$|\.png$|\.pdf$', // or check via name suffix
            // both regex are alternatives
      html: {
        main: {
          tag: 'form', inner: [
            { tag: 'input', type: 'file', accept: '%accept%' },
            { tag: 'progress', min: '0', max: '100', value: '0', inner: '0% complete' },
            { tag: 'button', id: 'abort', inner: 'Abort' },
            { id: 'reports' },
            { id: 'failure'}
          ]
        },
        response: {
          tag: 'a', target: '_blank', inner: '%filename%'
        }
      },
      message: {
        abort:   'Upload abgebrochen: ',
        success: 'Fertig hochgeladen: '
      },
      css: [ 'ccm.load',  './resources/default.css' ]
      // css: [ 'ccm.load',  'https://mkaul.github.io/ccm-components/upload/resources/default.css' ],
      // user:   [ 'ccm.instance', 'https://akless.github.io/ccm-components/user/versions/ccm.user-1.0.0.min.js', { sign_on: "hbrsinfkaul" } ],
      // logger: [ 'ccm.instance', 'https://akless.github.io/ccm-components/log/versions/ccm.log-1.0.0.min.js', [ 'ccm.get', 'https://akless.github.io/ccm-components/log/resources/log_configs.min.js', 'greedy' ] ],
      // onfinish: function( instance, results ){ console.log( results ); }
    },

    Instance: function () {

      var self = this;

      this.start = function ( callback ) {

        // has logger instance? => log 'render' event
        if ( self.logger ) self.logger.log( 'render' );

        // prepare main HTML structure
        var main_elem = self.ccm.helper.html( self.html.main, { accept: self.content_type } );

        // select inner containers
        var input = main_elem.querySelector( 'input' );
        var progress_bar = main_elem.querySelector( 'progress' );
        var abort_button = main_elem.querySelector( '#abort' );
        var reports = main_elem.querySelector( '#reports' );
        var failure = main_elem.querySelector( '#failure' );

        // set content of own website area
        self.ccm.helper.setContent( self.element, main_elem );

        var xhr;
        var database_keys = { key: self.key, view_server: self.view_server };

        input.addEventListener('change', selectFile, false);
        abort_button.addEventListener('click', abort, false);

        // "select file" handler
        function selectFile() {

          // get and check file
          var file = this.files[0];
          database_keys.filename = file.name;

          function wrong_file_type(file){
            return  self.type_regex && ! file.type.match( self.type_regex )
              &&  self.suffix_regex && ! file.name.match( self.suffix_regex );
          }

          if ( ( self.type_regex || self.suffix_regex ) && wrong_file_type(file) ){
            if ( self.type_regex && ! file.type.match( self.type_regex ) ){
              reports.textContent = 'File type is ' + file.type
                + ', required is type ' + self.type_regex;
              return false;
            }
            if ( self.suffix_regex && ! file.name.match( self.suffix_regex ) ){
              reports.textContent = 'File type is ' + file.type
                + ', required is suffix ' + self.suffix_regex;
              return false;
            }
          }

          // prepare next AJAX request
          xhr = new XMLHttpRequest();
          reports.textContent = ''; // clear old success reports
          failure.textContent = ''; // clear old error messages

          // prepare form data
          var formData = new FormData();
          formData.append('key', self.key);
          formData.append("file", file);

          // prepare AJAX call
          xhr.open('POST', self.upload_server, true);

          xhr.upload.onprogress = function (e) {
            if (e.lengthComputable) {
              progress_bar.value = (e.loaded / e.total) * 100;
              progress_bar.textContent = progress_bar.value; // Fallback for unsupported browsers.
            }
          };

          function error_message( event ){
            return ' Status: '+ xhr.status
              + ', Event: ' + JSON.stringify(event)
              + ', Response: ' + xhr.response + '.'
          }

          xhr.upload.onabort = function (event) {
            failure.textContent += 'Abort' + error_message( event );
          };

          xhr.upload.onerror = function (event) {
            failure.textContent += 'Error' + error_message( event );
          };

          xhr.upload.ontimeout = function (event) {
            failure.textContent += 'Timeout' + error_message( event );
          };

          xhr.onload = function () {
            if (this.status == 200) {
              reports.textContent = self.message.success;
              var element = self.ccm.helper.html( self.html.response, database_keys );
              element.href = self.view_server + '?'
                + 'user=' + database_keys.user
                + '&token=' + database_keys.token
                + '&key=' + self.key;
              reports.appendChild(element);
            }
          };

          // has user instance? => login user (if not already logged in)
          if (self.user) self.user.login(proceed); else proceed();

          function proceed() {
            database_keys.user = self.user.data().user;
            database_keys.token = self.user.data().token;
            formData.append('user', self.user.data().user);
            formData.append('token', self.user.data().token);
            xhr.send(formData);
          }

          function log_form(x) { // for debugging only
            console.log(x);
            var result = {};
            for (var entry of formData.entries()) {
              result[entry[0]] = entry[1];
            }
            result = JSON.stringify(result);
            console.log(result);
            return JSON.stringify(formData.entries());
          }
        }

        // interrupt current AJAX request without reloading the HTML page
        function abort(e){
          xhr.abort();

          // prevent reloading component and HTML page
          e.preventDefault();
          e.stopPropagation();
          return false;
        }

        if ( callback ) callback();
      };

    }

  };

  var namespace = window.ccm && ccm.components[ component_name ]; if ( namespace ) { if ( namespace.ccm_version ) ccm_version = namespace.ccm_version; if ( namespace.ccm_url ) ccm_url = namespace.ccm_url; }
  if ( !window.ccm || !ccm[ ccm_version ] ) { var tag = document.createElement( 'script' ); document.head.appendChild( tag ); tag.onload = register; tag.src = ccm_url; } else register();
  function register() { ccm[ ccm_version ].component( component_obj ); }
}() );<|endoftext|>


<commit_before>/********************************************************
 * Copyright (c) 2013 Azavea, Inc.
 * See LICENSE in the project root for copying permission
 * Location with definition of location objects and
 * data loader object
 *********************************************************/

define(['jquery', 'Leaflet', 'Handlebars', 'favorites', 'topojson', 'common'],
    function($, L, Handlebars, favorites, topojson, common) {

    /*
     * Constructor for location
     * data -- object returned from the /location/api/<id>/ url
     */
    var Location = function(data){
        if (data.position && data.item) {
            this.data = data;
        } else {
            throw "CELLocationInvalid";
        }

        this.mapMarker = null;
    };

    /*
     * Return ID of the location
     *      shortcut if we need more functionality later
     */
    Location.prototype.getId = function() {
        return this.data.item.key;
    };

    /*
     * Checks if location is starred
     * Returns:
     *      true if starred
     *      false if not starred
     */
    Location.prototype.isStarred = function() {
        return favorites.isStarred(this.getId());
    };

    /*
     * Add location to cookie string
     */
    Location.prototype.setStarred = function(add) {
        if (add) {
            favorites.addIdToCookie(this.getId());
        } else {
            favorites.removeIdFromCookie(this.getId());
        }
    };

    /*
     * Is the location accredited?
     */
    Location.prototype.isAccredited = function() {
        var isAccredited = false;
        $.each(this.data.sfields, function(key, value) {
            if (value.fieldname === "Accreditation" && value.value !== "None") {
                isAccredited = true;
                return false;
            }
        });
        return isAccredited;
    };

    /*
     * Determines if the location is a school or not
     * Returns:
     *      true if school
     *      false if not
     */
    Location.prototype.isSchool = function() {
        var isSchool = false;
        $.each(this.data.sfields, function(key, value) {
            if (value.fieldname === "Affiliations") {
                if (value.value.indexOf("CPS Based") !== -1) {
                    isSchool = true;
                }
                return false;
            }
        });
        return isSchool;
    };

    /* Function that returns appropriate icon based on
     * a location's properties
     */
    Location.prototype.getIcon = function(options) {
        if (!options) options = {};

        var scaleDimensions = function(option, scale) {
            scale = parseFloat(scale);
            if (isNaN(scale)) {
                return;
            }
            option[0] = Math.round(option[0] * scale);
            option[1] = Math.round(option[1] * scale);
        };

        var setHighlighted = function(options) {
            var scale = 1.25;
            scaleDimensions(options.iconSize, scale);
            scaleDimensions(options.shadowSize, scale);
            scaleDimensions(options.iconAnchor, scale);
            scaleDimensions(options.shadowAnchor, scale);
            scaleDimensions(options.popupAnchor, scale);
            options.iconUrl = options.iconUrl.replace(".png", "@2x.png");
            options.shadowUrl = options.shadowUrl.replace(".png", "@2x.png");
            return options;
        };

        var defaults = {
            key: null,
            highlighted: false,
            iconUrl: '/static/img/leaflet-icons/marker-icon.png',
            shadowUrl: '/static/img/leaflet-icons/marker-shadow.png',
            iconSize: [35, 45],
            shadowSize: [41, 41],
            iconAnchor: [17, 45],
            shadowAnchor: [10, 41],
            popupAnchor: [0, -60]
        };
        var iconOpts = $.extend({}, defaults, options),
            key = '',
            cacheKey;

        // build a key!
        key = iconOpts.key ? iconOpts.key : this.getIconKey();

        // set icon url if not provided in options
        if (!options.iconUrl) {
            iconOpts.iconUrl = common.getUrl('icon-' + key);
        }

        // cache the icon with a simple key
        cacheKey = key;
        if (iconOpts.highlighted) {
            cacheKey += '-highlighted';
        }
        if (_iconcache[cacheKey]) {
            return _iconcache[cacheKey];
        }

        // highlighting means we scale all dimensions up and replace the icon with @2x
        if (iconOpts.highlighted) {
            iconOpts = setHighlighted(iconOpts);
        }

        var icon = L.icon(iconOpts);
        _iconcache[cacheKey] = icon;
        return icon;
    };

    /*
     * Set the locations map marker icon
     */
    Location.prototype.setIcon = function(options) {
        if (this.mapMarker) {
            var icon = this.getIcon(options);
            this.mapMarker.setIcon(icon);
        }
    };

    /*
     * get the icon key used in getIcon and the iconcache
     */
    Location.prototype.getIconKey = function() {
        var key = this.isSchool() ? 'school' : 'center';
        key += this.isAccredited() ? '-accredited' : '';
        key += this.isStarred() ? '-starred' : '';
        return key;
    };

    /*
     * Get location marker object
     */
    Location.prototype.getMarker = function() {
        return this.mapMarker || null;
    };

    /*
     * Input: Leaflet Marker Icons object
     * Output: Reference to the created marker
     * If map marker already exists, updates existing marker icon
     * If map marker does not exist, creates marker with proper icon
     */
    Location.prototype.setMarker = function(options) {
        var popupTemplate = Handlebars.compile('{{item.site_name}}
{{item.address}}'),
            icon = this.getIcon(options),
            marker = this.getMarker();
        if (marker) {
            marker.setIcon(icon);
        } else {
            this.mapMarker = new L.Marker(this.getLatLng(), { icon: icon });
            this.mapMarker.bindPopup(popupTemplate(this.data), {key: this.getId()});
        }
    };

    Location.prototype.getLatLng = function() {
        return new L.LatLng(this.data.position.lat, this.data.position.lng);
    };

    /* Sets boolean value for if location should be on list
     */
    Location.prototype.isWithinMapBounds = function(map){
        var mapBounds = map.getBounds();
        return mapBounds.contains(this.getLatLng());
    };

    /**
     * Creates a new DataManager object
     * @param { Optional query collection of DOM filter objects to bind onFilterChange to } $filters
     */
    var DataManager = function($filters) {
            var that = this;
            this.locations = {};
            this.neighborhoods = {
                data: {} // data for neighborhood (e.g. number of schools)
            };
            this.events = $({});
            this.$filters = $filters;
        },
        _iconcache = {};

    DataManager.prototype = {
        /**
         * DOM filters to read filter settings from
         * Set in constructor
         */
        $filters: null,

        /**
         * Updates if location is shown in map and list based on
         * applied filters and bounding box of map
         */
        locationUpdate: function(map, locationLayer) {
            var filters = this.getFilters(map);
                that = this;
            $.getJSON(common.getUrl('location-api'), filters, function(data) {
                // Unfortunately we can't just blow away the locations here because that
                // makes the popovers disappear every time you pan, so we have to do it
                // the hard way.

                var locs = that.locations;

                // Mark...
                $.each(locs, function(i, location) {
                    // Only the truely worthy shall survive
                    location._mark = false;
                });

                // Load...
                $.each(data.locations, function(i, location) {
                    var key = location.item.key;
                    if (!locs[key]) {
                        var l = new Location(location);
                        l.setMarker();
                        locationLayer.addLayer(l.getMarker());
                        l._mark = true;
                        locs[key] = l;
                    } else {
                        locs[key]._mark = true;
                    }
                });

                // ...and Sweep
                $.each(locs, function(i, location) {
                    if (location._mark) {
                        delete location._mark;
                    } else {
                        locationLayer.removeLayer(location.getMarker());
                        delete locs[i];
                    }
                });

                that.events.trigger('DataManager.locationUpdated');
            });
        },

        /**
         * Updates school counts for neighborhoods based on
         * filters.
         *
         * Download topojson if not already downloaded.
         */
        neighborhoodUpdate: function(map) {
            var that = this,
                filters = that.getFilters(map);
            $.when(
                $.getJSON(common.getUrl('neighborhood-api'), filters, function(data) {
                    var neighborhoods = that.neighborhoods.data;
                    $.each(data.neighborhoods, function(i, neighborhood) {
                        var key = neighborhood.id;
                        neighborhoods[key] = neighborhood;
                    });
                }),
                that.geojsonUpdate()
            ).then(function() {
                that.events.trigger('DataManager.neighborhoodUpdated');
            });
        },

        /*
         * Get geojson for the neighborhoods and store in DataManager
         */
        geojsonUpdate: function() {
            var that = this;
            if (that.neighborhoods.geojson === undefined) {
                return $.getJSON(common.getUrl('neighborhoods-topo'), function(data) {
                        that.neighborhoods.geojson = topojson.feature(data, data.objects.neighborhoods);
                });
            }
        },

        /**
         * Alerts listeners that filter data has changed
         */
        onFilterChange: function() {
            this.events.trigger('DataManager.filtersUpdated');
        },

        /**
         * Gets the current state of location filters for passing into a GET request
         * @param { Leaflet map object to read current bounds from } map
         * @return { query params dict }
         */
        getFilters: function(map) {
            var opts = { },
                $filters = this.$filters;
            $filters.filter(':checked').each(function(idx, elem) {
                opts[elem.id] = elem.checked;
            });
            if (map && map.getBounds) {
                opts.bbox = map.getBounds().toBBoxString();
            }

            return opts;
        },

        /**
         * Map of locations & neighborhoods, key is the id for
         * each object
         */
        locations: null,
        neighborhoods: null,    // Has one key 'data' with a list of neighborhood objects

        /*
         * Available events:
         * neighborhoodUpdated: 'DataManager.neighborhoodUpdated',
         *      triggered when neighborhood data is finished loading
         * locationUpdated: 'DataManager.locationUpdated'
         *      triggered when location data is finished loading
         * filtersUpdated: 'DataManager.filtersChanged'
         *      fired when DOM filters change.  This includes checkboxes and map bounding box
         */
        events: null
    };

    return {
        Location: Location,
        DataManager: DataManager
    };
});

<commit_msg>Cleanup fixes for listicon v2<commit_after>/********************************************************
 * Copyright (c) 2013 Azavea, Inc.
 * See LICENSE in the project root for copying permission
 * Location with definition of location objects and
 * data loader object
 *********************************************************/

define(['jquery', 'Leaflet', 'Handlebars', 'favorites', 'topojson', 'common'],
    function($, L, Handlebars, favorites, topojson, common) {

    /*
     * Constructor for location
     * data -- object returned from the /location/api/<id>/ url
     */
    var Location = function(data){
        if (data.position && data.item) {
            this.data = data;
        } else {
            throw "CELLocationInvalid";
        }

        this.mapMarker = null;
    };

    /*
     * Return ID of the location
     *      shortcut if we need more functionality later
     */
    Location.prototype.getId = function() {
        return this.data.item.key;
    };

    /*
     * Checks if location is starred
     * Returns:
     *      true if starred
     *      false if not starred
     */
    Location.prototype.isStarred = function() {
        return favorites.isStarred(this.getId());
    };

    /*
     * Add location to cookie string
     */
    Location.prototype.setStarred = function(add) {
        if (add) {
            favorites.addIdToCookie(this.getId());
        } else {
            favorites.removeIdFromCookie(this.getId());
        }
    };

    /*
     * Is the location accredited?
     */
    Location.prototype.isAccredited = function() {
        var isAccredited = false;
        $.each(this.data.sfields, function(key, value) {
            if (value.fieldname === "Accreditation" && value.value !== "None") {
                isAccredited = true;
                return false;
            }
        });
        return isAccredited;
    };

    /*
     * Determines if the location is a school or not
     * Returns:
     *      true if school
     *      false if not
     */
    Location.prototype.isSchool = function() {
        var isSchool = false;
        $.each(this.data.sfields, function(key, value) {
            if (value.fieldname === "Affiliations") {
                if (value.value.indexOf("CPS Based") !== -1) {
                    isSchool = true;
                }
                return false;
            }
        });
        return isSchool;
    };

    /* Function that returns appropriate icon based on
     * a location's properties
     */
    Location.prototype.getIcon = function(options) {
        options = options || {};

        var scaleDimensions = function(option, scale) {
            scale = parseFloat(scale);
            if (isNaN(scale)) {
                return;
            }
            option[0] = Math.round(option[0] * scale);
            option[1] = Math.round(option[1] * scale);
        };

        var setHighlighted = function(options) {
            var scale = 1.25;
            scaleDimensions(options.iconSize, scale);
            scaleDimensions(options.shadowSize, scale);
            scaleDimensions(options.iconAnchor, scale);
            scaleDimensions(options.shadowAnchor, scale);
            scaleDimensions(options.popupAnchor, scale);
            options.iconUrl = options.iconUrl.replace(".png", "@2x.png");
            options.shadowUrl = options.shadowUrl.replace(".png", "@2x.png");
            return options;
        };

        var defaults = {
            key: null,
            highlighted: false,
            iconUrl: '/static/img/leaflet-icons/marker-icon.png',
            shadowUrl: '/static/img/leaflet-icons/marker-shadow.png',
            iconSize: [35, 45],
            shadowSize: [41, 41],
            iconAnchor: [17, 45],
            shadowAnchor: [10, 41],
            popupAnchor: [0, -60]
        };
        var iconOpts = $.extend({}, defaults, options),
            key = '',
            cacheKey;

        // build a key!
        key = iconOpts.key ? iconOpts.key : this.getIconKey();

        // set icon url if not provided in options
        if (!options.iconUrl) {
            iconOpts.iconUrl = common.getUrl('icon-' + key);
        }

        // cache the icon with a simple key
        cacheKey = key;
        if (iconOpts.highlighted) {
            cacheKey += '-highlighted';
        }
        if (_iconcache[cacheKey]) {
            return _iconcache[cacheKey];
        }

        // highlighting means we scale all dimensions up and replace the icon with @2x
        if (iconOpts.highlighted) {
            iconOpts = setHighlighted(iconOpts);
        }

        var icon = L.icon(iconOpts);
        _iconcache[cacheKey] = icon;
        return icon;
    };

    /*
     * Set the locations map marker icon
     */
    Location.prototype.setIcon = function(options) {
        if (this.mapMarker) {
            var icon = this.getIcon(options);
            this.mapMarker.setIcon(icon);
        }
    };

    /*
     * get the icon key used in getIcon and the iconcache
     */
    Location.prototype.getIconKey = function() {
        var key = this.isSchool() ? 'school' : 'center';
        key += this.isAccredited() ? '-accredited' : '';
        key += this.isStarred() ? '-starred' : '';
        return key;
    };

    /*
     * Get location marker object
     */
    Location.prototype.getMarker = function() {
        return this.mapMarker || null;
    };

    /*
     * Input: Leaflet Marker Icons object
     * Output: Reference to the created marker
     * If map marker already exists, updates existing marker icon
     * If map marker does not exist, creates marker with proper icon
     */
    Location.prototype.setMarker = function(options) {
        var popupTemplate = Handlebars.compile('{{item.site_name}}
{{item.address}}'),
            icon = this.getIcon(options),
            marker = this.getMarker();
        if (marker) {
            marker.setIcon(icon);
        } else {
            this.mapMarker = new L.Marker(this.getLatLng(), { icon: icon });
            this.mapMarker.bindPopup(popupTemplate(this.data), {key: this.getId()});
        }
    };

    Location.prototype.getLatLng = function() {
        return new L.LatLng(this.data.position.lat, this.data.position.lng);
    };

    /* Sets boolean value for if location should be on list
     */
    Location.prototype.isWithinMapBounds = function(map){
        var mapBounds = map.getBounds();
        return mapBounds.contains(this.getLatLng());
    };

    /**
     * Creates a new DataManager object
     * @param { Optional query collection of DOM filter objects to bind onFilterChange to } $filters
     */
    var DataManager = function($filters) {
            var that = this;
            this.locations = {};
            this.neighborhoods = {
                data: {} // data for neighborhood (e.g. number of schools)
            };
            this.events = $({});
            this.$filters = $filters;
        },
        _iconcache = {};

    DataManager.prototype = {
        /**
         * DOM filters to read filter settings from
         * Set in constructor
         */
        $filters: null,

        /**
         * Updates if location is shown in map and list based on
         * applied filters and bounding box of map
         */
        locationUpdate: function(map, locationLayer) {
            var filters = this.getFilters(map);
                that = this;
            $.getJSON(common.getUrl('location-api'), filters, function(data) {
                // Unfortunately we can't just blow away the locations here because that
                // makes the popovers disappear every time you pan, so we have to do it
                // the hard way.

                var locs = that.locations;

                // Mark...
                $.each(locs, function(i, location) {
                    // Only the truely worthy shall survive
                    location._mark = false;
                });

                // Load...
                $.each(data.locations, function(i, location) {
                    var key = location.item.key;
                    if (!locs[key]) {
                        var l = new Location(location);
                        l.setMarker();
                        locationLayer.addLayer(l.getMarker());
                        l._mark = true;
                        locs[key] = l;
                    } else {
                        locs[key]._mark = true;
                    }
                });

                // ...and Sweep
                $.each(locs, function(i, location) {
                    if (location._mark) {
                        delete location._mark;
                    } else {
                        locationLayer.removeLayer(location.getMarker());
                        delete locs[i];
                    }
                });

                that.events.trigger('DataManager.locationUpdated');
            });
        },

        /**
         * Updates school counts for neighborhoods based on
         * filters.
         *
         * Download topojson if not already downloaded.
         */
        neighborhoodUpdate: function(map) {
            var that = this,
                filters = that.getFilters(map);
            $.when(
                $.getJSON(common.getUrl('neighborhood-api'), filters, function(data) {
                    var neighborhoods = that.neighborhoods.data;
                    $.each(data.neighborhoods, function(i, neighborhood) {
                        var key = neighborhood.id;
                        neighborhoods[key] = neighborhood;
                    });
                }),
                that.geojsonUpdate()
            ).then(function() {
                that.events.trigger('DataManager.neighborhoodUpdated');
            });
        },

        /*
         * Get geojson for the neighborhoods and store in DataManager
         */
        geojsonUpdate: function() {
            var that = this;
            if (that.neighborhoods.geojson === undefined) {
                return $.getJSON(common.getUrl('neighborhoods-topo'), function(data) {
                        that.neighborhoods.geojson = topojson.feature(data, data.objects.neighborhoods);
                });
            }
        },

        /**
         * Alerts listeners that filter data has changed
         */
        onFilterChange: function() {
            this.events.trigger('DataManager.filtersUpdated');
        },

        /**
         * Gets the current state of location filters for passing into a GET request
         * @param { Leaflet map object to read current bounds from } map
         * @return { query params dict }
         */
        getFilters: function(map) {
            var opts = { },
                $filters = this.$filters;
            $filters.filter(':checked').each(function(idx, elem) {
                opts[elem.id] = elem.checked;
            });
            if (map && map.getBounds) {
                opts.bbox = map.getBounds().toBBoxString();
            }

            return opts;
        },

        /**
         * Map of locations & neighborhoods, key is the id for
         * each object
         */
        locations: null,
        neighborhoods: null,    // Has one key 'data' with a list of neighborhood objects

        /*
         * Available events:
         * neighborhoodUpdated: 'DataManager.neighborhoodUpdated',
         *      triggered when neighborhood data is finished loading
         * locationUpdated: 'DataManager.locationUpdated'
         *      triggered when location data is finished loading
         * filtersUpdated: 'DataManager.filtersChanged'
         *      fired when DOM filters change.  This includes checkboxes and map bounding box
         */
        events: null
    };

    return {
        Location: Location,
        DataManager: DataManager
    };
});

<|endoftext|>


<commit_before>/**
 * Passport configuration file where you should configure all your strategies
 * @description :: Configuration file where you configure your passport authentication
 */

var passport = require('passport'),
    LocalStrategy = require('passport-local').Strategy,
    JwtStrategy = require('passport-jwt').Strategy,
    FacebookTokenStrategy = require('passport-facebook-token').Strategy,
    TwitterTokenStrategy = require('passport-twitter-token').Strategy,
    YahooTokenStrategy = require('passport-yahoo-token').Strategy,
    GooglePlusTokenStrategy = require('passport-google-plus-token').Strategy;

// TODO: make this more stable and properly parse profile data

/**
 * Triggers when user authenticates via local strategy
 * @param {String} email Email from body field in request
 * @param {String} password Password from body field in request
 * @param {Function} next Callback
 * @private
 */
function _onLocalStrategyAuth(email, password, next) {
    User
        .findOne({email: email})
        .exec(function (error, user) {
            if (error) return next(error, false, {});

            if (!user) return next(null, false, {
                code: 'E_USER_NOT_FOUND',
                message: email + ' is not found'
            });

            if (!CipherService.create('bcrypt', user.password).compareSync(password)) return next(null, false, {
                code: 'E_WRONG_PASSWORD',
                message: 'Password is wrong'
            });

            return next(null, user, {});
        });
}

/**
 * Triggers when user authenticates via JWT strategy
 * @param {Object} payload Decoded payload from JWT
 * @param {Function} next Callback
 * @private
 */
function _onJwtStrategyAuth(payload, next) {
    User
        .findOne({id: payload.id})
        .exec(function (error, user) {
            if (error) return next(error, false, {});
            if (!user) return next(null, false, {
                code: 'E_USER_NOT_FOUND',
                message: 'User with that JWT not found'
            });

            return next(null, user, {});
        });
}

/**
 * Triggers when user authenticates via one of social strategies
 * @param {String} strategyName What strategy was used?
 * @param {Object} req Request object
 * @param {String} accessToken Access token from social network
 * @param {String} refreshToken Refresh token from social network
 * @param {Object} profile Social profile
 * @param {Function} next Callback
 * @private
 */
function _onSocialStrategyAuth(strategyName, req, accessToken, refreshToken, profile, next) {
    if (!req.user) {
        // TODO: move to ComputedPropertyName ES6
        var criteria = {};
        criteria[strategyName + '.id'] = profile.id;

        var model = {
            username: profile.username || profile.displayName || '',
            email: (profile.emails && profile.emails[0].value) || '',
            firstName: (profile.name && profile.name.givenName) || '',
            lastName: (profile.name && profile.name.familyName) || '',
            photo: (profile.photos && profile.photos[0].value) || ''
        };
        model[strategyName] = profile._json;

        User
            .findOrCreate(criteria, model)
            .exec(function (error, user) {
                if (error) return next(error, false, {});
                if (!user) return next(null, false, {
                    code: 'E_AUTH_FAILED',
                    message: [strategyName.charAt(0).toUpperCase(), strategyName.slice(1), ' auth failed'].join('')
                });

                return next(null, user, {});
            });
    } else {
        req.user[strategyName] = profile._json;
        req.user.save(next);
    }
}

passport.use(new LocalStrategy({
    usernameField: 'email',
    passwordField: 'password'
}, _onLocalStrategyAuth));

passport.use(new JwtStrategy({
    secretOrKey: "<%= answers['application:jwt-secret-key'] %>",
    tokenBodyField: 'bearer-token',
    tokenHeader: 'Bearer'
}, _onJwtStrategyAuth));

passport.use(new FacebookTokenStrategy({
    clientID: "-",
    clientSecret: "-",
    passReqToCallback: true
}, _onSocialStrategyAuth.bind(this, 'facebook')));

passport.use(new TwitterTokenStrategy({
    consumerKey: "-",
    consumerSecret: "-",
    passReqToCallback: true
}, _onSocialStrategyAuth.bind(this, 'twitter')));

passport.use(new YahooTokenStrategy({
    clientID: "-",
    clientSecret: "-",
    passReqToCallback: true
}, _onSocialStrategyAuth.bind(this, 'yahoo')));

passport.use(new GooglePlusTokenStrategy({
    clientID: "-",
    clientSecret: "-",
    passReqToCallback: true
}, _onSocialStrategyAuth.bind(this, 'google')));
<commit_msg>Replace strategyName via profile.provider<commit_after>/**
 * Passport configuration file where you should configure all your strategies
 * @description :: Configuration file where you configure your passport authentication
 */

var passport = require('passport'),
    LocalStrategy = require('passport-local').Strategy,
    JwtStrategy = require('passport-jwt').Strategy,
    FacebookTokenStrategy = require('passport-facebook-token').Strategy,
    TwitterTokenStrategy = require('passport-twitter-token').Strategy,
    YahooTokenStrategy = require('passport-yahoo-token').Strategy,
    GooglePlusTokenStrategy = require('passport-google-plus-token').Strategy;

// TODO: make this more stable and properly parse profile data

/**
 * Triggers when user authenticates via local strategy
 * @param {String} email Email from body field in request
 * @param {String} password Password from body field in request
 * @param {Function} next Callback
 * @private
 */
function _onLocalStrategyAuth(email, password, next) {
    User
        .findOne({email: email})
        .exec(function (error, user) {
            if (error) return next(error, false, {});

            if (!user) return next(null, false, {
                code: 'E_USER_NOT_FOUND',
                message: email + ' is not found'
            });

            if (!CipherService.create('bcrypt', user.password).compareSync(password)) return next(null, false, {
                code: 'E_WRONG_PASSWORD',
                message: 'Password is wrong'
            });

            return next(null, user, {});
        });
}

/**
 * Triggers when user authenticates via JWT strategy
 * @param {Object} payload Decoded payload from JWT
 * @param {Function} next Callback
 * @private
 */
function _onJwtStrategyAuth(payload, next) {
    User
        .findOne({id: payload.id})
        .exec(function (error, user) {
            if (error) return next(error, false, {});
            if (!user) return next(null, false, {
                code: 'E_USER_NOT_FOUND',
                message: 'User with that JWT not found'
            });

            return next(null, user, {});
        });
}

/**
 * Triggers when user authenticates via one of social strategies
 * @param {Object} req Request object
 * @param {String} accessToken Access token from social network
 * @param {String} refreshToken Refresh token from social network
 * @param {Object} profile Social profile
 * @param {Function} next Callback
 * @private
 */
function _onSocialStrategyAuth(req, accessToken, refreshToken, profile, next) {
    if (!req.user) {
        // TODO: move to ComputedPropertyName ES6
        var criteria = {};
        criteria[profile.provider + '.id'] = profile.id;

        var model = {
            username: profile.username || profile.displayName || '',
            email: (profile.emails && profile.emails[0].value) || '',
            firstName: (profile.name && profile.name.givenName) || '',
            lastName: (profile.name && profile.name.familyName) || '',
            photo: (profile.photos && profile.photos[0].value) || ''
        };
        model[profile.provider] = profile._json;

        User
            .findOrCreate(criteria, model)
            .exec(function (error, user) {
                if (error) return next(error, false, {});
                if (!user) return next(null, false, {
                    code: 'E_AUTH_FAILED',
                    message: [profile.provider.charAt(0).toUpperCase(), profile.provider.slice(1), ' auth failed'].join('')
                });

                return next(null, user, {});
            });
    } else {
        req.user[profile.provider] = profile._json;
        req.user.save(next);
    }
}

passport.use(new LocalStrategy({
    usernameField: 'email',
    passwordField: 'password'
}, _onLocalStrategyAuth));

passport.use(new JwtStrategy({
    secretOrKey: "<%= answers['application:jwt-secret-key'] %>",
    tokenBodyField: 'bearer-token',
    tokenHeader: 'Bearer'
}, _onJwtStrategyAuth));

passport.use(new FacebookTokenStrategy({
    clientID: "-",
    clientSecret: "-",
    passReqToCallback: true
}, _onSocialStrategyAuth));

passport.use(new TwitterTokenStrategy({
    consumerKey: "-",
    consumerSecret: "-",
    passReqToCallback: true
}, _onSocialStrategyAuth));

passport.use(new YahooTokenStrategy({
    clientID: "-",
    clientSecret: "-",
    passReqToCallback: true
}, _onSocialStrategyAuth));

passport.use(new GooglePlusTokenStrategy({
    clientID: "-",
    clientSecret: "-",
    passReqToCallback: true
}, _onSocialStrategyAuth));
<|endoftext|>


<commit_before>/**
 * Policy Mappings
 *
 * Policies are simple functions which run before your controllers.
 * You can apply one or more policies to a given controller, or protect
 * its actions individually.
 *
 * Any policy file (e.g. `api/policies/authenticated.js`) can be accessed
 * below by its filename, minus the extension, (e.g. "authenticated")
 */

module.exports.policies = {
  '*': ['isAllowed', 'isAuthenticated'],

  AuthController: {
    '*': 'isAllowed'
  }
};
<commit_msg>Add policy rule for Ping controller<commit_after>/**
 * Policy Mappings
 *
 * Policies are simple functions which run before your controllers.
 * You can apply one or more policies to a given controller, or protect
 * its actions individually.
 *
 * Any policy file (e.g. `api/policies/authenticated.js`) can be accessed
 * below by its filename, minus the extension, (e.g. "authenticated")
 */

module.exports.policies = {
  '*': ['isAllowed', 'isAuthenticated'],

  AuthController: {
    '*': 'isAllowed'
  },

  PingController: {
    '*': 'isAllowed',
    'index': true
  }
};
<|endoftext|>


<commit_before>var SmartLogic = {
  getToken: function(refresh=false) {
    tokenNeeded = !window.localStorage.getItem('ses_token')

    msInDay = 86400000
    tokenExpired = (Date.now() - window.localStorage.getItem('ses_issued')) / msInDay >= 14

    if ( refresh || (tokenNeeded || tokenExpired) ) {
      return $.get(
        '/ses-token/'
      ).then(function(response) {
          window.localStorage.setItem('ses_token', response.access_token);
          window.localStorage.setItem('ses_issued', Date.now());
      }).fail(function() {
          console.log('Failed to retrieve token');
      });
    };
  },
  buildServiceUrl: function(query) {
    return 'https://cloud.smartlogic.com/svc/0ef5d755-1f43-4a7e-8b06-7591bed8d453/ses/CombinedModel/hints/' + query + '.json?FILTER=AT=System:%20Legistar';
  }
};

/* Attaches autocomplete to the input div specified by the id passed */
function autocompleteSearchBar(element) {
  $(element).autocomplete({
    serviceUrl: SmartLogic.buildServiceUrl,
    ajaxSettings: {
      beforeSend: function (xhr) {
        SmartLogic.getToken();
        xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
        xhr.setRequestHeader('Authorization', 'Bearer ' + window.localStorage.getItem('ses_token'));
      },
      data: {}
    },
    paramName: '',
    transformResult: function(response) {
      var res = JSON.parse(response);
      if (res.status_code == 500 || res.termHints.length < 1) {
        return {
          suggestions: ['No topics found']
        };
      } else {
        return {
          suggestions: $.map(res.termHints, function(d) {
              /* If the search term is an acronym, displays that as a part of the suggestion */
              var nature = '';
              if (d.values[0]['nature'] == 'NPT') {
                nature = d.values[0]['value'];
                nature = ' (' + nature + ')';
              }
              return {'value': d.name + nature, 'data': d.id};
            }
          )
        };
      }
    },
    onSelect: function(suggestion) {
      $.when(
        $.get('/topic/', { 'guid': suggestion.data })
      ).then(function(response) {
        if (response.status_code == 200) {
          var base = '/search/?q=';
          var url = base + response.subject_safe;
          window.location.href = url;
        };
      });
    },
    deferRequestBy: 100
  });
};
<commit_msg>adding new text for not found<commit_after>var SmartLogic = {
  getToken: function(refresh=false) {
    tokenNeeded = !window.localStorage.getItem('ses_token')

    msInDay = 86400000
    tokenExpired = (Date.now() - window.localStorage.getItem('ses_issued')) / msInDay >= 14

    if ( refresh || (tokenNeeded || tokenExpired) ) {
      return $.get(
        '/ses-token/'
      ).then(function(response) {
          window.localStorage.setItem('ses_token', response.access_token);
          window.localStorage.setItem('ses_issued', Date.now());
      }).fail(function() {
          console.log('Failed to retrieve token');
      });
    };
  },
  buildServiceUrl: function(query) {
    return 'https://cloud.smartlogic.com/svc/0ef5d755-1f43-4a7e-8b06-7591bed8d453/ses/CombinedModel/hints/' + query + '.json?FILTER=AT=System:%20Legistar';
  }
};

/* Attaches autocomplete to the input div specified by the id passed */
function autocompleteSearchBar(element) {
  $(element).autocomplete({
    serviceUrl: SmartLogic.buildServiceUrl,
    ajaxSettings: {
      beforeSend: function (xhr) {
        SmartLogic.getToken();
        xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
        xhr.setRequestHeader('Authorization', 'Bearer ' + window.localStorage.getItem('ses_token'));
      },
      data: {}
    },
    paramName: '',
    transformResult: function(response) {
      var res = JSON.parse(response);
      var noneFoundText = "No suggestions found. Press enter to perform a keyword search."
      if (res.status_code == 500 || res.termHints.length < 1) {
        return {
          suggestions: [noneFoundText]
        };
      } else {
        return {
          suggestions: $.map(res.termHints, function(d) {
              /* If the search term is an acronym, displays that as a part of the suggestion */
              var nature = '';
              if (d.values[0]['nature'] == 'NPT') {
                nature = d.values[0]['value'];
                nature = ' (' + nature + ')';
              }
              return {'value': d.name + nature, 'data': d.id};
            }
          )
        };
      }
    },
    onSelect: function(suggestion) {
      $.when(
        $.get('/topic/', { 'guid': suggestion.data })
      ).then(function(response) {
        if (response.status_code == 200) {
          var base = '/search/?q=';
          var url = base + response.subject_safe;
          window.location.href = url;
        };
      });
    },
    deferRequestBy: 100
  });
};
<|endoftext|>


<commit_before>var SmartLogic = {
  getToken: function() {
    tokenNeeded = !window.localStorage.getItem('ses_token')

    msInDay = 86400000
    tokenExpired = (Date.now() - window.localStorage.getItem('ses_issued')) / msInDay >= 14

    if ( (tokenNeeded || tokenExpired) ) {
      return $.get(
        '/ses-token/'
      ).then(function(response) {
          window.localStorage.setItem('ses_token', response.access_token);
          window.localStorage.setItem('ses_issued', Date.now());
      }).fail(function() {
          console.log('Failed to retrieve token');
      });
    };
  },
  buildServiceUrl: function(query) {
    return 'https://cloud.smartlogic.com/svc/0ef5d755-1f43-4a7e-8b06-7591bed8d453/ses/CombinedModel/hints/' + query + '.json?FILTER=AT=System:%20Legistar';
  }
};

/* Attaches autocomplete to the input div specified by the id passed */
function autocompleteSearchBar(element) {
  $(element).autocomplete({
    serviceUrl: SmartLogic.buildServiceUrl,
    ajaxSettings: {
      beforeSend: function (xhr) {
        SmartLogic.getToken();
        xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
        xhr.setRequestHeader('Authorization', 'Bearer ' + window.localStorage.getItem('ses_token'));
      },
      data: {}
    },
    paramName: '',
    transformResult: function(response) {
      var res = JSON.parse(response);
      if (res.status_code == 500 || res.termHints.length < 1) {
        return {
          suggestions: ['No topics found']
        };
      } else {
        return {
          suggestions: $.map(res.termHints, function(d) {
              /* If the search term is an acronym, displays that as a part of the suggestion */
              var nature = '';
              if (d.values[0]['nature'] == 'NPT') {
                nature = d.values[0]['value'];
                nature = ' (' + nature + ')';
              }
              return {'value': d.name + nature, 'data': d.id};
            }
          )
        };
      }
    },
    onSelect: function(suggestion) {
      $.when(
        $.get('/topic/', { 'guid': suggestion.data })
      ).then(function(response) {
        if (response.status_code == 200) {
          var base = '/search/?q=';
          var url = base + response.subject_safe;
          window.location.href = url;
        };
      });
    },
    deferRequestBy: 100
  });
};
<commit_msg>Don't submit on matching input<commit_after>var SmartLogic = {
  getToken: function() {
    tokenNeeded = !window.localStorage.getItem('ses_token')

    msInDay = 86400000
    tokenExpired = (Date.now() - window.localStorage.getItem('ses_issued')) / msInDay >= 14

    if ( (tokenNeeded || tokenExpired) ) {
      return $.get(
        '/ses-token/'
      ).then(function(response) {
          window.localStorage.setItem('ses_token', response.access_token);
          window.localStorage.setItem('ses_issued', Date.now());
      }).fail(function() {
          console.log('Failed to retrieve token');
      });
    };
  },
  buildServiceUrl: function(query) {
    return 'https://cloud.smartlogic.com/svc/0ef5d755-1f43-4a7e-8b06-7591bed8d453/ses/CombinedModel/hints/' + query + '.json?FILTER=AT=System:%20Legistar';
  }
};

/* Attaches autocomplete to the input div specified by the id passed */
function autocompleteSearchBar(element) {
  $(element).autocomplete({
    serviceUrl: SmartLogic.buildServiceUrl,
    ajaxSettings: {
      beforeSend: function (xhr) {
        SmartLogic.getToken();
        xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
        xhr.setRequestHeader('Authorization', 'Bearer ' + window.localStorage.getItem('ses_token'));
      },
      data: {}
    },
    paramName: '',
    transformResult: function(response) {
      var res = JSON.parse(response);
      if (res.status_code == 500 || res.termHints.length < 1) {
        return {
          suggestions: ['No topics found']
        };
      } else {
        return {
          suggestions: $.map(res.termHints, function(d) {
              /* If the search term is an acronym, displays that as a part of the suggestion */
              var nature = '';
              if (d.values[0]['nature'] == 'NPT') {
                nature = d.values[0]['value'];
                nature = ' (' + nature + ')';
              }
              return {'value': d.name + nature, 'data': d.id};
            }
          )
        };
      }
    },
    onSelect: function(suggestion) {
      $.when(
        $.get('/topic/', { 'guid': suggestion.data })
      ).then(function(response) {
        if (response.status_code == 200) {
          var base = '/search/?q=';
          var url = base + response.subject_safe;
          window.location.href = url;
        };
      });
    },
    deferRequestBy: 100,
    triggerSelectOnValidInput: false,
  });
};
<|endoftext|>


<commit_before>var demand = require('must');
var RelationshipType = require('../RelationshipType');

exports.initList = function (List) {
	// We can use relationships that refer to the same List to test
	List.add({
		single: { type: RelationshipType, ref: List.key },
	});
};

exports.testFieldType = function (List) {

	var relatedItem = new List.model();
	before(function (done) {
		relatedItem.save(done);
	});

	describe('single', function () {
		it('should validate id input', function (done) {
			List.fields.single.validateInput({ single: relatedItem.id }, function (result) {
				demand(result).equal(true);
				done();
			});
		});

		it('should validate undefined input', function (done) {
			List.fields.single.validateInput({}, function (result) {
				demand(result).equal(true);
				done();
			});
		});

		it('should invalidate empty input', function (done) {
			List.fields.single.validateInput({ single: '' }, function (result) {
				demand(result).equal(false);
				done();
			});
		});

		it('should invalidate boolean input', function (done) {
			List.fields.single.validateInput({ single: true }, function (result) {
				demand(result).equal(false);
				done();
			});
		});

		it('should invalidate object input', function (done) {
			List.fields.single.validateInput({ single: {} }, function (result) {
				demand(result).equal(false);
				done();
			});
		});

		it('should invalidate array input', function (done) {
			List.fields.single.validateInput({ single: [] }, function (result) {
				demand(result).equal(false);
				done();
			});
		});

		it('should validate required present input', function (done) {
			var testItem = new List.model();
			List.fields.single.validateRequiredInput(testItem, { single: relatedItem.id }, function (result) {
				demand(result).equal(true);
				done();
			});
		});

		it('should validate required present input with existing value', function (done) {
			var testItem = new List.model({
				single: relatedItem.id,
			});
			List.fields.single.validateRequiredInput(testItem, { single: relatedItem.id }, function (result) {
				demand(result).equal(true);
				testItem.single = undefined;
				done();
			});
		});

		it('should invalidate required not present input', function (done) {
			var testItem = new List.model();
			List.fields.single.validateRequiredInput(testItem, {}, function (result) {
				demand(result).equal(false);
				done();
			});
		});

		it('should validate required input with existing value', function (done) {
			var testItem = new List.model({
				single: relatedItem.id,
			});
			List.fields.single.validateRequiredInput(testItem, {}, function (result) {
				demand(result).equal(true);
				testItem.single = undefined;
				done();
			});
		});

		it('should invalidate required blank input with existing value', function (done) {
			var testItem = new List.model({
				single: relatedItem.id,
			});
			List.fields.single.validateRequiredInput(testItem, { single: '' }, function (result) {
				demand(result).equal(false);
				testItem.single = undefined;
				done();
			});
		});

		it('should save the provided value', function (done) {
			var testItem = new List.model();
			List.fields.single.updateItem(testItem, { single: relatedItem.id }, function () {
				// TODO: We should be testing for errors here
				testItem.save(function (err, updatedItem) {
					demand(String(updatedItem.single)).equal(String(relatedItem.id));
					done();
				});
			});
		});

		it('should clear the current value when provided null', function (done) {
			var testItem = new List.model({
				single: relatedItem.id,
			});
			testItem.save(function (err) {
				List.fields.single.updateItem(testItem, { single: null }, function () {
					// TODO: We should be testing for errors here
					testItem.save(function (err, updatedItem) {
						demand(updatedItem.single).equal(null);
						done();
					});
				});
			});
		});

		it('should clear the current value when provided ""', function (done) {
			var testItem = new List.model({
				single: relatedItem.id,
			});
			testItem.save(function (err) {
				List.fields.single.updateItem(testItem, { single: '' }, function () {
					// TODO: We should be testing for errors here
					testItem.save(function (err, updatedItem) {
						demand(updatedItem.single).equal(null);
						done();
					});
				});
			});
		});
	});

};
<commit_msg>Remove relationship test value resets<commit_after>var demand = require('must');
var RelationshipType = require('../RelationshipType');

exports.initList = function (List) {
	// We can use relationships that refer to the same List to test
	List.add({
		single: { type: RelationshipType, ref: List.key },
	});
};

exports.testFieldType = function (List) {

	var relatedItem = new List.model();
	before(function (done) {
		relatedItem.save(done);
	});

	describe('single', function () {
		it('should validate id input', function (done) {
			List.fields.single.validateInput({ single: relatedItem.id }, function (result) {
				demand(result).equal(true);
				done();
			});
		});

		it('should validate undefined input', function (done) {
			List.fields.single.validateInput({}, function (result) {
				demand(result).equal(true);
				done();
			});
		});

		it('should invalidate empty input', function (done) {
			List.fields.single.validateInput({ single: '' }, function (result) {
				demand(result).equal(false);
				done();
			});
		});

		it('should invalidate boolean input', function (done) {
			List.fields.single.validateInput({ single: true }, function (result) {
				demand(result).equal(false);
				done();
			});
		});

		it('should invalidate object input', function (done) {
			List.fields.single.validateInput({ single: {} }, function (result) {
				demand(result).equal(false);
				done();
			});
		});

		it('should invalidate array input', function (done) {
			List.fields.single.validateInput({ single: [] }, function (result) {
				demand(result).equal(false);
				done();
			});
		});

		it('should validate required present input', function (done) {
			var testItem = new List.model();
			List.fields.single.validateRequiredInput(testItem, { single: relatedItem.id }, function (result) {
				demand(result).equal(true);
				done();
			});
		});

		it('should validate required present input with existing value', function (done) {
			var testItem = new List.model({
				single: relatedItem.id,
			});
			List.fields.single.validateRequiredInput(testItem, { single: relatedItem.id }, function (result) {
				demand(result).equal(true);
				done();
			});
		});

		it('should invalidate required not present input', function (done) {
			var testItem = new List.model();
			List.fields.single.validateRequiredInput(testItem, {}, function (result) {
				demand(result).equal(false);
				done();
			});
		});

		it('should validate required input with existing value', function (done) {
			var testItem = new List.model({
				single: relatedItem.id,
			});
			List.fields.single.validateRequiredInput(testItem, {}, function (result) {
				demand(result).equal(true);
				done();
			});
		});

		it('should invalidate required blank input with existing value', function (done) {
			var testItem = new List.model({
				single: relatedItem.id,
			});
			List.fields.single.validateRequiredInput(testItem, { single: '' }, function (result) {
				demand(result).equal(false);
				done();
			});
		});

		it('should save the provided value', function (done) {
			var testItem = new List.model();
			List.fields.single.updateItem(testItem, { single: relatedItem.id }, function () {
				// TODO: We should be testing for errors here
				testItem.save(function (err, updatedItem) {
					demand(String(updatedItem.single)).equal(String(relatedItem.id));
					done();
				});
			});
		});

		it('should clear the current value when provided null', function (done) {
			var testItem = new List.model({
				single: relatedItem.id,
			});
			testItem.save(function (err) {
				List.fields.single.updateItem(testItem, { single: null }, function () {
					// TODO: We should be testing for errors here
					testItem.save(function (err, updatedItem) {
						demand(updatedItem.single).equal(null);
						done();
					});
				});
			});
		});

		it('should clear the current value when provided ""', function (done) {
			var testItem = new List.model({
				single: relatedItem.id,
			});
			testItem.save(function (err) {
				List.fields.single.updateItem(testItem, { single: '' }, function () {
					// TODO: We should be testing for errors here
					testItem.save(function (err, updatedItem) {
						demand(updatedItem.single).equal(null);
						done();
					});
				});
			});
		});
	});

};
<|endoftext|>


<commit_before>/** Allows import of other files e.g. const GitHub=imports.github; = github.js */
imports.searchPath.push( imports.ui.appletManager.appletMeta["github-projects@morgan-design.com"].path );

/** Imports START **/
const Mainloop = imports.mainloop;
const Lang = imports.lang;
const Gettext = imports.gettext.domain('cinnamon-applets');
const _ = Gettext.gettext;
const Cinnamon = imports.gi.Cinnamon;

const Applet = imports.ui.applet;
const Util = imports.misc.util;
const GLib = imports.gi.GLib;

const PopupMenu = imports.ui.popupMenu;
const Main = imports.ui.main;

const Tooltips = imports.ui.tooltips;
const Settings = imports.ui.settings;
/** Imports END **/

/** Custom Files START **/
const GitHub=imports.github;
const Logger=imports.logger;
/** Custom Files END **/

const APPLET_ICON = global.userdatadir + "/applets/github-projects@morgan-design.com/icon.png";

const NotificationMessages = {
    AttemptingToLoad: 	{ title: "GitHub Explorer", 					content: "Attempting to Load your GitHub Repos" },
    SuccessfullyLoaded: { title: "GitHub Explorer", 					content: "Successfully Loaded GitHub Repos for user ", append: "USER_NAME" },
    ErrorOnLoad: 		{ title: "ERROR:: GitHub Explorer ::ERROR", 	content: "Failed to load GitHub Repositories! Check applet Configuration" }
};

/* Application Hook */
function main(metadata, orientation) {
	let myApplet = new MyApplet(metadata, orientation);
	return myApplet;
}

/* Constructor */
function MyApplet(metadata, orientation) {
	this._init(metadata, orientation);
}

MyApplet.prototype = {
	__proto__: Applet.IconApplet.prototype,

	_init: function(metadata, orientation, instance_id) {

	this.settings = new Settings.AppletSettings(this, "github-projects@morgan-design.com", instance_id);

	this._reloadGitHubFeedTimerId = 0;
	this._shouldDisplayLookupNotification = true;

	Applet.IconApplet.prototype._init.call(this, orientation, instance_id);
		try {
			this.set_applet_icon_path(APPLET_ICON);

			this.settings.bindProperty(Settings.BindingDirection.IN,   // The binding direction - IN means we only listen for changes from this applet
							 "username",                               // The setting key, from the setting schema file
							 "username",                               // The property to bind the setting to - in this case it will initialize this.icon_name to the setting value
							 this.on_settings_changed,                  // The method to call when this.icon_name has changed, so you can update your applet
							 null);                                     // Any extra information you want to pass to the callback (optional - pass null or just leave out this last argument)

			this.settings.bindProperty(Settings.BindingDirection.IN,
							 "enable-auto-refresh",
							 "enable_auto_refresh",
							 this.on_settings_changed,
							 null);

			this.settings.bindProperty(Settings.BindingDirection.IN,
							 "refresh-interval",
							 "refresh_interval",
							 this.on_settings_changed,
							 null);

			// Set version from metadata
			this.settings.setValue("applet-version", metadata.version);

			this.logger = new Logger.Logger({ 'UUID':metadata.uuid });

			// Menu setup
			this.menu = new Applet.AppletPopupMenu(this, orientation);
			this.menuManager = new PopupMenu.PopupMenuManager(this);
			this.menuManager.addMenu(this.menu);

			let self = this;

			// Create and Setup new GitHub object
			this.gh = new GitHub.GitHub({
				username: this.settings.getValue("username"),
				version: metadata.version,
				logger: this.logger
			});

			// Handle failures
			this.gh.onFailure(function(status_code, error_message){
				self._handleGitHubErrorResponse(status_code, error_message);
			});

			// Handle success
			this.gh.onSuccess(function(jsonData){
				self._handleGitHubSuccessResponse(jsonData);
			});

			// Add Settings menu item
			let settingsMenu = new PopupMenu.PopupImageMenuItem("Settings", "preferences-system-symbolic");
			settingsMenu.connect('activate', Lang.bind(this, function(){
				this._openSettingsConfiguration(metadata.uuid);
			}));
			this._applet_context_menu.addMenuItem(settingsMenu);

			// If no username set, launch configuration options and tell the user
			if(this.settings.getValue("username") == "" || this.settings.getValue("username") == undefined){
				this._openSettingsConfiguration(metadata.uuid);

				this.set_applet_tooltip(_("Check Applet Configuration"));
				this._displayErrorNotification(NotificationMessages['ErrorOnLoad']);
			} else {
				// Make first github lookup and trigger ticking timer!
				this._startGitHubLookupTimer()
			}
		}
		catch (e) {
			if(this.logger!=undefined){
				this.logger.error(e);
				global.logError(e);
			}else{
				global.logError(e);
			}
		}
	},

    on_applet_clicked: function(event){
		this.menu.toggle();
    },

	on_applet_removed_from_panel: function() {
		this._killPeriodicTimer();	// Stop the ticking timer
		this.settings.finalize();	// We want to remove any connections and file listeners here
	},

	on_open_github_home_pressed: function(){ this._openUrl("http://github.com/jamesemorgan/CustomCinnamonApplets"); },

	on_open_cinnamon_home_pressed: function(){ this._openUrl("http://cinnamon-spices.linuxmint.com/applets/view/105"); },

	on_open_developer_home_pressed: function(){	this._openUrl("http://morgan-design.com"); },

	on_settings_changed: function() {
		var newUserName = this.settings.getValue("username");

		var refreshStillEnabled = this.settings.getValue("enable-auto-refresh");

		var userNameChanged = this.gh.username != newUserName;

		if(userNameChanged){
			this.gh.username = newUserName;
		}

		if(refreshStillEnabled && userNameChanged){
			this._startGitHubLookupTimer(); // If timer not running and user changed trigger fresh lookup
		}
		else if(!refreshStillEnabled){
			this._killPeriodicTimer(); // If timer diabled remove it
		}
		else if(refreshStillEnabled) {
			this._startGitHubLookupTimer(); // If timer still enabled ensure it is still kicking
		}

		this.logger.debug("App : Username loaded = " + newUserName);
		this.logger.debug("App : Refresh Interval = " + this.settings.getValue("enable-auto-refresh"));
		this.logger.debug("App : Auto Refresh = " + this.settings.getValue("refresh-interval"));
	},

	_openSettingsConfiguration: function(uuid){
		Util.spawnCommandLine("cinnamon-settings applets " + uuid);
	},

    _handleGitHubErrorResponse: function(status_code, error_message){
		this.logger.error("Error Response, status code: " + status_code + " message: " + error_message);

		let notificationMessage = {};

		if(status_code === 403 && this.gh.hasExceededApiLimit()){
			notificationMessage = {title:"GitHub Explorer",content:error_message};
			this.set_applet_tooltip(_("API Rate Exceeded will try again once we are allowed"));
		}
		else {
			notificationMessage = NotificationMessages['ErrorOnLoad'];
			this.set_applet_tooltip(_("Check applet Configuration"))
		}

		this._displayErrorNotification(notificationMessage);
		this._shouldDisplayLookupNotification = true;
    },

    _handleGitHubSuccessResponse: function(jsonData) {
		if(this._shouldDisplayLookupNotification){
			this._displayNotification(NotificationMessages['SuccessfullyLoaded']);
			this._shouldDisplayLookupNotification = false;
    	}
		this.set_applet_tooltip(_("Click here to open GitHub\l\n"+this.gh.lastAttemptDateTime));
		this._createApplicationMenu(jsonData);
		this._startGitHubLookupTimer();
    },

	_displayNotification: function(notifyContent){
		let msg = notifyContent.content;
		if(notifyContent.append != undefined){
			switch(notifyContent.append){
				case "USER_NAME":
					msg += this.gh.username;
			}
		}
		let notification = "notify-send \""+notifyContent.title+"\" \""+msg+"\" -i " + APPLET_ICON + " -a GIT_HUB_EXPLORER -t 10 -u low";
		this.logger.debug("notification call = [" + notification + "]")
		Util.spawnCommandLine(notification);
	},

	_displayErrorNotification: function(notificationMessage) {
		this.menu.removeAll();
		this._addOpenGitHubMenuItem();
		this._displayNotification(notificationMessage);
	},

	_createApplicationMenu: function(repos) {
		this.logger.debug("Rebuilding Menu - attempt @ = " + this.gh.lastAttemptDateTime);
		this.menu.removeAll();

		this._addOpenGitHubMenuItem();

		for (i in repos) {
			let name = repos[i].name;

			// Main Menu Item
			let gitHubRepoMenuItem = new PopupMenu.PopupSubMenuMenuItem(_(name));

			// Open Repo Item
			let html_url = repos[i].html_url;
			let openRepoItem = this._createPopupImageMenuItem("Open Repo In Browser", "web-browser-symbolic", function() {
					this._openUrl(html_url);
			});
			gitHubRepoMenuItem.menu.addMenuItem(openRepoItem);

			// Project Home Item
			let homepage = repos[i].homepage;
			if(homepage != undefined && homepage != ""){
				let projectHomePageItem = this._createPopupImageMenuItem("Project Home", "user-home-symbolic", function() {
						this._openUrl(homepage);
				});
				gitHubRepoMenuItem.menu.addMenuItem(projectHomePageItem);
			}

			// Details : Watchers
			let gitHubRepoDetailsItem = new PopupMenu.PopupSubMenuMenuItem(_("Details"), "dialog-information-symbolic");
			gitHubRepoDetailsItem.menu.addMenuItem(new PopupMenu.PopupMenuItem(_('Watchers: ' + repos[i].watchers_count), { reactive: false }));

			// Details : Open Issues
			let open_issues_count = repos[i].open_issues_count;
			let issuesIcon = open_issues_count == '0' ? "dialog-information" : "dialog-warning-symbolic";
			let openIssuesCountItem = this._createPopupImageMenuItem(_('Open Issues: ' + open_issues_count), issuesIcon, function() {
					this._openUrl("https://github.com/"+this.gh.username+"/"+name+"/issues");
			}, { reactive: true });
			gitHubRepoDetailsItem.menu.addMenuItem(openIssuesCountItem);

			// Details : Forks
			let forks = repos[i].forks;
			let forksItem = this._createPopupImageMenuItem(_('Forks: ' + forks), "preferences-system-network-proxy-symbolic", function() {
					this._openUrl("https://github.com/"+this.gh.username+"/"+name+"/network");
			}, { reactive: true });
			gitHubRepoDetailsItem.menu.addMenuItem(forksItem);

			// Add Details
			gitHubRepoMenuItem.menu.addMenuItem(gitHubRepoDetailsItem);

		    this.menu.addMenuItem(gitHubRepoMenuItem);
		}
	},

	_createPopupImageMenuItem: function(title, icon, bindFunction, options){
		let options = options || {};
		let openRepoItem = new PopupMenu.PopupImageMenuItem(title, icon, options);
		openRepoItem.connect("activate", Lang.bind(this, bindFunction));
		return openRepoItem;
	},

	_openUrl: function(url) {
    	Util.spawnCommandLine("xdg-open " + url);
	},

	_addDefaultMenuItems: function() {
		this._addOpenGitHubMenuItem();
	},

	_addOpenGitHubMenuItem: function(){
		this.numMenuItem = this._createPopupImageMenuItem(_('Open GitHub Home'), "", function() {
				this._openUrl("https://github.com/"+this.gh.username);
		}, { reactive: true });

	    this.menu.addMenuItem(this.numMenuItem);
		this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
	},

	_killPeriodicTimer: function(){
		if (this._reloadGitHubFeedTimerId) {
			Mainloop.source_remove(this._reloadGitHubFeedTimerId);
		}
	},

	_triggerGitHubLookup: function() {
		if(this._shouldDisplayLookupNotification){
			this._displayNotification(NotificationMessages['AttemptingToLoad']);
		}
		this.gh.loadDataFeed();
	},

	_startGitHubLookupTimer: function() {
		this._killPeriodicTimer();

		this._triggerGitHubLookup();

		let timeout_in_minutes = this.gh.hasExceededApiLimit()
									? this.gh.minutesUntilNextRefreshWindow()
									: this.settings.getValue("refresh-interval")

		this.logger.debug("Time in minutes until next API request [" + timeout_in_minutes + "]");

		let timeout_in_seconds = timeout_in_minutes * 60 * 1000;

		if (timeout_in_seconds > 0 && this.settings.getValue("enable-auto-refresh")) {
			this._reloadGitHubFeedTimerId = Mainloop.timeout_add(timeout_in_seconds, Lang.bind(this, this._startGitHubLookupTimer));
		}
	},
};
<commit_msg>Remove 0 time check<commit_after>/** Allows import of other files e.g. const GitHub=imports.github; = github.js */
imports.searchPath.push( imports.ui.appletManager.appletMeta["github-projects@morgan-design.com"].path );

/** Imports START **/
const Mainloop = imports.mainloop;
const Lang = imports.lang;
const Gettext = imports.gettext.domain('cinnamon-applets');
const _ = Gettext.gettext;
const Cinnamon = imports.gi.Cinnamon;

const Applet = imports.ui.applet;
const Util = imports.misc.util;
const GLib = imports.gi.GLib;

const PopupMenu = imports.ui.popupMenu;
const Main = imports.ui.main;

const Tooltips = imports.ui.tooltips;
const Settings = imports.ui.settings;
/** Imports END **/

/** Custom Files START **/
const GitHub=imports.github;
const Logger=imports.logger;
/** Custom Files END **/

const APPLET_ICON = global.userdatadir + "/applets/github-projects@morgan-design.com/icon.png";

const NotificationMessages = {
    AttemptingToLoad: 	{ title: "GitHub Explorer", 					content: "Attempting to Load your GitHub Repos" },
    SuccessfullyLoaded: { title: "GitHub Explorer", 					content: "Successfully Loaded GitHub Repos for user ", append: "USER_NAME" },
    ErrorOnLoad: 		{ title: "ERROR:: GitHub Explorer ::ERROR", 	content: "Failed to load GitHub Repositories! Check applet Configuration" }
};

/* Application Hook */
function main(metadata, orientation) {
	let myApplet = new MyApplet(metadata, orientation);
	return myApplet;
}

/* Constructor */
function MyApplet(metadata, orientation) {
	this._init(metadata, orientation);
}

MyApplet.prototype = {
	__proto__: Applet.IconApplet.prototype,

	_init: function(metadata, orientation, instance_id) {

	this.settings = new Settings.AppletSettings(this, "github-projects@morgan-design.com", instance_id);

	this._reloadGitHubFeedTimerId = 0;
	this._shouldDisplayLookupNotification = true;

	Applet.IconApplet.prototype._init.call(this, orientation, instance_id);
		try {
			this.set_applet_icon_path(APPLET_ICON);

			this.settings.bindProperty(Settings.BindingDirection.IN,   // The binding direction - IN means we only listen for changes from this applet
							 "username",                               // The setting key, from the setting schema file
							 "username",                               // The property to bind the setting to - in this case it will initialize this.icon_name to the setting value
							 this.on_settings_changed,                  // The method to call when this.icon_name has changed, so you can update your applet
							 null);                                     // Any extra information you want to pass to the callback (optional - pass null or just leave out this last argument)

			this.settings.bindProperty(Settings.BindingDirection.IN,
							 "enable-auto-refresh",
							 "enable_auto_refresh",
							 this.on_settings_changed,
							 null);

			this.settings.bindProperty(Settings.BindingDirection.IN,
							 "refresh-interval",
							 "refresh_interval",
							 this.on_settings_changed,
							 null);

			// Set version from metadata
			this.settings.setValue("applet-version", metadata.version);

			this.logger = new Logger.Logger({ 'UUID':metadata.uuid });

			// Menu setup
			this.menu = new Applet.AppletPopupMenu(this, orientation);
			this.menuManager = new PopupMenu.PopupMenuManager(this);
			this.menuManager.addMenu(this.menu);

			let self = this;

			// Create and Setup new GitHub object
			this.gh = new GitHub.GitHub({
				username: this.settings.getValue("username"),
				version: metadata.version,
				logger: this.logger
			});

			// Handle failures
			this.gh.onFailure(function(status_code, error_message){
				self._handleGitHubErrorResponse(status_code, error_message);
			});

			// Handle success
			this.gh.onSuccess(function(jsonData){
				self._handleGitHubSuccessResponse(jsonData);
			});

			// Add Settings menu item
			let settingsMenu = new PopupMenu.PopupImageMenuItem("Settings", "preferences-system-symbolic");
			settingsMenu.connect('activate', Lang.bind(this, function(){
				this._openSettingsConfiguration(metadata.uuid);
			}));
			this._applet_context_menu.addMenuItem(settingsMenu);

			// If no username set, launch configuration options and tell the user
			if(this.settings.getValue("username") == "" || this.settings.getValue("username") == undefined){
				this._openSettingsConfiguration(metadata.uuid);

				this.set_applet_tooltip(_("Check Applet Configuration"));
				this._displayErrorNotification(NotificationMessages['ErrorOnLoad']);
			} else {
				// Make first github lookup and trigger ticking timer!
				this._startGitHubLookupTimer()
			}
		}
		catch (e) {
			if(this.logger!=undefined){
				this.logger.error(e);
				global.logError(e);
			}else{
				global.logError(e);
			}
		}
	},

    on_applet_clicked: function(event){
		this.menu.toggle();
    },

	on_applet_removed_from_panel: function() {
		this._killPeriodicTimer();	// Stop the ticking timer
		this.settings.finalize();	// We want to remove any connections and file listeners here
	},

	on_open_github_home_pressed: function(){ this._openUrl("http://github.com/jamesemorgan/CustomCinnamonApplets"); },

	on_open_cinnamon_home_pressed: function(){ this._openUrl("http://cinnamon-spices.linuxmint.com/applets/view/105"); },

	on_open_developer_home_pressed: function(){	this._openUrl("http://morgan-design.com"); },

	on_settings_changed: function() {
		var newUserName = this.settings.getValue("username");

		var refreshStillEnabled = this.settings.getValue("enable-auto-refresh");

		var userNameChanged = this.gh.username != newUserName;

		if(userNameChanged){
			this.gh.username = newUserName;
		}

		if(refreshStillEnabled && userNameChanged){
			this._startGitHubLookupTimer(); // If timer not running and user changed trigger fresh lookup
		}
		else if(!refreshStillEnabled){
			this._killPeriodicTimer(); // If timer diabled remove it
		}
		else if(refreshStillEnabled) {
			this._startGitHubLookupTimer(); // If timer still enabled ensure it is still kicking
		}

		this.logger.debug("App : Username loaded = " + newUserName);
		this.logger.debug("App : Refresh Interval = " + this.settings.getValue("enable-auto-refresh"));
		this.logger.debug("App : Auto Refresh = " + this.settings.getValue("refresh-interval"));
	},

	_openSettingsConfiguration: function(uuid){
		Util.spawnCommandLine("cinnamon-settings applets " + uuid);
	},

    _handleGitHubErrorResponse: function(status_code, error_message){
		this.logger.error("Error Response, status code: " + status_code + " message: " + error_message);

		let notificationMessage = {};

		if(status_code === 403 && this.gh.hasExceededApiLimit()){
			notificationMessage = {title:"GitHub Explorer",content:error_message};
			this.set_applet_tooltip(_("API Rate Exceeded will try again once we are allowed"));
		}
		else {
			notificationMessage = NotificationMessages['ErrorOnLoad'];
			this.set_applet_tooltip(_("Check applet Configuration"))
		}

		this._displayErrorNotification(notificationMessage);
		this._shouldDisplayLookupNotification = true;
    },

    _handleGitHubSuccessResponse: function(jsonData) {
		if(this._shouldDisplayLookupNotification){
			this._displayNotification(NotificationMessages['SuccessfullyLoaded']);
			this._shouldDisplayLookupNotification = false;
    	}
		this.set_applet_tooltip(_("Click here to open GitHub\l\n"+this.gh.lastAttemptDateTime));
		this._createApplicationMenu(jsonData);
		this._startGitHubLookupTimer();
    },

	_displayNotification: function(notifyContent){
		let msg = notifyContent.content;
		if(notifyContent.append != undefined){
			switch(notifyContent.append){
				case "USER_NAME":
					msg += this.gh.username;
			}
		}
		let notification = "notify-send \""+notifyContent.title+"\" \""+msg+"\" -i " + APPLET_ICON + " -a GIT_HUB_EXPLORER -t 10 -u low";
		this.logger.debug("notification call = [" + notification + "]")
		Util.spawnCommandLine(notification);
	},

	_displayErrorNotification: function(notificationMessage) {
		this.menu.removeAll();
		this._addOpenGitHubMenuItem();
		this._displayNotification(notificationMessage);
	},

	_createApplicationMenu: function(repos) {
		this.logger.debug("Rebuilding Menu - attempt @ = " + this.gh.lastAttemptDateTime);
		this.menu.removeAll();

		this._addOpenGitHubMenuItem();

		for (i in repos) {
			let name = repos[i].name;

			// Main Menu Item
			let gitHubRepoMenuItem = new PopupMenu.PopupSubMenuMenuItem(_(name));

			// Open Repo Item
			let html_url = repos[i].html_url;
			let openRepoItem = this._createPopupImageMenuItem("Open Repo In Browser", "web-browser-symbolic", function() {
					this._openUrl(html_url);
			});
			gitHubRepoMenuItem.menu.addMenuItem(openRepoItem);

			// Project Home Item
			let homepage = repos[i].homepage;
			if(homepage != undefined && homepage != ""){
				let projectHomePageItem = this._createPopupImageMenuItem("Project Home", "user-home-symbolic", function() {
						this._openUrl(homepage);
				});
				gitHubRepoMenuItem.menu.addMenuItem(projectHomePageItem);
			}

			// Details : Watchers
			let gitHubRepoDetailsItem = new PopupMenu.PopupSubMenuMenuItem(_("Details"), "dialog-information-symbolic");
			gitHubRepoDetailsItem.menu.addMenuItem(new PopupMenu.PopupMenuItem(_('Watchers: ' + repos[i].watchers_count), { reactive: false }));

			// Details : Open Issues
			let open_issues_count = repos[i].open_issues_count;
			let issuesIcon = open_issues_count == '0' ? "dialog-information" : "dialog-warning-symbolic";
			let openIssuesCountItem = this._createPopupImageMenuItem(_('Open Issues: ' + open_issues_count), issuesIcon, function() {
					this._openUrl("https://github.com/"+this.gh.username+"/"+name+"/issues");
			}, { reactive: true });
			gitHubRepoDetailsItem.menu.addMenuItem(openIssuesCountItem);

			// Details : Forks
			let forks = repos[i].forks;
			let forksItem = this._createPopupImageMenuItem(_('Forks: ' + forks), "preferences-system-network-proxy-symbolic", function() {
					this._openUrl("https://github.com/"+this.gh.username+"/"+name+"/network");
			}, { reactive: true });
			gitHubRepoDetailsItem.menu.addMenuItem(forksItem);

			// Add Details
			gitHubRepoMenuItem.menu.addMenuItem(gitHubRepoDetailsItem);

		    this.menu.addMenuItem(gitHubRepoMenuItem);
		}
	},

	_createPopupImageMenuItem: function(title, icon, bindFunction, options){
		let options = options || {};
		let openRepoItem = new PopupMenu.PopupImageMenuItem(title, icon, options);
		openRepoItem.connect("activate", Lang.bind(this, bindFunction));
		return openRepoItem;
	},

	_openUrl: function(url) {
    	Util.spawnCommandLine("xdg-open " + url);
	},

	_addDefaultMenuItems: function() {
		this._addOpenGitHubMenuItem();
	},

	_addOpenGitHubMenuItem: function(){
		this.numMenuItem = this._createPopupImageMenuItem(_('Open GitHub Home'), "", function() {
				this._openUrl("https://github.com/"+this.gh.username);
		}, { reactive: true });

	    this.menu.addMenuItem(this.numMenuItem);
		this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
	},

	_killPeriodicTimer: function(){
		if (this._reloadGitHubFeedTimerId) {
			Mainloop.source_remove(this._reloadGitHubFeedTimerId);
		}
	},

	_triggerGitHubLookup: function() {
		if(this._shouldDisplayLookupNotification){
			this._displayNotification(NotificationMessages['AttemptingToLoad']);
		}
		this.gh.loadDataFeed();
	},

	_startGitHubLookupTimer: function() {
		this._killPeriodicTimer();

		this._triggerGitHubLookup();

		let timeout_in_minutes = this.gh.hasExceededApiLimit()
									? this.gh.minutesUntilNextRefreshWindow()
									: this.settings.getValue("refresh-interval")

		this.logger.debug("Time in minutes until next API request [" + timeout_in_minutes + "]");

		let timeout_in_seconds = timeout_in_minutes * 60 * 1000;

		if (this.settings.getValue("enable-auto-refresh")) {
			this._reloadGitHubFeedTimerId = Mainloop.timeout_add(timeout_in_seconds, Lang.bind(this, this._startGitHubLookupTimer));
		}
	},
};
<|endoftext|>


<commit_before>/* See license.txt for terms of usage */

define([
    "firebug/firebug",
    "firebug/chrome/rep",
    "firebug/lib/trace",
    "firebug/lib/object",
    "firebug/lib/domplate",
    "firebug/console/errorMessageObj",
    "firebug/console/errorMessageRep",
    "firebug/console/errorCopy",
    "firebug/chrome/reps",
    "firebug/debugger/stack/stackFrame",
    "firebug/debugger/stack/stackTrace",
],
function(Firebug, Rep, FBTrace, Obj, Domplate, ErrorMessageObj, ErrorMessage, ErrorCopy,
    FirebugReps, StackFrame, StackTrace) {

"use strict"

// ********************************************************************************************* //
// Constants

const Cc = Components.classes;
const Ci = Components.interfaces;

var {domplate, TAG} = Domplate;

// ********************************************************************************************* //
// Exception Template Implementation

/**
 * @domplate This template represents exceptions that happen in the content and appear
 * within Firebug UI. It's registered as Firebug rep.
 */
var Exception = domplate(Rep,
/** @lends Exception */
{
    tag:
        TAG(ErrorMessage.tag, {object: "$object|getErrorMessage"}),

    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

    className: "exception",

    getTitle: function(object)
    {
        if (object.name)
            return object.name + (object.message ? ": " + object.message : "");

        if (object.message)
            return object.message;

        return "Exception";
    },

    getErrorMessage: function(object)
    {
        var context = Firebug.currentContext;
        var win = context ? context.window : null;

        var url = object.fileName ? object.fileName : (win ? win.location.href : "");
        var lineNo = object.lineNumber ? object.lineNumber : 0;
        var message = this.getTitle(object);

        var trace;
        if (object.stack)
        {
            trace = StackTrace.parseToStackTrace(object.stack, context);
            trace = StackFrame.cleanStackTraceOfFirebug(trace);

            if (!trace)
                lineNo = 0;
        }

        var errorObject = new ErrorMessageObj(message, url, lineNo, "", "js",
            context, trace);

        if (trace && trace.frames && trace.frames[0])
            errorObject.correctWithStackTrace(trace);

        errorObject.resetSource();
        return errorObject;
    },

    supportsObject: function(object, type)
    {
        return (object instanceof ErrorCopy) || Obj.XW_instanceof(object, Error);
    }
});

// ********************************************************************************************* //
// Registration

// xxxHonza: which one is needed for back compatibility
FirebugReps.ExceptionRep = Exception;
FirebugReps.Except = Exception;

Firebug.registerRep(Exception);

return Exception;

// ********************************************************************************************* //
});
<commit_msg>Display the source of the error (makes FBTest 6291 work)<commit_after>/* See license.txt for terms of usage */

define([
    "firebug/firebug",
    "firebug/chrome/rep",
    "firebug/lib/trace",
    "firebug/lib/object",
    "firebug/lib/domplate",
    "firebug/console/errorMessageObj",
    "firebug/console/errorMessageRep",
    "firebug/console/errorCopy",
    "firebug/chrome/reps",
    "firebug/debugger/stack/stackFrame",
    "firebug/debugger/stack/stackTrace",
],
function(Firebug, Rep, FBTrace, Obj, Domplate, ErrorMessageObj, ErrorMessage, ErrorCopy,
    FirebugReps, StackFrame, StackTrace) {

"use strict"

// ********************************************************************************************* //
// Constants

const Cc = Components.classes;
const Ci = Components.interfaces;

var {domplate, TAG} = Domplate;

// ********************************************************************************************* //
// Exception Template Implementation

/**
 * @domplate This template represents exceptions that happen in the content and appear
 * within Firebug UI. It's registered as Firebug rep.
 */
var Exception = domplate(Rep,
/** @lends Exception */
{
    tag:
        TAG(ErrorMessage.tag, {object: "$object|getErrorMessage"}),

    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

    className: "exception",

    getTitle: function(object)
    {
        if (object.name)
            return object.name + (object.message ? ": " + object.message : "");

        if (object.message)
            return object.message;

        return "Exception";
    },

    getErrorMessage: function(object)
    {
        var context = Firebug.currentContext;
        var win = context ? context.window : null;

        var url = object.fileName ? object.fileName : (win ? win.location.href : "");
        var lineNo = object.lineNumber ? object.lineNumber : 0;
        var message = this.getTitle(object);
        var source = object.source || "";

        var trace;
        if (object.stack)
        {
            trace = StackTrace.parseToStackTrace(object.stack, context);
            trace = StackFrame.cleanStackTraceOfFirebug(trace);

            if (!trace)
                lineNo = 0;
        }

        var errorObject = new ErrorMessageObj(message, url, lineNo, source, "js",
            context, trace);

        if (trace && trace.frames && trace.frames[0])
            errorObject.correctWithStackTrace(trace);

        errorObject.resetSource();
        return errorObject;
    },

    supportsObject: function(object, type)
    {
        return (object instanceof ErrorCopy) || Obj.XW_instanceof(object, Error);
    }
});

// ********************************************************************************************* //
// Registration

// xxxHonza: which one is needed for back compatibility
FirebugReps.ExceptionRep = Exception;
FirebugReps.Except = Exception;

Firebug.registerRep(Exception);

return Exception;

// ********************************************************************************************* //
});
<|endoftext|>