├── .gitignore ├── css ├── blank.gif ├── fancybox_loading.gif ├── fancybox_overlay.png ├── fancybox_sprite.png ├── fancybox_overlay_white.png ├── main.css └── jquery.fancybox.css ├── img └── loading.gif ├── map.html └── js ├── main.js ├── jquery.fancybox.pack.js └── handlebars.js /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /css/blank.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwinters0/MakeMusicMap/master/css/blank.gif -------------------------------------------------------------------------------- /img/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwinters0/MakeMusicMap/master/img/loading.gif -------------------------------------------------------------------------------- /css/fancybox_loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwinters0/MakeMusicMap/master/css/fancybox_loading.gif -------------------------------------------------------------------------------- /css/fancybox_overlay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwinters0/MakeMusicMap/master/css/fancybox_overlay.png -------------------------------------------------------------------------------- /css/fancybox_sprite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwinters0/MakeMusicMap/master/css/fancybox_sprite.png -------------------------------------------------------------------------------- /css/fancybox_overlay_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwinters0/MakeMusicMap/master/css/fancybox_overlay_white.png -------------------------------------------------------------------------------- /css/main.css: -------------------------------------------------------------------------------- 1 | html { height: 100% } 2 | 3 | body { 4 | height: 100%; 5 | margin: 0; 6 | padding: 0; 7 | font-family: sans-serif; 8 | } 9 | 10 | #loading { 11 | position: absolute; 12 | left: 0px; 13 | top: 0px; 14 | width: 100%; 15 | height: 100%; 16 | text-align: center; 17 | background-color: white; 18 | z-index: 99; 19 | } 20 | #loading div { 21 | position: absolute; 22 | width: 100px; 23 | height: 100px; 24 | top: 50%; 25 | left: 50%; 26 | margin-left: -50px; 27 | margin-top: -50px; 28 | } 29 | #loading img { 30 | margin-bottom: 8px; 31 | } 32 | 33 | 34 | #map-canvas { 35 | position: absolute; 36 | top: 0; bottom: 0; right: 0; 37 | left: 370px; 38 | } 39 | 40 | #performances-container { 41 | position: absolute; 42 | top: 0; bottom: 0; left: 0; 43 | width: 369px; 44 | background-color: #F4F4F4; 45 | border-right-style:solid; 46 | border-right-width:1px; 47 | border-right-color:#B4B4B4; 48 | } 49 | 50 | #filters { 51 | height: 165px; 52 | padding: 15px 20px 15px 140px; 53 | border-bottom-style:solid; 54 | border-bottom-width:1px; 55 | border-bottom-color:#B4B4B4; 56 | } 57 | 58 | #performances { 59 | position: absolute; 60 | bottom: 0; 61 | top: 195px; 62 | width: 350px; 63 | padding: 10px; 64 | text-align: center; 65 | overflow-y: auto; 66 | } 67 | 68 | #venue-name { 69 | padding: 10px; 70 | margin: 0; 71 | } 72 | 73 | .performance { 74 | clear: both; 75 | text-align: left; 76 | padding-top: 10px; 77 | padding-left: 3px; 78 | padding-right: 5px; 79 | margin-top: 10px; 80 | border-top-style: solid; 81 | border-top-width: 1px; 82 | border-top-color: #C6C6C6; 83 | } 84 | 85 | .performance img:hover { 86 | cursor:pointer; 87 | } 88 | 89 | #filters-title { 90 | font-size: 1.2em; 91 | text-align: center; 92 | margin-bottom: 10px; 93 | } 94 | 95 | .label { 96 | width: 55px; 97 | color: #777; 98 | font-weight: 100; 99 | } 100 | 101 | #venue-address { 102 | font-size: 0.9em; 103 | } 104 | 105 | #venue-rain { 106 | color: #888; 107 | font-size: 0.7em; 108 | margin-top: 8px; 109 | } 110 | 111 | .filter-label { 112 | text-align: right; 113 | font-size: 0.9em; 114 | padding-right: 5px; 115 | } 116 | 117 | .artist-image { 118 | float: right; 119 | max-width: 100px; 120 | max-height: 70px; 121 | width: auto; 122 | height: auto; 123 | } 124 | 125 | #city-logo { 126 | float: left; 127 | margin: 10px; 128 | max-width: 125px; 129 | max-height: 125px; 130 | width: auto; 131 | height: auto; 132 | } 133 | 134 | #artistname-filter { 135 | width: 100%; 136 | } 137 | 138 | #venuename-filter { 139 | width: 100%; 140 | } 141 | 142 | .fancybox-title .child a { 143 | color: white; 144 | } 145 | 146 | .fancybox-overlay { 147 | background: url('fancybox_overlay_white.png'); 148 | } 149 | -------------------------------------------------------------------------------- /map.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 42 | 43 | 44 |
45 |
46 |
47 | Loading... 48 |
49 |
50 |
51 | 52 |
53 |
Filters
54 | 55 | 56 | 57 | 62 | 63 | 64 | 65 | 68 | 69 | 70 | 71 | 74 | 75 | 76 | 77 | 82 | 83 | 84 | 85 | 89 | 90 |
Genre: 58 | 61 |
Artist: 66 | 67 |
Venue: 72 | 73 |
Time: 78 | 81 |
86 | Playing now: 87 | 88 |
91 |
92 |
93 |
Click map to view performances.
94 |

