<commit_before>'use strict';

angular.module('talis.services.echo', []).factory("echo",[
    "$rootScope",
    "$http",
    "applicationLoggingService",
    "ECHO_ENDPOINT",
    "APP_ID",
    "BROWSER_NAME",
    "BROWSER_VERSION",
    "BROWSER_COMPAT_MODE", function($rootScope, $http, applicationLoggingService,ECHO_ENDPOINT,APP_ID,BROWSER_NAME,BROWSER_VERSION,BROWSER_COMPAT_MODE) {
        var instance = function() {};
        var MAX_FLUSH_EVENTS = 10;
        var DEBOUNCE_INTERVAL = 2500;
        var MAX_INTERVAL = 5000;
        var BACK_OFF_FACTOR = 1.5;

        instance._instantFlush = function() {
            var ctx = this,
                _events = [];

            // pluck off up to MAX_FLUSH EVENTS
            if (this._events.length>MAX_FLUSH_EVENTS) {
                _events = _.first(this._events,MAX_FLUSH_EVENTS);
                this._events = _.rest(this._events,MAX_FLUSH_EVENTS);
                applicationLoggingService.debug("Only flushing "+MAX_FLUSH_EVENTS+" at a time - re-queued "+this._events.length+" events");
                this._flush();
            } else {
                _events = this._events;
                this._events = [];
            }

            if (_events.length>0) {
                $rootScope.$apply(function() {
                    $http.post(ECHO_ENDPOINT+'/1/events',_events, {"headers":{"Content-Type":"application/json"}}).success(function(response) {
                        applicationLoggingService.debug("Successfully flushed "+_events.length+" events to backend");
                        // ok!
                    }).error(function(response) {

                            // back off debounce and interval
                            DEBOUNCE_INTERVAL = Math.floor(DEBOUNCE_INTERVAL*BACK_OFF_FACTOR);
                            MAX_INTERVAL = Math.floor(MAX_INTERVAL*BACK_OFF_FACTOR);
                            instance._flush = _.debounce(instance._instantFlush,DEBOUNCE_INTERVAL,{'maxWait':MAX_INTERVAL});

                            applicationLoggingService.error("Error flushing "+_events.length+" events to backend, backing off to "+DEBOUNCE_INTERVAL+"/"+MAX_INTERVAL+", re-queueing...");

                            // have another crack
                            _events.forEach(function(event) {
                                ctx._events.push(event);
                            });
                            ctx._flush();
                        });
                });
            } else {
                applicationLoggingService.debug("No events to flush");
            }
        };

        instance._events = [];
        instance._flush = _.debounce(instance._instantFlush,DEBOUNCE_INTERVAL,{'maxWait':MAX_INTERVAL}); // debounce within 5s, wait no more than 10...

        instance.add = function(className,props) {
            if (_.isEmpty(className)) {
                applicationLoggingService.error("No key supplied to events.add");
                return;
            } else if (!_.isString(className)) {
                applicationLoggingService.error("key supplied to events.add is not a string: "+JSON.stringify(className));
                return;
            } else if (props!=null && !_.isObject(props)) {
                applicationLoggingService.error("props supplied to events.add should be null or object")
                return;
            }

            var event = {
                "class": className,
                source: APP_ID,
                timestamp: Math.floor(new Date().getTime() / 1000) // seconds since epoch
            };
            event['props'] = props || {};
            event.props.browser_name = BROWSER_NAME;
            event.props.browser_version = BROWSER_VERSION;
            event.props.browser_compat_mode = BROWSER_COMPAT_MODE;
            if ($rootScope.user != null && $rootScope.user.guid != null) event['user'] = $rootScope.user.guid;

            this._events.push(event);
            this._flush();
        };

        return instance;
    }]);<commit_msg>Commented out $rootScope.$apply<commit_after>'use strict';

angular.module('talis.services.echo', []).factory("echo",[
    "$rootScope",
    "$http",
    "applicationLoggingService",
    "ECHO_ENDPOINT",
    "APP_ID",
    "BROWSER_NAME",
    "BROWSER_VERSION",
    "BROWSER_COMPAT_MODE", function($rootScope, $http, applicationLoggingService,ECHO_ENDPOINT,APP_ID,BROWSER_NAME,BROWSER_VERSION,BROWSER_COMPAT_MODE) {
        var instance = function() {};
        var MAX_FLUSH_EVENTS = 10;
        var DEBOUNCE_INTERVAL = 2500;
        var MAX_INTERVAL = 5000;
        var BACK_OFF_FACTOR = 1.5;

        instance._instantFlush = function() {
            var ctx = this,
                _events = [];

            // pluck off up to MAX_FLUSH EVENTS
            if (this._events.length>MAX_FLUSH_EVENTS) {
                _events = _.first(this._events,MAX_FLUSH_EVENTS);
                this._events = _.rest(this._events,MAX_FLUSH_EVENTS);
                applicationLoggingService.debug("Only flushing "+MAX_FLUSH_EVENTS+" at a time - re-queued "+this._events.length+" events");
                this._flush();
            } else {
                _events = this._events;
                this._events = [];
            }

            if (_events.length>0) {
                //$rootScope.$apply(function() {
                    $http.post(ECHO_ENDPOINT+'/1/events',_events, {"headers":{"Content-Type":"application/json"}}).success(function(response) {
                        applicationLoggingService.debug("Successfully flushed "+_events.length+" events to backend");
                        // ok!
                    }).error(function(response) {

                            // back off debounce and interval
                            DEBOUNCE_INTERVAL = Math.floor(DEBOUNCE_INTERVAL*BACK_OFF_FACTOR);
                            MAX_INTERVAL = Math.floor(MAX_INTERVAL*BACK_OFF_FACTOR);
                            instance._flush = _.debounce(instance._instantFlush,DEBOUNCE_INTERVAL,{'maxWait':MAX_INTERVAL});

                            applicationLoggingService.error("Error flushing "+_events.length+" events to backend, backing off to "+DEBOUNCE_INTERVAL+"/"+MAX_INTERVAL+", re-queueing...");

                            // have another crack
                            _events.forEach(function(event) {
                                ctx._events.push(event);
                            });
                            ctx._flush();
                        });
                //});
            } else {
                applicationLoggingService.debug("No events to flush");
            }
        };

        instance._events = [];
        instance._flush = _.debounce(instance._instantFlush,DEBOUNCE_INTERVAL,{'maxWait':MAX_INTERVAL}); // debounce within 5s, wait no more than 10...

        instance.add = function(className,props) {
            if (_.isEmpty(className)) {
                applicationLoggingService.error("No key supplied to events.add");
                return;
            } else if (!_.isString(className)) {
                applicationLoggingService.error("key supplied to events.add is not a string: "+JSON.stringify(className));
                return;
            } else if (props!=null && !_.isObject(props)) {
                applicationLoggingService.error("props supplied to events.add should be null or object")
                return;
            }

            var event = {
                "class": className,
                source: APP_ID,
                timestamp: Math.floor(new Date().getTime() / 1000) // seconds since epoch
            };
            event['props'] = props || {};
            event.props.browser_name = BROWSER_NAME;
            event.props.browser_version = BROWSER_VERSION;
            event.props.browser_compat_mode = BROWSER_COMPAT_MODE;
            if ($rootScope.user != null && $rootScope.user.guid != null) event['user'] = $rootScope.user.guid;

            this._events.push(event);
            this._flush();
        };

        return instance;
    }]);
<|endoftext|>


<commit_before>//
// @Title = feed-the-burn.js
// @Author = Chris King
// @Description = The main JS file for feed-the-burn a soundcloud app for finding mixes for your workout.
//
//

