attr2_options.js.html 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="utf-8">
  5. <title>JSDoc: Source: services/attr2_options.js</title>
  6. <script src="scripts/prettify/prettify.js"> </script>
  7. <script src="scripts/prettify/lang-css.js"> </script>
  8. <!--[if lt IE 9]>
  9. <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
  10. <![endif]-->
  11. <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
  12. <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
  13. </head>
  14. <body>
  15. <div id="main">
  16. <h1 class="page-title">Source: services/attr2_options.js</h1>
  17. <section>
  18. <article>
  19. <pre class="prettyprint source linenums"><code>/**
  20. * @ngdoc service
  21. * @name Attr2Options
  22. * @description
  23. * Converts tag attributes to options used by google api v3 objects, map, marker, polygon, circle, etc.
  24. */
  25. /*jshint -W030*/
  26. ngMap.service('Attr2Options', ['$parse', 'NavigatorGeolocation', 'GeoCoder', function($parse, NavigatorGeolocation, GeoCoder) {
  27. var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g;
  28. var MOZ_HACK_REGEXP = /^moz([A-Z])/;
  29. var orgAttributes = function(el) {
  30. (el.length > 0) &amp;&amp; (el = el[0]);
  31. var orgAttributes = {};
  32. for (var i=0; i&lt;el.attributes.length; i++) {
  33. var attr = el.attributes[i];
  34. orgAttributes[attr.name] = attr.value;
  35. }
  36. return orgAttributes;
  37. }
  38. var camelCase = function(name) {
  39. return name.
  40. replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) {
  41. return offset ? letter.toUpperCase() : letter;
  42. }).
  43. replace(MOZ_HACK_REGEXP, 'Moz$1');
  44. }
  45. var JSONize = function(str) {
  46. try { // if parsable already, return as it is
  47. JSON.parse(str);
  48. return str;
  49. } catch(e) { // if not parsable, change little
  50. return str
  51. // wrap keys without quote with valid double quote
  52. .replace(/([\$\w]+)\s*:/g, function(_, $1){return '"'+$1+'":'})
  53. // replacing single quote wrapped ones to double quote
  54. .replace(/'([^']+)'/g, function(_, $1){return '"'+$1+'"'})
  55. }
  56. }
  57. var toOptionValue = function(input, options) {
  58. var output, key=options.key, scope=options.scope;
  59. try { // 1. Number?
  60. var num = Number(input);
  61. if (isNaN(num)) {
  62. throw "Not a number";
  63. } else {
  64. output = num;
  65. }
  66. } catch(err) {
  67. try { // 2.JSON?
  68. if (input.match(/^[\+\-]?[0-9\.]+,[ ]*\ ?[\+\-]?[0-9\.]+$/)) { // i.e "-1.0, 89.89"
  69. input = "["+input+"]";
  70. }
  71. output = JSON.parse(JSONize(input));
  72. if (output instanceof Array) {
  73. var t1stEl = output[0];
  74. if (t1stEl.constructor == Object) { // [{a:1}] : not lat/lng ones
  75. } else if (t1stEl.constructor == Array) { // [[1,2],[3,4]]
  76. output = output.map(function(el) {
  77. return new google.maps.LatLng(el[0], el[1]);
  78. });
  79. } else if(!isNaN(parseFloat(t1stEl)) &amp;&amp; isFinite(t1stEl)) {
  80. return new google.maps.LatLng(output[0], output[1]);
  81. }
  82. }
  83. } catch(err2) {
  84. // 3. Object Expression. i.e. LatLng(80,-49)
  85. if (input.match(/^[A-Z][a-zA-Z0-9]+\(.*\)$/)) {
  86. try {
  87. var exp = "new google.maps."+input;
  88. output = eval(exp); // TODO, still eval
  89. } catch(e) {
  90. output = input;
  91. }
  92. // 4. Object Expression. i.e. MayTypeId.HYBRID
  93. } else if (input.match(/^([A-Z][a-zA-Z0-9]+)\.([A-Z]+)$/)) {
  94. try {
  95. var matches = input.match(/^([A-Z][a-zA-Z0-9]+)\.([A-Z]+)$/);
  96. output = google.maps[matches[1]][matches[2]];
  97. } catch(e) {
  98. output = input;
  99. }
  100. // 5. Object Expression. i.e. HYBRID
  101. } else if (input.match(/^[A-Z]+$/)) {
  102. try {
  103. var capitalizedKey = key.charAt(0).toUpperCase() + key.slice(1);
  104. if (key.match(/temperatureUnit|windSpeedUnit|labelColor/)) {
  105. capitalizedKey = capitalizedKey.replace(/s$/,"");
  106. output = google.maps.weather[capitalizedKey][input];
  107. } else {
  108. output = google.maps[capitalizedKey][input];
  109. }
  110. } catch(e) {
  111. output = input;
  112. }
  113. } else {
  114. output = input;
  115. }
  116. } // catch(err2)
  117. } // catch(err)
  118. return output;
  119. };
  120. var setDelayedGeoLocation = function(object, method, param, options) {
  121. options = options || {};
  122. var centered = object.centered || options.centered;
  123. var errorFunc = function() {
  124. console.log('error occurred while', object, method, param, options);
  125. var fallbackLocation = options.fallbackLocation || new google.maps.LatLng(0,0);
  126. object[method](fallbackLocation);
  127. };
  128. if (!param || param.match(/^current/i)) { // sensored position
  129. NavigatorGeolocation.getCurrentPosition().then(
  130. function(position) { // success
  131. var lat = position.coords.latitude;
  132. var lng = position.coords.longitude;
  133. var latLng = new google.maps.LatLng(lat,lng);
  134. object[method](latLng);
  135. if (centered) {
  136. object.map.setCenter(latLng);
  137. }
  138. options.callback &amp;&amp; options.callback.apply(object);
  139. },
  140. errorFunc
  141. );
  142. } else { //assuming it is address
  143. GeoCoder.geocode({address: param}).then(
  144. function(results) { // success
  145. object[method](results[0].geometry.location);
  146. if (centered) {
  147. object.map.setCenter(results[0].geometry.location);
  148. }
  149. },
  150. errorFunc
  151. );
  152. }
  153. };
  154. var getAttrsToObserve = function(attrs) {
  155. var attrsToObserve = [];
  156. if (attrs["ng-repeat"] || attrs.ngRepeat) { // if element is created by ng-repeat, don't observe any
  157. } else {
  158. for (var attrName in attrs) {
  159. var attrValue = attrs[attrName];
  160. if (attrValue &amp;&amp; attrValue.match(/\{\{.*\}\}/)) { // if attr value is {{..}}
  161. console.log('setting attribute to observe', attrName, camelCase(attrName), attrValue);
  162. attrsToObserve.push(camelCase(attrName));
  163. }
  164. }
  165. }
  166. return attrsToObserve;
  167. };
  168. var observeAttrSetObj = function(orgAttrs, attrs, obj) {
  169. var attrsToObserve = getAttrsToObserve(orgAttrs);
  170. if (Object.keys(attrsToObserve).length) {
  171. console.log(obj, "attributes to observe", attrsToObserve);
  172. }
  173. for (var i=0; i&lt;attrsToObserve.length; i++) {
  174. observeAndSet(attrs, attrsToObserve[i], obj);
  175. }
  176. }
  177. var observeAndSet = function(attrs, attrName, object) {
  178. attrs.$observe(attrName, function(val) {
  179. if (val) {
  180. console.log('observing ', object, attrName, val);
  181. var setMethod = camelCase('set-'+attrName);
  182. var optionValue = toOptionValue(val, {key: attrName});
  183. console.log('setting ', object, attrName, 'with value', optionValue);
  184. if (object[setMethod]) { //if set method does exist
  185. /* if an location is being observed */
  186. if (attrName.match(/center|position/) &amp;&amp;
  187. typeof optionValue == 'string') {
  188. setDelayedGeoLocation(object, setMethod, optionValue);
  189. } else {
  190. object[setMethod](optionValue);
  191. }
  192. }
  193. }
  194. });
  195. };
  196. return {
  197. /**
  198. * filters attributes by skipping angularjs methods $.. $$..
  199. * @memberof Attr2Options
  200. * @param {Hash} attrs tag attributes
  201. * @returns {Hash} filterd attributes
  202. */
  203. filter: function(attrs) {
  204. var options = {};
  205. for(var key in attrs) {
  206. if (key.match(/^\$/) || key.match(/^ng[A-Z]/)) {
  207. } else {
  208. options[key] = attrs[key];
  209. }
  210. }
  211. return options;
  212. },
  213. /**
  214. * converts attributes hash to Google Maps API v3 options
  215. * ```
  216. * . converts numbers to number
  217. * . converts class-like string to google maps instance
  218. * i.e. `LatLng(1,1)` to `new google.maps.LatLng(1,1)`
  219. * . converts constant-like string to google maps constant
  220. * i.e. `MapTypeId.HYBRID` to `google.maps.MapTypeId.HYBRID`
  221. * i.e. `HYBRID"` to `google.maps.MapTypeId.HYBRID`
  222. * ```
  223. * @memberof Attr2Options
  224. * @param {Hash} attrs tag attributes
  225. * @param {scope} scope angularjs scope
  226. * @returns {Hash} options converted attributess
  227. */
  228. getOptions: function(attrs, scope) {
  229. var options = {};
  230. for(var key in attrs) {
  231. if (attrs[key]) {
  232. if (key.match(/^on[A-Z]/)) { //skip events, i.e. on-click
  233. continue;
  234. } else if (key.match(/ControlOptions$/)) { // skip controlOptions
  235. continue;
  236. } else {
  237. options[key] = toOptionValue(attrs[key], {scope:scope, key: key});
  238. }
  239. } // if (attrs[key])
  240. } // for(var key in attrs)
  241. return options;
  242. },
  243. /**
  244. * converts attributes hash to scope-specific event function
  245. * @memberof Attr2Options
  246. * @param {scope} scope angularjs scope
  247. * @param {Hash} attrs tag attributes
  248. * @returns {Hash} events converted events
  249. */
  250. getEvents: function(scope, attrs) {
  251. var events = {};
  252. var toLowercaseFunc = function($1){
  253. return "_"+$1.toLowerCase();
  254. };
  255. var eventFunc = function(attrValue) {
  256. var matches = attrValue.match(/([^\(]+)\(([^\)]*)\)/);
  257. var funcName = matches[1];
  258. var argsStr = matches[2].replace(/event[ ,]*/,''); //remove string 'event'
  259. var args = scope.$eval("["+argsStr+"]");
  260. return function(event) {
  261. function index(obj,i) {return obj[i]}
  262. f = funcName.split('.').reduce(index, scope)
  263. f.apply(this, [event].concat(args));
  264. scope.$apply();
  265. }
  266. }
  267. for(var key in attrs) {
  268. if (attrs[key]) {
  269. if (!key.match(/^on[A-Z]/)) { //skip if not events
  270. continue;
  271. }
  272. //get event name as underscored. i.e. zoom_changed
  273. var eventName = key.replace(/^on/,'');
  274. eventName = eventName.charAt(0).toLowerCase() + eventName.slice(1);
  275. eventName = eventName.replace(/([A-Z])/g, toLowercaseFunc);
  276. var attrValue = attrs[key];
  277. events[eventName] = new eventFunc(attrValue);
  278. }
  279. }
  280. return events;
  281. },
  282. /**
  283. * control means map controls, i.e streetview, pan, etc, not a general control
  284. * @memberof Attr2Options
  285. * @param {Hash} filtered filtered tag attributes
  286. * @returns {Hash} Google Map options
  287. */
  288. getControlOptions: function(filtered) {
  289. var controlOptions = {};
  290. if (typeof filtered != 'object')
  291. return false;
  292. for (var attr in filtered) {
  293. if (filtered[attr]) {
  294. if (!attr.match(/(.*)ControlOptions$/)) {
  295. continue; // if not controlOptions, skip it
  296. }
  297. //change invalid json to valid one, i.e. {foo:1} to {"foo": 1}
  298. var orgValue = filtered[attr];
  299. var newValue = orgValue.replace(/'/g, '"');
  300. newValue = newValue.replace(/([^"]+)|("[^"]+")/g, function($0, $1, $2) {
  301. if ($1) {
  302. return $1.replace(/([a-zA-Z0-9]+?):/g, '"$1":');
  303. } else {
  304. return $2;
  305. }
  306. });
  307. try {
  308. var options = JSON.parse(newValue);
  309. for (var key in options) { //assign the right values
  310. if (options[key]) {
  311. var value = options[key];
  312. if (typeof value === 'string') {
  313. value = value.toUpperCase();
  314. } else if (key === "mapTypeIds") {
  315. value = value.map( function(str) {
  316. if (str.match(/^[A-Z]+$/)) { // if constant
  317. return google.maps.MapTypeId[str.toUpperCase()];
  318. } else { // else, custom map-type
  319. return str;
  320. }
  321. });
  322. }
  323. if (key === "style") {
  324. var str = attr.charAt(0).toUpperCase() + attr.slice(1);
  325. var objName = str.replace(/Options$/,'')+"Style";
  326. options[key] = google.maps[objName][value];
  327. } else if (key === "position") {
  328. options[key] = google.maps.ControlPosition[value];
  329. } else {
  330. options[key] = value;
  331. }
  332. }
  333. }
  334. controlOptions[attr] = options;
  335. } catch (e) {
  336. console.error('invald option for', attr, newValue, e, e.stack);
  337. }
  338. }
  339. } // for
  340. return controlOptions;
  341. }, // function
  342. toOptionValue: toOptionValue,
  343. camelCase: camelCase,
  344. setDelayedGeoLocation: setDelayedGeoLocation,
  345. getAttrsToObserve: getAttrsToObserve,
  346. observeAndSet: observeAndSet,
  347. observeAttrSetObj: observeAttrSetObj,
  348. orgAttributes: orgAttributes
  349. }; // return
  350. }]);
  351. </code></pre>
  352. </article>
  353. </section>
  354. </div>
  355. <nav>
  356. <h2><a href="index.html">Index</a></h2><h3>service</h3><ul><li><a href="Attr2Options.html">Attr2Options</a></li><li><a href="GeoCoder.html">GeoCoder</a></li><li><a href="NavigatorGeolocation.html">NavigatorGeolocation</a></li><li><a href="StreetView.html">StreetView</a></li></ul><h3>directive</h3><ul><li><a href="bicycling-layer.html">bicycling-layer</a></li><li><a href="cloud-layer.html">cloud-layer</a></li><li><a href="custom-control.html">custom-control</a></li><li><a href="drawing-manager.html">drawing-manager</a></li><li><a href="dynamic-maps-engine-layer.html">dynamic-maps-engine-layer</a></li><li><a href="fusion-tables-layer.html">fusion-tables-layer</a></li><li><a href="heatmap-layer.html">heatmap-layer</a></li><li><a href="info-window.html">info-window</a></li><li><a href="kml-layer.html">kml-layer</a></li><li><a href="lazy-load.html">lazy-load</a></li><li><a href="map.html">map</a></li><li><a href="map-data.html">map-data</a></li><li><a href="map-type.html">map-type</a></li><li><a href="MapController.html">MapController</a></li><li><a href="maps-engine-layer.html">maps-engine-layer</a></li><li><a href="marker.html">marker</a></li><li><a href="overlay-map-type.html">overlay-map-type</a></li><li><a href="shape.html">shape</a></li><li><a href="traffic-layer.html">traffic-layer</a></li><li><a href="transit-layer.html">transit-layer</a></li><li><a href="weather-layer.html">weather-layer</a></li></ul>
  357. </nav>
  358. <br clear="both">
  359. <footer>
  360. Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.3.0-alpha9</a>
  361. and <a href="https://github.com/allenhwkim/angular-jsdoc">angular-jsdoc</a>
  362. </footer>
  363. <script> prettyPrint(); </script>
  364. <script src="scripts/linenumber.js"> </script>
  365. <script>
  366. var href=window.location.href.match(/\/([^\/]+$)/)[1];
  367. document.querySelector("nav a[href='"+href+"']").scrollIntoView(true);
  368. if (window.location.hash == "")
  369. document.querySelector("body").scrollIntoView(true);
  370. </script>
  371. </body>
  372. </html>