95 |
96 |
97 |
98 |
99 |
100 |
101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 115 | 116 | 117 | -------------------------------------------------------------------------------- /css/jquery.fancybox.css: -------------------------------------------------------------------------------- 1 | /*! fancyBox v2.1.4 fancyapps.com | fancyapps.com/fancybox/#license */ 2 | .fancybox-wrap, 3 | .fancybox-skin, 4 | .fancybox-outer, 5 | .fancybox-inner, 6 | .fancybox-image, 7 | .fancybox-wrap iframe, 8 | .fancybox-wrap object, 9 | .fancybox-nav, 10 | .fancybox-nav span, 11 | .fancybox-tmp 12 | { 13 | padding: 0; 14 | margin: 0; 15 | border: 0; 16 | outline: none; 17 | vertical-align: top; 18 | } 19 | 20 | .fancybox-wrap { 21 | position: absolute; 22 | top: 0; 23 | left: 0; 24 | z-index: 8020; 25 | } 26 | 27 | .fancybox-skin { 28 | position: relative; 29 | background: #f9f9f9; 30 | color: #444; 31 | text-shadow: none; 32 | -webkit-border-radius: 4px; 33 | -moz-border-radius: 4px; 34 | border-radius: 4px; 35 | } 36 | 37 | .fancybox-opened { 38 | z-index: 8030; 39 | } 40 | 41 | .fancybox-opened .fancybox-skin { 42 | -webkit-box-shadow: 0 10px 25px rgba(0, 0, 0, 0.5); 43 | -moz-box-shadow: 0 10px 25px rgba(0, 0, 0, 0.5); 44 | box-shadow: 0 10px 25px rgba(0, 0, 0, 0.5); 45 | } 46 | 47 | .fancybox-outer, .fancybox-inner { 48 | position: relative; 49 | } 50 | 51 | .fancybox-inner { 52 | overflow: hidden; 53 | } 54 | 55 | .fancybox-type-iframe .fancybox-inner { 56 | -webkit-overflow-scrolling: touch; 57 | } 58 | 59 | .fancybox-error { 60 | color: #444; 61 | font: 14px/20px "Helvetica Neue",Helvetica,Arial,sans-serif; 62 | margin: 0; 63 | padding: 15px; 64 | white-space: nowrap; 65 | } 66 | 67 | .fancybox-image, .fancybox-iframe { 68 | display: block; 69 | width: 100%; 70 | height: 100%; 71 | } 72 | 73 | .fancybox-image { 74 | max-width: 100%; 75 | max-height: 100%; 76 | } 77 | 78 | #fancybox-loading, .fancybox-close, .fancybox-prev span, .fancybox-next span { 79 | background-image: url('fancybox_sprite.png'); 80 | } 81 | 82 | #fancybox-loading { 83 | position: fixed; 84 | top: 50%; 85 | left: 50%; 86 | margin-top: -22px; 87 | margin-left: -22px; 88 | background-position: 0 -108px; 89 | opacity: 0.8; 90 | cursor: pointer; 91 | z-index: 8060; 92 | } 93 | 94 | #fancybox-loading div { 95 | width: 44px; 96 | height: 44px; 97 | background: url('fancybox_loading.gif') center center no-repeat; 98 | } 99 | 100 | .fancybox-close { 101 | position: absolute; 102 | top: -18px; 103 | right: -18px; 104 | width: 36px; 105 | height: 36px; 106 | cursor: pointer; 107 | z-index: 8040; 108 | } 109 | 110 | .fancybox-nav { 111 | position: absolute; 112 | top: 0; 113 | width: 40%; 114 | height: 100%; 115 | cursor: pointer; 116 | text-decoration: none; 117 | background: transparent url('blank.gif'); /* helps IE */ 118 | -webkit-tap-highlight-color: rgba(0,0,0,0); 119 | z-index: 8040; 120 | } 121 | 122 | .fancybox-prev { 123 | left: 0; 124 | } 125 | 126 | .fancybox-next { 127 | right: 0; 128 | } 129 | 130 | .fancybox-nav span { 131 | position: absolute; 132 | top: 50%; 133 | width: 36px; 134 | height: 34px; 135 | margin-top: -18px; 136 | cursor: pointer; 137 | z-index: 8040; 138 | visibility: hidden; 139 | } 140 | 141 | .fancybox-prev span { 142 | left: 10px; 143 | background-position: 0 -36px; 144 | } 145 | 146 | .fancybox-next span { 147 | right: 10px; 148 | background-position: 0 -72px; 149 | } 150 | 151 | .fancybox-nav:hover span { 152 | visibility: visible; 153 | } 154 | 155 | .fancybox-tmp { 156 | position: absolute; 157 | top: -99999px; 158 | left: -99999px; 159 | visibility: hidden; 160 | max-width: 99999px; 161 | max-height: 99999px; 162 | overflow: visible !important; 163 | } 164 | 165 | /* Overlay helper */ 166 | 167 | .fancybox-lock { 168 | overflow: hidden; 169 | } 170 | 171 | .fancybox-overlay { 172 | position: absolute; 173 | top: 0; 174 | left: 0; 175 | overflow: hidden; 176 | display: none; 177 | z-index: 8010; 178 | background: url('fancybox_overlay.png'); 179 | } 180 | 181 | .fancybox-overlay-fixed { 182 | position: fixed; 183 | bottom: 0; 184 | right: 0; 185 | } 186 | 187 | .fancybox-lock .fancybox-overlay { 188 | overflow: auto; 189 | overflow-y: scroll; 190 | } 191 | 192 | /* Title helper */ 193 | 194 | .fancybox-title { 195 | visibility: hidden; 196 | font: normal 13px/20px "Helvetica Neue",Helvetica,Arial,sans-serif; 197 | position: relative; 198 | text-shadow: none; 199 | z-index: 8050; 200 | } 201 | 202 | .fancybox-opened .fancybox-title { 203 | visibility: visible; 204 | } 205 | 206 | .fancybox-title-float-wrap { 207 | position: absolute; 208 | bottom: 0; 209 | right: 50%; 210 | margin-bottom: -35px; 211 | z-index: 8050; 212 | text-align: center; 213 | } 214 | 215 | .fancybox-title-float-wrap .child { 216 | display: inline-block; 217 | margin-right: -100%; 218 | padding: 2px 20px; 219 | background: transparent; /* Fallback for web browsers that doesn't support RGBa */ 220 | background: rgba(0, 0, 0, 0.8); 221 | -webkit-border-radius: 15px; 222 | -moz-border-radius: 15px; 223 | border-radius: 15px; 224 | text-shadow: 0 1px 2px #222; 225 | color: #FFF; 226 | font-weight: bold; 227 | line-height: 24px; 228 | white-space: nowrap; 229 | } 230 | 231 | .fancybox-title-outside-wrap { 232 | position: relative; 233 | margin-top: 10px; 234 | color: #fff; 235 | } 236 | 237 | .fancybox-title-inside-wrap { 238 | padding-top: 10px; 239 | } 240 | 241 | .fancybox-title-over-wrap { 242 | position: absolute; 243 | bottom: 0; 244 | left: 0; 245 | color: #fff; 246 | padding: 10px; 247 | background: #000; 248 | background: rgba(0, 0, 0, .8); 249 | } -------------------------------------------------------------------------------- /js/main.js: -------------------------------------------------------------------------------- 1 | function MakeMusicMap() { 2 | var genres = [ 3 | "Bluegrass", 4 | "Blues", 5 | "Cabaret", 6 | "Celtic", 7 | "Classical", 8 | "Country", 9 | "Electronic", 10 | "Experimental", 11 | "Folk", 12 | "Funk", 13 | "Gospel/Religious", 14 | "Gypsy", 15 | "Hip-Hop", 16 | "Indie-Folk", 17 | "Indie-Rock", 18 | "Irish", 19 | "Jazz", 20 | "Kids", 21 | "Latin", 22 | "Marching Band", 23 | "Opera", 24 | "Other", 25 | "Polka", 26 | "Pop", 27 | "R&B", 28 | "Reggae", 29 | "Rock", 30 | "Roots", 31 | "Soul", 32 | "Standards", 33 | "World" 34 | ]; 35 | 36 | var redMarkerIcon = { 37 | url: "http://www.google.com/intl/en_us/mapfiles/ms/micons/red-dot.png" 38 | }; 39 | 40 | var blueMarkerIcon = { 41 | url: "http://www.google.com/intl/en_us/mapfiles/ms/micons/blue-dot.png" 42 | }; 43 | 44 | var map = null; 45 | var mapData; 46 | var cities; 47 | var curCity; 48 | var perfTemplate; 49 | var markers = []; 50 | var selectedMarker; 51 | var filterOb = { "genre": null, "current": false, "time": null, "artist": "", "venue": "" }; // The current filter terms 52 | var keypressTimeoutObj; 53 | 54 | this.init = function() { 55 | var source = $("#performance-template").html(); 56 | perfTemplate = Handlebars.compile(source); 57 | 58 | $.ajax({ 59 | url: "http://s3.amazonaws.com/appdata2013/cities.json", 60 | dataType: "json", 61 | context: this, 62 | success: onCitiesLoaded, 63 | error: function(jqxhr, textStatus, errorThrown) { 64 | alert('Error retrieving city data: ' + textStatus + ' ' + errorThrown); 65 | } 66 | }); 67 | 68 | }; 69 | 70 | function onCitiesLoaded(data) { 71 | cities = data; 72 | 73 | var cityId = 0; 74 | var queryString = window.location.search; 75 | if (queryString != "") 76 | cityId = parseInt(queryString.split("=")[1], 10); 77 | 78 | changeCity(cityId); 79 | } 80 | 81 | 82 | function changeCity(id) { 83 | curCity = _.find(cities, function(c) { 84 | return c.id == id; 85 | }); 86 | mapData = new MapData(); 87 | mapData.load(curCity.performances, onMapDataLoaded); 88 | 89 | $("#city-logo").attr("src", curCity.logo); 90 | } 91 | 92 | 93 | function onMapDataLoaded() { 94 | showMap(); 95 | populateMap(); 96 | activateUI(); 97 | 98 | $("#loading").fadeOut(); 99 | } 100 | 101 | 102 | function showMap() { 103 | var mapOptions = { 104 | zoom: curCity.webzoom, 105 | mapTypeId: google.maps.MapTypeId.ROADMAP 106 | }; 107 | google.maps.visualRefresh = true; 108 | 109 | if (map === null) { 110 | map = new google.maps.Map(document.getElementById("map-canvas"), mapOptions); 111 | } 112 | 113 | map.setCenter(new google.maps.LatLng(curCity.lat, curCity.lng)); 114 | 115 | google.maps.event.addListener(map, 'click', function() { 116 | deselectVenue(); 117 | }); 118 | } 119 | 120 | 121 | function activateUI() { 122 | _.each(genres, function(g) { 123 | $("#genre-filter").append(""); 124 | }); 125 | 126 | $("#genre-filter").change(function(e) { 127 | var genre = $(e.target).val(); 128 | filterOb.genre = genre == "" ? null : genre; 129 | 130 | mapData.search(filterOb); 131 | 132 | deselectVenue(); 133 | clearMarkers(); 134 | populateMap(); 135 | }); 136 | 137 | for (var i = 0; i < 12; i++) { 138 | var from = 10 + i; 139 | var until = from + 1; 140 | 141 | fromOb = hour24to12(from); 142 | untilOb = hour24to12(until); 143 | 144 | $("#time-filter").append(""); 146 | } 147 | $("#time-filter").change(function(e) { 148 | var time = $(e.target).val(); 149 | filterOb.time = time == "" ? null : parseInt(time, 10); 150 | 151 | mapData.search(filterOb); 152 | 153 | deselectVenue(); 154 | clearMarkers(); 155 | populateMap(); 156 | }); 157 | 158 | $("#current").change(function(e) { 159 | var selected = $(e.target).prop('checked'); 160 | 161 | if (selected) { 162 | filterOb.current = true; 163 | $("#time-filter").attr("disabled", "disabled"); 164 | } else { 165 | filterOb.current = false; 166 | $("#time-filter").removeAttr("disabled"); 167 | } 168 | 169 | mapData.search(filterOb); 170 | 171 | deselectVenue(); 172 | clearMarkers(); 173 | populateMap(); 174 | }); 175 | 176 | $("#artistname-filter").keyup(function(e) { 177 | clearTimeout(keypressTimeoutObj); 178 | keypressTimeoutObj = setTimeout( 179 | function() { 180 | filterOb.artist = $(e.target).val(); 181 | 182 | mapData.search(filterOb); 183 | 184 | deselectVenue(); 185 | clearMarkers(); 186 | populateMap(); 187 | }, 188 | 200 189 | ); 190 | }); 191 | 192 | $("#venuename-filter").keyup(function(e) { 193 | clearTimeout(keypressTimeoutObj); 194 | keypressTimeoutObj = setTimeout( 195 | function() { 196 | filterOb.venue = $(e.target).val(); 197 | 198 | mapData.search(filterOb); 199 | 200 | deselectVenue(); 201 | clearMarkers(); 202 | populateMap(); 203 | }, 204 | 200 205 | ); 206 | }); 207 | } 208 | 209 | function populateMap() { 210 | var venues = mapData.data; 211 | for (var i = 0; i < venues.length; i++) { 212 | var venue = venues[i]; 213 | 214 | if (venue.performances.length != 0) { 215 | // TODO: handle venues with address but no lat lng 216 | if (venue.lat && venue.lng) { 217 | var position = new google.maps.LatLng(venue.lat, venue.lng); 218 | 219 | var icon; 220 | if (venue.rain_accommodations != "Performance will be canceled") { 221 | icon = blueMarkerIcon; 222 | } else { 223 | icon = redMarkerIcon; 224 | } 225 | 226 | var marker = new google.maps.Marker({ 227 | icon: icon, 228 | position: position, 229 | map: map, 230 | title: venue.name 231 | }); 232 | 233 | markers.push(marker); 234 | 235 | google.maps.event.addListener(marker, 'click', 236 | (function(venue, marker) { 237 | return function() { 238 | selectVenue(venue, marker) 239 | } 240 | })(venue, marker)); 241 | 242 | google.maps.event.addListener(marker, 'dblclick', 243 | (function(marker) { 244 | return function() { 245 | map.setCenter(marker.getPosition()); 246 | map.setZoom(map.getZoom() + 1); 247 | } 248 | })(marker)); 249 | } 250 | } 251 | } 252 | } 253 | 254 | 255 | function selectVenue(venue, marker) { 256 | deselectVenue(); 257 | $("#instructions").hide(); 258 | 259 | marker.setIcon({ 260 | url: marker.getIcon().url, 261 | scaledSize: new google.maps.Size(40, 40) 262 | }); 263 | marker.setZIndex(9999); 264 | selectedMarker = marker; 265 | 266 | $("#venue-name").html(venue.name); 267 | $("#venue-address").html(venue.address); 268 | $("#venue-rain").html("In case of rain: " + venue.rain_accommodations); 269 | 270 | var performances = _.sortBy(venue.performances, function(p) { 271 | var timeOb = parseTime(p.start_time); 272 | return timeOb.hour24 + timeOb.minute / 60; 273 | }); 274 | 275 | $("#performances-list").empty(); 276 | for (var j = 0; j < performances.length; j++) { 277 | // Don't modify exisiting object 278 | var performance = $.extend({}, performances[j]); 279 | 280 | var startTimeOb = parseTime(performance.start_time); 281 | var endTimeOb = parseTime(performance.end_time); 282 | 283 | performance.start_time = timeObToString(startTimeOb); 284 | performance.end_time = timeObToString(endTimeOb); 285 | 286 | var html = perfTemplate(performance); 287 | $("#performances-list").append(html); 288 | } 289 | 290 | $(".artist-image-link").fancybox(); 291 | } 292 | 293 | 294 | function deselectVenue() { 295 | if (typeof selectedMarker !== "undefined") { 296 | selectedMarker.setIcon({ 297 | url: selectedMarker.getIcon().url, 298 | scaledSize: new google.maps.Size(32, 32) 299 | }); 300 | selectedMarker.setZIndex(); 301 | } 302 | 303 | $("#performances-list").empty(); 304 | $("#venue-name").empty(); 305 | $("#venue-address").empty(); 306 | $("#venue-rain").empty(); 307 | 308 | $("#instructions").show(); 309 | } 310 | 311 | function clearMarkers() { 312 | _.each(markers, function(m) { 313 | m.setMap(null); 314 | }); 315 | markers = []; 316 | } 317 | } 318 | 319 | 320 | 321 | function MapData() { 322 | 323 | //public properties 324 | this.data = []; //where we store search results 325 | 326 | //private properties 327 | var assocData; //the 'database' we search 328 | var onAjaxCompleteCallback; //optionally set when calling MapData.load() 329 | 330 | 331 | //public functions 332 | 333 | this.load = function load(url, successCallback, failCallback) { 334 | if (typeof(successCallback) === 'function') { 335 | onAjaxCompleteCallback = successCallback; 336 | } 337 | if (typeof(failCallback) === 'function') { 338 | onAjaxFailCallback = failCallback; 339 | } 340 | $.ajax({ 341 | dataType: "json", 342 | url: url, 343 | success: onAjaxComplete, 344 | context: this 345 | }); 346 | } 347 | 348 | this.search = function search(terms) { 349 | /* 350 | Search the dataset, and both return the results and set them as the publicly-available 351 | property this.data. 352 | 353 | search({ 354 | "genre": "Jazz", 355 | "current": false, 356 | "time": null, 357 | "artist": "Miles", 358 | "venue": "blue" 359 | }); 360 | */ 361 | 362 | this.data = $.extend(true, [], assocData); //copy data so caller can't modify our source data 363 | 364 | if (terms.genre !== null && terms.genre !== '') { 365 | this.data = filterGenre(this.data, terms.genre); 366 | } 367 | 368 | if (terms.current) { 369 | this.data = filterNow(this.data); 370 | } else if (terms.time !== null) { 371 | this.data = filterTime(this.data, terms.time); 372 | } 373 | 374 | if (terms.artist !== null && terms.artist !== '') { 375 | this.data = filterArtistName(this.data, terms.artist); 376 | } 377 | 378 | if (terms.venue !== null && terms.venue !== '') { 379 | this.data = filterVenueName(this.data, terms.venue); 380 | } 381 | 382 | return this.data; 383 | }; 384 | 385 | 386 | //private functions 387 | 388 | function filterGenre(venues, genre) { 389 | var gSearch = new RegExp(genre); 390 | 391 | return filter(venues, function(p) { 392 | return p.artist.genres.match(gSearch); 393 | }); 394 | } 395 | 396 | // Pass in 24-hour time. Filters by performance time intersection with hour 397 | // to hour + 1. 398 | function filterTime(venues, hour) { 399 | return filter(venues, function(p) { 400 | startTimeOb = parseTime(p.start_time); 401 | endTimeOb = parseTime(p.end_time); 402 | 403 | return (startTimeOb.hour24 < hour && (endTimeOb.hour24 > hour || endTimeOb.hour24 == hour && endTimeOb.minute > 0)) || 404 | (startTimeOb.hour24 == hour); 405 | }); 406 | } 407 | 408 | // Filter by currently performing 409 | function filterNow(venues, hour, minute) { 410 | var now = new Date(); 411 | if (now.getMonth() != 5 || now.getDate() != 21) { 412 | return []; 413 | } 414 | 415 | var hour = now.getHours(); 416 | var minute = now.getMinutes(); 417 | 418 | return filter(venues, function(p) { 419 | startTimeOb = parseTime(p.start_time); 420 | endTimeOb = parseTime(p.end_time); 421 | 422 | return (hour == startTimeOb.hour24 && minute >= startTimeOb.minute || hour > startTimeOb.hour24) && 423 | (hour == endTimeOb.hour24 && minute <= endTimeOb.minute || hour < endTimeOb.hour24); 424 | }); 425 | } 426 | 427 | function filterArtistName(venues, name) { 428 | var aSearch = new RegExp(name, 'i'); 429 | 430 | return filter(venues, function(p) { 431 | return p.artist.groupname.match(aSearch); 432 | }); 433 | } 434 | 435 | function filterVenueName(venues, name) { 436 | var vSearch = new RegExp(name, 'i'); 437 | 438 | return _.filter(venues, function(v) { 439 | return v.name.match(vSearch); 440 | }); 441 | } 442 | 443 | function filter(venues, filterFunc) { 444 | return _.chain(venues) 445 | .map(function(v) { 446 | v.performances = _.filter(v.performances, filterFunc); 447 | return v; 448 | }) 449 | .filter(function(v) { 450 | //filter venues with no matching performances 451 | return v.performances.length > 0; 452 | }) 453 | .value(); 454 | } 455 | 456 | 457 | function onAjaxComplete(ajaxData) { 458 | //associate the data. 459 | assocData = []; 460 | for (var v = 0; v < ajaxData.venues.length; v++) { 461 | //venue 462 | assocData.push(ajaxData.venues[v]); 463 | //venue.performances 464 | assocData[v].performances = _.filter(ajaxData.performances, function(p) { 465 | return p.venue_id == assocData[v].id; 466 | }); 467 | for (var p = 0; p < assocData[v].performances.length; p++) { 468 | //venue.performances.artist 469 | assocData[v].performances[p].artist = _.filter(ajaxData.artists, function(a) { 470 | return a.id == assocData[v].performances[p].artist_id; 471 | })[0]; 472 | } 473 | } 474 | 475 | this.data = $.extend(true, [], assocData); //copy data into public property so caller can't modify our source data 476 | 477 | if (typeof(onAjaxCompleteCallback) === 'function') { 478 | onAjaxCompleteCallback(); 479 | } 480 | } 481 | } 482 | 483 | 484 | // Parse time in format "... HH:MM:SS" into { hour, minute, suffix } 485 | function parseTime(str) { 486 | var time = str.split(" ")[1]; 487 | var hms = _.map(time.split(":"), function(s) { return parseInt(s, 10); }); 488 | var hour12ob = hour24to12(hms[0]); 489 | 490 | return { "hour24": hms[0], "hour12": hour12ob.hour, "minute": hms[1], "suffix": hour12ob.suffix }; 491 | } 492 | 493 | // Convert timeOb produced by parseTime into a string 494 | function timeObToString(timeOb) { 495 | return timeOb.hour12 + ":" + 496 | (timeOb.minute < 10 ? "0" + timeOb.minute : timeOb.minute) + 497 | timeOb.suffix; 498 | } 499 | 500 | function hour24to12(hour24) { 501 | var hour12 = hour24 > 12 ? hour24 - 12 : hour24; 502 | var suffix = hour24 < 12 ? "AM" : "PM"; 503 | 504 | return { "hour": hour12, "suffix": suffix }; 505 | } 506 | 507 | 508 | var mmm = new MakeMusicMap(); 509 | $(document).ready(mmm.init); 510 | -------------------------------------------------------------------------------- /js/jquery.fancybox.pack.js: -------------------------------------------------------------------------------- 1 | /*! fancyBox v2.1.4 fancyapps.com | fancyapps.com/fancybox/#license */ 2 | (function(C,z,f,r){var q=f(C),n=f(z),b=f.fancybox=function(){b.open.apply(this,arguments)},H=navigator.userAgent.match(/msie/),w=null,s=z.createTouch!==r,t=function(a){return a&&a.hasOwnProperty&&a instanceof f},p=function(a){return a&&"string"===f.type(a)},F=function(a){return p(a)&&0
',image:'',iframe:'",error:'

The requested content cannot be loaded.
Please try again later.

',closeBtn:'',next:'',prev:''},openEffect:"fade",openSpeed:250,openEasing:"swing",openOpacity:!0, 6 | openMethod:"zoomIn",closeEffect:"fade",closeSpeed:250,closeEasing:"swing",closeOpacity:!0,closeMethod:"zoomOut",nextEffect:"elastic",nextSpeed:250,nextEasing:"swing",nextMethod:"changeIn",prevEffect:"elastic",prevSpeed:250,prevEasing:"swing",prevMethod:"changeOut",helpers:{overlay:!0,title:!0},onCancel:f.noop,beforeLoad:f.noop,afterLoad:f.noop,beforeShow:f.noop,afterShow:f.noop,beforeChange:f.noop,beforeClose:f.noop,afterClose:f.noop},group:{},opts:{},previous:null,coming:null,current:null,isActive:!1, 7 | isOpen:!1,isOpened:!1,wrap:null,skin:null,outer:null,inner:null,player:{timer:null,isActive:!1},ajaxLoad:null,imgPreload:null,transitions:{},helpers:{},open:function(a,d){if(a&&(f.isPlainObject(d)||(d={}),!1!==b.close(!0)))return f.isArray(a)||(a=t(a)?f(a).get():[a]),f.each(a,function(e,c){var k={},g,h,j,m,l;"object"===f.type(c)&&(c.nodeType&&(c=f(c)),t(c)?(k={href:c.data("fancybox-href")||c.attr("href"),title:c.data("fancybox-title")||c.attr("title"),isDom:!0,element:c},f.metadata&&f.extend(!0,k, 8 | c.metadata())):k=c);g=d.href||k.href||(p(c)?c:null);h=d.title!==r?d.title:k.title||"";m=(j=d.content||k.content)?"html":d.type||k.type;!m&&k.isDom&&(m=c.data("fancybox-type"),m||(m=(m=c.prop("class").match(/fancybox\.(\w+)/))?m[1]:null));p(g)&&(m||(b.isImage(g)?m="image":b.isSWF(g)?m="swf":"#"===g.charAt(0)?m="inline":p(c)&&(m="html",j=c)),"ajax"===m&&(l=g.split(/\s+/,2),g=l.shift(),l=l.shift()));j||("inline"===m?g?j=f(p(g)?g.replace(/.*(?=#[^\s]+$)/,""):g):k.isDom&&(j=c):"html"===m?j=g:!m&&(!g&& 9 | k.isDom)&&(m="inline",j=c));f.extend(k,{href:g,type:m,content:j,title:h,selector:l});a[e]=k}),b.opts=f.extend(!0,{},b.defaults,d),d.keys!==r&&(b.opts.keys=d.keys?f.extend({},b.defaults.keys,d.keys):!1),b.group=a,b._start(b.opts.index)},cancel:function(){var a=b.coming;a&&!1!==b.trigger("onCancel")&&(b.hideLoading(),b.ajaxLoad&&b.ajaxLoad.abort(),b.ajaxLoad=null,b.imgPreload&&(b.imgPreload.onload=b.imgPreload.onerror=null),a.wrap&&a.wrap.stop(!0,!0).trigger("onReset").remove(),b.coming=null,b.current|| 10 | b._afterZoomOut(a))},close:function(a){b.cancel();!1!==b.trigger("beforeClose")&&(b.unbindEvents(),b.isActive&&(!b.isOpen||!0===a?(f(".fancybox-wrap").stop(!0).trigger("onReset").remove(),b._afterZoomOut()):(b.isOpen=b.isOpened=!1,b.isClosing=!0,f(".fancybox-item, .fancybox-nav").remove(),b.wrap.stop(!0,!0).removeClass("fancybox-opened"),b.transitions[b.current.closeMethod]())))},play:function(a){var d=function(){clearTimeout(b.player.timer)},e=function(){d();b.current&&b.player.isActive&&(b.player.timer= 11 | setTimeout(b.next,b.current.playSpeed))},c=function(){d();f("body").unbind(".player");b.player.isActive=!1;b.trigger("onPlayEnd")};if(!0===a||!b.player.isActive&&!1!==a){if(b.current&&(b.current.loop||b.current.index=c.index?"next":"prev"],b.router=e||"jumpto",c.loop&&(0>a&&(a=c.group.length+a%c.group.length),a%=c.group.length),c.group[a]!==r&&(b.cancel(),b._start(a)))},reposition:function(a,d){var e=b.current,c=e?e.wrap:null,k;c&&(k=b._getPosition(d),a&&"scroll"===a.type?(delete k.position,c.stop(!0,!0).animate(k,200)):(c.css(k),e.pos=f.extend({}, 13 | e.dim,k)))},update:function(a){var d=a&&a.type,e=!d||"orientationchange"===d;e&&(clearTimeout(w),w=null);b.isOpen&&!w&&(w=setTimeout(function(){var c=b.current;c&&!b.isClosing&&(b.wrap.removeClass("fancybox-tmp"),(e||"load"===d||"resize"===d&&c.autoResize)&&b._setDimension(),"scroll"===d&&c.canShrink||b.reposition(a),b.trigger("onUpdate"),w=null)},e&&!s?0:300))},toggle:function(a){b.isOpen&&(b.current.fitToView="boolean"===f.type(a)?a:!b.current.fitToView,s&&(b.wrap.removeAttr("style").addClass("fancybox-tmp"), 14 | b.trigger("onUpdate")),b.update())},hideLoading:function(){n.unbind(".loading");f("#fancybox-loading").remove()},showLoading:function(){var a,d;b.hideLoading();a=f('
').click(b.cancel).appendTo("body");n.bind("keydown.loading",function(a){if(27===(a.which||a.keyCode))a.preventDefault(),b.cancel()});b.defaults.fixed||(d=b.getViewport(),a.css({position:"absolute",top:0.5*d.h+d.y,left:0.5*d.w+d.x}))},getViewport:function(){var a=b.current&&b.current.locked|| 15 | !1,d={x:q.scrollLeft(),y:q.scrollTop()};a?(d.w=a[0].clientWidth,d.h=a[0].clientHeight):(d.w=s&&C.innerWidth?C.innerWidth:q.width(),d.h=s&&C.innerHeight?C.innerHeight:q.height());return d},unbindEvents:function(){b.wrap&&t(b.wrap)&&b.wrap.unbind(".fb");n.unbind(".fb");q.unbind(".fb")},bindEvents:function(){var a=b.current,d;a&&(q.bind("orientationchange.fb"+(s?"":" resize.fb")+(a.autoCenter&&!a.locked?" scroll.fb":""),b.update),(d=a.keys)&&n.bind("keydown.fb",function(e){var c=e.which||e.keyCode,k= 16 | e.target||e.srcElement;if(27===c&&b.coming)return!1;!e.ctrlKey&&(!e.altKey&&!e.shiftKey&&!e.metaKey&&(!k||!k.type&&!f(k).is("[contenteditable]")))&&f.each(d,function(d,k){if(1h[0].clientWidth||h[0].clientHeight&&h[0].scrollHeight>h[0].clientHeight),h=f(h).parent();if(0!==c&&!j&&1g||0>k)b.next(0>g?"up":"right");d.preventDefault()}}))},trigger:function(a,d){var e,c=d||b.coming||b.current;if(c){f.isFunction(c[a])&&(e=c[a].apply(c,Array.prototype.slice.call(arguments,1)));if(!1===e)return!1;c.helpers&&f.each(c.helpers,function(d, 18 | e){e&&(b.helpers[d]&&f.isFunction(b.helpers[d][a]))&&(e=f.extend(!0,{},b.helpers[d].defaults,e),b.helpers[d][a](e,c))});f.event.trigger(a+".fb")}},isImage:function(a){return p(a)&&a.match(/(^data:image\/.*,)|(\.(jp(e|g|eg)|gif|png|bmp|webp)((\?|#).*)?$)/i)},isSWF:function(a){return p(a)&&a.match(/\.(swf)((\?|#).*)?$/i)},_start:function(a){var d={},e,c;a=l(a);e=b.group[a]||null;if(!e)return!1;d=f.extend(!0,{},b.opts,e);e=d.margin;c=d.padding;"number"===f.type(e)&&(d.margin=[e,e,e,e]);"number"===f.type(c)&& 19 | (d.padding=[c,c,c,c]);d.modal&&f.extend(!0,d,{closeBtn:!1,closeClick:!1,nextClick:!1,arrows:!1,mouseWheel:!1,keys:null,helpers:{overlay:{closeClick:!1}}});d.autoSize&&(d.autoWidth=d.autoHeight=!0);"auto"===d.width&&(d.autoWidth=!0);"auto"===d.height&&(d.autoHeight=!0);d.group=b.group;d.index=a;b.coming=d;if(!1===b.trigger("beforeLoad"))b.coming=null;else{c=d.type;e=d.href;if(!c)return b.coming=null,b.current&&b.router&&"jumpto"!==b.router?(b.current.index=a,b[b.router](b.direction)):!1;b.isActive= 20 | !0;if("image"===c||"swf"===c)d.autoHeight=d.autoWidth=!1,d.scrolling="visible";"image"===c&&(d.aspectRatio=!0);"iframe"===c&&s&&(d.scrolling="scroll");d.wrap=f(d.tpl.wrap).addClass("fancybox-"+(s?"mobile":"desktop")+" fancybox-type-"+c+" fancybox-tmp "+d.wrapCSS).appendTo(d.parent||"body");f.extend(d,{skin:f(".fancybox-skin",d.wrap),outer:f(".fancybox-outer",d.wrap),inner:f(".fancybox-inner",d.wrap)});f.each(["Top","Right","Bottom","Left"],function(a,b){d.skin.css("padding"+b,x(d.padding[a]))});b.trigger("onReady"); 21 | if("inline"===c||"html"===c){if(!d.content||!d.content.length)return b._error("content")}else if(!e)return b._error("href");"image"===c?b._loadImage():"ajax"===c?b._loadAjax():"iframe"===c?b._loadIframe():b._afterLoad()}},_error:function(a){f.extend(b.coming,{type:"html",autoWidth:!0,autoHeight:!0,minWidth:0,minHeight:0,scrolling:"no",hasError:a,content:b.coming.tpl.error});b._afterLoad()},_loadImage:function(){var a=b.imgPreload=new Image;a.onload=function(){this.onload=this.onerror=null;b.coming.width= 22 | this.width;b.coming.height=this.height;b._afterLoad()};a.onerror=function(){this.onload=this.onerror=null;b._error("image")};a.src=b.coming.href;!0!==a.complete&&b.showLoading()},_loadAjax:function(){var a=b.coming;b.showLoading();b.ajaxLoad=f.ajax(f.extend({},a.ajax,{url:a.href,error:function(a,e){b.coming&&"abort"!==e?b._error("ajax",a):b.hideLoading()},success:function(d,e){"success"===e&&(a.content=d,b._afterLoad())}}))},_loadIframe:function(){var a=b.coming,d=f(a.tpl.iframe.replace(/\{rnd\}/g, 23 | (new Date).getTime())).attr("scrolling",s?"auto":a.iframe.scrolling).attr("src",a.href);f(a.wrap).bind("onReset",function(){try{f(this).find("iframe").hide().attr("src","//about:blank").end().empty()}catch(a){}});a.iframe.preload&&(b.showLoading(),d.one("load",function(){f(this).data("ready",1);s||f(this).bind("load.fb",b.update);f(this).parents(".fancybox-wrap").width("100%").removeClass("fancybox-tmp").show();b._afterLoad()}));a.content=d.appendTo(a.inner);a.iframe.preload||b._afterLoad()},_preloadImages:function(){var a= 24 | b.group,d=b.current,e=a.length,c=d.preload?Math.min(d.preload,e-1):0,f,g;for(g=1;g<=c;g+=1)f=a[(d.index+g)%e],"image"===f.type&&f.href&&((new Image).src=f.href)},_afterLoad:function(){var a=b.coming,d=b.current,e,c,k,g,h;b.hideLoading();if(a&&!1!==b.isActive)if(!1===b.trigger("afterLoad",a,d))a.wrap.stop(!0).trigger("onReset").remove(),b.coming=null;else{d&&(b.trigger("beforeChange",d),d.wrap.stop(!0).removeClass("fancybox-opened").find(".fancybox-item, .fancybox-nav").remove());b.unbindEvents(); 25 | e=a.content;c=a.type;k=a.scrolling;f.extend(b,{wrap:a.wrap,skin:a.skin,outer:a.outer,inner:a.inner,current:a,previous:d});g=a.href;switch(c){case "inline":case "ajax":case "html":a.selector?e=f("
").html(e).find(a.selector):t(e)&&(e.data("fancybox-placeholder")||e.data("fancybox-placeholder",f('
').insertAfter(e).hide()),e=e.show().detach(),a.wrap.bind("onReset",function(){f(this).find(e).length&&e.hide().replaceAll(e.data("fancybox-placeholder")).data("fancybox-placeholder", 26 | !1)}));break;case "image":e=a.tpl.image.replace("{href}",g);break;case "swf":e='',h="",f.each(a.swf,function(a,b){e+='';h+=" "+a+'="'+b+'"'}),e+='"}(!t(e)||!e.parent().is(a.inner))&&a.inner.append(e);b.trigger("beforeShow"); 27 | a.inner.css("overflow","yes"===k?"scroll":"no"===k?"hidden":k);b._setDimension();b.reposition();b.isOpen=!1;b.coming=null;b.bindEvents();if(b.isOpened){if(d.prevMethod)b.transitions[d.prevMethod]()}else f(".fancybox-wrap").not(a.wrap).stop(!0).trigger("onReset").remove();b.transitions[b.isOpened?a.nextMethod:a.openMethod]();b._preloadImages()}},_setDimension:function(){var a=b.getViewport(),d=0,e=!1,c=!1,e=b.wrap,k=b.skin,g=b.inner,h=b.current,c=h.width,j=h.height,m=h.minWidth,u=h.minHeight,n=h.maxWidth, 28 | v=h.maxHeight,s=h.scrolling,q=h.scrollOutside?h.scrollbarWidth:0,y=h.margin,p=l(y[1]+y[3]),r=l(y[0]+y[2]),z,A,t,D,B,G,C,E,w;e.add(k).add(g).width("auto").height("auto").removeClass("fancybox-tmp");y=l(k.outerWidth(!0)-k.width());z=l(k.outerHeight(!0)-k.height());A=p+y;t=r+z;D=F(c)?(a.w-A)*l(c)/100:c;B=F(j)?(a.h-t)*l(j)/100:j;if("iframe"===h.type){if(w=h.content,h.autoHeight&&1===w.data("ready"))try{w[0].contentWindow.document.location&&(g.width(D).height(9999),G=w.contents().find("body"),q&&G.css("overflow-x", 29 | "hidden"),B=G.height())}catch(H){}}else if(h.autoWidth||h.autoHeight)g.addClass("fancybox-tmp"),h.autoWidth||g.width(D),h.autoHeight||g.height(B),h.autoWidth&&(D=g.width()),h.autoHeight&&(B=g.height()),g.removeClass("fancybox-tmp");c=l(D);j=l(B);E=D/B;m=l(F(m)?l(m,"w")-A:m);n=l(F(n)?l(n,"w")-A:n);u=l(F(u)?l(u,"h")-t:u);v=l(F(v)?l(v,"h")-t:v);G=n;C=v;h.fitToView&&(n=Math.min(a.w-A,n),v=Math.min(a.h-t,v));A=a.w-p;r=a.h-r;h.aspectRatio?(c>n&&(c=n,j=l(c/E)),j>v&&(j=v,c=l(j*E)),cA||p>r)&&(c>m&&j>u)&&!(19n&&(c=n,j=l(c/E)),g.width(c).height(j),e.width(c+y),a=e.width(),p=e.height();else c=Math.max(m,Math.min(c,c-(a-A))),j=Math.max(u,Math.min(j,j-(p-r)));q&&("auto"===s&&jA||p>r)&&c>m&&j>u;c=h.aspectRatio?cu&&j
').appendTo("body"); 39 | this.fixed=!1;a.fixed&&b.defaults.fixed&&(this.overlay.addClass("fancybox-overlay-fixed"),this.fixed=!0)},open:function(a){var d=this;a=f.extend({},this.defaults,a);this.overlay?this.overlay.unbind(".overlay").width("auto").height("auto"):this.create(a);this.fixed||(q.bind("resize.overlay",f.proxy(this.update,this)),this.update());a.closeClick&&this.overlay.bind("click.overlay",function(a){f(a.target).hasClass("fancybox-overlay")&&(b.isActive?b.close():d.close())});this.overlay.css(a.css).show()}, 40 | close:function(){f(".fancybox-overlay").remove();q.unbind("resize.overlay");this.overlay=null;!1!==this.margin&&(f("body").css("margin-right",this.margin),this.margin=!1);this.el&&this.el.removeClass("fancybox-lock")},update:function(){var a="100%",b;this.overlay.width(a).height("100%");H?(b=Math.max(z.documentElement.offsetWidth,z.body.offsetWidth),n.width()>b&&(a=n.width())):n.width()>q.width()&&(a=n.width());this.overlay.width(a).height(n.height())},onReady:function(a,b){f(".fancybox-overlay").stop(!0, 41 | !0);this.overlay||(this.margin=n.height()>q.height()||"scroll"===f("body").css("overflow-y")?f("body").css("margin-right"):!1,this.el=z.all&&!z.querySelector?f("html"):f("body"),this.create(a));a.locked&&this.fixed&&(b.locked=this.overlay.append(b.wrap),b.fixed=!1);!0===a.showEarly&&this.beforeShow.apply(this,arguments)},beforeShow:function(a,b){b.locked&&(this.el.addClass("fancybox-lock"),!1!==this.margin&&f("body").css("margin-right",l(this.margin)+b.scrollbarWidth));this.open(a)},onUpdate:function(){this.fixed|| 42 | this.update()},afterClose:function(a){this.overlay&&!b.isActive&&this.overlay.fadeOut(a.speedOut,f.proxy(this.close,this))}};b.helpers.title={defaults:{type:"float",position:"bottom"},beforeShow:function(a){var d=b.current,e=d.title,c=a.type;f.isFunction(e)&&(e=e.call(d.element,d));if(p(e)&&""!==f.trim(e)){d=f('
'+e+"
");switch(c){case "inside":c=b.skin;break;case "outside":c=b.wrap;break;case "over":c=b.inner;break;default:c=b.skin,d.appendTo("body"), 43 | H&&d.width(d.width()),d.wrapInner(''),b.current.margin[2]+=Math.abs(l(d.css("margin-bottom")))}d["top"===a.position?"prependTo":"appendTo"](c)}}};f.fn.fancybox=function(a){var d,e=f(this),c=this.selector||"",k=function(g){var h=f(this).blur(),j=d,k,l;!g.ctrlKey&&(!g.altKey&&!g.shiftKey&&!g.metaKey)&&!h.is(".fancybox-wrap")&&(k=a.groupAttr||"data-fancybox-group",l=h.attr(k),l||(k="rel",l=h.get(0)[k]),l&&(""!==l&&"nofollow"!==l)&&(h=c.length?f(c):e,h=h.filter("["+k+'="'+l+ 44 | '"]'),j=h.index(this)),a.index=j,!1!==b.open(h,a)&&g.preventDefault())};a=a||{};d=a.index||0;!c||!1===a.live?e.unbind("click.fb-start").bind("click.fb-start",k):n.undelegate(c,"click.fb-start").delegate(c+":not('.fancybox-item, .fancybox-nav')","click.fb-start",k);this.filter("[data-fancybox-start=1]").trigger("click");return this};n.ready(function(){f.scrollbarWidth===r&&(f.scrollbarWidth=function(){var a=f('
').appendTo("body"),b=a.children(), 45 | b=b.innerWidth()-b.height(99).innerWidth();a.remove();return b});if(f.support.fixedPosition===r){var a=f.support,d=f('
').appendTo("body"),e=20===d[0].offsetTop||15===d[0].offsetTop;d.remove();a.fixedPosition=e}f.extend(b.defaults,{scrollbarWidth:f.scrollbarWidth(),fixed:f.support.fixedPosition,parent:f("body")})})})(window,document,jQuery); -------------------------------------------------------------------------------- /js/handlebars.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (C) 2011 by Yehuda Katz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | */ 24 | 25 | // lib/handlebars/browser-prefix.js 26 | var Handlebars = {}; 27 | 28 | (function(Handlebars, undefined) { 29 | ; 30 | // lib/handlebars/base.js 31 | 32 | Handlebars.VERSION = "1.0.0-rc.4"; 33 | Handlebars.COMPILER_REVISION = 3; 34 | 35 | Handlebars.REVISION_CHANGES = { 36 | 1: '<= 1.0.rc.2', // 1.0.rc.2 is actually rev2 but doesn't report it 37 | 2: '== 1.0.0-rc.3', 38 | 3: '>= 1.0.0-rc.4' 39 | }; 40 | 41 | Handlebars.helpers = {}; 42 | Handlebars.partials = {}; 43 | 44 | var toString = Object.prototype.toString, 45 | functionType = '[object Function]', 46 | objectType = '[object Object]'; 47 | 48 | Handlebars.registerHelper = function(name, fn, inverse) { 49 | if (toString.call(name) === objectType) { 50 | if (inverse || fn) { throw new Handlebars.Exception('Arg not supported with multiple helpers'); } 51 | Handlebars.Utils.extend(this.helpers, name); 52 | } else { 53 | if (inverse) { fn.not = inverse; } 54 | this.helpers[name] = fn; 55 | } 56 | }; 57 | 58 | Handlebars.registerPartial = function(name, str) { 59 | if (toString.call(name) === objectType) { 60 | Handlebars.Utils.extend(this.partials, name); 61 | } else { 62 | this.partials[name] = str; 63 | } 64 | }; 65 | 66 | Handlebars.registerHelper('helperMissing', function(arg) { 67 | if(arguments.length === 2) { 68 | return undefined; 69 | } else { 70 | throw new Error("Could not find property '" + arg + "'"); 71 | } 72 | }); 73 | 74 | Handlebars.registerHelper('blockHelperMissing', function(context, options) { 75 | var inverse = options.inverse || function() {}, fn = options.fn; 76 | 77 | var type = toString.call(context); 78 | 79 | if(type === functionType) { context = context.call(this); } 80 | 81 | if(context === true) { 82 | return fn(this); 83 | } else if(context === false || context == null) { 84 | return inverse(this); 85 | } else if(type === "[object Array]") { 86 | if(context.length > 0) { 87 | return Handlebars.helpers.each(context, options); 88 | } else { 89 | return inverse(this); 90 | } 91 | } else { 92 | return fn(context); 93 | } 94 | }); 95 | 96 | Handlebars.K = function() {}; 97 | 98 | Handlebars.createFrame = Object.create || function(object) { 99 | Handlebars.K.prototype = object; 100 | var obj = new Handlebars.K(); 101 | Handlebars.K.prototype = null; 102 | return obj; 103 | }; 104 | 105 | Handlebars.logger = { 106 | DEBUG: 0, INFO: 1, WARN: 2, ERROR: 3, level: 3, 107 | 108 | methodMap: {0: 'debug', 1: 'info', 2: 'warn', 3: 'error'}, 109 | 110 | // can be overridden in the host environment 111 | log: function(level, obj) { 112 | if (Handlebars.logger.level <= level) { 113 | var method = Handlebars.logger.methodMap[level]; 114 | if (typeof console !== 'undefined' && console[method]) { 115 | console[method].call(console, obj); 116 | } 117 | } 118 | } 119 | }; 120 | 121 | Handlebars.log = function(level, obj) { Handlebars.logger.log(level, obj); }; 122 | 123 | Handlebars.registerHelper('each', function(context, options) { 124 | var fn = options.fn, inverse = options.inverse; 125 | var i = 0, ret = "", data; 126 | 127 | if (options.data) { 128 | data = Handlebars.createFrame(options.data); 129 | } 130 | 131 | if(context && typeof context === 'object') { 132 | if(context instanceof Array){ 133 | for(var j = context.length; i 2) { 335 | expected.push("'" + this.terminals_[p] + "'"); 336 | } 337 | if (this.lexer.showPosition) { 338 | errStr = "Parse error on line " + (yylineno + 1) + ":\n" + this.lexer.showPosition() + "\nExpecting " + expected.join(", ") + ", got '" + (this.terminals_[symbol] || symbol) + "'"; 339 | } else { 340 | errStr = "Parse error on line " + (yylineno + 1) + ": Unexpected " + (symbol == 1?"end of input":"'" + (this.terminals_[symbol] || symbol) + "'"); 341 | } 342 | this.parseError(errStr, {text: this.lexer.match, token: this.terminals_[symbol] || symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected}); 343 | } 344 | } 345 | if (action[0] instanceof Array && action.length > 1) { 346 | throw new Error("Parse Error: multiple actions possible at state: " + state + ", token: " + symbol); 347 | } 348 | switch (action[0]) { 349 | case 1: 350 | stack.push(symbol); 351 | vstack.push(this.lexer.yytext); 352 | lstack.push(this.lexer.yylloc); 353 | stack.push(action[1]); 354 | symbol = null; 355 | if (!preErrorSymbol) { 356 | yyleng = this.lexer.yyleng; 357 | yytext = this.lexer.yytext; 358 | yylineno = this.lexer.yylineno; 359 | yyloc = this.lexer.yylloc; 360 | if (recovering > 0) 361 | recovering--; 362 | } else { 363 | symbol = preErrorSymbol; 364 | preErrorSymbol = null; 365 | } 366 | break; 367 | case 2: 368 | len = this.productions_[action[1]][1]; 369 | yyval.$ = vstack[vstack.length - len]; 370 | yyval._$ = {first_line: lstack[lstack.length - (len || 1)].first_line, last_line: lstack[lstack.length - 1].last_line, first_column: lstack[lstack.length - (len || 1)].first_column, last_column: lstack[lstack.length - 1].last_column}; 371 | if (ranges) { 372 | yyval._$.range = [lstack[lstack.length - (len || 1)].range[0], lstack[lstack.length - 1].range[1]]; 373 | } 374 | r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack); 375 | if (typeof r !== "undefined") { 376 | return r; 377 | } 378 | if (len) { 379 | stack = stack.slice(0, -1 * len * 2); 380 | vstack = vstack.slice(0, -1 * len); 381 | lstack = lstack.slice(0, -1 * len); 382 | } 383 | stack.push(this.productions_[action[1]][0]); 384 | vstack.push(yyval.$); 385 | lstack.push(yyval._$); 386 | newState = table[stack[stack.length - 2]][stack[stack.length - 1]]; 387 | stack.push(newState); 388 | break; 389 | case 3: 390 | return true; 391 | } 392 | } 393 | return true; 394 | } 395 | }; 396 | /* Jison generated lexer */ 397 | var lexer = (function(){ 398 | var lexer = ({EOF:1, 399 | parseError:function parseError(str, hash) { 400 | if (this.yy.parser) { 401 | this.yy.parser.parseError(str, hash); 402 | } else { 403 | throw new Error(str); 404 | } 405 | }, 406 | setInput:function (input) { 407 | this._input = input; 408 | this._more = this._less = this.done = false; 409 | this.yylineno = this.yyleng = 0; 410 | this.yytext = this.matched = this.match = ''; 411 | this.conditionStack = ['INITIAL']; 412 | this.yylloc = {first_line:1,first_column:0,last_line:1,last_column:0}; 413 | if (this.options.ranges) this.yylloc.range = [0,0]; 414 | this.offset = 0; 415 | return this; 416 | }, 417 | input:function () { 418 | var ch = this._input[0]; 419 | this.yytext += ch; 420 | this.yyleng++; 421 | this.offset++; 422 | this.match += ch; 423 | this.matched += ch; 424 | var lines = ch.match(/(?:\r\n?|\n).*/g); 425 | if (lines) { 426 | this.yylineno++; 427 | this.yylloc.last_line++; 428 | } else { 429 | this.yylloc.last_column++; 430 | } 431 | if (this.options.ranges) this.yylloc.range[1]++; 432 | 433 | this._input = this._input.slice(1); 434 | return ch; 435 | }, 436 | unput:function (ch) { 437 | var len = ch.length; 438 | var lines = ch.split(/(?:\r\n?|\n)/g); 439 | 440 | this._input = ch + this._input; 441 | this.yytext = this.yytext.substr(0, this.yytext.length-len-1); 442 | //this.yyleng -= len; 443 | this.offset -= len; 444 | var oldLines = this.match.split(/(?:\r\n?|\n)/g); 445 | this.match = this.match.substr(0, this.match.length-1); 446 | this.matched = this.matched.substr(0, this.matched.length-1); 447 | 448 | if (lines.length-1) this.yylineno -= lines.length-1; 449 | var r = this.yylloc.range; 450 | 451 | this.yylloc = {first_line: this.yylloc.first_line, 452 | last_line: this.yylineno+1, 453 | first_column: this.yylloc.first_column, 454 | last_column: lines ? 455 | (lines.length === oldLines.length ? this.yylloc.first_column : 0) + oldLines[oldLines.length - lines.length].length - lines[0].length: 456 | this.yylloc.first_column - len 457 | }; 458 | 459 | if (this.options.ranges) { 460 | this.yylloc.range = [r[0], r[0] + this.yyleng - len]; 461 | } 462 | return this; 463 | }, 464 | more:function () { 465 | this._more = true; 466 | return this; 467 | }, 468 | less:function (n) { 469 | this.unput(this.match.slice(n)); 470 | }, 471 | pastInput:function () { 472 | var past = this.matched.substr(0, this.matched.length - this.match.length); 473 | return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, ""); 474 | }, 475 | upcomingInput:function () { 476 | var next = this.match; 477 | if (next.length < 20) { 478 | next += this._input.substr(0, 20-next.length); 479 | } 480 | return (next.substr(0,20)+(next.length > 20 ? '...':'')).replace(/\n/g, ""); 481 | }, 482 | showPosition:function () { 483 | var pre = this.pastInput(); 484 | var c = new Array(pre.length + 1).join("-"); 485 | return pre + this.upcomingInput() + "\n" + c+"^"; 486 | }, 487 | next:function () { 488 | if (this.done) { 489 | return this.EOF; 490 | } 491 | if (!this._input) this.done = true; 492 | 493 | var token, 494 | match, 495 | tempMatch, 496 | index, 497 | col, 498 | lines; 499 | if (!this._more) { 500 | this.yytext = ''; 501 | this.match = ''; 502 | } 503 | var rules = this._currentRules(); 504 | for (var i=0;i < rules.length; i++) { 505 | tempMatch = this._input.match(this.rules[rules[i]]); 506 | if (tempMatch && (!match || tempMatch[0].length > match[0].length)) { 507 | match = tempMatch; 508 | index = i; 509 | if (!this.options.flex) break; 510 | } 511 | } 512 | if (match) { 513 | lines = match[0].match(/(?:\r\n?|\n).*/g); 514 | if (lines) this.yylineno += lines.length; 515 | this.yylloc = {first_line: this.yylloc.last_line, 516 | last_line: this.yylineno+1, 517 | first_column: this.yylloc.last_column, 518 | last_column: lines ? lines[lines.length-1].length-lines[lines.length-1].match(/\r?\n?/)[0].length : this.yylloc.last_column + match[0].length}; 519 | this.yytext += match[0]; 520 | this.match += match[0]; 521 | this.matches = match; 522 | this.yyleng = this.yytext.length; 523 | if (this.options.ranges) { 524 | this.yylloc.range = [this.offset, this.offset += this.yyleng]; 525 | } 526 | this._more = false; 527 | this._input = this._input.slice(match[0].length); 528 | this.matched += match[0]; 529 | token = this.performAction.call(this, this.yy, this, rules[index],this.conditionStack[this.conditionStack.length-1]); 530 | if (this.done && this._input) this.done = false; 531 | if (token) return token; 532 | else return; 533 | } 534 | if (this._input === "") { 535 | return this.EOF; 536 | } else { 537 | return this.parseError('Lexical error on line '+(this.yylineno+1)+'. Unrecognized text.\n'+this.showPosition(), 538 | {text: "", token: null, line: this.yylineno}); 539 | } 540 | }, 541 | lex:function lex() { 542 | var r = this.next(); 543 | if (typeof r !== 'undefined') { 544 | return r; 545 | } else { 546 | return this.lex(); 547 | } 548 | }, 549 | begin:function begin(condition) { 550 | this.conditionStack.push(condition); 551 | }, 552 | popState:function popState() { 553 | return this.conditionStack.pop(); 554 | }, 555 | _currentRules:function _currentRules() { 556 | return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules; 557 | }, 558 | topState:function () { 559 | return this.conditionStack[this.conditionStack.length-2]; 560 | }, 561 | pushState:function begin(condition) { 562 | this.begin(condition); 563 | }}); 564 | lexer.options = {}; 565 | lexer.performAction = function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) { 566 | 567 | var YYSTATE=YY_START 568 | switch($avoiding_name_collisions) { 569 | case 0: yy_.yytext = "\\"; return 14; 570 | break; 571 | case 1: 572 | if(yy_.yytext.slice(-1) !== "\\") this.begin("mu"); 573 | if(yy_.yytext.slice(-1) === "\\") yy_.yytext = yy_.yytext.substr(0,yy_.yyleng-1), this.begin("emu"); 574 | if(yy_.yytext) return 14; 575 | 576 | break; 577 | case 2: return 14; 578 | break; 579 | case 3: 580 | if(yy_.yytext.slice(-1) !== "\\") this.popState(); 581 | if(yy_.yytext.slice(-1) === "\\") yy_.yytext = yy_.yytext.substr(0,yy_.yyleng-1); 582 | return 14; 583 | 584 | break; 585 | case 4: yy_.yytext = yy_.yytext.substr(0, yy_.yyleng-4); this.popState(); return 15; 586 | break; 587 | case 5: this.begin("par"); return 24; 588 | break; 589 | case 6: return 16; 590 | break; 591 | case 7: return 20; 592 | break; 593 | case 8: return 19; 594 | break; 595 | case 9: return 19; 596 | break; 597 | case 10: return 23; 598 | break; 599 | case 11: return 23; 600 | break; 601 | case 12: this.popState(); this.begin('com'); 602 | break; 603 | case 13: yy_.yytext = yy_.yytext.substr(3,yy_.yyleng-5); this.popState(); return 15; 604 | break; 605 | case 14: return 22; 606 | break; 607 | case 15: return 36; 608 | break; 609 | case 16: return 35; 610 | break; 611 | case 17: return 35; 612 | break; 613 | case 18: return 39; 614 | break; 615 | case 19: /*ignore whitespace*/ 616 | break; 617 | case 20: this.popState(); return 18; 618 | break; 619 | case 21: this.popState(); return 18; 620 | break; 621 | case 22: yy_.yytext = yy_.yytext.substr(1,yy_.yyleng-2).replace(/\\"/g,'"'); return 30; 622 | break; 623 | case 23: yy_.yytext = yy_.yytext.substr(1,yy_.yyleng-2).replace(/\\'/g,"'"); return 30; 624 | break; 625 | case 24: yy_.yytext = yy_.yytext.substr(1); return 28; 626 | break; 627 | case 25: return 32; 628 | break; 629 | case 26: return 32; 630 | break; 631 | case 27: return 31; 632 | break; 633 | case 28: return 35; 634 | break; 635 | case 29: yy_.yytext = yy_.yytext.substr(1, yy_.yyleng-2); return 35; 636 | break; 637 | case 30: return 'INVALID'; 638 | break; 639 | case 31: /*ignore whitespace*/ 640 | break; 641 | case 32: this.popState(); return 37; 642 | break; 643 | case 33: return 5; 644 | break; 645 | } 646 | }; 647 | lexer.rules = [/^(?:\\\\(?=(\{\{)))/,/^(?:[^\x00]*?(?=(\{\{)))/,/^(?:[^\x00]+)/,/^(?:[^\x00]{2,}?(?=(\{\{|$)))/,/^(?:[\s\S]*?--\}\})/,/^(?:\{\{>)/,/^(?:\{\{#)/,/^(?:\{\{\/)/,/^(?:\{\{\^)/,/^(?:\{\{\s*else\b)/,/^(?:\{\{\{)/,/^(?:\{\{&)/,/^(?:\{\{!--)/,/^(?:\{\{![\s\S]*?\}\})/,/^(?:\{\{)/,/^(?:=)/,/^(?:\.(?=[}/ ]))/,/^(?:\.\.)/,/^(?:[\/.])/,/^(?:\s+)/,/^(?:\}\}\})/,/^(?:\}\})/,/^(?:"(\\["]|[^"])*")/,/^(?:'(\\[']|[^'])*')/,/^(?:@[a-zA-Z]+)/,/^(?:true(?=[}\s]))/,/^(?:false(?=[}\s]))/,/^(?:-?[0-9]+(?=[}\s]))/,/^(?:[a-zA-Z0-9_$:\-]+(?=[=}\s\/.]))/,/^(?:\[[^\]]*\])/,/^(?:.)/,/^(?:\s+)/,/^(?:[a-zA-Z0-9_$\-\/]+)/,/^(?:$)/]; 648 | lexer.conditions = {"mu":{"rules":[5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,33],"inclusive":false},"emu":{"rules":[3],"inclusive":false},"com":{"rules":[4],"inclusive":false},"par":{"rules":[31,32],"inclusive":false},"INITIAL":{"rules":[0,1,2,33],"inclusive":true}}; 649 | return lexer;})() 650 | parser.lexer = lexer; 651 | function Parser () { this.yy = {}; }Parser.prototype = parser;parser.Parser = Parser; 652 | return new Parser; 653 | })();; 654 | // lib/handlebars/compiler/base.js 655 | 656 | Handlebars.Parser = handlebars; 657 | 658 | Handlebars.parse = function(input) { 659 | 660 | // Just return if an already-compile AST was passed in. 661 | if(input.constructor === Handlebars.AST.ProgramNode) { return input; } 662 | 663 | Handlebars.Parser.yy = Handlebars.AST; 664 | return Handlebars.Parser.parse(input); 665 | }; 666 | ; 667 | // lib/handlebars/compiler/ast.js 668 | Handlebars.AST = {}; 669 | 670 | Handlebars.AST.ProgramNode = function(statements, inverse) { 671 | this.type = "program"; 672 | this.statements = statements; 673 | if(inverse) { this.inverse = new Handlebars.AST.ProgramNode(inverse); } 674 | }; 675 | 676 | Handlebars.AST.MustacheNode = function(rawParams, hash, unescaped) { 677 | this.type = "mustache"; 678 | this.escaped = !unescaped; 679 | this.hash = hash; 680 | 681 | var id = this.id = rawParams[0]; 682 | var params = this.params = rawParams.slice(1); 683 | 684 | // a mustache is an eligible helper if: 685 | // * its id is simple (a single part, not `this` or `..`) 686 | var eligibleHelper = this.eligibleHelper = id.isSimple; 687 | 688 | // a mustache is definitely a helper if: 689 | // * it is an eligible helper, and 690 | // * it has at least one parameter or hash segment 691 | this.isHelper = eligibleHelper && (params.length || hash); 692 | 693 | // if a mustache is an eligible helper but not a definite 694 | // helper, it is ambiguous, and will be resolved in a later 695 | // pass or at runtime. 696 | }; 697 | 698 | Handlebars.AST.PartialNode = function(partialName, context) { 699 | this.type = "partial"; 700 | this.partialName = partialName; 701 | this.context = context; 702 | }; 703 | 704 | Handlebars.AST.BlockNode = function(mustache, program, inverse, close) { 705 | var verifyMatch = function(open, close) { 706 | if(open.original !== close.original) { 707 | throw new Handlebars.Exception(open.original + " doesn't match " + close.original); 708 | } 709 | }; 710 | 711 | verifyMatch(mustache.id, close); 712 | this.type = "block"; 713 | this.mustache = mustache; 714 | this.program = program; 715 | this.inverse = inverse; 716 | 717 | if (this.inverse && !this.program) { 718 | this.isInverse = true; 719 | } 720 | }; 721 | 722 | Handlebars.AST.ContentNode = function(string) { 723 | this.type = "content"; 724 | this.string = string; 725 | }; 726 | 727 | Handlebars.AST.HashNode = function(pairs) { 728 | this.type = "hash"; 729 | this.pairs = pairs; 730 | }; 731 | 732 | Handlebars.AST.IdNode = function(parts) { 733 | this.type = "ID"; 734 | this.original = parts.join("."); 735 | 736 | var dig = [], depth = 0; 737 | 738 | for(var i=0,l=parts.length; i 0) { throw new Handlebars.Exception("Invalid path: " + this.original); } 743 | else if (part === "..") { depth++; } 744 | else { this.isScoped = true; } 745 | } 746 | else { dig.push(part); } 747 | } 748 | 749 | this.parts = dig; 750 | this.string = dig.join('.'); 751 | this.depth = depth; 752 | 753 | // an ID is simple if it only has one part, and that part is not 754 | // `..` or `this`. 755 | this.isSimple = parts.length === 1 && !this.isScoped && depth === 0; 756 | 757 | this.stringModeValue = this.string; 758 | }; 759 | 760 | Handlebars.AST.PartialNameNode = function(name) { 761 | this.type = "PARTIAL_NAME"; 762 | this.name = name; 763 | }; 764 | 765 | Handlebars.AST.DataNode = function(id) { 766 | this.type = "DATA"; 767 | this.id = id; 768 | }; 769 | 770 | Handlebars.AST.StringNode = function(string) { 771 | this.type = "STRING"; 772 | this.string = string; 773 | this.stringModeValue = string; 774 | }; 775 | 776 | Handlebars.AST.IntegerNode = function(integer) { 777 | this.type = "INTEGER"; 778 | this.integer = integer; 779 | this.stringModeValue = Number(integer); 780 | }; 781 | 782 | Handlebars.AST.BooleanNode = function(bool) { 783 | this.type = "BOOLEAN"; 784 | this.bool = bool; 785 | this.stringModeValue = bool === "true"; 786 | }; 787 | 788 | Handlebars.AST.CommentNode = function(comment) { 789 | this.type = "comment"; 790 | this.comment = comment; 791 | }; 792 | ; 793 | // lib/handlebars/utils.js 794 | 795 | var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack']; 796 | 797 | Handlebars.Exception = function(message) { 798 | var tmp = Error.prototype.constructor.apply(this, arguments); 799 | 800 | // Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work. 801 | for (var idx = 0; idx < errorProps.length; idx++) { 802 | this[errorProps[idx]] = tmp[errorProps[idx]]; 803 | } 804 | }; 805 | Handlebars.Exception.prototype = new Error(); 806 | 807 | // Build out our basic SafeString type 808 | Handlebars.SafeString = function(string) { 809 | this.string = string; 810 | }; 811 | Handlebars.SafeString.prototype.toString = function() { 812 | return this.string.toString(); 813 | }; 814 | 815 | var escape = { 816 | "&": "&", 817 | "<": "<", 818 | ">": ">", 819 | '"': """, 820 | "'": "'", 821 | "`": "`" 822 | }; 823 | 824 | var badChars = /[&<>"'`]/g; 825 | var possible = /[&<>"'`]/; 826 | 827 | var escapeChar = function(chr) { 828 | return escape[chr] || "&"; 829 | }; 830 | 831 | Handlebars.Utils = { 832 | extend: function(obj, value) { 833 | for(var key in value) { 834 | if(value.hasOwnProperty(key)) { 835 | obj[key] = value[key]; 836 | } 837 | } 838 | }, 839 | 840 | escapeExpression: function(string) { 841 | // don't escape SafeStrings, since they're already safe 842 | if (string instanceof Handlebars.SafeString) { 843 | return string.toString(); 844 | } else if (string == null || string === false) { 845 | return ""; 846 | } 847 | 848 | // Force a string conversion as this will be done by the append regardless and 849 | // the regex test will do this transparently behind the scenes, causing issues if 850 | // an object's to string has escaped characters in it. 851 | string = string.toString(); 852 | 853 | if(!possible.test(string)) { return string; } 854 | return string.replace(badChars, escapeChar); 855 | }, 856 | 857 | isEmpty: function(value) { 858 | if (!value && value !== 0) { 859 | return true; 860 | } else if(toString.call(value) === "[object Array]" && value.length === 0) { 861 | return true; 862 | } else { 863 | return false; 864 | } 865 | } 866 | }; 867 | ; 868 | // lib/handlebars/compiler/compiler.js 869 | 870 | /*jshint eqnull:true*/ 871 | var Compiler = Handlebars.Compiler = function() {}; 872 | var JavaScriptCompiler = Handlebars.JavaScriptCompiler = function() {}; 873 | 874 | // the foundHelper register will disambiguate helper lookup from finding a 875 | // function in a context. This is necessary for mustache compatibility, which 876 | // requires that context functions in blocks are evaluated by blockHelperMissing, 877 | // and then proceed as if the resulting value was provided to blockHelperMissing. 878 | 879 | Compiler.prototype = { 880 | compiler: Compiler, 881 | 882 | disassemble: function() { 883 | var opcodes = this.opcodes, opcode, out = [], params, param; 884 | 885 | for (var i=0, l=opcodes.length; i 0) { 1388 | this.source[1] = this.source[1] + ", " + locals.join(", "); 1389 | } 1390 | 1391 | // Generate minimizer alias mappings 1392 | if (!this.isChild) { 1393 | for (var alias in this.context.aliases) { 1394 | this.source[1] = this.source[1] + ', ' + alias + '=' + this.context.aliases[alias]; 1395 | } 1396 | } 1397 | 1398 | if (this.source[1]) { 1399 | this.source[1] = "var " + this.source[1].substring(2) + ";"; 1400 | } 1401 | 1402 | // Merge children 1403 | if (!this.isChild) { 1404 | this.source[1] += '\n' + this.context.programs.join('\n') + '\n'; 1405 | } 1406 | 1407 | if (!this.environment.isSimple) { 1408 | this.source.push("return buffer;"); 1409 | } 1410 | 1411 | var params = this.isChild ? ["depth0", "data"] : ["Handlebars", "depth0", "helpers", "partials", "data"]; 1412 | 1413 | for(var i=0, l=this.environment.depths.list.length; i this.stackVars.length) { this.stackVars.push("stack" + this.stackSlot); } 1946 | return this.topStackName(); 1947 | }, 1948 | topStackName: function() { 1949 | return "stack" + this.stackSlot; 1950 | }, 1951 | flushInline: function() { 1952 | var inlineStack = this.inlineStack; 1953 | if (inlineStack.length) { 1954 | this.inlineStack = []; 1955 | for (var i = 0, len = inlineStack.length; i < len; i++) { 1956 | var entry = inlineStack[i]; 1957 | if (entry instanceof Literal) { 1958 | this.compileStack.push(entry); 1959 | } else { 1960 | this.pushStack(entry); 1961 | } 1962 | } 1963 | } 1964 | }, 1965 | isInline: function() { 1966 | return this.inlineStack.length; 1967 | }, 1968 | 1969 | popStack: function(wrapped) { 1970 | var inline = this.isInline(), 1971 | item = (inline ? this.inlineStack : this.compileStack).pop(); 1972 | 1973 | if (!wrapped && (item instanceof Literal)) { 1974 | return item.value; 1975 | } else { 1976 | if (!inline) { 1977 | this.stackSlot--; 1978 | } 1979 | return item; 1980 | } 1981 | }, 1982 | 1983 | topStack: function(wrapped) { 1984 | var stack = (this.isInline() ? this.inlineStack : this.compileStack), 1985 | item = stack[stack.length - 1]; 1986 | 1987 | if (!wrapped && (item instanceof Literal)) { 1988 | return item.value; 1989 | } else { 1990 | return item; 1991 | } 1992 | }, 1993 | 1994 | quotedString: function(str) { 1995 | return '"' + str 1996 | .replace(/\\/g, '\\\\') 1997 | .replace(/"/g, '\\"') 1998 | .replace(/\n/g, '\\n') 1999 | .replace(/\r/g, '\\r') 2000 | .replace(/\u2028/g, '\\u2028') // Per Ecma-262 7.3 + 7.8.4 2001 | .replace(/\u2029/g, '\\u2029') + '"'; 2002 | }, 2003 | 2004 | setupHelper: function(paramSize, name, missingParams) { 2005 | var params = []; 2006 | this.setupParams(paramSize, params, missingParams); 2007 | var foundHelper = this.nameLookup('helpers', name, 'helper'); 2008 | 2009 | return { 2010 | params: params, 2011 | name: foundHelper, 2012 | callParams: ["depth0"].concat(params).join(", "), 2013 | helperMissingParams: missingParams && ["depth0", this.quotedString(name)].concat(params).join(", ") 2014 | }; 2015 | }, 2016 | 2017 | // the params and contexts arguments are passed in arrays 2018 | // to fill in 2019 | setupParams: function(paramSize, params, useRegister) { 2020 | var options = [], contexts = [], types = [], param, inverse, program; 2021 | 2022 | options.push("hash:" + this.popStack()); 2023 | 2024 | inverse = this.popStack(); 2025 | program = this.popStack(); 2026 | 2027 | // Avoid setting fn and inverse if neither are set. This allows 2028 | // helpers to do a check for `if (options.fn)` 2029 | if (program || inverse) { 2030 | if (!program) { 2031 | this.context.aliases.self = "this"; 2032 | program = "self.noop"; 2033 | } 2034 | 2035 | if (!inverse) { 2036 | this.context.aliases.self = "this"; 2037 | inverse = "self.noop"; 2038 | } 2039 | 2040 | options.push("inverse:" + inverse); 2041 | options.push("fn:" + program); 2042 | } 2043 | 2044 | for(var i=0; i