//Add Tracks to Tracklist:
function AddTracksToTracklist(tracks, list){
    $(tracks).each(function(track) {
        //console.log(this);
        list.append("
 "+ this.user.username + " - " + this.title + "
");
    });
}

function CreateTrackRow(track){
    var row_string = "";
    row_string += (
        "
" + track.user.username +
        "	" + track.title + "	" +
        track.bpm + "	" + track.genre +
        "	" + track.duration + "	"
    )
    if (track.downloadable === true) {
        row_string += "Yes";
    }
    row_string += "
";
    return row_string;
}

//Add Tracks to Table:
function AddTracksToTable(tracks, table){
    $(tracks).each(function(track) {
        //console.log(this);
        table.append(CreateTrackRow(this));
    });
}

//Get state info of form
function GetStateOfForm(form){
    var form_state = {};
    console.log($('#BPM').children('select.list option:selected'));
    return form_state;
}

//Setup
$(document).ready(function() {
    //Initialize the connection to soundcloud
    SC.initialize({
        client_id: 'e30e2c947a1035c10c66b6ab5780d629'
    });

    //Listen to submit button
    $('#submit').click(function() {
        console.log(GetStateOfForm($('filterForm')));
        alert("Handler for .click() called.");
    });
    // BPM
    // Find all tracks with a bpm of 120.
    // Log them to the console, then add them to a list
    //
    SC.get('/tracks', {
        bpm: {
            from: 120
        }
    }, function(tracks) {
        //console.log(tracks);
        //AddTracksToTable(tracks, $('#resultsTable > tbody'));
    });

    // Duration
    // Find all tacks with a duration of 30 - 40 minutes
    // Then Add them to a list
    //
    var thirty_minutes_in_milis = 1800000;
    var fourty_minutes_in_milis = 2400000;
    SC.get('/tracks', {
        duration: {
            from: thirty_minutes_in_milis,
            to: fourty_minutes_in_milis
        },
        bpm: {
            from: 120
        }
    }, function(tracks) {
        //console.log(tracks);
        AddTracksToTable(tracks, $('#resultsTable > tbody'));
    });

    // Genre
    // Find all tracks with a genre of "dubstep"
    //
    SC.get('/tracks', {
        genre: "dubstep"
    }, function(tracks) {
        //console.log(tracks);
        AddTracksToTracklist(tracks, $('#GenreList'));
    });
});<commit_msg>js can handle basic form processing of 140bpm, it is a bit messy though<commit_after>//
// @Title = feed-the-burn.js
// @Author = Chris King
// @Description = The main JS file for feed-the-burn a soundcloud app for finding mixes for your workout.
//
//

//Add Tracks to Tracklist:
function AddTracksToTracklist(tracks, list){
    $(tracks).each(function(track) {
        //console.log(this);
        list.append("
 "+ this.user.username + " - " + this.title + "
");
    });
}

function CreateTrackRow(track){
    var row_string = "";
    row_string += (
        "
" + track.user.username +
        "	" + track.title + "	" +
        track.bpm + "	" + track.genre +
        "	" + track.duration + "	"
    )
    if (track.downloadable === true) {
        row_string += "Yes";
    }
    row_string += "
";
    return row_string;
}

//Add Tracks to Table:
function AddTracksToTable(tracks, table){
    $(tracks).each(function(track) {
        //console.log(this);
        table.append(CreateTrackRow(this));
    });
}

//Get state info of form
function GetStateOfForm(form){
    var form_state = {};
    var bpm = $('#BPM :selected').val();
    if (parseInt(bpm) === 140){
        console.log("BPM is 140");
        form_state['bpm'] = {
            from: 140,
            to: 144
        }
    }
    return form_state;
}

//Setup
$(document).ready(function() {
    //Initialize the connection to soundcloud
    SC.initialize({
        client_id: 'e30e2c947a1035c10c66b6ab5780d629'
    });

    //Listen to submit button
    $('#submit').click(function() {
        var query = GetStateOfForm($('filterForm'));
        SC.get('/tracks', query , function(tracks) {
            console.log(tracks);
            AddTracksToTable(tracks, $('#resultsTable > tbody'));
        });
    });
    // BPM
    // Find all tracks with a bpm of 120.
    // Log them to the console, then add them to a list
    //
    SC.get('/tracks', {
        bpm: {
            from: 120
        }
    }, function(tracks) {
        //console.log(tracks);
        //AddTracksToTable(tracks, $('#resultsTable > tbody'));
    });

    // Duration
    // Find all tacks with a duration of 30 - 40 minutes
    // Then Add them to a list
    //
    var thirty_minutes_in_milis = 1800000;
    var fourty_minutes_in_milis = 2400000;
    SC.get('/tracks', {
        duration: {
            from: thirty_minutes_in_milis,
            to: fourty_minutes_in_milis
        },
        bpm: {
            from: 120
        }
    }, function(tracks) {
        //console.log(tracks);
        //AddTracksToTable(tracks, $('#resultsTable > tbody'));
    });

    // Genre
    // Find all tracks with a genre of "dubstep"
    //
    SC.get('/tracks', {
        genre: "dubstep"
    }, function(tracks) {
        //console.log(tracks);
        //AddTracksToTracklist(tracks, $('#GenreList'));
    });
});<|endoftext|>


<commit_before>// Grouping module
;(function($){
$.jgrid.extend({
	groupingSetup : function () {
		return this.each(function (){
			var $t = this,
			grp = $t.p.groupingView;
			if(grp !== null && ( (typeof grp === 'object') || $.isFunction(grp) ) ) {
				if(!grp.groupField.length) {
					$t.p.grouping = false;
        } else {
          if ( typeof(grp.visibiltyOnNextGrouping) == 'undefined') {
            grp.visibiltyOnNextGrouping = [];
          }
					for(var i=0;i<grp.groupField.length;i++) {
						if(!grp.groupOrder[i]) {
							grp.groupOrder[i] = 'asc';
						}
						if(!grp.groupText[i]) {
							grp.groupText[i] = '{0}';
						}
						if( typeof(grp.groupColumnShow[i]) != 'boolean') {
							grp.groupColumnShow[i] = true;
						}
						if( typeof(grp.groupSummary[i]) != 'boolean') {
							grp.groupSummary[i] = false;
						}
						if(grp.groupColumnShow[i] === true) {
              grp.visibiltyOnNextGrouping[i] = true;
							$($t).jqGrid('showCol',grp.groupField[i]);
						} else {
              grp.visibiltyOnNextGrouping[i] = $("#"+$t.p.id+"_"+grp.groupField[i]).is(":visible");
							$($t).jqGrid('hideCol',grp.groupField[i]);
						}
						grp.sortitems[i] = [];
						grp.sortnames[i] = [];
						grp.summaryval[i] = [];
						if(grp.groupSummary[i]) {
							grp.summary[i] =[];
							var cm = $t.p.colModel;
							for(var j=0, cml = cm.length; j < cml; j++) {
								if(cm[j].summaryType) {
									grp.summary[i].push({nm:cm[j].name,st:cm[j].summaryType, v:''});
								}
							}
						}
					}
					$t.p.scroll = false;
					$t.p.rownumbers = false;
					$t.p.subGrid = false;
					$t.p.treeGrid = false;
					$t.p.gridview = true;
				}
			} else {
				$t.p.grouping = false;
			}
		});
	},
	groupingPrepare : function (rData, items, gdata, record) {
		this.each(function(){
			// currently only one level
			// Is this a good idea to do it so!!!!?????
			items[0]  += "";
			var itm = items[0].toString().split(' ').join('');

			var grp = this.p.groupingView, $t= this;
			if(gdata.hasOwnProperty(itm)) {
				gdata[itm].push(rData);
			} else {
				gdata[itm] = [];
				gdata[itm].push(rData);
				grp.sortitems[0].push(itm);
				grp.sortnames[0].push($.trim(items[0].toString()));
				grp.summaryval[0][itm] = $.extend(true,[],grp.summary[0]);
			}
			if(grp.groupSummary[0]) {
				$.each(grp.summaryval[0][itm],function(i,n) {
					if ($.isFunction(this.st)) {
						this.v = this.st.call($t, this.v, this.nm, record);
					} else {
						this.v = $($t).jqGrid('groupingCalculations.'+this.st, this.v, this.nm, record);
					}
				});
			}
		});
		return gdata;
	},
	groupingToggle : function(hid){
		this.each(function(){
			var $t = this,
			grp = $t.p.groupingView,
			strpos = hid.lastIndexOf('_'),
			uid = hid.substring(0,strpos+1),
			num = parseInt(hid.substring(strpos+1),10)+1,
			minus = grp.minusicon,
			plus = grp.plusicon,
			tar = $("#"+hid),
			r = tar[0].nextSibling,
			tarspan = $("#"+hid+" span."+"tree-wrap-"+$t.p.direction),
			collapsed = false;
			if( tarspan.hasClass(minus) ) {
				if(grp.showSummaryOnHide && grp.groupSummary[0]) {
					if(r){
						while(r) {
							if($(r).hasClass('jqfoot') ) { break; }
							$(r).hide();
							r = r.nextSibling;
						}
					}
				} else  {
					if(r){
						while(r) {
							if($(r).attr('id') ==uid+String(num) ) { break; }
							$(r).hide();
							r = r.nextSibling;
				}
					}
				}
				tarspan.removeClass(minus).addClass(plus);
				collapsed = true;
			} else {
				if(r){
					while(r) {
						if($(r).attr('id') ==uid+String(num) ) { break; }
						$(r).show();
						r = r.nextSibling;
					}
				}
				tarspan.removeClass(plus).addClass(minus);
				collapsed = false;
			}
			if( $.isFunction($t.p.onClickGroup)) { $t.p.onClickGroup.call($t, hid , collapsed); }

		});
		return false;
	},
	groupingRender : function (grdata, colspans ) {
		return this.each(function(){
			var $t = this,
			grp = $t.p.groupingView,
			str = "", icon = "", hid, pmrtl ="", gv, cp, ii;
			//only one level for now
			if(!grp.groupDataSorted) {
				// ???? TO BE IMPROVED
				grp.sortitems[0].sort();
				grp.sortnames[0].sort();
				if(grp.groupOrder[0].toLowerCase() == 'desc')
				{
					grp.sortitems[0].reverse();
					grp.sortnames[0].reverse();
				}
			}
			if(grp.groupCollapse) { pmrtl = grp.plusicon; }
			else {pmrtl = grp.minusicon;}
			pmrtl += " tree-wrap-"+$t.p.direction;
			ii = 0;
			while(ii < colspans) {
				if($t.p.colModel[ii].name == grp.groupField[0]) {
					cp = ii;
					break;
				}
				ii++;
			}
			$.each(grp.sortitems[0],function(i,n){
				hid = $t.p.id+"ghead_"+i;
				icon = "";
				try {
					gv = $t.formatter(hid, grp.sortnames[0][i], cp, grp.sortitems[0] );
				} catch (egv) {
					gv = grp.sortnames[0][i];
				}
				str += "
"+icon+$.jgrid.format(grp.groupText[0], gv, grdata[n].length)+"
";
				for(var kk=0;kk<grdata[n].length;kk++) {
					str += grdata[n][kk].join('');
				}
				if(grp.groupSummary[0]) {
					var hhdr = "";
					if(grp.groupCollapse && !grp.showSummaryOnHide) {
						hhdr = " style=\"display:none;\"";
					}
					str += "
";
					var fdata = grp.summaryval[0][n],
					cm = $t.p.colModel,
					vv, grlen = grdata[n].length;
					for(var k=0; k<colspans;k++) {
						var tmpdata = " 	",
						tplfld = "{0}";
						$.each(fdata,function(){
							if(this.nm == cm[k].name) {
								if(cm[k].summaryTpl)  {
									tplfld = cm[k].summaryTpl;
								}
								if(this.st == 'avg') {
									if(this.v && grlen > 0) {
										this.v = (this.v/grlen);
									}
								}
								try {
									vv = $t.formatter('', this.v, k, this);
								} catch (ef) {
									vv = this.v;
								}
								tmpdata= ""+$.jgrid.format(tplfld,vv)+ "	";
								return false;
							}
						});
						str += tmpdata;
					}
					str += "
";
				}
			});
			$("#"+$t.p.id+" tbody:first").append(str);
			// free up memory
			str = null;
		});
	},
	groupingGroupBy : function (name, options, current) {
		return this.each(function(){
			var $t = this;
			if(typeof(name) == "string") {
				name = [name];
			}
			var grp = $t.p.groupingView;
			$t.p.grouping = true;
      // show previous hidden groups if they are hidden and weren't removed yet
      for(var i=0;i<grp.groupField.length;i++) {
        if(!grp.groupColumnShow[i] && grp.visibiltyOnNextGrouping[i]) {
          $($t).jqGrid('showCol',grp.groupField[i]);
        }
      }
      // set visibility status of current group columns on next grouping
      for(var i=0;i<name.length;i++) {
        grp.visibiltyOnNextGrouping[i] = $("#"+$t.p.id+"_"+name[i]).is(":visible");
      }
			$t.p.groupingView = $.extend($t.p.groupingView, options || {});
			grp.groupField = name;
			$($t).trigger("reloadGrid");
		});
	},
	groupingRemove : function (current) {
		return this.each(function(){
			var $t = this;
			if(typeof(current) == 'undefined') {
				current = true;
			}
			$t.p.grouping = false;
			if(current===true) {
        var grp = $t.p.groupingView;
        // show previous hidden groups if they are hidden and weren't removed yet
        for(var i=0;i<grp.groupField.length;i++) {
          if (!grp.groupColumnShow[i] && grp.visibiltyOnNextGrouping[i]) {
            $($t).jqGrid('showCol', grp.groupField);
          }
        }
				$("tr.jqgroup, tr.jqfoot","#"+$t.p.id+" tbody:first").remove();
				$("tr.jqgrow:hidden","#"+$t.p.id+" tbody:first").show();
			} else {
				$($t).trigger("reloadGrid");
			}
		});
	},
	groupingCalculations : {
		"sum" : function(v, field, rc) {
			return parseFloat(v||0) + parseFloat((rc[field]||0));
		},
		"min" : function(v, field, rc) {
			if(v==="") {
				return parseFloat(rc[field]||0);
			}
			return Math.min(parseFloat(v),parseFloat(rc[field]||0));
		},
		"max" : function(v, field, rc) {
			if(v==="") {
				return parseFloat(rc[field]||0);
			}
			return Math.max(parseFloat(v),parseFloat(rc[field]||0));
		},
		"count" : function(v, field, rc) {
			if(v==="") {v=0;}
			if(rc.hasOwnProperty(field)) {
				return v+1;
			} else {
				return 0;
			}
		},
		"avg" : function(v, field, rc) {
			// the same as sum, but at end we divide it
			return parseFloat(v||0) + parseFloat((rc[field]||0));
		}
	}
});
})(jQuery);
<commit_msg>Formatting changes<commit_after>// Grouping module
;(function($){
$.jgrid.extend({
	groupingSetup : function () {
		return this.each(function (){
			var $t = this,
			grp = $t.p.groupingView;
			if(grp !== null && ( (typeof grp === 'object') || $.isFunction(grp) ) ) {
				if(!grp.groupField.length) {
					$t.p.grouping = false;
				} else {
					if ( typeof(grp.visibiltyOnNextGrouping) == 'undefined') {
						grp.visibiltyOnNextGrouping = [];
					}
					for(var i=0;i<grp.groupField.length;i++) {
						if(!grp.groupOrder[i]) {
							grp.groupOrder[i] = 'asc';
						}
						if(!grp.groupText[i]) {
							grp.groupText[i] = '{0}';
						}
						if( typeof(grp.groupColumnShow[i]) != 'boolean') {
							grp.groupColumnShow[i] = true;
						}
						if( typeof(grp.groupSummary[i]) != 'boolean') {
							grp.groupSummary[i] = false;
						}
						if(grp.groupColumnShow[i] === true) {
							grp.visibiltyOnNextGrouping[i] = true;
							$($t).jqGrid('showCol',grp.groupField[i]);
						} else {
							grp.visibiltyOnNextGrouping[i] = $("#"+$t.p.id+"_"+grp.groupField[i]).is(":visible");
							$($t).jqGrid('hideCol',grp.groupField[i]);
						}
						grp.sortitems[i] = [];
						grp.sortnames[i] = [];
						grp.summaryval[i] = [];
						if(grp.groupSummary[i]) {
							grp.summary[i] =[];
							var cm = $t.p.colModel;
							for(var j=0, cml = cm.length; j < cml; j++) {
								if(cm[j].summaryType) {
									grp.summary[i].push({nm:cm[j].name,st:cm[j].summaryType, v:''});
								}
							}
						}
					}
					$t.p.scroll = false;
					$t.p.rownumbers = false;
					$t.p.subGrid = false;
					$t.p.treeGrid = false;
					$t.p.gridview = true;
				}
			} else {
				$t.p.grouping = false;
			}
		});
	},
	groupingPrepare : function (rData, items, gdata, record) {
		this.each(function(){
			// currently only one level
			// Is this a good idea to do it so!!!!?????
			items[0]  += "";
			var itm = items[0].toString().split(' ').join('');

			var grp = this.p.groupingView, $t= this;
			if(gdata.hasOwnProperty(itm)) {
				gdata[itm].push(rData);
			} else {
				gdata[itm] = [];
				gdata[itm].push(rData);
				grp.sortitems[0].push(itm);
				grp.sortnames[0].push($.trim(items[0].toString()));
				grp.summaryval[0][itm] = $.extend(true,[],grp.summary[0]);
			}
			if(grp.groupSummary[0]) {
				$.each(grp.summaryval[0][itm],function(i,n) {
					if ($.isFunction(this.st)) {
						this.v = this.st.call($t, this.v, this.nm, record);
					} else {
						this.v = $($t).jqGrid('groupingCalculations.'+this.st, this.v, this.nm, record);
					}
				});
			}
		});
		return gdata;
	},
	groupingToggle : function(hid){
		this.each(function(){
			var $t = this,
			grp = $t.p.groupingView,
			strpos = hid.lastIndexOf('_'),
			uid = hid.substring(0,strpos+1),
			num = parseInt(hid.substring(strpos+1),10)+1,
			minus = grp.minusicon,
			plus = grp.plusicon,
			tar = $("#"+hid),
			r = tar[0].nextSibling,
			tarspan = $("#"+hid+" span."+"tree-wrap-"+$t.p.direction),
			collapsed = false;
			if( tarspan.hasClass(minus) ) {
				if(grp.showSummaryOnHide && grp.groupSummary[0]) {
					if(r){
						while(r) {
							if($(r).hasClass('jqfoot') ) { break; }
							$(r).hide();
							r = r.nextSibling;
						}
					}
				} else  {
					if(r){
						while(r) {
							if($(r).attr('id') ==uid+String(num) ) { break; }
							$(r).hide();
							r = r.nextSibling;
				}
					}
				}
				tarspan.removeClass(minus).addClass(plus);
				collapsed = true;
			} else {
				if(r){
					while(r) {
						if($(r).attr('id') ==uid+String(num) ) { break; }
						$(r).show();
						r = r.nextSibling;
					}
				}
				tarspan.removeClass(plus).addClass(minus);
				collapsed = false;
			}
			if( $.isFunction($t.p.onClickGroup)) { $t.p.onClickGroup.call($t, hid , collapsed); }

		});
		return false;
	},
	groupingRender : function (grdata, colspans ) {
		return this.each(function(){
			var $t = this,
			grp = $t.p.groupingView,
			str = "", icon = "", hid, pmrtl ="", gv, cp, ii;
			//only one level for now
			if(!grp.groupDataSorted) {
				// ???? TO BE IMPROVED
				grp.sortitems[0].sort();
				grp.sortnames[0].sort();
				if(grp.groupOrder[0].toLowerCase() == 'desc')
				{
					grp.sortitems[0].reverse();
					grp.sortnames[0].reverse();
				}
			}
			if(grp.groupCollapse) { pmrtl = grp.plusicon; }
			else {pmrtl = grp.minusicon;}
			pmrtl += " tree-wrap-"+$t.p.direction;
			ii = 0;
			while(ii < colspans) {
				if($t.p.colModel[ii].name == grp.groupField[0]) {
					cp = ii;
					break;
				}
				ii++;
			}
			$.each(grp.sortitems[0],function(i,n){
				hid = $t.p.id+"ghead_"+i;
				icon = "";
				try {
					gv = $t.formatter(hid, grp.sortnames[0][i], cp, grp.sortitems[0] );
				} catch (egv) {
					gv = grp.sortnames[0][i];
				}
				str += "
"+icon+$.jgrid.format(grp.groupText[0], gv, grdata[n].length)+"
";
				for(var kk=0;kk<grdata[n].length;kk++) {
					str += grdata[n][kk].join('');
				}
				if(grp.groupSummary[0]) {
					var hhdr = "";
					if(grp.groupCollapse && !grp.showSummaryOnHide) {
						hhdr = " style=\"display:none;\"";
					}
					str += "
";
					var fdata = grp.summaryval[0][n],
					cm = $t.p.colModel,
					vv, grlen = grdata[n].length;
					for(var k=0; k<colspans;k++) {
						var tmpdata = " 	",
						tplfld = "{0}";
						$.each(fdata,function(){
							if(this.nm == cm[k].name) {
								if(cm[k].summaryTpl)  {
									tplfld = cm[k].summaryTpl;
								}
								if(this.st == 'avg') {
									if(this.v && grlen > 0) {
										this.v = (this.v/grlen);
									}
								}
								try {
									vv = $t.formatter('', this.v, k, this);
								} catch (ef) {
									vv = this.v;
								}
								tmpdata= ""+$.jgrid.format(tplfld,vv)+ "	";
								return false;
							}
						});
						str += tmpdata;
					}
					str += "
";
				}
			});
			$("#"+$t.p.id+" tbody:first").append(str);
			// free up memory
			str = null;
		});
	},
	groupingGroupBy : function (name, options, current) {
		return this.each(function(){
			var $t = this;
			if(typeof(name) == "string") {
				name = [name];
			}
			var grp = $t.p.groupingView;
			$t.p.grouping = true;
      // show previous hidden groups if they are hidden and weren't removed yet
      for(var i=0;i<grp.groupField.length;i++) {
        if(!grp.groupColumnShow[i] && grp.visibiltyOnNextGrouping[i]) {
          $($t).jqGrid('showCol',grp.groupField[i]);
        }
      }
      // set visibility status of current group columns on next grouping
      for(var i=0;i<name.length;i++) {
        grp.visibiltyOnNextGrouping[i] = $("#"+$t.p.id+"_"+name[i]).is(":visible");
      }
			$t.p.groupingView = $.extend($t.p.groupingView, options || {});
			grp.groupField = name;
			$($t).trigger("reloadGrid");
		});
	},
	groupingRemove : function (current) {
		return this.each(function(){
			var $t = this;
			if(typeof(current) == 'undefined') {
				current = true;
			}
			$t.p.grouping = false;
			if(current===true) {
        var grp = $t.p.groupingView;
        // show previous hidden groups if they are hidden and weren't removed yet
        for(var i=0;i<grp.groupField.length;i++) {
          if (!grp.groupColumnShow[i] && grp.visibiltyOnNextGrouping[i]) {
            $($t).jqGrid('showCol', grp.groupField);
          }
        }
				$("tr.jqgroup, tr.jqfoot","#"+$t.p.id+" tbody:first").remove();
				$("tr.jqgrow:hidden","#"+$t.p.id+" tbody:first").show();
			} else {
				$($t).trigger("reloadGrid");
			}
		});
	},
	groupingCalculations : {
		"sum" : function(v, field, rc) {
			return parseFloat(v||0) + parseFloat((rc[field]||0));
		},
		"min" : function(v, field, rc) {
			if(v==="") {
				return parseFloat(rc[field]||0);
			}
			return Math.min(parseFloat(v),parseFloat(rc[field]||0));
		},
		"max" : function(v, field, rc) {
			if(v==="") {
				return parseFloat(rc[field]||0);
			}
			return Math.max(parseFloat(v),parseFloat(rc[field]||0));
		},
		"count" : function(v, field, rc) {
			if(v==="") {v=0;}
			if(rc.hasOwnProperty(field)) {
				return v+1;
			} else {
				return 0;
			}
		},
		"avg" : function(v, field, rc) {
			// the same as sum, but at end we divide it
			return parseFloat(v||0) + parseFloat((rc[field]||0));
		}
	}
});
})(jQuery);
<|endoftext|>


<commit_before>/*
Pseudocode:
1. Create a function that takes in an array of number.
	Input: array of numbers
	Output: array of number
	Steps:
		1. Define a funtion that will take an array as its input.
			IF the input is not an array of numbers
				- Raise an error that will tell the user that the input is invalid
		2. Return the array of numbers.
2. Create a function that will sum all the numbers of an array
	Input: array of numbers
	Output: sum of the numbers in the array
	Steps:
		1. Create a variable of the running sum and set it to 0
		2. Iterate through each element of the array
		3. Add each element of the array to the running sum
		4. Return the running sum.
3. Create a function that will give me the mean of numbers in an array
	Input: array of numbers
	Output: (mean) -> sum of all the numbers in array divided by the number of elements.
	Steps:
		1. Create a variable of the running sum and set it to 0
		2. Iterate through each element of the array
		3. Add each element of the array to the running sum
		4. Divide the running sum by the length of the array
		5. Return the result
4. Create a function that will return the median
	Input: array of numbers
	Output: the median of the sorted array
	Steps:
		1. Sort the array in order from least to greatest
		2. Find the length of the list and divide by 2, store result in a variable
		3. IF length is odd
			return list[length/2]
		   ELSE
		   	return (list[length/2] + list[(length/2)-1]) / 2
*/<commit_msg>Add completed group portion<commit_after>/*
Pseudocode:
1. Create a function that takes in an array of number.
	Input: array of numbers
	Output: array of number
	Steps:
		1. Define a funtion that will take an array as its input.
			IF the input is not an array of numbers
				- Raise an error that will tell the user that the input is invalid
		2. Return the array of numbers.
2. Create a function that will sum all the numbers of an array
	Input: array of numbers
	Output: sum of the numbers in the array
	Steps:
		1. Create a variable of the running sum and set it to 0
		2. Iterate through each element of the array
		3. Add each element of the array to the running sum
		4. Return the running sum.
3. Create a function that will give me the mean of numbers in an array
	Input: array of numbers
	Output: (mean) -> sum of all the numbers in array divided by the number of elements.
	Steps:
		1. Create a variable of the running sum and set it to 0
		2. Iterate through each element of the array
		3. Add each element of the array to the running sum
		4. Divide the running sum by the length of the array
		5. Return the result
4. Create a function that will return the median
	Input: array of numbers
	Output: the median of the sorted array
	Steps:
		1. Sort the array in order from least to greatest
		2. Find the length of the list and divide by 2, store result in a variable
			-3. IF length is odd
				return list[length/2]
			   ELSE
			   	return (list[length/2] + list[(length/2)-1]) / 2
*/
// Function 1
function isArrayOfNum(arr) {
	if(!Array.isArray(arr)) throw new Error("Input is not an array");
	for(var i = 0; i < arr.length; i++) {
		if(isNaN(arr[i]))	throw new Error("Input is an array with at least 1 NaN value");
	}
	return arr;
}

// Function 2
function sum(arr) {
	var runSum = 0;
	for(var i = 0; i < arr.length; i++)
		runSum += arr[i];
	return runSum;
}

// Function 3
function mean(arr) {
	var runSum = 0;
	for(var i = 0; i < arr.length; i++)
		runSum += arr[i];
	return runSum / arr.length;
}

// Function 4
function median(arr) {
	sortedArr = arr.sort(function(a, b) {
		return a - b;
	});
	length = arr.length;
	if (length % 2 == 1)
		return arr[length/2];
	else
		return (arr[length/2] + arr[length/2 - 1]) / 2;
}

var meantest = mean([1,2,3,4,5,6]);
var mediantest = median([1,2,3,4,5,9]);

console.log(mediantest);<|endoftext|>


<commit_before>'use strict';

(function (angular) {
  angular.module('youtubePluginWidget')
    .controller('WidgetSingleCtrl', ['$routeParams', '$scope', 'YoutubeApi', 'DataStore', 'TAG_NAMES', 'Location', 'LAYOUTS', function ($routeParams, $scope, YoutubeApi, DataStore, TAG_NAMES, Location, LAYOUTS) {
      var currentItemDetailsBgImage = '',
        currentPlayListID = null,
        currentItemListLayout = null;

      var WidgetSingle = this;
      WidgetSingle.data = null;
      WidgetSingle.video = null;

      /*
       * Fetch user's data from datastore
       */

      var init = function () {
        var success = function (result) {
            WidgetSingle.data = result.data;
            if (!WidgetSingle.data.design)
              WidgetSingle.data.design = {};
            if (!WidgetSingle.data.design.itemListLayout) {
              WidgetSingle.data.design.itemListLayout = LAYOUTS.listLayouts[0].name;
            }
            currentItemListLayout = WidgetSingle.data.design.itemListLayout;
            currentPlayListID = WidgetSingle.data.content.playListID;
          }
          , error = function (err) {
            console.error('Error while getting data', err);
          };
        DataStore.get(TAG_NAMES.YOUTUBE_INFO).then(success, error);
      };
      init();

      var getSingleVideoDetails = function (_videoId) {
        var success = function (result) {
            WidgetSingle.video = result.items && result.items[0];
          }
          , error = function (err) {
            console.error('Error In Fetching Single Video Details', err);
          };
        YoutubeApi.getSingleVideoDetails(_videoId).then(success, error);
      };

      if ($routeParams.videoId) {
        getSingleVideoDetails($routeParams.videoId);
      } else {
        console.error('Undefined Video Id Provided');
      }

      var onUpdateCallback = function (event) {
        if (event && event.tag === TAG_NAMES.YOUTUBE_INFO) {
          WidgetSingle.data = event.data;
          if (!WidgetSingle.data.design)
            WidgetSingle.data.design = {};
          if (!WidgetSingle.data.content.rssUrl) {
            $routeParams.videoId = '';
            WidgetSingle.video = null;
          } else if (!WidgetSingle.video && WidgetSingle.data.content.videoID && !$routeParams.videoId) {
            $routeParams.videoId = WidgetSingle.data.content.videoID;
            getSingleVideoDetails(WidgetSingle.data.content.videoID);
          } else if (!WidgetSingle.video && WidgetSingle.data.content.playListID && !$routeParams.videoId) {
            currentPlayListID = WidgetSingle.data.content.playListID;
            Location.goTo("#/feed/" + WidgetSingle.data.content.playListID);
          }

          if (WidgetSingle.data.content.videoID && (WidgetSingle.data.content.videoID !== $routeParams.videoId)) {
            getSingleVideoDetails(WidgetSingle.data.content.videoID);
          } else if (WidgetSingle.data.content.playListID && (!$routeParams.videoId || (WidgetSingle.data.design.itemListLayout !== currentItemListLayout) || (WidgetSingle.data.content.playListID !== currentPlayListID))) {
            currentPlayListID = WidgetSingle.data.content.playListID;
            currentItemListLayout = WidgetSingle.data.design.itemListLayout;
            Location.goTo("#/feed/" + WidgetSingle.data.content.playListID);
          }
        }
      };
      DataStore.onUpdate().then(null, null, onUpdateCallback);

      $scope.$on("$destroy", function () {
        DataStore.clearListener();
      });
    }])
})(window.angular);
<commit_msg>Made changes for single video page<commit_after>'use strict';

(function (angular) {
  angular.module('youtubePluginWidget')
    .controller('WidgetSingleCtrl', ['$routeParams', '$scope', 'YoutubeApi', 'DataStore', 'TAG_NAMES', 'Location', 'LAYOUTS', function ($routeParams, $scope, YoutubeApi, DataStore, TAG_NAMES, Location, LAYOUTS) {
      var currentItemDetailsBgImage = '',
        currentPlayListID = null,
        currentItemListLayout = null;

      var WidgetSingle = this;
      WidgetSingle.data = null;
      WidgetSingle.video = null;

      /*
       * Fetch user's data from datastore
       */

      var init = function () {
        var success = function (result) {
            WidgetSingle.data = result.data;
            if (!WidgetSingle.data.design)
              WidgetSingle.data.design = {};
            if (!WidgetSingle.data.content)
              WidgetSingle.data.content = {};
            if (!WidgetSingle.data.design.itemListLayout) {
              WidgetSingle.data.design.itemListLayout = LAYOUTS.listLayouts[0].name;
            }
            currentItemListLayout = WidgetSingle.data.design.itemListLayout;
            currentPlayListID = WidgetSingle.data.content.playListID;
          }
          , error = function (err) {
            console.error('Error while getting data', err);
          };
        DataStore.get(TAG_NAMES.YOUTUBE_INFO).then(success, error);
      };
      init();

      var getSingleVideoDetails = function (_videoId) {
        var success = function (result) {
            WidgetSingle.video = result.items && result.items[0];
          }
          , error = function (err) {
            console.error('Error In Fetching Single Video Details', err);
          };
        YoutubeApi.getSingleVideoDetails(_videoId).then(success, error);
      };

      if ($routeParams.videoId) {
        getSingleVideoDetails($routeParams.videoId);
      } else {
        console.error('Undefined Video Id Provided');
      }

      var onUpdateCallback = function (event) {
        if (event && event.tag === TAG_NAMES.YOUTUBE_INFO) {
          WidgetSingle.data = event.data;
          if (!WidgetSingle.data.design)
            WidgetSingle.data.design = {};
          if (!WidgetSingle.data.content)
            WidgetSingle.data.content = {};
          if (!WidgetSingle.data.content.rssUrl) {
            $routeParams.videoId = '';
            WidgetSingle.video = null;
          } else if (!WidgetSingle.video && WidgetSingle.data.content.videoID && !$routeParams.videoId) {
            $routeParams.videoId = WidgetSingle.data.content.videoID;
            getSingleVideoDetails(WidgetSingle.data.content.videoID);
          } else if (!WidgetSingle.video && WidgetSingle.data.content.playListID && !$routeParams.videoId) {
            currentPlayListID = WidgetSingle.data.content.playListID;
            Location.goTo("#/feed/" + WidgetSingle.data.content.playListID);
          }

          if (WidgetSingle.data.content.videoID && (WidgetSingle.data.content.videoID !== $routeParams.videoId)) {
            getSingleVideoDetails(WidgetSingle.data.content.videoID);
          } else if (WidgetSingle.data.content.playListID && (!$routeParams.videoId || (WidgetSingle.data.design.itemListLayout !== currentItemListLayout) || (WidgetSingle.data.content.playListID !== currentPlayListID))) {
            currentPlayListID = WidgetSingle.data.content.playListID;
            currentItemListLayout = WidgetSingle.data.design.itemListLayout;
            Location.goTo("#/feed/" + WidgetSingle.data.content.playListID);
          }
        }
      };
      DataStore.onUpdate().then(null, null, onUpdateCallback);

      $scope.$on("$destroy", function () {
        DataStore.clearListener();
      });
    }])
})(window.angular);
<|endoftext|>


<commit_before>iD.Connection = function() {

    var event = d3.dispatch('auth', 'load'),
        url = 'http://www.openstreetmap.org',
        connection = {},
        refNodes = {},
        user = {},
        inflight = [],
        loadedTiles = {},
        oauth = iD.OAuth();

    function bboxUrl(b) {
        return url + '/api/0.6/map?bbox=' + [b[0][0],b[1][1],b[1][0],b[0][1]];
    }

    function bboxFromAPI(box, tile, callback) {
        function done(err, parsed) {
             loadedTiles[tile.toString()] = true;
             callback(err, parsed);
         }
         loadFromURL(bboxUrl(box), done);
    }

    function loadFromURL(url, callback) {
        function done(dom) {
            return callback(null, parse(dom));
        }
        inflight.push(d3.xml(url).get()
            .on('load', done));
    }

    function getNodes(obj) {
        var nelems = obj.getElementsByTagName('nd'), nodes = new Array(nelems.length);
        for (var i = 0, l = nelems.length; i < l; i++) {
            nodes[i] = 'n' + nelems[i].attributes.ref.nodeValue;
            refNodes['n' + nelems[i].attributes.ref.nodeValue] = true;
        }
        return nodes;
    }

    function getTags(obj) {
        var tags = {}, tagelems = obj.getElementsByTagName('tag');
        for (var i = 0, l = tagelems.length; i < l; i++) {
            var item = tagelems[i];
            tags[item.attributes.k.nodeValue] = item.attributes.v.nodeValue;
        }
        return tags;
    }

    function getMembers(obj) {
        var elems = obj.getElementsByTagName('member'),
            members = new Array(elems.length);

        for (var i = 0, l = elems.length; i < l; i++) {
            members[i] = {
                id: elems[i].attributes.type.nodeValue[0] + elems[i].attributes.ref.nodeValue,
                type: elems[i].attributes.type.nodeValue,
                role: elems[i].attributes.role.nodeValue
            };
        }
        return members;
    }

    function objectData(obj) {
        var o = {
            type: obj.nodeName,
            members: getMembers(obj),
            nodes: getNodes(obj),
            tags: getTags(obj)
        };
        for (var i = 0, l = obj.attributes.length; i < l; i++) {
            o[obj.attributes[i].nodeName] = obj.attributes[i].nodeValue;
        }
        if (o.lon && o.lat) {
            o.loc = [parseFloat(o.lon), parseFloat(o.lat)];
            delete o.lon;
            delete o.lat;
        }
        o.id = iD.Entity.id.fromOSM(o.type, o.id);
        switch (o.type) {
            case 'node':
                o._poi = !refNodes[o.id];
                return iD.Node(o);
            case 'way':
                return iD.Way(o);
            case 'relation':
                return iD.Relation(o);
        }
    }

    function parse(dom) {
        if (!dom || !dom.childNodes) return new Error('Bad request');
        var root = dom.childNodes[0];
        var entities = {};
        refNodes = {};
        var rparentsOf = {},
            wparentsOf = {};

        function addEntity(obj) {
            var o = objectData(obj);
            entities[o.id] = o;

            var i;
            if (o.type === 'relation') {
                for (i = 0; i < o.members.length; i++) {
                    if (o.members[i].id) {
                        if (rparentsOf[o.members[i].id] === undefined) {
                            rparentsOf[o.members[i].id] = [];
                        }
                        rparentsOf[o.members[i].id].push(o.id);
                    }
                }
            }
            if (o.type === 'way') {
                for (i = 0; i < o.nodes.length; i++) {
                    if (o.nodes[i]) {
                        if (wparentsOf[o.nodes[i]] === undefined) {
                            wparentsOf[o.nodes[i]] = [];
                        }
                        wparentsOf[o.nodes[i]].push(o.id);
                    }
                }
            }
        }

        _.forEach(root.getElementsByTagName('way'), addEntity);
        _.forEach(root.getElementsByTagName('node'), addEntity);
        _.forEach(root.getElementsByTagName('relation'), addEntity);

        var g = iD.Graph(entities);

        for (var i in wparentsOf) {
            if (entities[i]) g.transient(entities[i],
                'parentWays', d3.functor(wparentsOf[i]));
        }

        for (i in rparentsOf) {
            if (entities[i]) g.transient(entities[i],
                'parentRelations', d3.functor(rparentsOf[i]));
        }

        return g;
    }

    function authenticated() {
        return oauth.authenticated();
    }

    connection.putChangeset = function(changes, comment, imagery_used, callback) {
        oauth.xhr({
                method: 'PUT',
                path: '/api/0.6/changeset/create',
                options: { header: { 'Content-Type': 'text/xml' } },
                content: iD.format.XML.changeset(comment, imagery_used)
            }, function (err, changeset_id) {
                if (err) return callback(err);
                oauth.xhr({
                    method: 'POST',
                    path: '/api/0.6/changeset/' + changeset_id + '/upload',
                    options: { header: { 'Content-Type': 'text/xml' } },
                    content: iD.format.XML.osmChange(user.id, changeset_id, changes)
                }, function (err) {
                    if (err) return callback(err);
                    oauth.xhr({
                        method: 'PUT',
                        path: '/api/0.6/changeset/' + changeset_id + '/close'
                    }, function (err) {
                        callback(err, changeset_id);
                    });
                });
            });
    };

    function userDetails(callback) {
        function done(err, user_details) {
            var u = user_details.getElementsByTagName('user')[0],
                img = u.getElementsByTagName('img'),
                image_url = '';
            if (img && img[0].getAttribute('href')) {
                image_url = img[0].getAttribute('href');
            }
            callback(connection.user({
                display_name: u.attributes.display_name.nodeValue,
                image_url: image_url,
                id: u.attributes.id.nodeValue
            }).user());
        }
        oauth.xhr({ method: 'GET', path: '/api/0.6/user/details' }, done);
    }

    function tileAlreadyLoaded(c) { return !loadedTiles[c.toString()]; }

    function abortRequest(i) { i.abort(); }

    function loadTile(e) {
        function done(err, g) {
            event.load(err, g);
        }
        bboxFromAPI(e.box, e.tile, done);
    }

    function loadTiles(projection) {
        var scaleExtent = [16, 16],
            s = projection.scale(),
            tiles = d3.geo.tile()
                .scaleExtent(scaleExtent)
                .scale(s)
                .translate(projection.translate())(),
            z = Math.max(Math.log(s) / Math.log(2) - 8, 0),
            rz = Math.max(scaleExtent[0], Math.min(scaleExtent[1], Math.floor(z))),
            ts = 256 * Math.pow(2, z - rz),
            tile_origin = [
                s / 2 - projection.translate()[0],
                s / 2 - projection.translate()[1]];

        function apiExtentBox(c) {
            var x = (c[0] * ts) - tile_origin[0];
            var y = (c[1] * ts) - tile_origin[1];
            return {
                box: [
                    projection.invert([x, y]),
                    projection.invert([x + ts, y + ts])],
                tile: c
            };
        }

        inflight.map(abortRequest);
        inflight = [];

        tiles
            .filter(tileAlreadyLoaded)
            .map(apiExtentBox)
            .forEach(loadTile);
    }

    connection.userUrl = function(username) {
        return url + "/user/" + username;
    };

    connection.url = function(_) {
        if (!arguments.length) return url;
        url = _;
        oauth.url(_);
        return connection;
    };

    connection.user = function(_) {
        if (!arguments.length) return user;
        user = _;
        return connection;
    };

    connection.flush = function() {
        loadedTiles = {};
        return connection;
    };

    connection.logout = function() {
        oauth.logout();
        event.auth();
        return connection;
    };

    connection.authenticate = function(callback) {
        function done(err, res) {
            event.auth();
            if (callback) callback(err, res);
        }
        return oauth.authenticate(done);
    };

    connection.bboxFromAPI = bboxFromAPI;
    connection.loadFromURL = loadFromURL;
    connection.loadTiles = _.debounce(loadTiles, 100);
    connection.userDetails = userDetails;
    connection.authenticated = authenticated;
    connection.objectData = objectData;

    return d3.rebind(connection, event, 'on');
};
<commit_msg>Avoid aborting and re-requesting still wanted reqs<commit_after>iD.Connection = function() {

    var event = d3.dispatch('auth', 'load'),
        url = 'http://www.openstreetmap.org',
        connection = {},
        refNodes = {},
        user = {},
        inflight = {},
        loadedTiles = {},
        oauth = iD.OAuth();

    function bboxUrl(b) {
        return url + '/api/0.6/map?bbox=' + [b[0][0],b[1][1],b[1][0],b[0][1]];
    }

    function bboxFromAPI(box, tile, callback) {
        function done(err, parsed) {
             loadedTiles[tile.toString()] = true;
             callback(err, parsed);
         }
         inflight[tile.toString()] = loadFromURL(bboxUrl(box), done);
    }

    function loadFromURL(url, callback) {
        function done(dom) {
            return callback(null, parse(dom));
        }
        return d3.xml(url).get().on('load', done);
        inflight.push(d3.xml(url).get()
            .on('load', done));
    }

    function getNodes(obj) {
        var nelems = obj.getElementsByTagName('nd'), nodes = new Array(nelems.length);
        for (var i = 0, l = nelems.length; i < l; i++) {
            nodes[i] = 'n' + nelems[i].attributes.ref.nodeValue;
            refNodes['n' + nelems[i].attributes.ref.nodeValue] = true;
        }
        return nodes;
    }

    function getTags(obj) {
        var tags = {}, tagelems = obj.getElementsByTagName('tag');
        for (var i = 0, l = tagelems.length; i < l; i++) {
            var item = tagelems[i];
            tags[item.attributes.k.nodeValue] = item.attributes.v.nodeValue;
        }
        return tags;
    }

    function getMembers(obj) {
        var elems = obj.getElementsByTagName('member'),
            members = new Array(elems.length);

        for (var i = 0, l = elems.length; i < l; i++) {
            members[i] = {
                id: elems[i].attributes.type.nodeValue[0] + elems[i].attributes.ref.nodeValue,
                type: elems[i].attributes.type.nodeValue,
                role: elems[i].attributes.role.nodeValue
            };
        }
        return members;
    }

    function objectData(obj) {
        var o = {
            type: obj.nodeName,
            members: getMembers(obj),
            nodes: getNodes(obj),
            tags: getTags(obj)
        };
        for (var i = 0, l = obj.attributes.length; i < l; i++) {
            o[obj.attributes[i].nodeName] = obj.attributes[i].nodeValue;
        }
        if (o.lon && o.lat) {
            o.loc = [parseFloat(o.lon), parseFloat(o.lat)];
            delete o.lon;
            delete o.lat;
        }
        o.id = iD.Entity.id.fromOSM(o.type, o.id);
        switch (o.type) {
            case 'node':
                o._poi = !refNodes[o.id];
                return iD.Node(o);
            case 'way':
                return iD.Way(o);
            case 'relation':
                return iD.Relation(o);
        }
    }

    function parse(dom) {
        if (!dom || !dom.childNodes) return new Error('Bad request');
        var root = dom.childNodes[0];
        var entities = {};
        refNodes = {};
        var rparentsOf = {},
            wparentsOf = {};

        function addEntity(obj) {
            var o = objectData(obj);
            entities[o.id] = o;

            var i;
            if (o.type === 'relation') {
                for (i = 0; i < o.members.length; i++) {
                    if (o.members[i].id) {
                        if (rparentsOf[o.members[i].id] === undefined) {
                            rparentsOf[o.members[i].id] = [];
                        }
                        rparentsOf[o.members[i].id].push(o.id);
                    }
                }
            }
            if (o.type === 'way') {
                for (i = 0; i < o.nodes.length; i++) {
                    if (o.nodes[i]) {
                        if (wparentsOf[o.nodes[i]] === undefined) {
                            wparentsOf[o.nodes[i]] = [];
                        }
                        wparentsOf[o.nodes[i]].push(o.id);
                    }
                }
            }
        }

        _.forEach(root.getElementsByTagName('way'), addEntity);
        _.forEach(root.getElementsByTagName('node'), addEntity);
        _.forEach(root.getElementsByTagName('relation'), addEntity);

        var g = iD.Graph(entities);

        for (var i in wparentsOf) {
            if (entities[i]) g.transient(entities[i],
                'parentWays', d3.functor(wparentsOf[i]));
        }

        for (i in rparentsOf) {
            if (entities[i]) g.transient(entities[i],
                'parentRelations', d3.functor(rparentsOf[i]));
        }

        return g;
    }

    function authenticated() {
        return oauth.authenticated();
    }

    connection.putChangeset = function(changes, comment, imagery_used, callback) {
        oauth.xhr({
                method: 'PUT',
                path: '/api/0.6/changeset/create',
                options: { header: { 'Content-Type': 'text/xml' } },
                content: iD.format.XML.changeset(comment, imagery_used)
            }, function (err, changeset_id) {
                if (err) return callback(err);
                oauth.xhr({
                    method: 'POST',
                    path: '/api/0.6/changeset/' + changeset_id + '/upload',
                    options: { header: { 'Content-Type': 'text/xml' } },
                    content: iD.format.XML.osmChange(user.id, changeset_id, changes)
                }, function (err) {
                    if (err) return callback(err);
                    oauth.xhr({
                        method: 'PUT',
                        path: '/api/0.6/changeset/' + changeset_id + '/close'
                    }, function (err) {
                        callback(err, changeset_id);
                    });
                });
            });
    };

    function userDetails(callback) {
        function done(err, user_details) {
            var u = user_details.getElementsByTagName('user')[0],
                img = u.getElementsByTagName('img'),
                image_url = '';
            if (img && img[0].getAttribute('href')) {
                image_url = img[0].getAttribute('href');
            }
            callback(connection.user({
                display_name: u.attributes.display_name.nodeValue,
                image_url: image_url,
                id: u.attributes.id.nodeValue
            }).user());
        }
        oauth.xhr({ method: 'GET', path: '/api/0.6/user/details' }, done);
    }

    function tileAlreadyLoaded(c) { return !loadedTiles[c.toString()] && !inflight[c.toString()]; }

    function abortRequest(i) { i.abort(); }

    function loadTile(e) {
        function done(err, g) {
            event.load(err, g);
        }
        bboxFromAPI(e.box, e.tile, done);
    }

    function loadTiles(projection) {
        var scaleExtent = [16, 16],
            s = projection.scale(),
            tiles = d3.geo.tile()
                .scaleExtent(scaleExtent)
                .scale(s)
                .translate(projection.translate())(),
            z = Math.max(Math.log(s) / Math.log(2) - 8, 0),
            rz = Math.max(scaleExtent[0], Math.min(scaleExtent[1], Math.floor(z))),
            ts = 256 * Math.pow(2, z - rz),
            tile_origin = [
                s / 2 - projection.translate()[0],
                s / 2 - projection.translate()[1]];

        function apiExtentBox(c) {
            var x = (c[0] * ts) - tile_origin[0];
            var y = (c[1] * ts) - tile_origin[1];
            return {
                box: [
                    projection.invert([x, y]),
                    projection.invert([x + ts, y + ts])],
                tile: c
            };
        }

        _.filter(inflight, function(v, i) {
            var wanted = _.find(tiles, function(tile) {
                return i === tile.toString();
            });
            if (!wanted) delete inflight[i];
            return !wanted;
        }).map(abortRequest);

        tiles
            .filter(tileAlreadyLoaded)
            .map(apiExtentBox)
            .forEach(loadTile);
    }

    connection.userUrl = function(username) {
        return url + "/user/" + username;
    };

    connection.url = function(_) {
        if (!arguments.length) return url;
        url = _;
        oauth.url(_);
        return connection;
    };

    connection.user = function(_) {
        if (!arguments.length) return user;
        user = _;
        return connection;
    };

    connection.flush = function() {
        loadedTiles = {};
        return connection;
    };

    connection.logout = function() {
        oauth.logout();
        event.auth();
        return connection;
    };

    connection.authenticate = function(callback) {
        function done(err, res) {
            event.auth();
            if (callback) callback(err, res);
        }
        return oauth.authenticate(done);
    };

    connection.bboxFromAPI = bboxFromAPI;
    connection.loadFromURL = loadFromURL;
    connection.loadTiles = _.debounce(loadTiles, 100);
    connection.userDetails = userDetails;
    connection.authenticated = authenticated;
    connection.objectData = objectData;

    return d3.rebind(connection, event, 'on');
};
<|endoftext|>


<commit_before>iD.svg.Labels = function(projection) {

    // Replace with dict and iterate over entities tags instead?
    var label_stack = [
        ['line', 'highway'],
        ['area', 'building', 'yes'],
        ['area', 'leisure', 'park'],
        ['area', 'natural'],
        ['point', 'amenity'],
        ['point', 'shop'],
    ];

    var default_size = 12;
    var font_sizes = label_stack.map(function(d) {
        var style = iD.util.getStyle(
            'text.' + d[0] + '.tag-' + d.slice(1).join('-'));
        var m = style && style.cssText.match("font-size: ([0-9]{1,2})px;");
        if (!m) return default_size;
        return parseInt(m[1], 10);
    });

    var pointOffsets = [
        [15, 3, 'start'], // right
        [10, 0, 'start'], // unused right now
        [-15, 0, 'end']
    ];

    var lineOffsets = [
        50, 40, 60, 30, 70
    ];

    function get(array, prop) {
        return function(d, i) { return array[i][prop] };
    }

    var textWidthCache = {};
    function textWidth(text, size, elem) {
        var c = textWidthCache[size];
        if (!c) c = textWidthCache[size] = {};
        if (c[text]) return c[text];
        else if (elem) return c[text] = elem.getComputedTextLength();
        else return size / 3 * 2 * text.length;
    }

    function drawLineLabels(group, entities, filter, classes, labels) {

        var texts = group.selectAll('text.' + classes)
            .filter(filter)
            .data(entities, iD.Entity.key)

        var tp = texts.enter()
            .append('text')
            .attr('class', function(d, i) { return classes + ' ' + labels[i]['classes'];})
            .append('textPath')
            .attr('class', 'textpath');

        var tps = texts.selectAll('.textpath')
            .filter(filter)
            .data(entities, iD.Entity.key)
            .attr({
                'startOffset': '50%',
                'xlink:href': function(d, i) { return '#halo-' + d.id}
            })
            .text(function(d, i) { return d.tags.name });

        texts.exit().remove();

    }

    function drawLineHalos(group, entities, filter, classes, labels) {

        var halos = group.selectAll('path')
            .filter(filter)
            .data(entities, iD.Entity.key);

        halos.enter()
            .append('path')
            .style('stroke-width', get(labels, 'font-size'))
            .attr('id', function(d, i) { return 'halo-' + d.id })
            .attr('class', classes);

        halos.attr('d', get(labels, 'lineString'));

        halos.exit().remove();
    }

    function drawPointHalos(group, entities, filter, classes, labels) {

        var halos = group.selectAll('rect.' + classes)
            .filter(filter)
            .data(entities, iD.Entity.key);

        halos.enter()
            .append('rect')
            .attr('class', function(d, i) { return classes + ' ' + labels[i]['classes'];});

        halos.attr({
            'x': function(d, i) {
                var x = labels[i]['x'] - 2;
                if (labels[i]['textAnchor'] === 'middle') {
                    x -= textWidth(d.tags.name, labels[i]['height']) / 2;
                }
                return x;
            },
            'y': function(d, i) { return labels[i]['y'] - labels[i]['height'] + 1 - 2; },
            'rx': 3,
            'ry': 3,
            'width': function(d, i) { return textWidth(d.tags.name, labels[i]['height']) + 4 },
            'height': function(d, i) { return labels[i]['height'] + 4 },
            'fill': 'white',
        });

        halos.exit().remove();
    }

    function drawPointLabels(group, entities, filter, classes, labels) {

        var texts = group.selectAll('text.' + classes)
            .filter(filter)
            .data(entities, iD.Entity.key);

        texts.enter()
            .append('text')
            .attr('class', function(d, i) { return classes + ' ' + labels[i]['classes'] })

        texts.attr('x', get(labels, 'x'))
            .attr('y', get(labels, 'y'))
            .attr('transform', get(labels, 'transform'))
            .style('text-anchor', get(labels, 'textAnchor'))
            .text(function(d) { return d.tags.name })
            .each(function(d, i) { textWidth(d.tags.name, labels[i]['height'], this); });

        texts.exit().remove();
        return texts;
    }

    function reverse(p) {
        var angle = Math.atan2(p[1][1] - p[0][1], p[1][0] - p[0][0]),
            reverse = !(p[0][0] < p[p.length - 1][0] && angle < Math.PI/2 && angle > - Math.PI/2);
        return reverse;
    }

    function lineString(nodes) {
        return 'M' + nodes.join('L');
    }

    function subpath(nodes, from, to) {
        function segmentLength(i) {
            var dx = nodes[i][0] - nodes[i + 1][0];
            var dy = nodes[i][1] - nodes[i + 1][1];
            return Math.sqrt(dx * dx + dy * dy);
        }

        var sofar = 0,
            start, end, i0, i1;
        for (var i = 0; i < nodes.length - 1; i++) {
            var current = segmentLength(i);
            if (!start && sofar + current > from) {
                var portion = (from - sofar) / current;
                start = [
                    nodes[i][0] + portion * (nodes[i + 1][0] - nodes[i][0]),
                    nodes[i][1] + portion * (nodes[i + 1][1] - nodes[i][1])
                ];
                i0 = i + 1;
            }
            if (!end && sofar + current > to) {
                var portion = (to - sofar) / current;
                end = [
                    nodes[i][0] + portion * (nodes[i + 1][0] - nodes[i][0]),
                    nodes[i][1] + portion * (nodes[i + 1][1] - nodes[i][1])
                ];
                i1 = i + 1;
            }
            sofar += current;

        }
        var ret = nodes.slice(i0, i1);
        ret.unshift(start);
        ret.push(end);
        return ret;

    }

    return function drawLabels(surface, graph, entities, filter) {

        var rtree = new RTree();
        var hidePoints = !d3.select('.node.point').node();

        var labelable = [];
        for (var i = 0; i < label_stack.length; i++) labelable.push([]);

        // Split entities into groups specified by label_stack
        for (var i = 0; i < entities.length; i++) {
            var entity = entities[i];
            if (!entity.tags.name) continue;
            if (hidePoints && entity.geometry(graph) === 'point') continue;
            for (var k = 0; k < label_stack.length; k ++) {
                if (entity.geometry(graph) === label_stack[k][0] &&
                    entity.tags[label_stack[k][1]] && !entity.tags[label_stack[k][2]]) {
                    labelable[k].push(entity);
                    break;
                }
            }
        }

        var positions = {
            point: [],
            line: [],
            area: []
        };

        var labelled = {
            point: [],
            line: [],
            area: []
        };

        // Try and find a valid label for labellable entities
        for (var k = 0; k < labelable.length; k++) {
            var font_size = font_sizes[k];
            for (var i = 0; i < labelable[k].length; i ++) {
                var entity = labelable[k][i],
                    width = textWidth(entity.tags.name, font_size),
                    p;
                if (entity.geometry(graph) === 'point') {
                    p = getPointLabel(entity, width, font_size);
                } else if (entity.geometry(graph) === 'line') {
                    p = getLineLabel(entity, width, font_size);
                } else if (entity.geometry(graph) === 'area') {
                    p = getAreaLabel(entity, width, font_size);
                }
                if (p) {
                    p.classes = entity.geometry(graph) + ' tag-' + label_stack[k].slice(1).join('-');
                    positions[entity.geometry(graph)].push(p);
                    labelled[entity.geometry(graph)].push(entity);
                }
            }
        }

        function getPointLabel(entity, width, height) {
            var coord = projection(entity.loc),
                m = 5,  // margin
                offset = pointOffsets[0],
                p = {
                    height: height,
                    width: width,
                    x: coord[0] + offset[0],
                    y: coord[1] + offset[1],
                    textAnchor: offset[2]
                }
            var rect = new RTree.Rectangle(p.x - m, p.y - m, width + 2*m, height + 2*m);
            if (tryInsert(rect)) return p;
        }

        function getLineLabel(entity, width, height) {
            var nodes = _.pluck(graph.childNodes(entity), 'loc').map(projection),
                length = iD.geo.pathLength(nodes);
            if (length < width + 20) return;

            // 50, 40, 60, 30, 70
            for (var i = 0; i < 5; i ++) {
                var offset = lineOffsets[i],
                    middle = offset / 100 * length;
                if (middle <= width / 2) return;
                var start = middle - width/2,
                    sub = subpath(nodes, start, start + width),
                    rev = reverse(sub),
                    rect = new RTree.Rectangle(
                    Math.min(sub[0][0], sub[sub.length - 1][0]) - 10,
                    Math.min(sub[0][1], sub[sub.length - 1][1]) - 10,
                    Math.abs(sub[0][0] - sub[sub.length - 1][0]) + 20,
                    Math.abs(sub[0][1] - sub[sub.length - 1][1]) + 30
                );
                if (rev) sub = sub.reverse();
                if (tryInsert(rect)) return {
                    'font-size': height + 2,
                    lineString: lineString(sub),
                    startOffset: offset + '%'
                }
            }
        }

        function getAreaLabel(entity, width, height) {
            var nodes = _.pluck(graph.childNodes(entity), 'loc')
                .map(iD.svg.RoundProjection(projection)),
                centroid = d3.geom.polygon(nodes).centroid(),
                extent = entity.extent(graph),
                entitywidth = projection(extent[1])[0] - projection(extent[0])[0];

            if (entitywidth < width + 20) return;
            var p = {
                x: centroid[0],
                y: centroid[1],
                textAnchor: 'middle',
                height: height
            }
            var rect = new RTree.Rectangle(p.x - width/2, p.y, width, height);
            if (tryInsert(rect)) return p;

        }

        function tryInsert(rect) {
            var v = rtree.search(rect, true).length === 0;
            if (v) rtree.insert(rect);
            return v;
        }

        var label = surface.select('.layer-label'),
            halo = surface.select('.layer-halo'),
            points = drawPointLabels(label, labelled['point'], filter, 'pointlabel', positions['point']),
            pointHalos = drawPointHalos(halo, labelled['point'], filter, 'pointlabel-halo', positions['point']),
            linesHalos = drawLineHalos(halo, labelled['line'], filter, 'linelabel-halo', positions['line']),
            lines = drawLineLabels(label, labelled['line'], filter, 'pathlabel', positions['line']),
            areas = drawPointLabels(label, labelled['area'], filter, 'arealabel', positions['area']),
            areaHalos = drawPointHalos(halo, labelled['area'], filter, 'arealabel-halo', positions['area']);
    };

};
<commit_msg>jshint please<commit_after>iD.svg.Labels = function(projection) {

    // Replace with dict and iterate over entities tags instead?
    var label_stack = [
        ['line', 'highway'],
        ['area', 'building', 'yes'],
        ['area', 'leisure', 'park'],
        ['area', 'natural'],
        ['point', 'amenity'],
        ['point', 'shop']
    ];

    var default_size = 12;
    var font_sizes = label_stack.map(function(d) {
        var style = iD.util.getStyle(
            'text.' + d[0] + '.tag-' + d.slice(1).join('-'));
        var m = style && style.cssText.match("font-size: ([0-9]{1,2})px;");
        if (!m) return default_size;
        return parseInt(m[1], 10);
    });

    var pointOffsets = [
        [15, 3, 'start'], // right
        [10, 0, 'start'], // unused right now
        [-15, 0, 'end']
    ];

    var lineOffsets = [
        50, 40, 60, 30, 70
    ];

    function get(array, prop) {
        return function(d, i) { return array[i][prop]; };
    }

    var textWidthCache = {};
    function textWidth(text, size, elem) {
        var c = textWidthCache[size];
        if (!c) c = textWidthCache[size] = {};
        if (c[text]) return c[text];
        else if (elem) return c[text] = elem.getComputedTextLength();
        else return size / 3 * 2 * text.length;
    }

    function drawLineLabels(group, entities, filter, classes, labels) {

        var texts = group.selectAll('text.' + classes)
            .filter(filter)
            .data(entities, iD.Entity.key);

        var tp = texts.enter()
            .append('text')
            .attr('class', function(d, i) { return classes + ' ' + labels[i].classes;})
            .append('textPath')
            .attr('class', 'textpath');

        var tps = texts.selectAll('.textpath')
            .filter(filter)
            .data(entities, iD.Entity.key)
            .attr({
                'startOffset': '50%',
                'xlink:href': function(d, i) { return '#halo-' + d.id; }
            })
            .text(function(d, i) { return d.tags.name; });

        texts.exit().remove();

    }

    function drawLineHalos(group, entities, filter, classes, labels) {

        var halos = group.selectAll('path')
            .filter(filter)
            .data(entities, iD.Entity.key);

        halos.enter()
            .append('path')
            .style('stroke-width', get(labels, 'font-size'))
            .attr('id', function(d, i) { return 'halo-' + d.id; })
            .attr('class', classes);

        halos.attr('d', get(labels, 'lineString'));

        halos.exit().remove();
    }

    function drawPointHalos(group, entities, filter, classes, labels) {

        var halos = group.selectAll('rect.' + classes)
            .filter(filter)
            .data(entities, iD.Entity.key);

        halos.enter()
            .append('rect')
            .attr('class', function(d, i) { return classes + ' ' + labels[i].classes;});

        halos.attr({
            'x': function(d, i) {
                var x = labels[i].x - 2;
                if (labels[i].textAnchor === 'middle') {
                    x -= textWidth(d.tags.name, labels[i].height) / 2;
                }
                return x;
            },
            'y': function(d, i) { return labels[i].y - labels[i].height + 1 - 2; },
            'rx': 3,
            'ry': 3,
            'width': function(d, i) { return textWidth(d.tags.name, labels[i].height) + 4; },
            'height': function(d, i) { return labels[i].height + 4; },
            'fill': 'white'
        });

        halos.exit().remove();
    }

    function drawPointLabels(group, entities, filter, classes, labels) {

        var texts = group.selectAll('text.' + classes)
            .filter(filter)
            .data(entities, iD.Entity.key);

        texts.enter()
            .append('text')
            .attr('class', function(d, i) { return classes + ' ' + labels[i].classes; });

        texts.attr('x', get(labels, 'x'))
            .attr('y', get(labels, 'y'))
            .attr('transform', get(labels, 'transform'))
            .style('text-anchor', get(labels, 'textAnchor'))
            .text(function(d) { return d.tags.name; })
            .each(function(d, i) { textWidth(d.tags.name, labels[i].height, this); });

        texts.exit().remove();
        return texts;
    }

    function reverse(p) {
        var angle = Math.atan2(p[1][1] - p[0][1], p[1][0] - p[0][0]);
        return !(p[0][0] < p[p.length - 1][0] && angle < Math.PI/2 && angle > - Math.PI/2);
    }

    function lineString(nodes) {
        return 'M' + nodes.join('L');
    }

    function subpath(nodes, from, to) {
        function segmentLength(i) {
            var dx = nodes[i][0] - nodes[i + 1][0];
            var dy = nodes[i][1] - nodes[i + 1][1];
            return Math.sqrt(dx * dx + dy * dy);
        }

        var sofar = 0,
            start, end, i0, i1;
        for (var i = 0; i < nodes.length - 1; i++) {
            var current = segmentLength(i);
            var portion;
            if (!start && sofar + current > from) {
                portion = (from - sofar) / current;
                start = [
                    nodes[i][0] + portion * (nodes[i + 1][0] - nodes[i][0]),
                    nodes[i][1] + portion * (nodes[i + 1][1] - nodes[i][1])
                ];
                i0 = i + 1;
            }
            if (!end && sofar + current > to) {
                portion = (to - sofar) / current;
                end = [
                    nodes[i][0] + portion * (nodes[i + 1][0] - nodes[i][0]),
                    nodes[i][1] + portion * (nodes[i + 1][1] - nodes[i][1])
                ];
                i1 = i + 1;
            }
            sofar += current;

        }
        var ret = nodes.slice(i0, i1);
        ret.unshift(start);
        ret.push(end);
        return ret;

    }

    return function drawLabels(surface, graph, entities, filter) {

        var rtree = new RTree();
        var hidePoints = !d3.select('.node.point').node();

        var labelable = [], i, k, entity;
        for (i = 0; i < label_stack.length; i++) labelable.push([]);

        // Split entities into groups specified by label_stack
        for (i = 0; i < entities.length; i++) {
            entity = entities[i];
            if (!entity.tags.name) continue;
            if (hidePoints && entity.geometry(graph) === 'point') continue;
            for (k = 0; k < label_stack.length; k ++) {
                if (entity.geometry(graph) === label_stack[k][0] &&
                    entity.tags[label_stack[k][1]] && !entity.tags[label_stack[k][2]]) {
                    labelable[k].push(entity);
                    break;
                }
            }
        }

        var positions = {
            point: [],
            line: [],
            area: []
        };

        var labelled = {
            point: [],
            line: [],
            area: []
        };

        // Try and find a valid label for labellable entities
        for (k = 0; k < labelable.length; k++) {
            var font_size = font_sizes[k];
            for (i = 0; i < labelable[k].length; i ++) {
                entity = labelable[k][i];
                var width = textWidth(entity.tags.name, font_size),
                    p;
                if (entity.geometry(graph) === 'point') {
                    p = getPointLabel(entity, width, font_size);
                } else if (entity.geometry(graph) === 'line') {
                    p = getLineLabel(entity, width, font_size);
                } else if (entity.geometry(graph) === 'area') {
                    p = getAreaLabel(entity, width, font_size);
                }
                if (p) {
                    p.classes = entity.geometry(graph) + ' tag-' + label_stack[k].slice(1).join('-');
                    positions[entity.geometry(graph)].push(p);
                    labelled[entity.geometry(graph)].push(entity);
                }
            }
        }

        function getPointLabel(entity, width, height) {
            var coord = projection(entity.loc),
                m = 5,  // margin
                offset = pointOffsets[0],
                p = {
                    height: height,
                    width: width,
                    x: coord[0] + offset[0],
                    y: coord[1] + offset[1],
                    textAnchor: offset[2]
                };
            var rect = new RTree.Rectangle(p.x - m, p.y - m, width + 2*m, height + 2*m);
            if (tryInsert(rect)) return p;
        }

        function getLineLabel(entity, width, height) {
            var nodes = _.pluck(graph.childNodes(entity), 'loc').map(projection),
                length = iD.geo.pathLength(nodes);
            if (length < width + 20) return;

            // 50, 40, 60, 30, 70
            for (var i = 0; i < 5; i ++) {
                var offset = lineOffsets[i],
                    middle = offset / 100 * length;
                if (middle <= width / 2) return;
                var start = middle - width/2,
                    sub = subpath(nodes, start, start + width),
                    rev = reverse(sub),
                    rect = new RTree.Rectangle(
                    Math.min(sub[0][0], sub[sub.length - 1][0]) - 10,
                    Math.min(sub[0][1], sub[sub.length - 1][1]) - 10,
                    Math.abs(sub[0][0] - sub[sub.length - 1][0]) + 20,
                    Math.abs(sub[0][1] - sub[sub.length - 1][1]) + 30
                );
                if (rev) sub = sub.reverse();
                if (tryInsert(rect)) return {
                    'font-size': height + 2,
                    lineString: lineString(sub),
                    startOffset: offset + '%'
                };
            }
        }

        function getAreaLabel(entity, width, height) {
            var nodes = _.pluck(graph.childNodes(entity), 'loc')
                .map(iD.svg.RoundProjection(projection)),
                centroid = d3.geom.polygon(nodes).centroid(),
                extent = entity.extent(graph),
                entitywidth = projection(extent[1])[0] - projection(extent[0])[0];

            if (entitywidth < width + 20) return;
            var p = {
                x: centroid[0],
                y: centroid[1],
                textAnchor: 'middle',
                height: height
            };
            var rect = new RTree.Rectangle(p.x - width/2, p.y, width, height);
            if (tryInsert(rect)) return p;

        }

        function tryInsert(rect) {
            var v = rtree.search(rect, true).length === 0;
            if (v) rtree.insert(rect);
            return v;
        }

        var label = surface.select('.layer-label'),
            halo = surface.select('.layer-halo'),
            points = drawPointLabels(label, labelled.point, filter, 'pointlabel', positions.point),
            pointHalos = drawPointHalos(halo, labelled.point, filter, 'pointlabel-halo', positions.point),
            linesHalos = drawLineHalos(halo, labelled.line, filter, 'linelabel-halo', positions.line),
            lines = drawLineLabels(label, labelled.line, filter, 'pathlabel', positions.line),
            areas = drawPointLabels(label, labelled.area, filter, 'arealabel', positions.area),
            areaHalos = drawPointHalos(halo, labelled.area, filter, 'arealabel-halo', positions.area);
    };

};
<|endoftext|>