diff --git a/.gitignore b/.gitignore index 0d906c7fc..68edb922e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,9 @@ +.DS_Store /.settings /.project node_modules old/ tmp/ *~ -app/components \ No newline at end of file +app/components +/nbproject \ No newline at end of file diff --git a/Gruntfile.js b/Gruntfile.js index c8b328395..36005e47d 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -10,13 +10,16 @@ module.exports = function(grunt) { grunt.loadNpmTasks('grunt-contrib-watch'); grunt.loadNpmTasks('grunt-open'); grunt.loadNpmTasks('grunt-mkdir'); + grunt.loadNpmTasks('grunt-contrib-coffee'); + // Project configuration. grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), clean: { + coffee: ['tmp/output_coffee.js'], dist: ['dist/*', 'tmp'], example: ['example/<%= pkg.name %>.js'] }, @@ -29,33 +32,57 @@ module.exports = function(grunt) { } } }, + + coffee: { + compile: { + files: { + 'tmp/output_coffee.js': + ['src/coffee/*.coffee', + 'src/coffee/oo/ng-gmap-module.coffee', + 'src/coffee/oo/base-object.coffee', + 'src/coffee/directives/api/utils/*.coffee', + 'src/coffee/directives/api/models/child/*.coffee', + 'src/coffee/directives/api/models/parent/*.coffee', + 'src/coffee/directives/api/*.coffee', + 'src/coffee/directives/*.coffee'] // concat then compile into single file + } + } + }, concat: { options: { separator: ';' }, dist: { - src: ['src/module.js', - 'src/controllers/polyline-display.js', - 'src/utils/LatLngArraySync.js', - 'src/utils/MapEvents.js', - 'src/directives/map.js', - 'src/directives/marker.js', - 'src/directives/polyline.js', - 'src/directives/window.js', - 'src/directives/trafficlayer.js'], + src: ['src/js/module.js', + 'tmp/output_coffee.js', + 'src/js/utils/LatLngArraySync.js', + 'src/js/utils/MapEvents.js', + 'src/js/controllers/polyline-display.js', + 'src/js/directives/map.js', + 'src/js/directives/marker.js', + 'src/js/directives/markers.js', + 'src/js/directives/polygon.js', + 'src/js/directives/polyline.js', + 'src/js/directives/window.js', + 'src/js/directives/windows.js', + 'src/js/directives/trafficlayer.js'], dest: 'tmp/output.js' }, example: { - src: ['src/module.js', - 'src/controllers/polyline-display.js', - 'src/utils/LatLngArraySync.js', - 'src/utils/MapEvents.js', - 'src/directives/map.js', - 'src/directives/marker.js', - 'src/directives/polyline.js', - 'src/directives/window.js', - 'src/directives/trafficlayer.js'], + src: ['src/js/module.js', + 'tmp/output_coffee.js', + 'src/js/utils/LatLngArraySync.js', + 'src/js/utils/MapEvents.js', + 'src/js/controllers/polyline-display.js', + 'src/js/directives/map.js', + 'src/js/directives/marker.js', + 'src/js/directives/markers.js', + 'src/js/directives/polygon.js', + 'src/js/directives/polyline.js', + 'src/js/directives/window.js', + 'src/js/directives/windows.js', + 'src/js/directives/trafficlayer.js'], dest: 'example/<%= pkg.name %>.js' } }, @@ -82,7 +109,7 @@ module.exports = function(grunt) { }, jshint: { - all: ['Gruntfile.js', 'src/**/*.js', 'test/**/*.js'] + all: ['Gruntfile.js', 'src/js/**/*.js', 'test/js/**/*.js'] }, test: { @@ -92,9 +119,9 @@ module.exports = function(grunt) { watch: { all: { options: { livereload: true }, - files: ['src/**/*.js'], - tasks: ['clean:example', 'concat:example'] - } + files: ['src/js/**/*.js','src/coffee/**/*.coffee','src/coffee/*.coffee'], + tasks: ['clean:example','coffee','concat:example'], + }, }, open: { @@ -122,6 +149,7 @@ module.exports = function(grunt) { 'test', 'jshint', 'mkdir', + 'coffee', 'concat:dist', 'copy:dist', 'uglify']); diff --git a/dist/angular-google-maps.js b/dist/angular-google-maps.js index 76418f4ae..eec029e08 100644 --- a/dist/angular-google-maps.js +++ b/dist/angular-google-maps.js @@ -27,7 +27,1358 @@ * @author Nicolas Laplante https://plus.google.com/108189012221374960701 */ -angular.module('google-maps', []);;/**! +(function(){ + var app = angular.module('google-maps', []); + + app.factory('debounce', ['$timeout', function ($timeout) { + return function(fn){ // debounce fn + var nthCall = 0; + return function(){ // intercepting fn + var that = this; + var argz = arguments; + nthCall++; + var later = (function(version){ + return function(){ + if (version === nthCall){ + return fn.apply(that, argz); + } + }; + })(nthCall); + return $timeout(later,0, true); + }; + }; + }]); +})();;(function() { + this.ngGmapModule = function(names, fn) { + var space, _name; + if (typeof names === 'string') { + names = names.split('.'); + } + space = this[_name = names.shift()] || (this[_name] = {}); + space.ngGmapModule || (space.ngGmapModule = this.ngGmapModule); + if (names.length) { + return space.ngGmapModule(names, fn); + } else { + return fn.call(space); + } + }; + +}).call(this); + +(function() { + var __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; + + this.ngGmapModule("oo", function() { + var baseObjectKeywords; + baseObjectKeywords = ['extended', 'included']; + return this.BaseObject = (function() { + function BaseObject() {} + + BaseObject.extend = function(obj) { + var key, value, _ref; + for (key in obj) { + value = obj[key]; + if (__indexOf.call(baseObjectKeywords, key) < 0) { + this[key] = value; + } + } + if ((_ref = obj.extended) != null) { + _ref.apply(0); + } + return this; + }; + + BaseObject.include = function(obj) { + var key, value, _ref; + for (key in obj) { + value = obj[key]; + if (__indexOf.call(baseObjectKeywords, key) < 0) { + this.prototype[key] = value; + } + } + if ((_ref = obj.included) != null) { + _ref.apply(0); + } + return this; + }; + + return BaseObject; + + })(); + }); + +}).call(this); + +/* + Author: Nicholas McCready & jfriend00 + AsyncProcessor handles things asynchronous-like :), to allow the UI to be free'd to do other things + Code taken from http://stackoverflow.com/questions/10344498/best-way-to-iterate-over-an-array-without-blocking-the-ui +*/ + + +(function() { + this.ngGmapModule("directives.api.utils", function() { + return this.AsyncProcessor = { + handleLargeArray: function(array, callback, pausedCallBack, chunk, index) { + var doChunk; + if (chunk == null) { + chunk = 100; + } + if (index == null) { + index = 0; + } + if (array === void 0 || array.length <= 0) { + return; + } + doChunk = function() { + var cnt, i; + cnt = chunk; + i = index; + while (cnt-- && i < array.length) { + callback(array[i]); + ++i; + } + if (i < array.length) { + index = i; + if (pausedCallBack != null) { + pausedCallBack(); + } + return setTimeout(doChunk(), 1); + } + }; + return doChunk(); + } + }; + }); + +}).call(this); + +(function() { + this.ngGmapModule("directives.api.utils", function() { + return this.GmapUtil = { + createMarkerOptions: function(map, coords, icon, animate, defaults) { + var opts; + opts = angular.extend({}, defaults, { + position: new google.maps.LatLng(coords.latitude, coords.longitude), + map: map.getMap(), + icon: icon, + visible: (coords.latitude != null) && (coords.longitude != null) + }); + if (!animate) { + delete opts.animation; + } + return opts; + }, + createWindowOptions: function(gMarker, scope, content, defaults) { + return angular.extend({}, defaults, { + content: content, + position: angular.isObject(gMarker) ? gMarker.getPosition() : new google.maps.LatLng(scope.coords.latitude, scope.coords.longitude) + }); + } + }; + }); + +}).call(this); + +(function() { + var __hasProp = {}.hasOwnProperty, + __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; + + this.ngGmapModule("directives.api.utils", function() { + return this.Linked = (function(_super) { + __extends(Linked, _super); + + function Linked(scope, element, attrs, ctrls) { + this.scope = scope; + this.element = element; + this.attrs = attrs; + this.ctrls = ctrls; + } + + return Linked; + + })(oo.BaseObject); + }); + +}).call(this); + +(function() { + this.ngGmapModule("directives.api.utils", function() { + return this.Logger = { + logger: void 0, + doLog: false, + info: function(msg) { + if (directives.api.utils.Logger.doLog) { + if (directives.api.utils.Logger.logger != null) { + return directives.api.utils.Logger.logger.info(msg); + } else { + return console.info(msg); + } + } + } + }; + }); + +}).call(this); + +(function() { + var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, + __hasProp = {}.hasOwnProperty, + __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; + + this.ngGmapModule("directives.api.models.child", function() { + return this.MarkerChildModel = (function(_super) { + __extends(MarkerChildModel, _super); + + MarkerChildModel.include(directives.api.utils.GmapUtil); + + function MarkerChildModel(index, model, parentScope, gMap, $timeout, notifyLocalDestroy, defaults, doClick) { + this.watchDestroy = __bind(this.watchDestroy, this); + this.watchIcon = __bind(this.watchIcon, this); + this.watchCoords = __bind(this.watchCoords, this); + this.setIcon = __bind(this.setIcon, this); + this.setCoords = __bind(this.setCoords, this); + this.destroy = __bind(this.destroy, this); + this.setMyScope = __bind(this.setMyScope, this); + var _this = this; + this.index = index; + this.model = model; + this.parentScope = parentScope; + this.iconKey = parentScope.icon; + this.coordsKey = parentScope.coords; + this.clickKey = parentScope.click(); + this.animateKey = parentScope.animate; + this.myScope = parentScope.$new(false); + this.setMyScope(model); + this.myScope.$watch('model', function(newValue, oldValue) { + if (newValue !== oldValue) { + return _this.setMyScope(newValue); + } + }, true); + this.defaults = defaults; + this.gMap = gMap; + this.opts = this.createMarkerOptions(this.gMap, this.myScope.coords, this.myScope.icon, this.myScope.animate, this.defaults); + this.gMarker = new google.maps.Marker(this.opts); + this.doClick = doClick; + this.$log = directives.api.utils.Logger; + google.maps.event.addListener(this.gMarker, 'click', function() { + if (_this.doClick && (_this.myScope.click != null)) { + return $timeout(function() { + return _this.myScope.click(); + }); + } + }); + this.watchCoords(this.myScope); + this.watchIcon(this.myScope); + this.watchDestroy(this.myScope); + } + + MarkerChildModel.prototype.setMyScope = function(model) { + this.myScope.icon = this.iconKey === 'self' ? model : model[this.iconKey]; + this.myScope.coords = this.coordsKey === 'self' ? model : model[this.coordsKey]; + this.myScope.click = this.clickKey === 'self' ? model : model[this.clickKey]; + this.myScope.animate = this.animateKey === 'self' ? model : model[this.animateKey]; + this.myScope.animate = this.animateKey === void 0 ? false : this.myScope.animate; + return this.myScope.model = model; + }; + + MarkerChildModel.prototype.destroy = function() { + return this.myScope.$destroy(); + }; + + MarkerChildModel.prototype.setCoords = function(scope) { + if (scope.$id !== this.myScope.$id) { + return; + } + if ((scope.coords != null)) { + this.gMarker.setMap(this.gMap.getMap()); + this.gMarker.setPosition(new google.maps.LatLng(scope.coords.latitude, scope.coords.longitude)); + return this.gMarker.setVisible((scope.coords.latitude != null) && (scope.coords.longitude != null)); + } else { + return this.gMarker.setMap(null); + } + }; + + MarkerChildModel.prototype.setIcon = function(scope) { + if (scope.$id !== this.myScope.$id) { + return; + } + this.gMarker.icon = scope.icon; + this.gMarker.setMap(null); + this.gMarker.setMap(this.gMap.getMap()); + this.gMarker.setPosition(new google.maps.LatLng(scope.coords.latitude, scope.coords.longitude)); + return this.gMarker.setVisible(scope.coords.latitude && (scope.coords.longitude != null)); + }; + + MarkerChildModel.prototype.watchCoords = function(scope) { + var _this = this; + return scope.$watch('coords', function(newValue, oldValue) { + if (newValue !== oldValue) { + _this.parentScope.doRebuild = false; + _this.setCoords(scope); + return _this.parentScope.doRebuild = true; + } + }, true); + }; + + MarkerChildModel.prototype.watchIcon = function(scope) { + var _this = this; + return scope.$watch('icon', function(newValue, oldValue) { + if (newValue !== oldValue) { + _this.parentScope.doRebuild = false; + _this.setIcon(scope); + return _this.parentScope.doRebuild = true; + } + }, true); + }; + + MarkerChildModel.prototype.watchDestroy = function(scope) { + var _this = this; + return scope.$on("$destroy", function() { + _this.gMarker.setMap(null); + if (typeof notifyLocalDestroy !== "undefined" && notifyLocalDestroy !== null) { + return notifyLocalDestroy(_this.index); + } + }); + }; + + return MarkerChildModel; + + })(oo.BaseObject); + }); + +}).call(this); + +(function() { + var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, + __hasProp = {}.hasOwnProperty, + __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; + + this.ngGmapModule("directives.api.models.child", function() { + return this.WindowChildModel = (function(_super) { + __extends(WindowChildModel, _super); + + function WindowChildModel(scope, opts, isIconVisibleOnClick, mapCtrl, markerCtrl, $http, $templateCache, $compile, needToManualDestroy) { + if (needToManualDestroy == null) { + needToManualDestroy = false; + } + this.destroy = __bind(this.destroy, this); + this.scope = scope; + this.opts = opts; + this.mapCtrl = mapCtrl; + this.markerCtrl = markerCtrl; + this.isIconVisibleOnClick = isIconVisibleOnClick; + this.initialMarkerVisibility = this.markerCtrl != null ? this.markerCtrl.getVisible() : false; + this.$log = directives.api.utils.Logger; + this.$http = $http; + this.$templateCache = $templateCache; + this.$compile = $compile; + this.gWin = new google.maps.InfoWindow(opts); + if (this.markerCtrl != null) { + this.markerCtrl.setClickable(true); + } + this.handleClick(this.scope, this.mapCtrl, this.markerCtrl, this.gWin, this.isIconVisibleOnClick, this.initialMarkerVisibility); + this.watchShow(scope, $http, $templateCache, this.$compile, this.gWin, this.showWindow, this.hideWindow, this.mapCtrl); + this.needToManualDestroy = needToManualDestroy; + this.$log.info(this); + } + + WindowChildModel.prototype.watchShow = function(scope, $http, $templateCache, $compile, gWin, showHandle, hideHandle, mapCtrl) { + return scope.$watch('show', function(newValue, oldValue) { + if (newValue !== oldValue) { + if (newValue) { + return showHandle(scope, $http, $templateCache, $compile, gWin, mapCtrl); + } else { + return hideHandle(gWin); + } + } else { + if (newValue && !gWin.getMap()) { + return showHandle(scope, $http, $templateCache, $compile, gWin, mapCtrl); + } + } + }, true); + }; + + WindowChildModel.prototype.handleClick = function(scope, mapCtrl, markerInstance, gWin, isIconVisibleOnClick, initialMarkerVisibility) { + if (markerInstance != null) { + google.maps.event.addListener(markerInstance, 'click', function() { + gWin.setPosition(markerInstance.getPosition()); + gWin.open(mapCtrl); + return markerInstance.setVisible(isIconVisibleOnClick); + }); + return google.maps.event.addListener(gWin, 'closeclick', function() { + markerInstance.setVisible(initialMarkerVisibility); + return scope.closeClick(); + }); + } + }; + + WindowChildModel.prototype.showWindow = function(scope, $http, $templateCache, $compile, gWin, mapCtrl) { + if (scope.templateUrl) { + return $http.get(scope.templateUrl, { + cache: $templateCache + }).then(function(content) { + var compiled, templateScope; + templateScope = scope.$new(); + if (angular.isDefined(scope.templateParameter)) { + templateScope.parameter = scope.templateParameter; + } + compiled = $compile(content.data)(templateScope); + gWin.setContent(compiled.get(0)); + return gWin.open(mapCtrl); + }); + } else { + return gWin.open(mapCtrl); + } + }; + + WindowChildModel.prototype.hideWindow = function(gWin) { + return gWin.close(); + }; + + WindowChildModel.prototype.destroy = function() { + this.hideWindow(this.gWin); + if ((this.scope != null) && this.needToManualDestroy) { + this.scope.$destroy(); + } + delete this.gWin; + return delete this; + }; + + return WindowChildModel; + + })(oo.BaseObject); + }); + +}).call(this); + +/* + - interface for all markers to derrive from + - to enforce a minimum set of requirements + - attributes + - coords + - icon + - implementation needed on watches +*/ + + +(function() { + var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, + __hasProp = {}.hasOwnProperty, + __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; + + this.ngGmapModule("directives.api.models.parent", function() { + return this.IMarkerParentModel = (function(_super) { + __extends(IMarkerParentModel, _super); + + IMarkerParentModel.prototype.DEFAULTS = { + animation: google.maps.Animation.DROP + }; + + IMarkerParentModel.prototype.isFalse = function(value) { + return ['false', 'FALSE', 0, 'n', 'N', 'no', 'NO'].indexOf(value) !== -1; + }; + + function IMarkerParentModel(scope, element, attrs, mapCtrl, $timeout) { + this.linkInit = __bind(this.linkInit, this); + this.onDestroy = __bind(this.onDestroy, this); + this.onWatch = __bind(this.onWatch, this); + this.watch = __bind(this.watch, this); + this.validateScope = __bind(this.validateScope, this); + this.onTimeOut = __bind(this.onTimeOut, this); + var self, + _this = this; + self = this; + if (this.validateScope(scope)) { + return; + } + this.animate = angular.isDefined(attrs.animate) ? !this.isFalse(attrs.animate) : false; + this.doClick = angular.isDefined(attrs.click); + this.mapCtrl = mapCtrl; + this.clsName = "IMarker"; + this.$log = directives.api.utils.Logger; + this.$timeout = $timeout; + this.$timeout(function() { + _this.watch('coords', scope); + _this.watch('icon', scope); + _this.watch('animate', scope); + _this.onTimeOut(scope); + return scope.$on("$destroy", function() { + return _this.onDestroy(scope); + }); + }); + } + + IMarkerParentModel.prototype.onTimeOut = function(scope) {}; + + IMarkerParentModel.prototype.validateScope = function(scope) { + var ret; + ret = angular.isUndefined(scope.coords) || scope.coords === void 0; + if (ret) { + this.$log.error(this.clsName + ": no valid coords attribute found"); + } + return ret; + }; + + IMarkerParentModel.prototype.watch = function(propNameToWatch, scope) { + var _this = this; + return scope.$watch(propNameToWatch, function(newValue, oldValue) { + if (newValue !== oldValue) { + return _this.onWatch(propNameToWatch, scope, newValue, oldValue); + } + }, true); + }; + + IMarkerParentModel.prototype.onWatch = function(propNameToWatch, scope, newValue, oldValue) { + throw new Exception("Not Implemented!!"); + }; + + IMarkerParentModel.prototype.onDestroy = function(scope) { + throw new Exception("Not Implemented!!"); + }; + + IMarkerParentModel.prototype.linkInit = function(element, mapCtrl, scope, animate) { + throw new Exception("Not Implemented!!"); + }; + + return IMarkerParentModel; + + })(oo.BaseObject); + }); + +}).call(this); + +/* + - interface directive for all window(s) to derrive from +*/ + + +(function() { + var __hasProp = {}.hasOwnProperty, + __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; + + this.ngGmapModule("directives.api.models.parent", function() { + return this.IWindowParentModel = (function(_super) { + __extends(IWindowParentModel, _super); + + IWindowParentModel.include(directives.api.utils.GmapUtil); + + IWindowParentModel.prototype.DEFAULTS = {}; + + function IWindowParentModel(scope, element, attrs, ctrls, $timeout, $compile, $http, $templateCache) { + var self; + self = this; + this.clsName = "directives.api.models.parent.IWindow"; + this.$log = directives.api.utils.Logger; + this.$timeout = $timeout; + this.$compile = $compile; + this.$http = $http; + this.$templateCache = $templateCache; + } + + return IWindowParentModel; + + })(oo.BaseObject); + }); + +}).call(this); + +/* + Basic Directive api for a marker. Basic in the sense that this directive contains 1:1 on scope and model. + Thus there will be one html element per marker within the directive. +*/ + + +(function() { + var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, + __hasProp = {}.hasOwnProperty, + __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; + + this.ngGmapModule("directives.api.models.parent", function() { + return this.MarkerParentModel = (function(_super) { + __extends(MarkerParentModel, _super); + + MarkerParentModel.include(directives.api.utils.GmapUtil); + + function MarkerParentModel(scope, element, attrs, mapCtrl, $timeout) { + this.onDestroy = __bind(this.onDestroy, this); + this.onWatch = __bind(this.onWatch, this); + this.validateScope = __bind(this.validateScope, this); + var opts, self, + _this = this; + MarkerParentModel.__super__.constructor.call(this, scope, element, attrs, mapCtrl, $timeout); + self = this; + this.clsName = "MarkerParentModel"; + opts = this.createMarkerOptions(mapCtrl, scope.coords, scope.icon, this.animate, this.DEFAULTS); + this.gMarker = new google.maps.Marker(opts); + element.data('instance', this.gMarker); + this.scope = scope; + google.maps.event.addListener(this.gMarker, 'click', function() { + if (_this.doClick && (scope.click != null)) { + return $timeout(function() { + return _this.scope.click(); + }); + } + }); + this.$log.info(this); + } + + MarkerParentModel.prototype.validateScope = function(scope) { + return MarkerParentModel.__super__.validateScope.call(this, scope) || angular.isUndefined(scope.coords.latitude) || angular.isUndefined(scope.coords.longitude); + }; + + MarkerParentModel.prototype.onWatch = function(propNameToWatch, scope) { + switch (propNameToWatch) { + case 'coords': + if ((scope.coords != null) && (this.gMarker != null)) { + this.gMarker.setMap(this.mapCtrl.getMap()); + this.gMarker.setPosition(new google.maps.LatLng(scope.coords.latitude, scope.coords.longitude)); + return this.gMarker.setVisible((scope.coords.latitude != null) && (scope.coords.longitude != null)); + } else { + return this.gMarker.setMap(null); + } + break; + case 'icon': + if ((scope.icon != null) && (scope.coords != null) && (this.gMarker != null)) { + this.gMarker.icon = scope.icon; + this.gMarker.setMap(null); + this.gMarker.setMap(this.mapCtrl.getMap()); + this.gMarker.setPosition(new google.maps.LatLng(scope.coords.latitude, scope.coords.longitude)); + return this.gMarker.setVisible(scope.coords.latitude && (scope.coords.longitude != null)); + } + break; + case 'animate': + break; + } + }; + + MarkerParentModel.prototype.onDestroy = function(scope) { + if (this.gMarker === void 0) { + delete this; + return; + } + this.gMarker.setMap(null); + delete this.gMarker; + return delete this; + }; + + return MarkerParentModel; + + })(directives.api.models.parent.IMarkerParentModel); + }); + +}).call(this); + +(function() { + var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, + __hasProp = {}.hasOwnProperty, + __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; + + this.ngGmapModule("directives.api.models.parent", function() { + return this.MarkersParentModel = (function(_super) { + __extends(MarkersParentModel, _super); + + function MarkersParentModel(scope, element, attrs, mapCtrl, $timeout) { + this.onDestroy = __bind(this.onDestroy, this); + this.onWatch = __bind(this.onWatch, this); + this.reBuildMarkers = __bind(this.reBuildMarkers, this); + this.createMarkers = __bind(this.createMarkers, this); + this.validateScope = __bind(this.validateScope, this); + this.onTimeOut = __bind(this.onTimeOut, this); + var self; + MarkersParentModel.__super__.constructor.call(this, scope, element, attrs, mapCtrl, $timeout); + self = this; + this.clsName = "MarkersParentModel"; + this.markers = []; + this.markersIndex = 0; + this.scope = scope; + this.bigGulp = directives.api.utils.AsyncProcessor; + this.$log.info(this); + } + + MarkersParentModel.prototype.onTimeOut = function(scope) { + this.watch('models', scope); + return this.createMarkers(scope); + }; + + MarkersParentModel.prototype.validateScope = function(scope) { + var modelsNotDefined; + modelsNotDefined = angular.isUndefined(scope.models) || scope.models === void 0; + if (modelsNotDefined) { + this.$log.error(this.clsName + ": no valid models attribute found"); + } + return MarkersParentModel.__super__.validateScope.call(this, scope) || modelsNotDefined; + }; + + MarkersParentModel.prototype.createMarkers = function(scope) { + var _this = this; + this.bigGulp.handleLargeArray(scope.models, function(model) { + scope.doRebuild = true; + _this.markers.push(new directives.api.models.child.MarkerChildModel(_this.markersIndex, model, scope, _this.mapCtrl, _this.$timeout, function(index) { + return delete _this.markers[index]; + }, _this.DEFAULTS, _this.doClick)); + return _this.markersIndex++; + }); + return scope.markerModels = this.markers; + }; + + MarkersParentModel.prototype.reBuildMarkers = function(scope) { + var oldM, _fn, _i, _len, _ref, + _this = this; + if (!scope.doRebuild && scope.doRebuild !== void 0) { + return; + } + _ref = this.markers; + _fn = function(oldM) { + return oldM.destroy(); + }; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + oldM = _ref[_i]; + _fn(oldM); + } + delete this.markers; + this.markers = []; + this.markersIndex = 0; + return this.createMarkers(scope); + }; + + MarkersParentModel.prototype.onWatch = function(propNameToWatch, scope, newValue, oldValue) { + if (propNameToWatch === 'models' && newValue.length === oldValue.length) { + return; + } + return this.reBuildMarkers(scope); + }; + + MarkersParentModel.prototype.onDestroy = function(scope) { + var model, _i, _len, _ref, _results; + _ref = this.markers; + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + model = _ref[_i]; + _results.push(model.destroy()); + } + return _results; + }; + + return MarkersParentModel; + + })(directives.api.models.parent.IMarkerParentModel); + }); + +}).call(this); + +/* + Windows directive where many windows map to the models property +*/ + + +(function() { + var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, + __hasProp = {}.hasOwnProperty, + __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; + + this.ngGmapModule("directives.api.models.parent", function() { + return this.WindowsParentModel = (function(_super) { + __extends(WindowsParentModel, _super); + + function WindowsParentModel(scope, element, attrs, ctrls, $timeout, $compile, $http, $templateCache, $interpolate) { + this.interpolateContent = __bind(this.interpolateContent, this); + this.setChildScope = __bind(this.setChildScope, this); + this.createWindow = __bind(this.createWindow, this); + this.setContentKeys = __bind(this.setContentKeys, this); + this.createChildScopesWindows = __bind(this.createChildScopesWindows, this); + this.watchOurScope = __bind(this.watchOurScope, this); + this.watchDestroy = __bind(this.watchDestroy, this); + this.watchModels = __bind(this.watchModels, this); + this.watch = __bind(this.watch, this); + var name, self, _i, _len, _ref, + _this = this; + WindowsParentModel.__super__.constructor.call(this, scope, element, attrs, ctrls, $timeout, $compile, $http, $templateCache, $interpolate); + self = this; + this.$interpolate = $interpolate; + this.clsName = "WindowsParentModel"; + this.windows = []; + this.windwsIndex = 0; + this.scopePropNames = ['show', 'coords', 'templateUrl', 'templateParameter', 'isIconVisibleOnClick', 'closeClick']; + _ref = this.scopePropNames; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + name = _ref[_i]; + this[name + 'Key'] = void 0; + } + this.linked = new directives.api.utils.Linked(scope, element, attrs, ctrls); + this.models = void 0; + this.contentKeys = void 0; + this.isIconVisibleOnClick = void 0; + this.firstTime = true; + this.bigGulp = directives.api.utils.AsyncProcessor; + this.$log.info(self); + this.$timeout(function() { + _this.watchOurScope(scope); + return _this.createChildScopesWindows(); + }, 50); + } + + WindowsParentModel.prototype.watch = function(scope, name, nameKey) { + var _this = this; + return scope.$watch(name, function(newValue, oldValue) { + var model, _i, _len, _ref, _results; + if (newValue !== oldValue) { + _this[nameKey] = typeof newValue === 'function' ? newValue() : newValue; + _ref = _this.windows; + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + model = _ref[_i]; + _results.push((function(model) { + return model.scope[name] = _this[nameKey] === 'self' ? model : model[_this[nameKey]]; + })(model)); + } + return _results; + } + }, true); + }; + + WindowsParentModel.prototype.watchModels = function(scope) { + var _this = this; + return scope.$watch('models', function(newValue, oldValue) { + if (newValue !== oldValue && newValue.length !== oldValue.length) { + _this.bigGulp.handleLargeArray(_this.windows, function(model) { + return model.destroy(); + }); + _this.windows = []; + _this.windowsIndex = 0; + return _this.createChildScopesWindows(); + } + }, true); + }; + + WindowsParentModel.prototype.watchDestroy = function(scope) { + var _this = this; + return scope.$on("$destroy", function() { + _this.bigGulp.handleLargeArray(_this.windows, function(model) { + return model.destroy(); + }); + delete _this.windows; + _this.windows = []; + return _this.windowsIndex = 0; + }); + }; + + WindowsParentModel.prototype.watchOurScope = function(scope) { + var name, _i, _len, _ref, _results, + _this = this; + _ref = this.scopePropNames; + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + name = _ref[_i]; + _results.push((function(name) { + var nameKey; + nameKey = name + 'Key'; + _this[nameKey] = typeof scope[name] === 'function' ? scope[name]() : scope[name]; + return _this.watch(scope, name, nameKey); + })(name)); + } + return _results; + }; + + WindowsParentModel.prototype.createChildScopesWindows = function() { + /* + being that we cannot tell the difference in Key String vs. a normal value string (TemplateUrl) + we will assume that all scope values are string expressions either pointing to a key (propName) or using + 'self' to point the model as container/object of interest. + + This may force redundant information into the model, but this appears to be the most flexible approach. + */ + + var gMap, markersScope, modelsNotDefined, + _this = this; + this.isIconVisibleOnClick = true; + if (angular.isDefined(this.linked.attrs.isiconvisibleonclick)) { + this.isIconVisibleOnClick = this.linked.scope.isIconVisibleOnClick; + } + gMap = this.linked.ctrls[0].getMap(); + markersScope = this.linked.ctrls.length > 1 && (this.linked.ctrls[1] != null) ? this.linked.ctrls[1].getMarkersScope() : void 0; + modelsNotDefined = angular.isUndefined(this.linked.scope.models) || scope.models === void 0; + if (modelsNotDefined && (markersScope === void 0 || markersScope.markerModels === void 0 || markersScope.models === void 0)) { + this.$log.info("No models to create windows from! Need direct models or models derrived from markers!"); + return; + } + if (gMap != null) { + if (this.linked.scope.models != null) { + this.models = this.linked.scope.models; + if (this.firstTime) { + this.watchModels(this.linked.scope); + this.watchDestroy(this.linked.scope); + } + this.setContentKeys(this.linked.scope.models); + this.bigGulp.handleLargeArray(this.linked.scope.models, function(model) { + return _this.createWindow(model, void 0, gMap); + }); + } else { + this.models = markersScope.models; + if (this.firstTime) { + this.watchModels(markersScope); + this.watchDestroy(markersScope); + } + this.setContentKeys(markersScope.models); + this.bigGulp.handleLargeArray(markersScope.markerModels, function(mm) { + return _this.createWindow(mm.model, mm.gMarker, gMap); + }); + } + } + return this.firstTime = false; + }; + + WindowsParentModel.prototype.setContentKeys = function(models) { + if (models.length > 0) { + return this.contentKeys = Object.keys(models[0]); + } + }; + + WindowsParentModel.prototype.createWindow = function(model, gMarker, gMap) { + /* + Create ChildScope to Mimmick an ng-repeat created scope, must define the below scope + scope= { + coords: '=coords', + show: '&show', + templateUrl: '=templateurl', + templateParameter: '=templateparameter', + isIconVisibleOnClick: '=isiconvisibleonclick', + closeClick: '&closeclick' + } + */ + + var childScope, opts, parsedContent, + _this = this; + childScope = this.linked.scope.$new(false); + this.setChildScope(childScope, model); + childScope.$watch('model', function(newValue, oldValue) { + if (newValue !== oldValue) { + return _this.setChildScope(childScope, newValue); + } + }, true); + parsedContent = this.interpolateContent(this.linked.element.html(), model); + opts = this.createWindowOptions(gMarker, childScope, parsedContent, this.DEFAULTS); + return this.windows.push(new directives.api.models.child.WindowChildModel(childScope, opts, this.isIconVisibleOnClick, gMap, gMarker, this.$http, this.$templateCache, this.$compile, true)); + }; + + WindowsParentModel.prototype.setChildScope = function(childScope, model) { + var name, _fn, _i, _len, _ref, + _this = this; + _ref = this.scopePropNames; + _fn = function(name) { + var nameKey, newValue; + nameKey = name + 'Key'; + newValue = _this[nameKey] === 'self' ? model : model[_this[nameKey]]; + if (newValue !== childScope[name]) { + return childScope[name] = newValue; + } + }; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + name = _ref[_i]; + _fn(name); + } + return childScope.model = model; + }; + + WindowsParentModel.prototype.interpolateContent = function(content, model) { + var exp, interpModel, key, _i, _len, _ref; + if (this.contentKeys === void 0 || this.contentKeys.length === 0) { + return; + } + exp = this.$interpolate(content); + interpModel = {}; + _ref = this.contentKeys; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + key = _ref[_i]; + interpModel[key] = model[key]; + } + return exp(interpModel); + }; + + return WindowsParentModel; + + })(directives.api.models.parent.IWindowParentModel); + }); + +}).call(this); + +/* + - interface for all markers to derrive from + - to enforce a minimum set of requirements + - attributes + - coords + - icon + - implementation needed on watches +*/ + + +(function() { + var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, + __hasProp = {}.hasOwnProperty, + __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; + + this.ngGmapModule("directives.api", function() { + return this.IMarker = (function(_super) { + __extends(IMarker, _super); + + function IMarker($timeout) { + this.link = __bind(this.link, this); + var self; + self = this; + this.clsName = "IMarker"; + this.$log = directives.api.utils.Logger; + this.$timeout = $timeout; + this.restrict = 'ECMA'; + this.require = '^googleMap'; + this.priority = -1; + this.transclude = true; + this.replace = true; + this.scope = { + coords: '=coords', + icon: '=icon', + click: '&click' + }; + } + + IMarker.prototype.controller = function($scope, $element) { + throw new Exception("Not Implemented!!"); + }; + + IMarker.prototype.link = function(scope, element, attrs, ctrl) { + throw new Exception("Not implemented!!"); + }; + + return IMarker; + + })(oo.BaseObject); + }); + +}).call(this); + +/* + - interface directive for all window(s) to derrive from +*/ + + +(function() { + var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, + __hasProp = {}.hasOwnProperty, + __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; + + this.ngGmapModule("directives.api", function() { + return this.IWindow = (function(_super) { + __extends(IWindow, _super); + + function IWindow($timeout, $compile, $http, $templateCache) { + this.link = __bind(this.link, this); + var self; + self = this; + this.clsName = "IWindow"; + this.restrict = 'ECMA'; + this.template = void 0; + this.transclude = true; + this.priority = -100; + this.require = void 0; + this.scope = { + coords: '=coords', + show: '=show', + templateUrl: '=templateurl', + templateParameter: '=templateparameter', + isIconVisibleOnClick: '=isiconvisibleonclick', + closeClick: '&closeclick' + }; + this.$log = directives.api.utils.Logger; + this.$timeout = $timeout; + this.$compile = $compile; + this.$http = $http; + this.$templateCache = $templateCache; + } + + IWindow.prototype.link = function(scope, element, attrs, ctrls) { + throw new Exception("Not Implemented!!"); + }; + + return IWindow; + + })(oo.BaseObject); + }); + +}).call(this); + +/* + Basic Directive api for a marker. Basic in the sense that this directive contains 1:1 on scope and model. + Thus there will be one html element per marker within the directive. +*/ + + +(function() { + var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, + __hasProp = {}.hasOwnProperty, + __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; + + this.ngGmapModule("directives.api", function() { + return this.Marker = (function(_super) { + __extends(Marker, _super); + + function Marker($timeout) { + this.link = __bind(this.link, this); + var self; + Marker.__super__.constructor.call(this, $timeout); + self = this; + this.template = ''; + this.clsName = "Marker"; + this.$log.info(this); + } + + Marker.prototype.controller = function($scope, $element) { + return this.getMarker = function() { + return $element.data('instance'); + }; + }; + + Marker.prototype.link = function(scope, element, attrs, ctrl) { + return new directives.api.models.parent.MarkerParentModel(scope, element, attrs, ctrl, this.$timeout); + }; + + return Marker; + + })(directives.api.IMarker); + }); + +}).call(this); + +/* +Markers will map icon and coords differently than directibes.api.Marker. This is because Scope and the model marker are +not 1:1 in this setting. + + - icon - will be the iconKey to the marker value ie: to get the icon marker[iconKey] + - coords - will be the coordsKey to the marker value ie: to get the icon marker[coordsKey] + + - watches from IMarker reflect that the look up key for a value has changed and not the actual icon or coords itself + - actual changes to a model are tracked inside directives.api.model.MarkerModel +*/ + + +(function() { + var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, + __hasProp = {}.hasOwnProperty, + __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; + + this.ngGmapModule("directives.api", function() { + return this.Markers = (function(_super) { + __extends(Markers, _super); + + function Markers($timeout) { + this.link = __bind(this.link, this); + var self; + Markers.__super__.constructor.call(this, $timeout); + self = this; + this.template = ''; + this.clsName = "Markers"; + this.scope.models = '=models'; + this.$timeout = $timeout; + this.$log.info(this); + } + + Markers.prototype.controller = function($scope, $element) { + return this.getMarkersScope = function() { + return $scope; + }; + }; + + Markers.prototype.link = function(scope, element, attrs, ctrl) { + return new directives.api.models.parent.MarkersParentModel(scope, element, attrs, ctrl, this.$timeout); + }; + + return Markers; + + })(directives.api.IMarker); + }); + +}).call(this); + +/* + Window directive for GoogleMap Info Windows, where ng-repeat is being used.... + Where Html DOM element is 1:1 on Scope and a Model +*/ + + +(function() { + var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, + __hasProp = {}.hasOwnProperty, + __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; + + this.ngGmapModule("directives.api", function() { + return this.Window = (function(_super) { + __extends(Window, _super); + + Window.include(directives.api.utils.GmapUtil); + + function Window($timeout, $compile, $http, $templateCache) { + this.link = __bind(this.link, this); + var self; + Window.__super__.constructor.call(this, $timeout, $compile, $http, $templateCache); + self = this; + this.clsName = "Window"; + this.require = ['^googleMap', '^?marker']; + this.template = ''; + this.$log.info(self); + } + + Window.prototype.link = function(scope, element, attrs, ctrls) { + var _this = this; + return this.$timeout(function() { + var isIconVisibleOnClick, mapCtrl, markerCtrl, opts, window; + isIconVisibleOnClick = true; + if (angular.isDefined(attrs.isiconvisibleonclick)) { + isIconVisibleOnClick = scope.isIconVisibleOnClick; + } + mapCtrl = ctrls[0].getMap(); + markerCtrl = ctrls.length > 1 && (ctrls[1] != null) ? ctrls[1].getMarker() : void 0; + opts = _this.createWindowOptions(markerCtrl, scope, element.html(), _this.DEFAULTS); + if (mapCtrl != null) { + window = new directives.api.models.child.WindowChildModel(scope, opts, isIconVisibleOnClick, mapCtrl, markerCtrl, _this.$http, _this.$templateCache, _this.$compile); + } + return scope.$on("$destroy", function() { + return window.destroy(); + }); + }, 50); + }; + + return Window; + + })(directives.api.IWindow); + }); + +}).call(this); + +/* + Windows directive where many windows map to the models property +*/ + + +(function() { + var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, + __hasProp = {}.hasOwnProperty, + __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; + + this.ngGmapModule("directives.api", function() { + return this.Windows = (function(_super) { + __extends(Windows, _super); + + function Windows($timeout, $compile, $http, $templateCache, $interpolate) { + this.link = __bind(this.link, this); + var self; + Windows.__super__.constructor.call(this, $timeout, $compile, $http, $templateCache); + self = this; + this.$interpolate = $interpolate; + this.clsName = "Windows"; + this.require = ['^googleMap', '^?markers']; + this.template = ''; + this.scope.models = '=models'; + this.$log.info(self); + } + + Windows.prototype.link = function(scope, element, attrs, ctrls) { + return new directives.api.models.parent.WindowsParentModel(scope, element, attrs, ctrls, this.$timeout, this.$compile, this.$http, this.$templateCache, this.$interpolate); + }; + + return Windows; + + })(directives.api.IWindow); + }); + +}).call(this); +;angular.module("google-maps") + .factory('array-sync',['add-events',function(mapEvents){ + + return function LatLngArraySync(mapArray,scope,pathEval){ + var scopeArray = scope.$eval(pathEval); + + var mapArrayListener = mapEvents(mapArray,{ + 'set_at':function(index){ + var value = mapArray.getAt(index); + scopeArray[index].latitude = value.lat(); + scopeArray[index].longitude = value.lng(); + }, + 'insert_at':function(index){ + var value = mapArray.getAt(index); + scopeArray.splice(index,0,{latitude:value.lat(),longitude:value.lng()}); + }, + 'remove_at':function(index){ + scopeArray.splice(index,1); + } + }); + + var watchListener = scope.$watch(pathEval, function (newArray) { + var oldArray = mapArray; + if (newArray) { + var i = 0; + var oldLength = oldArray.getLength(); + var newLength = newArray.length; + var l = Math.min(oldLength,newLength); + var newValue; + for(;i < l; i++){ + var oldValue = oldArray.getAt(i); + newValue = newArray[i]; + if((oldValue.lat() != newValue.latitude) || (oldValue.lng() != newValue.longitude)){ + oldArray.setAt(i,new google.maps.LatLng(newValue.latitude, newValue.longitude)); + } + } + for(; i < newLength; i++){ + newValue = newArray[i]; + oldArray.push(new google.maps.LatLng(newValue.latitude, newValue.longitude)); + } + for(; i < oldLength; i++){ + oldArray.pop(); + } + } + + }, true); + + return function(){ + if(mapArrayListener){ + mapArrayListener(); + mapArrayListener = null; + } + if(watchListener){ + watchListener(); + watchListener = null; + } + }; + }; + }]);;angular.module('google-maps').factory('add-events', ['$timeout',function($timeout){ + + function addEvent(target,eventName,handler){ + return google.maps.event.addListener(target,eventName,function(){ + handler.apply(this,arguments); + $timeout(function(){},true); + }); + } + + function addEvents(target,eventName,handler){ + if(handler){ + return addEvent(target,eventName,handler); + } + var remove = []; + angular.forEach(eventName,function(_handler,key){ + console.log('adding listener: ' + key + ": " + _handler.toString() + " to : " + target); + remove.push(addEvent(target,key,_handler)); + }); + + return function(){ + angular.forEach(remove,function(fn){fn();}); + remove = null; + }; + } + + return addEvents; + +}]);;angular.module('google-maps').controller('PolylineDisplayController',['$scope',function($scope){ + $scope.toggleStrokeColor = function(){ + $scope.stroke.color = ($scope.stroke.color == "#6060FB") ? "red" : "#6060FB"; + }; +}]);;/**! * The MIT License * * Copyright (c) 2010-2012 Google, Inc. http://angularjs.org @@ -60,6 +1411,8 @@ angular.module('google-maps') .directive('googleMap', ['$log', '$timeout', function ($log, $timeout) { "use strict"; + + directives.api.utils.Logger.logger = $log; var DEFAULTS = { mapTypeId: google.maps.MapTypeId.ROADMAP @@ -373,7 +1726,7 @@ angular.module('google-maps') * angular-google-maps * https://github.com/nlaplante/angular-google-maps * - * @author Nicolas Laplante https://plus.google.com/108189012221374960701 + * @authors Nicolas Laplante, Nicholas McCready https://plus.google.com/108189012221374960701 */ /** @@ -387,110 +1740,52 @@ angular.module('google-maps') * {attribute animate optional} if set to false, the marker won't be animated (on by default) */ -angular.module('google-maps') - .directive('marker', ['$log', '$timeout', function ($log, $timeout) { - - "use strict"; - - var DEFAULTS = { - // Animation is enabled by default - animation: google.maps.Animation.DROP - }; - - /** - * Check if a value is literally false - * @param value the value to test - * @returns {boolean} true if value is literally false, false otherwise - */ - function isFalse(value) { - return ['false', 'FALSE', 0, 'n', 'N', 'no', 'NO'].indexOf(value ) !== -1; - } - - return { - restrict: 'ECMA', - require: '^googleMap', - priority: -1, - transclude: true, - template: '', - replace: true, - scope: { - coords: '=coords', - icon: '=icon', - click: '&click' - }, - controller: function ($scope, $element) { - this.getMarker = function () { - return $element.data('instance'); - }; - }, - link: function (scope, element, attrs, mapCtrl) { - - // Validate required properties - if (angular.isUndefined(scope.coords) || - scope.coords === null || - angular.isUndefined(scope.coords.latitude) || - angular.isUndefined(scope.coords.longitude)) { - - $log.error("marker: no valid coords attribute found"); - return; - } - - // Wrap marker initialization inside a $timeout() call to make sure the map is created already - $timeout(function () { - var opts = angular.extend({}, DEFAULTS, { - position: new google.maps.LatLng(scope.coords.latitude, scope.coords.longitude), - map: mapCtrl.getMap(), - icon: scope.icon, - visible: scope.coords.latitude !== null && scope.coords.longitude !== null - }); - - // Disable animations - if (angular.isDefined(attrs.animate) && isFalse(attrs.animate)) { - delete opts.animation; - } - - var marker = new google.maps.Marker(opts); - element.data('instance', marker); - - google.maps.event.addListener(marker, 'click', function () { - if (angular.isDefined(attrs.click) && scope.click !== null) - $timeout(function() { - scope.click(); - }); - }); - - scope.$watch('coords', function (newValue, oldValue) { - if (newValue !== oldValue) { - if (newValue) { - marker.setMap(mapCtrl.getMap()); - marker.setPosition(new google.maps.LatLng(newValue.latitude, newValue.longitude)); - marker.setVisible(newValue.latitude !== null && newValue.longitude !== null); - } - else { - // Remove marker - marker.setMap(null); - } - } - }, true); +angular.module('google-maps').directive('marker', ['$timeout', function ($timeout) { + return new directives.api.Marker($timeout); +}]); +;/**! + * The MIT License + * + * Copyright (c) 2010-2012 Google, Inc. http://angularjs.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * angular-google-maps + * https://github.com/nlaplante/angular-google-maps + * + * @author Nicolas Laplante, Nicholas McCready https://plus.google.com/108189012221374960701 + */ - scope.$watch('icon', function (newValue, oldValue) { - if (newValue !== oldValue) { - marker.icon = newValue; - marker.setMap(null); - marker.setMap(mapCtrl.getMap()); - marker.setPosition(new google.maps.LatLng(scope.coords.latitude, scope.coords.longitude)); - marker.setVisible(scope.coords.latitude !== null && scope.coords.longitude !== null); - } - }, true); +/** + * Map marker directive + * + * This directive is used to create a marker on an existing map. + * This directive creates a new scope. + * + * {attribute coords required} object containing latitude and longitude properties + * {attribute icon optional} string url to image used for marker icon + * {attribute animate optional} if set to false, the marker won't be animated (on by default) + */ - // remove marker on scope $destroy - scope.$on("$destroy", function () { - marker.setMap(null); - }); - }); - } - }; - }]); +angular.module('google-maps').directive('markers', ['$timeout', function ($timeout) { + return new directives.api.Markers($timeout); +}]); ;/**! * The MIT License * @@ -521,7 +1816,7 @@ angular.module('google-maps') */ angular.module("google-maps") - .directive("polyline", ['$log', '$timeout', function ($log, $timeout) { + .directive("polygon", ['$log', '$timeout', function ($log, $timeout) { "use strict"; @@ -541,7 +1836,7 @@ angular.module("google-maps") } function convertPathPoints(path) { - var result = []; + var result = new google.maps.MVCArray(); for (var i = 0; i < path.length; i++) { result.push(new google.maps.LatLng(path[i].latitude, path[i].longitude)); @@ -554,7 +1849,7 @@ angular.module("google-maps") var bounds = new google.maps.LatLngBounds(); for (var i = 0; i < points.length; i++) { - bounds.extend(points[i]); + bounds.extend(points.getAt(i)); } map.fitBounds(bounds); @@ -581,7 +1876,13 @@ angular.module("google-maps") require: '^googleMap', scope: { path: '=path', - stroke: '=stroke' + stroke: '=stroke', + clickable: '=', + draggable: '=', + editable: '=', + geodesic: '=', + icons:'=icons', + visible:'=' }, link: function (scope, element, attrs, mapCtrl) { // Validate required properties @@ -594,12 +1895,15 @@ angular.module("google-maps") return; } + // Wrap polyline initialization inside a $timeout() call to make sure the map is created already $timeout(function () { var map = mapCtrl.getMap(); var pathPoints = convertPathPoints(scope.path); + + var opts = angular.extend({}, DEFAULTS, { map: map, path: pathPoints, @@ -608,34 +1912,116 @@ angular.module("google-maps") strokeWeight: scope.stroke && scope.stroke.weight }); + + angular.forEach({ + clickable:true, + draggable:false, + editable:false, + geodesic:false, + visible:true + },function (defaultValue, key){ + if(angular.isUndefined(scope[key]) || scope[key] === null){ + opts[key] = defaultValue; + } + else { + opts[key] = scope[key]; + } + }); + var polyline = new google.maps.Polyline(opts); if (isTrue(attrs.fit)) { extendMapBounds(map, pathPoints); } - scope.$watch('path', function (newValue, oldValue) { - if (newValue !== oldValue) { - if (newValue) { - var newPathPoints = convertPathPoints(newValue); + if(angular.isDefined(scope.editable)) { + scope.$watch('editable',function(newValue,oldValue){ + polyline.setEditable(newValue); + }); + } + if(angular.isDefined(scope.draggable)){ + scope.$watch('draggable',function(newValue,oldValue){ + polyline.setDraggable(newValue); + }); + } + if(angular.isDefined(scope.visible)){ + scope.$watch('visible',function(newValue,oldValue){ + polyline.setVisible(newValue); + }); + } + + var pathSetAtListener, pathInsertAtListener, pathRemoveAtListener; - polyline.setMap(map); - polyline.setPath(newPathPoints); + var polyPath = polyline.getPath(); + + pathSetAtListener = google.maps.event.addListener(polyPath, 'set_at',function(index){ + var value = polyPath.getAt(index); + scope.path[index].latitude = value.lat(); + scope.path[index].longitude = value.lng(); + scope.$apply(); + }); + pathInsertAtListener = google.maps.event.addListener(polyPath, 'insert_at',function(index){ + var value = polyPath.getAt(index); + scope.path.splice(index,0,{latitude:value.lat(),longitude:value.lng()}); + scope.$apply(); + }); + pathRemoveAtListener = google.maps.event.addListener(polyPath, 'remove_at',function(index){ + scope.path.splice(index,1); + scope.$apply(); + }); + + + + scope.$watch('path', function (newArray) { + var oldArray = polyline.getPath(); + if (newArray !== oldArray) { + if (newArray) { + + polyline.setMap(map); + + + var i = 0; + var oldLength = oldArray.getLength(); + var newLength = newArray.length; + var l = Math.min(oldLength,newLength); + for(;i < l; i++){ + oldValue = oldArray.getAt(i); + newValue = newArray[i]; + if((oldValue.lat() != newValue.latitude) || (oldValue.lng() != newValue.longitude)){ + oldArray.setAt(i,new google.maps.LatLng(newValue.latitude, newValue.longitude)); + } + } + for(; i < newLength; i++){ + newValue = newArray[i]; + oldArray.push(new google.maps.LatLng(newValue.latitude, newValue.longitude)); + } + for(; i < oldLength; i++){ + oldArray.pop(); + } - if (isTrue(attrs.fit)) { - extendMapBounds(map, newPathPoints); + if (isTrue(attrs.fit)) { + extendMapBounds(map, oldArray); + } + } + else { + // Remove polyline + polyline.setMap(null); } } - else { - // Remove polyline - polyline.setMap(null); - } - } + + }, true); // Remove polyline on scope $destroy scope.$on("$destroy", function () { polyline.setMap(null); + pathSetAtListener(); + pathSetAtListener = null; + pathInsertAtListener(); + pathInsertAtListener = null; + pathRemoveAtListener(); + pathRemoveAtListener = null; + }); }); } @@ -670,18 +2056,8 @@ angular.module("google-maps") * @author Nicolas Laplante https://plus.google.com/108189012221374960701 */ -/** - * Map info window directive - * - * This directive is used to create an info window on an existing map. - * This directive creates a new scope. - * - * {attribute coords required} object containing latitude and longitude properties - * {attribute show optional} map will show when this expression returns true - */ - -angular.module("google-maps"). - directive("window", ['$log', '$timeout','$compile', '$http', '$templateCache', function ($log, $timeout, $compile, $http, $templateCache) { +angular.module("google-maps") + .directive("polyline", ['$log', '$timeout','array-sync', function ($log, $timeout,arraySync) { "use strict"; @@ -689,102 +2065,265 @@ angular.module("google-maps"). }; + function validatePathPoints(path) { + for (var i = 0; i < path.length; i++) { + if (angular.isUndefined(path[i].latitude) || + angular.isUndefined(path[i].longitude)) { + return false; + } + } + + return true; + } + + function convertPathPoints(path) { + var result = new google.maps.MVCArray(); + + for (var i = 0; i < path.length; i++) { + result.push(new google.maps.LatLng(path[i].latitude, path[i].longitude)); + } + + return result; + } + + function extendMapBounds(map, points) { + var bounds = new google.maps.LatLngBounds(); + + for (var i = 0; i < points.length; i++) { + bounds.extend(points.getAt(i)); + } + + map.fitBounds(bounds); + } + + /* + * Utility functions + */ + + /** + * Check if a value is true + */ + function isTrue(val) { + return angular.isDefined(val) && + val !== null && + val === true || + val === '1' || + val === 'y' || + val === 'true'; + } + return { - restrict: 'ECMA', - template: '', - transclude: true, - priority: -100, - require: ['^googleMap', '^?marker'], - scope: { - coords: '=coords', - show: '&show', - templateUrl: '=templateurl', - templateParameter: '=templateparameter', - isIconVisibleOnClick: '=isiconvisibleonclick', - closeClick: '&closeclick' //scope glue to gmap InfoWindow closeclick - }, - link: function (scope, element, attrs, ctrls) { - $timeout(function () { - - var isIconVisibleOnClick = true; - - if (angular.isDefined(attrs.isiconvisibleonclick)) - isIconVisibleOnClick = scope.isIconVisibleOnClick; - - var mapCtrl = ctrls[0], - markerCtrl = ctrls.length > 1 ? ctrls[1] : null; - - var opts = angular.extend({}, DEFAULTS, { - content: element.html(), - position: angular.isDefined(markerCtrl) ? markerCtrl.getMarker().getPosition() : - new google.maps.LatLng(scope.coords.latitude, scope.coords.longitude) - }); - - var win = new google.maps.InfoWindow(opts); - - if (angular.isDefined(markerCtrl)) { - // Open window on click - var markerInstance = markerCtrl.getMarker(); - - markerInstance.setClickable(true); - - // Show the window and hide the marker on click - var initialMarkerVisibility; - google.maps.event.addListener(markerInstance, 'click', function () { - win.setPosition(markerInstance.getPosition()); - win.open(mapCtrl.getMap()); - - initialMarkerVisibility = markerInstance.getVisible(); - - markerInstance.setVisible(isIconVisibleOnClick); - }); + restrict: 'ECA', + require: '^googleMap', + scope: { + path: '=path', + stroke: '=stroke', + clickable: '=', + draggable: '=', + editable: '=', + geodesic: '=', + icons:'=icons', + visible:'=' + }, + link: function (scope, element, attrs, mapCtrl) { + // Validate required properties + if (angular.isUndefined(scope.path) || + scope.path === null || + scope.path.length < 2 || + !validatePathPoints(scope.path)) { - // Set visibility of marker back to what it was before opening the window - google.maps.event.addListener(win, 'closeclick', function () { - markerInstance.setVisible(initialMarkerVisibility); - scope.closeClick(); - }); - } - - function showWindow() { - if (scope.templateUrl) { - $http.get(scope.templateUrl, { cache: $templateCache }) - .then(function (content) { - var templateScope = scope.$new(); - if (angular.isDefined(scope.templateParameter)) { - templateScope.parameter = scope.templateParameter; - } - var compiled = $compile(content.data)(templateScope); - win.setContent(compiled.get(0)); - win.open(mapCtrl.getMap()); - }); - } else { - win.open(mapCtrl.getMap()); + $log.error("polyline: no valid path attribute found"); + return; + } + + + // Wrap polyline initialization inside a $timeout() call to make sure the map is created already + $timeout(function () { + var map = mapCtrl.getMap(); + + + + + function buildOpts (pathPoints){ + + + var opts = angular.extend({}, DEFAULTS, { + map: map, + path: pathPoints, + strokeColor: scope.stroke && scope.stroke.color, + strokeOpacity: scope.stroke && scope.stroke.opacity, + strokeWeight: scope.stroke && scope.stroke.weight + }); + + + angular.forEach({ + clickable:true, + draggable:false, + editable:false, + geodesic:false, + visible:true + },function (defaultValue, key){ + if(angular.isUndefined(scope[key]) || scope[key] === null){ + opts[key] = defaultValue; + } + else { + opts[key] = scope[key]; + } + }); + + return opts; } - } - function hideWindow() { - win.close(); - } + var polyline = new google.maps.Polyline(buildOpts(convertPathPoints(scope.path))); - scope.$watch('show()', function (newValue, oldValue) { - if (newValue !== oldValue) { - if (newValue) { - showWindow(); - } - else { - hideWindow(); - } - } else if (newValue && !win.getMap()) { - // If we're initially showing the marker and it's not yet visible, show it. - showWindow(); + if (isTrue(attrs.fit)) { + extendMapBounds(map, pathPoints); } - },true); - }, 50); - } + + if(angular.isDefined(scope.editable)) { + scope.$watch('editable',function(newValue,oldValue){ + polyline.setEditable(newValue); + }); + } + if(angular.isDefined(scope.draggable)){ + scope.$watch('draggable',function(newValue,oldValue){ + polyline.setDraggable(newValue); + }); + } + if(angular.isDefined(scope.visible)){ + scope.$watch('visible',function(newValue,oldValue){ + polyline.setVisible(newValue); + }); + } + if(angular.isDefined(scope.geodesic)){ + scope.$watch('geodesic',function(newValue,oldValue){ + polyline.setOptions(buildOpts(polyline.getPath())); + }); + } + + if(angular.isDefined(scope.stroke) && angular.isDefined(scope.stroke.weight)){ + scope.$watch('stroke.weight',function(newValue,oldValue){ + polyline.setOptions(buildOpts(polyline.getPath())); + }); + } + + + if(angular.isDefined(scope.stroke) && angular.isDefined(scope.stroke.color)){ + scope.$watch('stroke.color',function(newValue,oldValue){ + polyline.setOptions(buildOpts(polyline.getPath())); + }); + } + + + + + var arraySyncer = arraySync(polyline.getPath(),scope,'path'); + + + + + + // Remove polyline on scope $destroy + scope.$on("$destroy", function () { + polyline.setMap(null); + + if(arraySyncer) { + arraySyncer(); + arraySyncer= null; + } + + }); + }); + } }; }]); -;/**! +;/**! + * The MIT License + * + * Copyright (c) 2010-2012 Google, Inc. http://angularjs.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * angular-google-maps + * https://github.com/nlaplante/angular-google-maps + * + * @author Nicolas Laplante https://plus.google.com/108189012221374960701 + */ + +/** + * Map info window directive + * + * This directive is used to create an info window on an existing map. + * This directive creates a new scope. + * + * {attribute coords required} object containing latitude and longitude properties + * {attribute show optional} map will show when this expression returns true + */ + +angular.module("google-maps").directive("window", ['$timeout','$compile', '$http', '$templateCache', + function ($timeout, $compile, $http, $templateCache) { + return new directives.api.Window($timeout, $compile, $http, $templateCache); + }]);;/**! + * The MIT License + * + * Copyright (c) 2010-2012 Google, Inc. http://angularjs.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * angular-google-maps + * https://github.com/nlaplante/angular-google-maps + * + * @authors: + * - Nicolas Laplante https://plus.google.com/108189012221374960701 + * - Nicholas McCready https://plus.google.com/112199819969944829348 + */ + +/** + * Map info window directive + * + * This directive is used to create an info window on an existing map. + * This directive creates a new scope. + * + * {attribute coords required} object containing latitude and longitude properties + * {attribute show optional} map will show when this expression returns true + */ + +angular.module("google-maps").directive("windows", ['$timeout','$compile', '$http', '$templateCache', '$interpolate', + function ($timeout, $compile, $http, $templateCache,$interpolate) { + return new directives.api.Windows($timeout, $compile, $http, $templateCache, $interpolate); + }]);;/**! * The MIT License * * Copyright (c) 2010-2012 Google, Inc. http://angularjs.org diff --git a/dist/angular-google-maps.min.js b/dist/angular-google-maps.min.js index ec20d358a..ce88a871e 100644 --- a/dist/angular-google-maps.min.js +++ b/dist/angular-google-maps.min.js @@ -1,5 +1,6 @@ -/*! angular-google-maps 0.0.0 2013-08-08 +/*! angular-google-maps 0.0.0 2013-08-15 * AngularJS directives for Google Maps * git: https://github.com/nlaplante/angular-google-maps.git */ -angular.module("google-maps",[]),angular.module("google-maps").directive("googleMap",["$log","$timeout",function(a,b){"use strict";function c(a){return angular.isDefined(a)&&null!==a&&a===!0||"1"===a||"y"===a||"true"===a}var d={mapTypeId:google.maps.MapTypeId.ROADMAP};return{restrict:"ECMA",transclude:!0,replace:!1,template:'
',scope:{center:"=center",zoom:"=zoom",dragging:"=dragging",markers:"=markers",refresh:"&refresh",windows:"=windows",events:"=events",bounds:"=bounds"},controller:["$scope",function(a){this.getMap=function(){return a.map}}],link:function(e,f,g){if(!angular.isDefined(e.center)||!angular.isDefined(e.center.latitude)||!angular.isDefined(e.center.longitude))return a.error("angular-google-maps: could not find a valid center property"),void 0;if(!angular.isDefined(e.zoom))return a.error("angular-google-maps: map zoom property not set"),void 0;var h=angular.element(f);h.addClass("angular-google-map");var i={options:{}};if(g.options&&(i.options=angular.fromJson(g.options)),g.type){var j=g.type.toUpperCase();google.maps.MapTypeId.hasOwnProperty(j)?i.mapTypeId=google.maps.MapTypeId[g.type.toUpperCase()]:a.error('angular-google-maps: invalid map type "'+g.type+'"')}var k=new google.maps.Map(h.find("div")[1],angular.extend({},d,i,{center:new google.maps.LatLng(e.center.latitude,e.center.longitude),draggable:c(g.draggable),zoom:e.zoom,bounds:e.bounds})),l=!1;google.maps.event.addListener(k,"dragstart",function(){l=!0,b(function(){e.$apply(function(a){a.dragging=l})})}),google.maps.event.addListener(k,"dragend",function(){l=!1,b(function(){e.$apply(function(a){a.dragging=l})})}),google.maps.event.addListener(k,"drag",function(){var a=k.center;b(function(){e.$apply(function(b){b.center.latitude=a.lat(),b.center.longitude=a.lng()})})}),google.maps.event.addListener(k,"zoom_changed",function(){e.zoom!=k.zoom&&b(function(){e.$apply(function(a){a.zoom=k.zoom})})});var m=!1;if(google.maps.event.addListener(k,"center_changed",function(){var a=k.center;m||b(function(){e.$apply(function(b){k.dragging||(b.center.latitude!==a.lat()&&(b.center.latitude=a.lat()),b.center.longitude!==a.lng()&&(b.center.longitude=a.lng()))})})}),google.maps.event.addListener(k,"idle",function(){var a=k.getBounds(),c=a.getNorthEast(),d=a.getSouthWest();b(function(){e.$apply(function(a){null!==a.bounds&&void 0!==a.bounds&&void 0!==a.bounds&&(a.bounds.northeast={latitude:c.lat(),longitude:c.lng()},a.bounds.southwest={latitude:d.lat(),longitude:d.lng()})})})}),angular.isDefined(e.events)&&null!==e.events&&angular.isObject(e.events)){var n=function(a){return function(){e.events[a].apply(e,[k,a,arguments])}};for(var o in e.events)e.events.hasOwnProperty(o)&&angular.isFunction(e.events[o])&&google.maps.event.addListener(k,o,n(o))}e.map=k,google.maps.event.trigger(k,"resize"),angular.isUndefined(e.refresh())||e.$watch("refresh()",function(a,b){if(a&&!b){var d=new google.maps.LatLng(a.latitude,a.longitude);c(g.pan)?k.panTo(d):k.setCenter(d)}}),e.$watch("center",function(a,b){if(a!==b){if(m=!0,!l){var d=new google.maps.LatLng(a.latitude,a.longitude);c(g.pan)?k.panTo(d):k.setCenter(d)}m=!1}},!0),e.$watch("zoom",function(a,b){a!==b&&k.setZoom(a)}),e.$watch("bounds",function(a,b){if(a!==b){var c=new google.maps.LatLng(a.northeast.latitude,a.northeast.longitude),d=new google.maps.LatLng(a.southwest.latitude,a.southwest.longitude),e=new google.maps.LatLngBounds(d,c);k.fitBounds(e)}})}}}]),angular.module("google-maps").directive("marker",["$log","$timeout",function(a,b){"use strict";function c(a){return-1!==["false","FALSE",0,"n","N","no","NO"].indexOf(a)}var d={animation:google.maps.Animation.DROP};return{restrict:"ECMA",require:"^googleMap",priority:-1,transclude:!0,template:'',replace:!0,scope:{coords:"=coords",icon:"=icon",click:"&click"},controller:function(a,b){this.getMarker=function(){return b.data("instance")}},link:function(e,f,g,h){return angular.isUndefined(e.coords)||null===e.coords||angular.isUndefined(e.coords.latitude)||angular.isUndefined(e.coords.longitude)?(a.error("marker: no valid coords attribute found"),void 0):(b(function(){var a=angular.extend({},d,{position:new google.maps.LatLng(e.coords.latitude,e.coords.longitude),map:h.getMap(),icon:e.icon,visible:null!==e.coords.latitude&&null!==e.coords.longitude});angular.isDefined(g.animate)&&c(g.animate)&&delete a.animation;var i=new google.maps.Marker(a);f.data("instance",i),google.maps.event.addListener(i,"click",function(){angular.isDefined(g.click)&&null!==e.click&&b(function(){e.click()})}),e.$watch("coords",function(a,b){a!==b&&(a?(i.setMap(h.getMap()),i.setPosition(new google.maps.LatLng(a.latitude,a.longitude)),i.setVisible(null!==a.latitude&&null!==a.longitude)):i.setMap(null))},!0),e.$watch("icon",function(a,b){a!==b&&(i.icon=a,i.setMap(null),i.setMap(h.getMap()),i.setPosition(new google.maps.LatLng(e.coords.latitude,e.coords.longitude)),i.setVisible(null!==e.coords.latitude&&null!==e.coords.longitude))},!0),e.$on("$destroy",function(){i.setMap(null)})}),void 0)}}}]),angular.module("google-maps").directive("polyline",["$log","$timeout",function(a,b){"use strict";function c(a){for(var b=0;b',transclude:!0,priority:-100,require:["^googleMap","^?marker"],scope:{coords:"=coords",show:"&show",templateUrl:"=templateurl",templateParameter:"=templateparameter",isIconVisibleOnClick:"=isiconvisibleonclick",closeClick:"&closeclick"},link:function(a,g,h,i){b(function(){function b(){a.templateUrl?d.get(a.templateUrl,{cache:e}).then(function(b){var d=a.$new();angular.isDefined(a.templateParameter)&&(d.parameter=a.templateParameter);var e=c(b.data)(d);o.setContent(e.get(0)),o.open(l.getMap())}):o.open(l.getMap())}function j(){o.close()}var k=!0;angular.isDefined(h.isiconvisibleonclick)&&(k=a.isIconVisibleOnClick);var l=i[0],m=i.length>1?i[1]:null,n=angular.extend({},f,{content:g.html(),position:angular.isDefined(m)?m.getMarker().getPosition():new google.maps.LatLng(a.coords.latitude,a.coords.longitude)}),o=new google.maps.InfoWindow(n);if(angular.isDefined(m)){var p=m.getMarker();p.setClickable(!0);var q;google.maps.event.addListener(p,"click",function(){o.setPosition(p.getPosition()),o.open(l.getMap()),q=p.getVisible(),p.setVisible(k)}),google.maps.event.addListener(o,"closeclick",function(){p.setVisible(q),a.closeClick()})}a.$watch("show()",function(a,c){a!==c?a?b():j():a&&!o.getMap()&&b()},!0)},50)}}}]),angular.module("google-maps").directive("trafficlayer",["$log","$timeout",function(a,b){"use strict";return{restrict:"ECMA",require:"^googleMap",priority:-1,transclude:!0,template:'',replace:!0,scope:{show:"=show"},link:function(a,c,d,e){var f,g=new google.maps.TrafficLayer,h=!0;b(function(){f=e.getMap(),angular.isDefined(d.show)&&(h=a.show),null!==h&&h&&null!==f&&g.setMap(f),a.$watch("show",function(a,b){a!==b&&(h=a,a?g.setMap(f):g.setMap(null))},!0),a.$on("$destroy",function(){g.setMap(null)})})}}}]); \ No newline at end of file +!function(){var a=angular.module("google-maps",[]);a.factory("debounce",["$timeout",function(a){return function(b){var c=0;return function(){var d=this,e=arguments;c++;var f=function(a){return function(){return a===c?b.apply(d,e):void 0}}(c);return a(f,0,!0)}}}])}(),function(){this.ngGmapModule=function(a,b){var c,d;return"string"==typeof a&&(a=a.split(".")),c=this[d=a.shift()]||(this[d]={}),c.ngGmapModule||(c.ngGmapModule=this.ngGmapModule),a.length?c.ngGmapModule(a,b):b.call(c)}}.call(this),function(){var a=[].indexOf||function(a){for(var b=0,c=this.length;c>b;b++)if(b in this&&this[b]===a)return b;return-1};this.ngGmapModule("oo",function(){var b;return b=["extended","included"],this.BaseObject=function(){function c(){}return c.extend=function(c){var d,e,f;for(d in c)e=c[d],a.call(b,d)<0&&(this[d]=e);return null!=(f=c.extended)&&f.apply(0),this},c.include=function(c){var d,e,f;for(d in c)e=c[d],a.call(b,d)<0&&(this.prototype[d]=e);return null!=(f=c.included)&&f.apply(0),this},c}()})}.call(this),function(){this.ngGmapModule("directives.api.utils",function(){return this.AsyncProcessor={handleLargeArray:function(a,b,c,d,e){var f;return null==d&&(d=100),null==e&&(e=0),void 0===a||a.length<=0?void 0:(f=function(){var g,h;for(g=d,h=e;g--&&hd;d++)b=f[d],c(b);return delete this.markers,this.markers=[],this.markersIndex=0,this.createMarkers(a)}},d.prototype.onWatch=function(a,b,c,d){return"models"!==a||c.length!==d.length?this.reBuildMarkers(b):void 0},d.prototype.onDestroy=function(){var a,b,c,d,e;for(d=this.markers,e=[],b=0,c=d.length;c>b;b++)a=d[b],e.push(a.destroy());return e},d}(directives.api.models.parent.IMarkerParentModel)})}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};this.ngGmapModule("directives.api.models.parent",function(){return this.WindowsParentModel=function(b){function d(b,c,e,f,g,h,i,j,k){this.interpolateContent=a(this.interpolateContent,this),this.setChildScope=a(this.setChildScope,this),this.createWindow=a(this.createWindow,this),this.setContentKeys=a(this.setContentKeys,this),this.createChildScopesWindows=a(this.createChildScopesWindows,this),this.watchOurScope=a(this.watchOurScope,this),this.watchDestroy=a(this.watchDestroy,this),this.watchModels=a(this.watchModels,this),this.watch=a(this.watch,this);var l,m,n,o,p,q=this;for(d.__super__.constructor.call(this,b,c,e,f,g,h,i,j,k),m=this,this.$interpolate=k,this.clsName="WindowsParentModel",this.windows=[],this.windwsIndex=0,this.scopePropNames=["show","coords","templateUrl","templateParameter","isIconVisibleOnClick","closeClick"],p=this.scopePropNames,n=0,o=p.length;o>n;n++)l=p[n],this[l+"Key"]=void 0;this.linked=new directives.api.utils.Linked(b,c,e,f),this.models=void 0,this.contentKeys=void 0,this.isIconVisibleOnClick=void 0,this.firstTime=!0,this.bigGulp=directives.api.utils.AsyncProcessor,this.$log.info(m),this.$timeout(function(){return q.watchOurScope(b),q.createChildScopesWindows()},50)}return c(d,b),d.prototype.watch=function(a,b,c){var d=this;return a.$watch(b,function(a,e){var f,g,h,i,j;if(a!==e){for(d[c]="function"==typeof a?a():a,i=d.windows,j=[],g=0,h=i.length;h>g;g++)f=i[g],j.push(function(a){return a.scope[b]="self"===d[c]?a:a[d[c]]}(f));return j}},!0)},d.prototype.watchModels=function(a){var b=this;return a.$watch("models",function(a,c){return a!==c&&a.length!==c.length?(b.bigGulp.handleLargeArray(b.windows,function(a){return a.destroy()}),b.windows=[],b.windowsIndex=0,b.createChildScopesWindows()):void 0},!0)},d.prototype.watchDestroy=function(a){var b=this;return a.$on("$destroy",function(){return b.bigGulp.handleLargeArray(b.windows,function(a){return a.destroy()}),delete b.windows,b.windows=[],b.windowsIndex=0})},d.prototype.watchOurScope=function(a){var b,c,d,e,f,g=this;for(e=this.scopePropNames,f=[],c=0,d=e.length;d>c;c++)b=e[c],f.push(function(b){var c;return c=b+"Key",g[c]="function"==typeof a[b]?a[b]():a[b],g.watch(a,b,c)}(b));return f},d.prototype.createChildScopesWindows=function(){var a,b,c,d=this;return this.isIconVisibleOnClick=!0,angular.isDefined(this.linked.attrs.isiconvisibleonclick)&&(this.isIconVisibleOnClick=this.linked.scope.isIconVisibleOnClick),a=this.linked.ctrls[0].getMap(),b=this.linked.ctrls.length>1&&null!=this.linked.ctrls[1]?this.linked.ctrls[1].getMarkersScope():void 0,c=angular.isUndefined(this.linked.scope.models)||void 0===scope.models,!c||void 0!==b&&void 0!==b.markerModels&&void 0!==b.models?(null!=a&&(null!=this.linked.scope.models?(this.models=this.linked.scope.models,this.firstTime&&(this.watchModels(this.linked.scope),this.watchDestroy(this.linked.scope)),this.setContentKeys(this.linked.scope.models),this.bigGulp.handleLargeArray(this.linked.scope.models,function(b){return d.createWindow(b,void 0,a)})):(this.models=b.models,this.firstTime&&(this.watchModels(b),this.watchDestroy(b)),this.setContentKeys(b.models),this.bigGulp.handleLargeArray(b.markerModels,function(b){return d.createWindow(b.model,b.gMarker,a)}))),this.firstTime=!1):(this.$log.info("No models to create windows from! Need direct models or models derrived from markers!"),void 0)},d.prototype.setContentKeys=function(a){return a.length>0?this.contentKeys=Object.keys(a[0]):void 0},d.prototype.createWindow=function(a,b,c){var d,e,f,g=this;return d=this.linked.scope.$new(!1),this.setChildScope(d,a),d.$watch("model",function(a,b){return a!==b?g.setChildScope(d,a):void 0},!0),f=this.interpolateContent(this.linked.element.html(),a),e=this.createWindowOptions(b,d,f,this.DEFAULTS),this.windows.push(new directives.api.models.child.WindowChildModel(d,e,this.isIconVisibleOnClick,c,b,this.$http,this.$templateCache,this.$compile,!0))},d.prototype.setChildScope=function(a,b){var c,d,e,f,g,h=this;for(g=this.scopePropNames,d=function(c){var d,e;return d=c+"Key",e="self"===h[d]?b:b[h[d]],e!==a[c]?a[c]=e:void 0},e=0,f=g.length;f>e;e++)c=g[e],d(c);return a.model=b},d.prototype.interpolateContent=function(a,b){var c,d,e,f,g,h;if(void 0!==this.contentKeys&&0!==this.contentKeys.length){for(c=this.$interpolate(a),d={},h=this.contentKeys,f=0,g=h.length;g>f;f++)e=h[f],d[e]=b[e];return c(d)}},d}(directives.api.models.parent.IWindowParentModel)})}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};this.ngGmapModule("directives.api",function(){return this.IMarker=function(b){function d(b){this.link=a(this.link,this);var c;c=this,this.clsName="IMarker",this.$log=directives.api.utils.Logger,this.$timeout=b,this.restrict="ECMA",this.require="^googleMap",this.priority=-1,this.transclude=!0,this.replace=!0,this.scope={coords:"=coords",icon:"=icon",click:"&click"}}return c(d,b),d.prototype.controller=function(){throw new Exception("Not Implemented!!")},d.prototype.link=function(){throw new Exception("Not implemented!!")},d}(oo.BaseObject)})}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};this.ngGmapModule("directives.api",function(){return this.IWindow=function(b){function d(b,c,d,e){this.link=a(this.link,this);var f;f=this,this.clsName="IWindow",this.restrict="ECMA",this.template=void 0,this.transclude=!0,this.priority=-100,this.require=void 0,this.scope={coords:"=coords",show:"=show",templateUrl:"=templateurl",templateParameter:"=templateparameter",isIconVisibleOnClick:"=isiconvisibleonclick",closeClick:"&closeclick"},this.$log=directives.api.utils.Logger,this.$timeout=b,this.$compile=c,this.$http=d,this.$templateCache=e}return c(d,b),d.prototype.link=function(){throw new Exception("Not Implemented!!")},d}(oo.BaseObject)})}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};this.ngGmapModule("directives.api",function(){return this.Marker=function(b){function d(b){this.link=a(this.link,this);var c;d.__super__.constructor.call(this,b),c=this,this.template='',this.clsName="Marker",this.$log.info(this)}return c(d,b),d.prototype.controller=function(a,b){return this.getMarker=function(){return b.data("instance")}},d.prototype.link=function(a,b,c,d){return new directives.api.models.parent.MarkerParentModel(a,b,c,d,this.$timeout)},d}(directives.api.IMarker)})}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};this.ngGmapModule("directives.api",function(){return this.Markers=function(b){function d(b){this.link=a(this.link,this);var c;d.__super__.constructor.call(this,b),c=this,this.template='',this.clsName="Markers",this.scope.models="=models",this.$timeout=b,this.$log.info(this)}return c(d,b),d.prototype.controller=function(a){return this.getMarkersScope=function(){return a}},d.prototype.link=function(a,b,c,d){return new directives.api.models.parent.MarkersParentModel(a,b,c,d,this.$timeout)},d}(directives.api.IMarker)})}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};this.ngGmapModule("directives.api",function(){return this.Window=function(b){function d(b,c,e,f){this.link=a(this.link,this);var g;d.__super__.constructor.call(this,b,c,e,f),g=this,this.clsName="Window",this.require=["^googleMap","^?marker"],this.template='',this.$log.info(g)}return c(d,b),d.include(directives.api.utils.GmapUtil),d.prototype.link=function(a,b,c,d){var e=this;return this.$timeout(function(){var f,g,h,i,j;return f=!0,angular.isDefined(c.isiconvisibleonclick)&&(f=a.isIconVisibleOnClick),g=d[0].getMap(),h=d.length>1&&null!=d[1]?d[1].getMarker():void 0,i=e.createWindowOptions(h,a,b.html(),e.DEFAULTS),null!=g&&(j=new directives.api.models.child.WindowChildModel(a,i,f,g,h,e.$http,e.$templateCache,e.$compile)),a.$on("$destroy",function(){return j.destroy()})},50)},d}(directives.api.IWindow)})}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};this.ngGmapModule("directives.api",function(){return this.Windows=function(b){function d(b,c,e,f,g){this.link=a(this.link,this);var h;d.__super__.constructor.call(this,b,c,e,f),h=this,this.$interpolate=g,this.clsName="Windows",this.require=["^googleMap","^?markers"],this.template='',this.scope.models="=models",this.$log.info(h)}return c(d,b),d.prototype.link=function(a,b,c,d){return new directives.api.models.parent.WindowsParentModel(a,b,c,d,this.$timeout,this.$compile,this.$http,this.$templateCache,this.$interpolate)},d}(directives.api.IWindow)})}.call(this),angular.module("google-maps").factory("array-sync",["add-events",function(a){return function(b,c,d){var e=c.$eval(d),f=a(b,{set_at:function(a){var c=b.getAt(a);e[a].latitude=c.lat(),e[a].longitude=c.lng()},insert_at:function(a){var c=b.getAt(a);e.splice(a,0,{latitude:c.lat(),longitude:c.lng()})},remove_at:function(a){e.splice(a,1)}}),g=c.$watch(d,function(a){var c=b;if(a){for(var d,e=0,f=c.getLength(),g=a.length,h=Math.min(f,g);h>e;e++){var i=c.getAt(e);d=a[e],(i.lat()!=d.latitude||i.lng()!=d.longitude)&&c.setAt(e,new google.maps.LatLng(d.latitude,d.longitude))}for(;g>e;e++)d=a[e],c.push(new google.maps.LatLng(d.latitude,d.longitude));for(;f>e;e++)c.pop()}},!0);return function(){f&&(f(),f=null),g&&(g(),g=null)}}}]),angular.module("google-maps").factory("add-events",["$timeout",function(a){function b(b,c,d){return google.maps.event.addListener(b,c,function(){d.apply(this,arguments),a(function(){},!0)})}function c(a,c,d){if(d)return b(a,c,d);var e=[];return angular.forEach(c,function(c,d){console.log("adding listener: "+d+": "+c.toString()+" to : "+a),e.push(b(a,d,c))}),function(){angular.forEach(e,function(a){a()}),e=null}}return c}]),angular.module("google-maps").controller("PolylineDisplayController",["$scope",function(a){a.toggleStrokeColor=function(){a.stroke.color="#6060FB"==a.stroke.color?"red":"#6060FB"}}]),angular.module("google-maps").directive("googleMap",["$log","$timeout",function(a,b){"use strict";function c(a){return angular.isDefined(a)&&null!==a&&a===!0||"1"===a||"y"===a||"true"===a}directives.api.utils.Logger.logger=a;var d={mapTypeId:google.maps.MapTypeId.ROADMAP};return{restrict:"ECMA",transclude:!0,replace:!1,template:'
',scope:{center:"=center",zoom:"=zoom",dragging:"=dragging",markers:"=markers",refresh:"&refresh",windows:"=windows",events:"=events",bounds:"=bounds"},controller:["$scope",function(a){this.getMap=function(){return a.map}}],link:function(e,f,g){if(!angular.isDefined(e.center)||!angular.isDefined(e.center.latitude)||!angular.isDefined(e.center.longitude))return a.error("angular-google-maps: could not find a valid center property"),void 0;if(!angular.isDefined(e.zoom))return a.error("angular-google-maps: map zoom property not set"),void 0;var h=angular.element(f);h.addClass("angular-google-map");var i={options:{}};if(g.options&&(i.options=angular.fromJson(g.options)),g.type){var j=g.type.toUpperCase();google.maps.MapTypeId.hasOwnProperty(j)?i.mapTypeId=google.maps.MapTypeId[g.type.toUpperCase()]:a.error('angular-google-maps: invalid map type "'+g.type+'"')}var k=new google.maps.Map(h.find("div")[1],angular.extend({},d,i,{center:new google.maps.LatLng(e.center.latitude,e.center.longitude),draggable:c(g.draggable),zoom:e.zoom,bounds:e.bounds})),l=!1;google.maps.event.addListener(k,"dragstart",function(){l=!0,b(function(){e.$apply(function(a){a.dragging=l})})}),google.maps.event.addListener(k,"dragend",function(){l=!1,b(function(){e.$apply(function(a){a.dragging=l})})}),google.maps.event.addListener(k,"drag",function(){var a=k.center;b(function(){e.$apply(function(b){b.center.latitude=a.lat(),b.center.longitude=a.lng()})})}),google.maps.event.addListener(k,"zoom_changed",function(){e.zoom!=k.zoom&&b(function(){e.$apply(function(a){a.zoom=k.zoom})})});var m=!1;if(google.maps.event.addListener(k,"center_changed",function(){var a=k.center;m||b(function(){e.$apply(function(b){k.dragging||(b.center.latitude!==a.lat()&&(b.center.latitude=a.lat()),b.center.longitude!==a.lng()&&(b.center.longitude=a.lng()))})})}),google.maps.event.addListener(k,"idle",function(){var a=k.getBounds(),c=a.getNorthEast(),d=a.getSouthWest();b(function(){e.$apply(function(a){null!==a.bounds&&void 0!==a.bounds&&void 0!==a.bounds&&(a.bounds.northeast={latitude:c.lat(),longitude:c.lng()},a.bounds.southwest={latitude:d.lat(),longitude:d.lng()})})})}),angular.isDefined(e.events)&&null!==e.events&&angular.isObject(e.events)){var n=function(a){return function(){e.events[a].apply(e,[k,a,arguments])}};for(var o in e.events)e.events.hasOwnProperty(o)&&angular.isFunction(e.events[o])&&google.maps.event.addListener(k,o,n(o))}e.map=k,google.maps.event.trigger(k,"resize"),angular.isUndefined(e.refresh())||e.$watch("refresh()",function(a,b){if(a&&!b){var d=new google.maps.LatLng(a.latitude,a.longitude);c(g.pan)?k.panTo(d):k.setCenter(d)}}),e.$watch("center",function(a,b){if(a!==b){if(m=!0,!l){var d=new google.maps.LatLng(a.latitude,a.longitude);c(g.pan)?k.panTo(d):k.setCenter(d)}m=!1}},!0),e.$watch("zoom",function(a,b){a!==b&&k.setZoom(a)}),e.$watch("bounds",function(a,b){if(a!==b){var c=new google.maps.LatLng(a.northeast.latitude,a.northeast.longitude),d=new google.maps.LatLng(a.southwest.latitude,a.southwest.longitude),e=new google.maps.LatLngBounds(d,c);k.fitBounds(e)}})}}}]),angular.module("google-maps").directive("marker",["$timeout",function(a){return new directives.api.Marker(a)}]),angular.module("google-maps").directive("markers",["$timeout",function(a){return new directives.api.Markers(a)}]),angular.module("google-maps").directive("polygon",["$log","$timeout",function(a,b){"use strict";function c(a){for(var b=0;bd;d++)oldValue=c.getAt(d),newValue=b[d],(oldValue.lat()!=newValue.latitude||oldValue.lng()!=newValue.longitude)&&c.setAt(d,new google.maps.LatLng(newValue.latitude,newValue.longitude));for(;h>d;d++)newValue=b[d],c.push(new google.maps.LatLng(newValue.latitude,newValue.longitude));for(;g>d;d++)c.pop();f(j.fit)&&e(a,c)}else i.setMap(null)},!0),h.$on("$destroy",function(){i.setMap(null),l(),l=null,m(),m=null,n(),n=null +})}),void 0)}}}]),angular.module("google-maps").directive("polyline",["$log","$timeout","array-sync",function(a,b,c){"use strict";function d(a){for(var b=0;b',replace:!0,scope:{show:"=show"},link:function(a,c,d,e){var f,g=new google.maps.TrafficLayer,h=!0;b(function(){f=e.getMap(),angular.isDefined(d.show)&&(h=a.show),null!==h&&h&&null!==f&&g.setMap(f),a.$watch("show",function(a,b){a!==b&&(h=a,a?g.setMap(f):g.setMap(null))},!0),a.$on("$destroy",function(){g.setMap(null)})})}}}]); \ No newline at end of file diff --git a/example/example-controller.js b/example/example-controller.js index c1760e85e..753f08827 100644 --- a/example/example-controller.js +++ b/example/example-controller.js @@ -9,6 +9,11 @@ function ExampleController ($scope, $timeout, $log) { // See http://googlegeodevelopers.blogspot.ca/2013/05/a-fresh-new-look-for-maps-api-for-all.html google.maps.visualRefresh = true; + onMarkerClicked = function(marker){ + marker.showWindow = true; + window.alert("Marker: lat: " + marker.latitude +", lon: " + marker.longitude + " clicked!!") + }; + angular.extend($scope, { map: { showTraffic: true, @@ -37,6 +42,25 @@ function ExampleController ($scope, $timeout, $log) { showWindow: false } ], + markers2: [ + { + latitude: 46, + longitude: -77, + showWindow: false + }, + { + latitude: 33, + longitude: -77, + showWindow: false + }, + { + icon: 'plane.png', + latitude: 35, + longitude: -125, + showWindow: false + } + ], + dynamicMarkers: [], clickedMarker: { latitude: null, longitude: null @@ -146,18 +170,63 @@ function ExampleController ($scope, $timeout, $log) { _.each($scope.map.markers,function(marker){ marker.closeClick = function(){ - this.showWindow = false; + marker.showWindow = false; $scope.$apply(); }; + marker.onClicked = function(){ + onMarkerClicked(marker); + }; + }); + + _.each($scope.map.markers2,function(marker){ + marker.closeClick = function(){ + marker.showWindow = false; + $scope.$apply(); + }; + marker.onClicked = function(){ + onMarkerClicked(marker); + }; }); $scope.removeMarkers = function () { $log.info("Clearing markers. They should disappear from the map now"); $scope.map.markers.length = 0; + $scope.map.markers2.length = 0; + $scope.map.dynamicMarkers.length = 0; $scope.map.clickedMarker = null; }; + $scope.onMarkerClicked = onMarkerClicked + $timeout(function () { $scope.map.infoWindow.show = true; + dynamicMarkers = [ + { + latitude: 46, + longitude: -79, + showWindow: false + }, + { + latitude: 33, + longitude: -79, + showWindow: false + }, + { + icon: 'plane.png', + latitude: 35, + longitude: -127, + showWindow: false + } + ]; + _.each(dynamicMarkers,function(marker){ + marker.closeClick = function(){ + marker.showWindow = false; + $scope.$apply(); + }; + marker.onClicked = function(){ + onMarkerClicked(marker); + }; + }); + $scope.map.dynamicMarkers = dynamicMarkers; }, 2000); } diff --git a/example/example.html b/example/example.html index 85ceaff70..47b3e8648 100644 --- a/example/example.html +++ b/example/example.html @@ -23,22 +23,22 @@ } - .angular-google-map { - top:80px; + .angular-google-map { + top:80px; - } + } .shrink{ font-size: 8; } - .true:hover { - background-color: lightgreen; - } + .true:hover { + background-color: lightgreen; + } - .false:hover { - background-color: lightpink; - } + .false:hover { + background-color: lightpink; + } /* uncomment this if you are using the element instead of a div .angular-google-map { @@ -50,9 +50,9 @@ -
-

angular-google-maps example

-
+
+

angular-google-maps example

+
@@ -64,8 +64,26 @@

angular-google-maps example

bounds="map.bounds" events="map.events"> + + + +

This is an info window at {{ latitude | number:4 }}, {{ longitude | number:4 }}!

+

My marker will stay open when the window is popped up!

+
+
+ + + + +

Dynamic Marker created via a delay!

+

This is an info window at {{ latitude | number:4 }}, {{ longitude | number:4 }}!

+

My marker will stay open when the window is popped up!

+
+
+ - + +

This is an info window at {{ m.latitude | number:4 }}, {{ m.longitude | number:4 }}!

My marker will stay open when the window is popped up!

@@ -73,124 +91,163 @@

angular-google-maps example

- + This is my clicked marker!

My marker will reappear when you close me.

- I should not be attached to a - marker. + + + I should not be attached to a + marker. + - - + + + - -
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TrafficLayer - -
markers - -
polylines - -
center
 {{ map.center.latitude | number:4 }} lat
 {{ map.center.longitude | number:4 }} lng
zoom{{ map.zoom }}
bounds -
north-east: {{ map.bounds.northeast.latitude | number:4 }},{{ map.bounds.northeast.longitude | number:4 }} 
south-west: {{ map.bounds.southwest.latitude | number:4 }},{{ map.bounds.southwest.longitude | number:4 }}
-
dragging{{ map.dragging }}
clicked position -
 {{ map.clickedMarker.latitude | number:4 }} lat
 {{ map.clickedMarker.longitude | number:4 }} lng
- Click the map to see -
- -
+
- - - - - - - - - - +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TrafficLayer + +
markers + +
markers2 + +
dynamicMarkers + +
center
 {{ map.center.latitude | number:4 }} lat
 {{ map.center.longitude | number:4 }} lng
zoom{{ map.zoom }}
bounds +
north-east: {{ map.bounds.northeast.latitude | number:4 }},{{ map.bounds.northeast.longitude | number:4 }} 
south-west: {{ map.bounds.southwest.latitude | number:4 }},{{ map.bounds.southwest.longitude | number:4 }}
+
dragging{{ map.dragging }}
clicked position +
 {{ map.clickedMarker.latitude | number:4 }} lat
 {{ map.clickedMarker.longitude | number:4 }} lng
+ Click the map to see +
polylines + +
+ +
+
+ + + + + + + + + + + + diff --git a/example/templates/info.html b/example/templates/info.html index 2825dbad3..3c9fcac0e 100644 --- a/example/templates/info.html +++ b/example/templates/info.html @@ -1,4 +1,4 @@ -
+
I'm loaded from an external template, yay!
Parameter from opener: {{ parameter.message }} diff --git a/package.json b/package.json index 40f3b65e4..8de859088 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,8 @@ "grunt-mkdir": "~0.1.1", "grunt-contrib-connect": "~0.3.0", "grunt-open": "~0.2.0", - "grunt-contrib-watch": "~0.4.4" + "grunt-contrib-watch": "~0.4.4", + "grunt-contrib-coffee": "~0.7.0" }, "dependencies": { "grunt-cli": "~0.1.9", diff --git a/src/coffee/directives/api/i-marker.coffee b/src/coffee/directives/api/i-marker.coffee new file mode 100644 index 000000000..c494b9472 --- /dev/null +++ b/src/coffee/directives/api/i-marker.coffee @@ -0,0 +1,29 @@ +### + - interface for all markers to derrive from + - to enforce a minimum set of requirements + - attributes + - coords + - icon + - implementation needed on watches +### +@ngGmapModule "directives.api", -> + class @IMarker extends oo.BaseObject + constructor: ($timeout) -> + self = @ + @clsName = "IMarker" + @$log = directives.api.utils.Logger + @$timeout = $timeout + @restrict = 'ECMA' + @require = '^googleMap' + @priority = -1 + @transclude = true + @replace = true + @scope = { + coords: '=coords', + icon: '=icon', + click: '&click' + } + controller: ($scope, $element) -> + throw new Exception("Not Implemented!!") + link: (scope, element, attrs, ctrl) => + throw new Exception("Not implemented!!") \ No newline at end of file diff --git a/src/coffee/directives/api/i-window.coffee b/src/coffee/directives/api/i-window.coffee new file mode 100644 index 000000000..66ca455fe --- /dev/null +++ b/src/coffee/directives/api/i-window.coffee @@ -0,0 +1,29 @@ +### + - interface directive for all window(s) to derrive from +### +@ngGmapModule "directives.api", -> + class @IWindow extends oo.BaseObject + constructor: ($timeout, $compile, $http, $templateCache) -> + self = @ + @clsName = "IWindow" + @restrict= 'ECMA' + @template= undefined + @transclude= true + @priority= -100 + @require = undefined + @scope= { + coords: '=coords', + show: '=show', + templateUrl: '=templateurl', + templateParameter: '=templateparameter', + isIconVisibleOnClick: '=isiconvisibleonclick', + closeClick: '&closeclick' #scope glue to gmap InfoWindow closeclick + } + @$log = directives.api.utils.Logger + @$timeout = $timeout + @$compile = $compile + @$http = $http + @$templateCache = $templateCache + + link: (scope, element, attrs, ctrls) => + throw new Exception("Not Implemented!!") \ No newline at end of file diff --git a/src/coffee/directives/api/marker.coffee b/src/coffee/directives/api/marker.coffee new file mode 100644 index 000000000..a12fa1555 --- /dev/null +++ b/src/coffee/directives/api/marker.coffee @@ -0,0 +1,19 @@ +### + Basic Directive api for a marker. Basic in the sense that this directive contains 1:1 on scope and model. + Thus there will be one html element per marker within the directive. +### +@ngGmapModule "directives.api", -> + class @Marker extends directives.api.IMarker + constructor: ($timeout) -> + super($timeout) + self = @ + @template = '' + @clsName = "Marker" + @$log.info(@) + + controller:($scope, $element) -> + @getMarker = -> + $element.data('instance') + + link: (scope, element, attrs, ctrl) => + new directives.api.models.parent.MarkerParentModel(scope, element, attrs, ctrl, @$timeout) \ No newline at end of file diff --git a/src/coffee/directives/api/markers.coffee b/src/coffee/directives/api/markers.coffee new file mode 100644 index 000000000..4716f308d --- /dev/null +++ b/src/coffee/directives/api/markers.coffee @@ -0,0 +1,28 @@ +### +Markers will map icon and coords differently than directibes.api.Marker. This is because Scope and the model marker are +not 1:1 in this setting. + + - icon - will be the iconKey to the marker value ie: to get the icon marker[iconKey] + - coords - will be the coordsKey to the marker value ie: to get the icon marker[coordsKey] + + - watches from IMarker reflect that the look up key for a value has changed and not the actual icon or coords itself + - actual changes to a model are tracked inside directives.api.model.MarkerModel + +### +@ngGmapModule "directives.api", -> + class @Markers extends directives.api.IMarker + constructor: ($timeout) -> + super($timeout) + self = @ + @template = '' + @clsName = "Markers" + @scope.models = '=models' + @$timeout = $timeout + @$log.info(@) + + controller:($scope, $element) -> + @getMarkersScope = -> + $scope + + link: (scope, element, attrs, ctrl) => + new directives.api.models.parent.MarkersParentModel(scope, element, attrs, ctrl, @$timeout) \ No newline at end of file diff --git a/src/coffee/directives/api/models/child/marker-child-model.coffee b/src/coffee/directives/api/models/child/marker-child-model.coffee new file mode 100644 index 000000000..78c906f7b --- /dev/null +++ b/src/coffee/directives/api/models/child/marker-child-model.coffee @@ -0,0 +1,89 @@ +@ngGmapModule "directives.api.models.child", -> + class @MarkerChildModel extends oo.BaseObject + @include directives.api.utils.GmapUtil + constructor:(index,model,parentScope,gMap,$timeout,notifyLocalDestroy,defaults,doClick)-> + @index = index + @model = model + @parentScope = parentScope + @iconKey = parentScope.icon + @coordsKey = parentScope.coords + @clickKey = parentScope.click() + @animateKey = parentScope.animate + @myScope = parentScope.$new(false) + # @myScope = {} + @setMyScope(model) + @myScope.$watch('model',(newValue, oldValue) => + if (newValue != oldValue) + @setMyScope(newValue) + ,true) + @defaults = defaults + @gMap = gMap + @opts = @createMarkerOptions(@gMap,@myScope.coords,@myScope.icon,@myScope.animate,@defaults) + @gMarker = new google.maps.Marker(@opts) + @doClick = doClick + @$log = directives.api.utils.Logger + google.maps.event.addListener(@gMarker, 'click', => + #this needs to be thought about as scope is not 1:1 on clicking..... hmmmmm :/ + if @doClick and @myScope.click? + $timeout(=> + @myScope.click() + ) + ) + # $timeout( => + @watchCoords(@myScope) + @watchIcon(@myScope) + @watchDestroy(@myScope) + # ) + setMyScope:(model)=> + @myScope.icon = if @iconKey == 'self' then model else model[@iconKey] + @myScope.coords = if @coordsKey == 'self' then model else model[@coordsKey] + @myScope.click = if @clickKey == 'self' then model else model[@clickKey] + @myScope.animate = if @animateKey == 'self' then model else model[@animateKey] + @myScope.animate = if @animateKey == undefined then false else @myScope.animate + @myScope.model = model + + destroy:() => + @myScope.$destroy() + + setCoords:(scope) => + if(scope.$id != @myScope.$id) + return + if (scope.coords?) + @gMarker.setMap(@gMap.getMap()) + @gMarker.setPosition(new google.maps.LatLng(scope.coords.latitude, scope.coords.longitude)) + @gMarker.setVisible(scope.coords.latitude? and scope.coords.longitude?) + else + # Remove marker + @gMarker.setMap(null) + + setIcon:(scope) => + if(scope.$id != @myScope.$id) + return + @gMarker.icon = scope.icon + @gMarker.setMap(null) + @gMarker.setMap(@gMap.getMap()) + @gMarker.setPosition(new google.maps.LatLng(scope.coords.latitude, scope.coords.longitude)) + @gMarker.setVisible(scope.coords.latitude and scope.coords.longitude?) + + + watchCoords:(scope) => + scope.$watch('coords', (newValue, oldValue) => + if (newValue != oldValue) + @parentScope.doRebuild = false + @setCoords(scope) + @parentScope.doRebuild = true + , true) + + watchIcon:(scope) => + scope.$watch('icon', (newValue, oldValue) => + if (newValue != oldValue) + @parentScope.doRebuild = false + @setIcon(scope) + @parentScope.doRebuild = true + , true) + + watchDestroy:(scope)=> + scope.$on("$destroy", => + @gMarker.setMap(null) + notifyLocalDestroy(@index) if notifyLocalDestroy? + ) \ No newline at end of file diff --git a/src/coffee/directives/api/models/child/window-child-model.coffee b/src/coffee/directives/api/models/child/window-child-model.coffee new file mode 100644 index 000000000..5991669a1 --- /dev/null +++ b/src/coffee/directives/api/models/child/window-child-model.coffee @@ -0,0 +1,72 @@ +@ngGmapModule "directives.api.models.child", -> + class @WindowChildModel extends oo.BaseObject + constructor:(scope,opts,isIconVisibleOnClick,mapCtrl, markerCtrl,$http,$templateCache,$compile,needToManualDestroy = false)-> + @scope = scope + @opts = opts + @mapCtrl = mapCtrl + @markerCtrl = markerCtrl + @isIconVisibleOnClick = isIconVisibleOnClick + @initialMarkerVisibility = if @markerCtrl? then @markerCtrl.getVisible() else false + @$log = directives.api.utils.Logger + @$http = $http + @$templateCache = $templateCache + @$compile = $compile + @gWin = new google.maps.InfoWindow(opts) + # Open window on click + @markerCtrl.setClickable(true) if @markerCtrl? + + @handleClick(@scope,@mapCtrl,@markerCtrl,@gWin,@isIconVisibleOnClick,@initialMarkerVisibility) + @watchShow(scope,$http,$templateCache,@$compile,@gWin,@showWindow,@hideWindow,@mapCtrl) + @needToManualDestroy = needToManualDestroy + @$log.info(@) + + watchShow:(scope,$http,$templateCache,$compile,gWin,showHandle,hideHandle,mapCtrl) -> + scope.$watch('show', (newValue, oldValue) -> + if (newValue != oldValue) + if (newValue) + showHandle(scope,$http,$templateCache,$compile,gWin,mapCtrl) + else + hideHandle(gWin) + else + if (newValue and !gWin.getMap()) + # If we're initially showing the marker and it's not yet visible, show it. + showHandle(scope,$http,$templateCache,$compile,gWin,mapCtrl) + ,true) + + handleClick:(scope,mapCtrl,markerInstance,gWin,isIconVisibleOnClick,initialMarkerVisibility) -> + if markerInstance? + # Show the window and hide the marker on click + google.maps.event.addListener(markerInstance, 'click', -> + gWin.setPosition(markerInstance.getPosition()) + gWin.open(mapCtrl) + markerInstance.setVisible(isIconVisibleOnClick) + ) + # Set visibility of marker back to what it was before opening the window + google.maps.event.addListener(gWin, 'closeclick', -> + markerInstance.setVisible(initialMarkerVisibility) + scope.closeClick() + ) + + showWindow:(scope,$http,$templateCache,$compile,gWin,mapCtrl) -> + if scope.templateUrl + $http.get(scope.templateUrl, { cache: $templateCache }).then((content) -> + templateScope = scope.$new() + if angular.isDefined(scope.templateParameter) + templateScope.parameter = scope.templateParameter + compiled = $compile(content.data)(templateScope) + gWin.setContent(compiled.get(0)) + gWin.open(mapCtrl) + ) + else + gWin.open(mapCtrl) + + hideWindow: (gWin) -> + gWin.close() + + destroy:()=> + @hideWindow(@gWin) + if(@scope? and @needToManualDestroy) + @scope.$destroy() + delete @gWin + delete @ + diff --git a/src/coffee/directives/api/models/parent/i-marker-parent-model.coffee b/src/coffee/directives/api/models/parent/i-marker-parent-model.coffee new file mode 100644 index 000000000..5585c805d --- /dev/null +++ b/src/coffee/directives/api/models/parent/i-marker-parent-model.coffee @@ -0,0 +1,64 @@ +### + - interface for all markers to derrive from + - to enforce a minimum set of requirements + - attributes + - coords + - icon + - implementation needed on watches +### +@ngGmapModule "directives.api.models.parent", -> + class @IMarkerParentModel extends oo.BaseObject + # Animation is enabled by default + DEFAULTS: { animation: google.maps.Animation.DROP } + + # Check if a value is literally false + # @param value the value to test + # @returns {boolean} true if value is literally false, false otherwise + isFalse: (value) -> + ['false', 'FALSE', 0, 'n', 'N', 'no', 'NO'].indexOf(value) != -1 + + constructor: (scope, element, attrs, mapCtrl,$timeout) -> + self = @ + # Validate required properties + if (@validateScope(scope)) + return + @animate = if angular.isDefined(attrs.animate) then !@isFalse(attrs.animate) else false + @doClick = angular.isDefined(attrs.click) + @mapCtrl = mapCtrl + @clsName = "IMarker" + @$log = directives.api.utils.Logger + @$timeout = $timeout + # Wrap marker initialization inside a $timeout() call to make sure the map is created already + @$timeout( => + @watch('coords',scope) + @watch('icon',scope) + @watch('animate',scope) + @onTimeOut(scope) + scope.$on("$destroy", => + @onDestroy(scope) + ) + ) + + onTimeOut:(scope)=> + + validateScope:(scope)=> + ret = angular.isUndefined(scope.coords) or + scope.coords == undefined + if(ret) + @$log.error(@clsName + ": no valid coords attribute found") + ret + + watch:(propNameToWatch,scope) => + scope.$watch(propNameToWatch, (newValue, oldValue) => + if (newValue != oldValue) + @onWatch(propNameToWatch,scope,newValue,oldValue) + , true) + + onWatch:(propNameToWatch,scope,newValue,oldValue) => + throw new Exception("Not Implemented!!") + + onDestroy:(scope) => + throw new Exception("Not Implemented!!") + + linkInit:(element,mapCtrl,scope,animate)=> + throw new Exception("Not Implemented!!") \ No newline at end of file diff --git a/src/coffee/directives/api/models/parent/i-window-parent-model.coffee b/src/coffee/directives/api/models/parent/i-window-parent-model.coffee new file mode 100644 index 000000000..4147c9788 --- /dev/null +++ b/src/coffee/directives/api/models/parent/i-window-parent-model.coffee @@ -0,0 +1,17 @@ +### + - interface directive for all window(s) to derrive from +### +@ngGmapModule "directives.api.models.parent", -> + class @IWindowParentModel extends oo.BaseObject + @include directives.api.utils.GmapUtil + # Animation is enabled by default + DEFAULTS: {} + + constructor: (scope, element, attrs, ctrls,$timeout, $compile, $http, $templateCache) -> + self = @ + @clsName = "directives.api.models.parent.IWindow" + @$log = directives.api.utils.Logger + @$timeout = $timeout + @$compile = $compile + @$http = $http + @$templateCache = $templateCache \ No newline at end of file diff --git a/src/coffee/directives/api/models/parent/marker-parent-model.coffee b/src/coffee/directives/api/models/parent/marker-parent-model.coffee new file mode 100644 index 000000000..2ee1f95d3 --- /dev/null +++ b/src/coffee/directives/api/models/parent/marker-parent-model.coffee @@ -0,0 +1,58 @@ +### + Basic Directive api for a marker. Basic in the sense that this directive contains 1:1 on scope and model. + Thus there will be one html element per marker within the directive. +### +@ngGmapModule "directives.api.models.parent", -> + class @MarkerParentModel extends directives.api.models.parent.IMarkerParentModel + @include directives.api.utils.GmapUtil + constructor: (scope, element, attrs, mapCtrl,$timeout) -> + super(scope, element, attrs, mapCtrl,$timeout) + self = @ + @clsName = "MarkerParentModel" + opts = @createMarkerOptions(mapCtrl,scope.coords,scope.icon,@animate,@DEFAULTS) + #using scope.$id as the identifier for a marker as scope.$id should be unique, no need for an index (as it is the index) + @gMarker = new google.maps.Marker(opts) + element.data('instance', @gMarker) + @scope = scope + google.maps.event.addListener(@gMarker, 'click', => + if @doClick and scope.click? + $timeout( => + @scope.click() + ) + ) + @$log.info(@) + + validateScope:(scope)=> + super(scope) or angular.isUndefined(scope.coords.latitude) or angular.isUndefined(scope.coords.longitude) + + onWatch:(propNameToWatch,scope) => + + switch propNameToWatch + when 'coords' + if (scope.coords? and @gMarker?) + @gMarker.setMap(@mapCtrl.getMap()) + @gMarker.setPosition(new google.maps.LatLng(scope.coords.latitude, scope.coords.longitude)) + @gMarker.setVisible(scope.coords.latitude? and scope.coords.longitude?) + else + # Remove marker + @gMarker.setMap(null) + when 'icon' + if (scope.icon? and scope.coords? and @gMarker?) + @gMarker.icon = scope.icon + @gMarker.setMap(null) + @gMarker.setMap(@mapCtrl.getMap()) + @gMarker.setPosition(new google.maps.LatLng(scope.coords.latitude, scope.coords.longitude)) + @gMarker.setVisible(scope.coords.latitude and scope.coords.longitude?) + when 'animate' + else + + + onDestroy:(scope)=> + if @gMarker == undefined + delete @ + return + #remove from gMaps and then free resources + @gMarker.setMap(null) + delete @gMarker + delete @ + diff --git a/src/coffee/directives/api/models/parent/markers-parent-model.coffee b/src/coffee/directives/api/models/parent/markers-parent-model.coffee new file mode 100644 index 000000000..69780651b --- /dev/null +++ b/src/coffee/directives/api/models/parent/markers-parent-model.coffee @@ -0,0 +1,59 @@ +@ngGmapModule "directives.api.models.parent", -> + class @MarkersParentModel extends directives.api.models.parent.IMarkerParentModel + constructor: (scope, element, attrs, mapCtrl,$timeout) -> + super(scope, element, attrs, mapCtrl,$timeout) + self = @ + @clsName = "MarkersParentModel" + @markers = [] + @markersIndex = 0 + @scope = scope + @bigGulp = directives.api.utils.AsyncProcessor + @$log.info(@) + + onTimeOut:(scope)=> + @watch('models',scope) + @createMarkers(scope) + + validateScope:(scope)=> + modelsNotDefined = angular.isUndefined(scope.models) or scope.models == undefined + if(modelsNotDefined) + @$log.error(@clsName + ": no valid models attribute found") + + super(scope) or modelsNotDefined + + createMarkers:(scope) => + @bigGulp.handleLargeArray(scope.models,(model) => + scope.doRebuild = true + @markers.push( + new directives.api.models.child.MarkerChildModel(@markersIndex,model,scope,@mapCtrl,@$timeout,(index) => + delete @markers[index] + ,@DEFAULTS,@doClick) + ) + @markersIndex++ + ) + #put MarkerModels into local scope + scope.markerModels = @markers + + reBuildMarkers:(scope) => + if(!scope.doRebuild and scope.doRebuild != undefined) + return + for oldM in @markers + do(oldM) => + oldM.destroy() + delete @markers + @markers = [] + @markersIndex = 0 + @createMarkers(scope) + + onWatch:(propNameToWatch,scope,newValue,oldValue) => + if(propNameToWatch == 'models' and newValue.length == oldValue.length) + return + @reBuildMarkers(scope) + + onDestroy:(scope)=> + #need to figure out how to handle individual destroys + #slap index to the external model so that when they pass external back + #for destroy we have a lookup? + #this will require another attribute for destroySingle(marker) + model.destroy() for model in @markers + \ No newline at end of file diff --git a/src/coffee/directives/api/models/parent/windows-parent-model.coffee b/src/coffee/directives/api/models/parent/windows-parent-model.coffee new file mode 100644 index 000000000..fe77125b9 --- /dev/null +++ b/src/coffee/directives/api/models/parent/windows-parent-model.coffee @@ -0,0 +1,156 @@ +### + Windows directive where many windows map to the models property +### +@ngGmapModule "directives.api.models.parent", -> + class @WindowsParentModel extends directives.api.models.parent.IWindowParentModel + constructor: (scope, element, attrs, ctrls, $timeout, $compile, $http, $templateCache,$interpolate) -> + super(scope, element, attrs, ctrls, $timeout, $compile, $http, $templateCache,$interpolate) + self = @ + @$interpolate = $interpolate + @clsName = "WindowsParentModel" + @windows = [] + @windwsIndex = 0 + @scopePropNames = ['show','coords','templateUrl','templateParameter', + 'isIconVisibleOnClick','closeClick'] + #setting up local references to propety keys IE: @coordsKey + @[name + 'Key'] = undefined for name in @scopePropNames + @linked = new directives.api.utils.Linked(scope,element,attrs,ctrls) + @models = undefined + @contentKeys = undefined #model keys to parse html angular content + @isIconVisibleOnClick = undefined + @firstTime = true + @bigGulp = directives.api.utils.AsyncProcessor + @$log.info(self) + + @$timeout( => + @watchOurScope(scope) + @createChildScopesWindows() + ,50) + + #watch this scope(Parent to all WindowModels), these updates reflect expression / Key changes + #thus they need to be pushed to all the children models so that they are bound to the correct objects / keys + watch:(scope,name,nameKey) => + scope.$watch(name, (newValue, oldValue) => + if (newValue != oldValue) + @[nameKey] = if typeof newValue == 'function' then newValue() else newValue + for model in @windows + do(model) => + model.scope[name] = if @[nameKey] == 'self' then model else model[@[nameKey]] + ,true) + + watchModels:(scope) => + scope.$watch('models', (newValue, oldValue) => + if (newValue != oldValue and newValue.length != oldValue.length) + @bigGulp.handleLargeArray(@windows,(model) => + model.destroy() + ) + # delete @windows + @windows = [] + @windowsIndex = 0 + @createChildScopesWindows() + , true) + + watchDestroy:(scope)=> + scope.$on("$destroy", => + @bigGulp.handleLargeArray(@windows,(model) => + model.destroy() + ) + delete @windows + @windows = [] + @windowsIndex = 0 + ) + + watchOurScope:(scope) => + for name in @scopePropNames + do(name) => + nameKey = name + 'Key' + @[nameKey] = if typeof scope[name] == 'function' then scope[name]() else scope[name] + @watch(scope,name,nameKey) + + createChildScopesWindows: => + ### + being that we cannot tell the difference in Key String vs. a normal value string (TemplateUrl) + we will assume that all scope values are string expressions either pointing to a key (propName) or using + 'self' to point the model as container/object of interest. + + This may force redundant information into the model, but this appears to be the most flexible approach. + ### + @isIconVisibleOnClick = true + if angular.isDefined(@linked.attrs.isiconvisibleonclick) + @isIconVisibleOnClick = @linked.scope.isIconVisibleOnClick + gMap = @linked.ctrls[0].getMap() + markersScope = if @linked.ctrls.length > 1 and @linked.ctrls[1]? then @linked.ctrls[1].getMarkersScope() else undefined + + modelsNotDefined = angular.isUndefined(@linked.scope.models) or scope.models == undefined + + if(modelsNotDefined and (markersScope == undefined or markersScope.markerModels == undefined or markersScope.models == undefined )) + @$log.info("No models to create windows from! Need direct models or models derrived from markers!") + return + if gMap? + #at the very least we need a Map, the marker is optional as we can create Windows without markers + if @linked.scope.models? + #we are creating windows with no markers + @models = @linked.scope.models + if(@firstTime) + @watchModels(@linked.scope) + @watchDestroy(@linked.scope) + @setContentKeys(@linked.scope.models) #only setting content keys once per model array + @bigGulp.handleLargeArray(@linked.scope.models,(model) => + @createWindow(model,undefined,gMap) + ) + else + #creating windows with parent markers + @models = markersScope.models + if(@firstTime) + @watchModels(markersScope) + @watchDestroy(markersScope) + @setContentKeys(markersScope.models) #only setting content keys once per model array + @bigGulp.handleLargeArray(markersScope.markerModels,(mm) => + @createWindow(mm.model,mm.gMarker,gMap) + ) + @firstTime = false + + setContentKeys:(models)=> + if(models.length > 0) + @contentKeys = Object.keys(models[0]) + + createWindow: (model,gMarker,gMap)=> + ### + Create ChildScope to Mimmick an ng-repeat created scope, must define the below scope + scope= { + coords: '=coords', + show: '&show', + templateUrl: '=templateurl', + templateParameter: '=templateparameter', + isIconVisibleOnClick: '=isiconvisibleonclick', + closeClick: '&closeclick' + } + ### + childScope = @linked.scope.$new(false) + @setChildScope(childScope,model) + childScope.$watch('model',(newValue, oldValue) => + if(newValue != oldValue) + @setChildScope(childScope,newValue) + ,true) + parsedContent = @interpolateContent(@linked.element.html(),model) + opts = @createWindowOptions(gMarker,childScope,parsedContent,@DEFAULTS) + @windows.push( + new directives.api.models.child.WindowChildModel( childScope,opts,@isIconVisibleOnClick,gMap,gMarker,@$http,@$templateCache,@$compile,true) + ) + + setChildScope:(childScope,model) => + for name in @scopePropNames + do (name) => + nameKey = name + 'Key' + newValue = if @[nameKey] == 'self' then model else model[@[nameKey]] + if(newValue != childScope[name]) + childScope[name] = newValue + childScope.model = model + + interpolateContent: (content,model) => + if @contentKeys == undefined or @contentKeys.length == 0 + return + exp = @$interpolate(content) + interpModel = {} + interpModel[key] = model[key] for key in @contentKeys + exp(interpModel) \ No newline at end of file diff --git a/src/coffee/directives/api/utils/async-processor.coffee b/src/coffee/directives/api/utils/async-processor.coffee new file mode 100644 index 000000000..6ba0df17f --- /dev/null +++ b/src/coffee/directives/api/utils/async-processor.coffee @@ -0,0 +1,25 @@ + +### + Author: Nicholas McCready & jfriend00 + AsyncProcessor handles things asynchronous-like :), to allow the UI to be free'd to do other things + Code taken from http://stackoverflow.com/questions/10344498/best-way-to-iterate-over-an-array-without-blocking-the-ui +### +@ngGmapModule "directives.api.utils", -> + @AsyncProcessor = + handleLargeArray:(array, callback, pausedCallBack ,chunk = 100, index = 0) -> + if array == undefined or array.length <= 0 + return + # set this to whatever number of items you can process at once + doChunk = () -> + cnt = chunk + i = index + + while cnt-- and i < array.length + # process array[index] here + callback(array[i]) + ++i + if i < array.length + index = i + pausedCallBack() if pausedCallBack? + setTimeout(doChunk(), 1) + doChunk() \ No newline at end of file diff --git a/src/coffee/directives/api/utils/gmap-util.coffee b/src/coffee/directives/api/utils/gmap-util.coffee new file mode 100644 index 000000000..57d3368ad --- /dev/null +++ b/src/coffee/directives/api/utils/gmap-util.coffee @@ -0,0 +1,22 @@ +@ngGmapModule "directives.api.utils", -> + @GmapUtil = + createMarkerOptions:(map,coords,icon,animate,defaults) -> + opts = angular.extend({}, defaults, { + position: new google.maps.LatLng(coords.latitude, coords.longitude), + map: map.getMap(), + icon: icon, + visible: coords.latitude? and coords.longitude? + }) + if !animate + delete opts.animation; + opts + + createWindowOptions:(gMarker,scope,content,defaults) -> + angular.extend({}, defaults, { + content: content, + position: + if angular.isObject(gMarker) + gMarker.getPosition() + else + new google.maps.LatLng(scope.coords.latitude, scope.coords.longitude) + }) diff --git a/src/coffee/directives/api/utils/linked.coffee b/src/coffee/directives/api/utils/linked.coffee new file mode 100644 index 000000000..d71579ca8 --- /dev/null +++ b/src/coffee/directives/api/utils/linked.coffee @@ -0,0 +1,7 @@ +@ngGmapModule "directives.api.utils", -> + class @Linked extends oo.BaseObject + constructor:(scope, element, attrs, ctrls)-> + @scope = scope + @element = element + @attrs = attrs + @ctrls = ctrls \ No newline at end of file diff --git a/src/coffee/directives/api/utils/logger.coffee b/src/coffee/directives/api/utils/logger.coffee new file mode 100644 index 000000000..685773ca4 --- /dev/null +++ b/src/coffee/directives/api/utils/logger.coffee @@ -0,0 +1,10 @@ +@ngGmapModule "directives.api.utils", -> + @Logger = + logger: undefined + doLog: false + info:(msg) -> + if(directives.api.utils.Logger.doLog) + if directives.api.utils.Logger.logger? + directives.api.utils.Logger.logger.info(msg) + else + console.info(msg) \ No newline at end of file diff --git a/src/coffee/directives/api/window.coffee b/src/coffee/directives/api/window.coffee new file mode 100644 index 000000000..2887084ad --- /dev/null +++ b/src/coffee/directives/api/window.coffee @@ -0,0 +1,34 @@ +### + Window directive for GoogleMap Info Windows, where ng-repeat is being used.... + Where Html DOM element is 1:1 on Scope and a Model +### +@ngGmapModule "directives.api", -> + class @Window extends directives.api.IWindow + @include directives.api.utils.GmapUtil + constructor: ($timeout, $compile, $http, $templateCache) -> + super($timeout, $compile, $http, $templateCache) + self = @ + @clsName = "Window" + @require= ['^googleMap', '^?marker'] + @template = '' + @$log.info(self) + + link: (scope, element, attrs, ctrls) => + @$timeout( => + isIconVisibleOnClick = true + if angular.isDefined(attrs.isiconvisibleonclick) + isIconVisibleOnClick = scope.isIconVisibleOnClick + mapCtrl = ctrls[0].getMap() + markerCtrl = if ctrls.length > 1 and ctrls[1]? then ctrls[1].getMarker() else undefined + + opts = @createWindowOptions(markerCtrl,scope,element.html(),@DEFAULTS) + + if mapCtrl? #at the very least we need a Map, the marker is optional as we can create Windows without markers + window = new directives.api.models.child.WindowChildModel( + scope,opts,isIconVisibleOnClick,mapCtrl, + markerCtrl,@$http,@$templateCache,@$compile + ) + scope.$on("$destroy", => + window.destroy() + ) + ,50) \ No newline at end of file diff --git a/src/coffee/directives/api/windows.coffee b/src/coffee/directives/api/windows.coffee new file mode 100644 index 000000000..b09ba9c7c --- /dev/null +++ b/src/coffee/directives/api/windows.coffee @@ -0,0 +1,18 @@ +### + Windows directive where many windows map to the models property +### +@ngGmapModule "directives.api", -> + class @Windows extends directives.api.IWindow + constructor: ($timeout, $compile, $http, $templateCache,$interpolate) -> + super($timeout, $compile, $http, $templateCache) + self = @ + @$interpolate = $interpolate + @clsName = "Windows" + @require= ['^googleMap', '^?markers'] + @template = '' + @scope.models = '=models' #if undefined it will try get a markers models + @$log.info(self) + + link: (scope, element, attrs, ctrls) => + new directives.api.models.parent.WindowsParentModel(scope, element, attrs, ctrls, @$timeout, + @$compile, @$http, @$templateCache,@$interpolate) \ No newline at end of file diff --git a/src/coffee/oo/base-object.coffee b/src/coffee/oo/base-object.coffee new file mode 100644 index 000000000..384ec359d --- /dev/null +++ b/src/coffee/oo/base-object.coffee @@ -0,0 +1,15 @@ +@ngGmapModule "oo", -> + baseObjectKeywords = ['extended', 'included'] + + class @BaseObject + @extend: (obj) -> + for key, value of obj when key not in baseObjectKeywords + @[key] = value + obj.extended?.apply(0) + this + @include: (obj) -> + for key, value of obj when key not in baseObjectKeywords + #Assign properties to the prototype + @::[key] = value + obj.included?.apply(0) + this \ No newline at end of file diff --git a/src/coffee/oo/ng-gmap-module.coffee b/src/coffee/oo/ng-gmap-module.coffee new file mode 100644 index 000000000..49f6412e0 --- /dev/null +++ b/src/coffee/oo/ng-gmap-module.coffee @@ -0,0 +1,8 @@ +@ngGmapModule = (names, fn) -> + names = names.split '.' if typeof names is 'string' + space = @[names.shift()] ||= {} + space.ngGmapModule ||= @ngGmapModule + if names.length + space.ngGmapModule names, fn + else + fn.call space \ No newline at end of file diff --git a/src/directives/marker.js b/src/directives/marker.js deleted file mode 100644 index 095373f6b..000000000 --- a/src/directives/marker.js +++ /dev/null @@ -1,144 +0,0 @@ -/**! - * The MIT License - * - * Copyright (c) 2010-2012 Google, Inc. http://angularjs.org - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * angular-google-maps - * https://github.com/nlaplante/angular-google-maps - * - * @author Nicolas Laplante https://plus.google.com/108189012221374960701 - */ - -/** - * Map marker directive - * - * This directive is used to create a marker on an existing map. - * This directive creates a new scope. - * - * {attribute coords required} object containing latitude and longitude properties - * {attribute icon optional} string url to image used for marker icon - * {attribute animate optional} if set to false, the marker won't be animated (on by default) - */ - -angular.module('google-maps') - .directive('marker', ['$log', '$timeout', function ($log, $timeout) { - - "use strict"; - - var DEFAULTS = { - // Animation is enabled by default - animation: google.maps.Animation.DROP - }; - - /** - * Check if a value is literally false - * @param value the value to test - * @returns {boolean} true if value is literally false, false otherwise - */ - function isFalse(value) { - return ['false', 'FALSE', 0, 'n', 'N', 'no', 'NO'].indexOf(value ) !== -1; - } - - return { - restrict: 'ECMA', - require: '^googleMap', - priority: -1, - transclude: true, - template: '', - replace: true, - scope: { - coords: '=coords', - icon: '=icon', - click: '&click' - }, - controller: function ($scope, $element) { - this.getMarker = function () { - return $element.data('instance'); - }; - }, - link: function (scope, element, attrs, mapCtrl) { - - // Validate required properties - if (angular.isUndefined(scope.coords) || - scope.coords === null || - angular.isUndefined(scope.coords.latitude) || - angular.isUndefined(scope.coords.longitude)) { - - $log.error("marker: no valid coords attribute found"); - return; - } - - // Wrap marker initialization inside a $timeout() call to make sure the map is created already - $timeout(function () { - var opts = angular.extend({}, DEFAULTS, { - position: new google.maps.LatLng(scope.coords.latitude, scope.coords.longitude), - map: mapCtrl.getMap(), - icon: scope.icon, - visible: scope.coords.latitude !== null && scope.coords.longitude !== null - }); - - // Disable animations - if (angular.isDefined(attrs.animate) && isFalse(attrs.animate)) { - delete opts.animation; - } - - var marker = new google.maps.Marker(opts); - element.data('instance', marker); - - google.maps.event.addListener(marker, 'click', function () { - if (angular.isDefined(attrs.click) && scope.click !== null) - $timeout(function() { - scope.click(); - }); - }); - - scope.$watch('coords', function (newValue, oldValue) { - if (newValue !== oldValue) { - if (newValue) { - marker.setMap(mapCtrl.getMap()); - marker.setPosition(new google.maps.LatLng(newValue.latitude, newValue.longitude)); - marker.setVisible(newValue.latitude !== null && newValue.longitude !== null); - } - else { - // Remove marker - marker.setMap(null); - } - } - }, true); - - scope.$watch('icon', function (newValue, oldValue) { - if (newValue !== oldValue) { - marker.icon = newValue; - marker.setMap(null); - marker.setMap(mapCtrl.getMap()); - marker.setPosition(new google.maps.LatLng(scope.coords.latitude, scope.coords.longitude)); - marker.setVisible(scope.coords.latitude !== null && scope.coords.longitude !== null); - } - }, true); - - // remove marker on scope $destroy - scope.$on("$destroy", function () { - marker.setMap(null); - }); - }); - } - }; - }]); diff --git a/src/directives/window.js b/src/directives/window.js deleted file mode 100644 index 2dfc80512..000000000 --- a/src/directives/window.js +++ /dev/null @@ -1,143 +0,0 @@ -/**! - * The MIT License - * - * Copyright (c) 2010-2012 Google, Inc. http://angularjs.org - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * angular-google-maps - * https://github.com/nlaplante/angular-google-maps - * - * @author Nicolas Laplante https://plus.google.com/108189012221374960701 - */ - -/** - * Map info window directive - * - * This directive is used to create an info window on an existing map. - * This directive creates a new scope. - * - * {attribute coords required} object containing latitude and longitude properties - * {attribute show optional} map will show when this expression returns true - */ - -angular.module("google-maps"). - directive("window", ['$log', '$timeout','$compile', '$http', '$templateCache', function ($log, $timeout, $compile, $http, $templateCache) { - - "use strict"; - - var DEFAULTS = { - - }; - - return { - restrict: 'ECMA', - template: '', - transclude: true, - priority: -100, - require: ['^googleMap', '^?marker'], - scope: { - coords: '=coords', - show: '&show', - templateUrl: '=templateurl', - templateParameter: '=templateparameter', - isIconVisibleOnClick: '=isiconvisibleonclick', - closeClick: '&closeclick' //scope glue to gmap InfoWindow closeclick - }, - link: function (scope, element, attrs, ctrls) { - $timeout(function () { - - var isIconVisibleOnClick = true; - - if (angular.isDefined(attrs.isiconvisibleonclick)) - isIconVisibleOnClick = scope.isIconVisibleOnClick; - - var mapCtrl = ctrls[0], - markerCtrl = ctrls.length > 1 ? ctrls[1] : null; - - var opts = angular.extend({}, DEFAULTS, { - content: element.html(), - position: angular.isDefined(markerCtrl) ? markerCtrl.getMarker().getPosition() : - new google.maps.LatLng(scope.coords.latitude, scope.coords.longitude) - }); - - var win = new google.maps.InfoWindow(opts); - - if (angular.isDefined(markerCtrl)) { - // Open window on click - var markerInstance = markerCtrl.getMarker(); - - markerInstance.setClickable(true); - - // Show the window and hide the marker on click - var initialMarkerVisibility; - google.maps.event.addListener(markerInstance, 'click', function () { - win.setPosition(markerInstance.getPosition()); - win.open(mapCtrl.getMap()); - - initialMarkerVisibility = markerInstance.getVisible(); - - markerInstance.setVisible(isIconVisibleOnClick); - }); - - // Set visibility of marker back to what it was before opening the window - google.maps.event.addListener(win, 'closeclick', function () { - markerInstance.setVisible(initialMarkerVisibility); - scope.closeClick(); - }); - } - - function showWindow() { - if (scope.templateUrl) { - $http.get(scope.templateUrl, { cache: $templateCache }) - .then(function (content) { - var templateScope = scope.$new(); - if (angular.isDefined(scope.templateParameter)) { - templateScope.parameter = scope.templateParameter; - } - var compiled = $compile(content.data)(templateScope); - win.setContent(compiled.get(0)); - win.open(mapCtrl.getMap()); - }); - } else { - win.open(mapCtrl.getMap()); - } - } - - function hideWindow() { - win.close(); - } - - scope.$watch('show()', function (newValue, oldValue) { - if (newValue !== oldValue) { - if (newValue) { - showWindow(); - } - else { - hideWindow(); - } - } else if (newValue && !win.getMap()) { - // If we're initially showing the marker and it's not yet visible, show it. - showWindow(); - } - },true); - }, 50); - } - }; - }]); diff --git a/src/controllers/polyline-display.js b/src/js/controllers/polyline-display.js similarity index 97% rename from src/controllers/polyline-display.js rename to src/js/controllers/polyline-display.js index e70422256..cebcf0b80 100644 --- a/src/controllers/polyline-display.js +++ b/src/js/controllers/polyline-display.js @@ -1,5 +1,5 @@ angular.module('google-maps').controller('PolylineDisplayController',['$scope',function($scope){ $scope.toggleStrokeColor = function(){ $scope.stroke.color = ($scope.stroke.color == "#6060FB") ? "red" : "#6060FB"; - } + }; }]); \ No newline at end of file diff --git a/src/directives/map.js b/src/js/directives/map.js similarity index 99% rename from src/directives/map.js rename to src/js/directives/map.js index ef1136e87..fda67030e 100644 --- a/src/directives/map.js +++ b/src/js/directives/map.js @@ -31,6 +31,8 @@ angular.module('google-maps') .directive('googleMap', ['$log', '$timeout', function ($log, $timeout) { "use strict"; + + directives.api.utils.Logger.logger = $log; var DEFAULTS = { mapTypeId: google.maps.MapTypeId.ROADMAP diff --git a/src/js/directives/marker.js b/src/js/directives/marker.js new file mode 100644 index 000000000..e88f34efc --- /dev/null +++ b/src/js/directives/marker.js @@ -0,0 +1,43 @@ +/**! + * The MIT License + * + * Copyright (c) 2010-2012 Google, Inc. http://angularjs.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * angular-google-maps + * https://github.com/nlaplante/angular-google-maps + * + * @authors Nicolas Laplante, Nicholas McCready https://plus.google.com/108189012221374960701 + */ + +/** + * Map marker directive + * + * This directive is used to create a marker on an existing map. + * This directive creates a new scope. + * + * {attribute coords required} object containing latitude and longitude properties + * {attribute icon optional} string url to image used for marker icon + * {attribute animate optional} if set to false, the marker won't be animated (on by default) + */ + +angular.module('google-maps').directive('marker', ['$timeout', function ($timeout) { + return new directives.api.Marker($timeout); +}]); diff --git a/src/js/directives/markers.js b/src/js/directives/markers.js new file mode 100644 index 000000000..6fa2f4ddd --- /dev/null +++ b/src/js/directives/markers.js @@ -0,0 +1,43 @@ +/**! + * The MIT License + * + * Copyright (c) 2010-2012 Google, Inc. http://angularjs.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * angular-google-maps + * https://github.com/nlaplante/angular-google-maps + * + * @author Nicolas Laplante, Nicholas McCready https://plus.google.com/108189012221374960701 + */ + +/** + * Map marker directive + * + * This directive is used to create a marker on an existing map. + * This directive creates a new scope. + * + * {attribute coords required} object containing latitude and longitude properties + * {attribute icon optional} string url to image used for marker icon + * {attribute animate optional} if set to false, the marker won't be animated (on by default) + */ + +angular.module('google-maps').directive('markers', ['$timeout', function ($timeout) { + return new directives.api.Markers($timeout); +}]); diff --git a/src/directives/polygon.js b/src/js/directives/polygon.js similarity index 97% rename from src/directives/polygon.js rename to src/js/directives/polygon.js index 23e4c9664..3b56bdad8 100644 --- a/src/directives/polygon.js +++ b/src/js/directives/polygon.js @@ -197,14 +197,14 @@ angular.module("google-maps") var newLength = newArray.length; var l = Math.min(oldLength,newLength); for(;i < l; i++){ - var oldValue = oldArray.getAt(i); - var newValue = newArray[i]; + oldValue = oldArray.getAt(i); + newValue = newArray[i]; if((oldValue.lat() != newValue.latitude) || (oldValue.lng() != newValue.longitude)){ oldArray.setAt(i,new google.maps.LatLng(newValue.latitude, newValue.longitude)); } } for(; i < newLength; i++){ - var newValue = newArray[i]; + newValue = newArray[i]; oldArray.push(new google.maps.LatLng(newValue.latitude, newValue.longitude)); } for(; i < oldLength; i++){ diff --git a/src/directives/polyline.js b/src/js/directives/polyline.js similarity index 100% rename from src/directives/polyline.js rename to src/js/directives/polyline.js diff --git a/src/directives/trafficlayer.js b/src/js/directives/trafficlayer.js similarity index 100% rename from src/directives/trafficlayer.js rename to src/js/directives/trafficlayer.js diff --git a/src/js/directives/window.js b/src/js/directives/window.js new file mode 100644 index 000000000..3a3258caf --- /dev/null +++ b/src/js/directives/window.js @@ -0,0 +1,43 @@ +/**! + * The MIT License + * + * Copyright (c) 2010-2012 Google, Inc. http://angularjs.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * angular-google-maps + * https://github.com/nlaplante/angular-google-maps + * + * @author Nicolas Laplante https://plus.google.com/108189012221374960701 + */ + +/** + * Map info window directive + * + * This directive is used to create an info window on an existing map. + * This directive creates a new scope. + * + * {attribute coords required} object containing latitude and longitude properties + * {attribute show optional} map will show when this expression returns true + */ + +angular.module("google-maps").directive("window", ['$timeout','$compile', '$http', '$templateCache', + function ($timeout, $compile, $http, $templateCache) { + return new directives.api.Window($timeout, $compile, $http, $templateCache); + }]); \ No newline at end of file diff --git a/src/js/directives/windows.js b/src/js/directives/windows.js new file mode 100644 index 000000000..521e35bcf --- /dev/null +++ b/src/js/directives/windows.js @@ -0,0 +1,45 @@ +/**! + * The MIT License + * + * Copyright (c) 2010-2012 Google, Inc. http://angularjs.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * angular-google-maps + * https://github.com/nlaplante/angular-google-maps + * + * @authors: + * - Nicolas Laplante https://plus.google.com/108189012221374960701 + * - Nicholas McCready https://plus.google.com/112199819969944829348 + */ + +/** + * Map info window directive + * + * This directive is used to create an info window on an existing map. + * This directive creates a new scope. + * + * {attribute coords required} object containing latitude and longitude properties + * {attribute show optional} map will show when this expression returns true + */ + +angular.module("google-maps").directive("windows", ['$timeout','$compile', '$http', '$templateCache', '$interpolate', + function ($timeout, $compile, $http, $templateCache,$interpolate) { + return new directives.api.Windows($timeout, $compile, $http, $templateCache, $interpolate); + }]); \ No newline at end of file diff --git a/src/module.js b/src/js/module.js similarity index 100% rename from src/module.js rename to src/js/module.js diff --git a/src/utils/LatLngArraySync.js b/src/js/utils/LatLngArraySync.js similarity index 98% rename from src/utils/LatLngArraySync.js rename to src/js/utils/LatLngArraySync.js index 220645bc6..bb38e1eca 100644 --- a/src/utils/LatLngArraySync.js +++ b/src/js/utils/LatLngArraySync.js @@ -54,6 +54,6 @@ angular.module("google-maps") watchListener(); watchListener = null; } - } - } + }; + }; }]); \ No newline at end of file diff --git a/src/utils/MapEvents.js b/src/js/utils/MapEvents.js similarity index 92% rename from src/utils/MapEvents.js rename to src/js/utils/MapEvents.js index b989a8126..e13168ae4 100644 --- a/src/utils/MapEvents.js +++ b/src/js/utils/MapEvents.js @@ -18,9 +18,9 @@ angular.module('google-maps').factory('add-events', ['$timeout',function($timeou }); return function(){ - angular.forEach(remove,function(fn){fn()}); + angular.forEach(remove,function(fn){fn();}); remove = null; - } + }; } return addEvents;