1angular.module('homepage', ['ngAnimate', 'ui.bootstrap', 'download-data']) 2 3 .config(function($provide, $locationProvider) { 4 var pulseElements = $(), 5 pulseColor = {r:0xE6, g:0xF0, b: 0xFF}, 6 baseColor = {r:0x99, g:0xc2, b: 0xFF}, 7 pulseDuration = 1000, 8 pulseDelay = 15000; 9 10 function hex(number) { 11 return ('0' + Number(number).toString(16)).slice(-2); 12 } 13 14 jQuery.fn.pulse = function () { 15 pulseElements = pulseElements.add(this); 16 }; 17 var lastPulse; 18 19 function tick() { 20 var duration = new Date().getTime() - lastPulse, 21 index = duration * Math.PI / pulseDuration , 22 level = Math.pow(Math.sin(index), 10), 23 color = { 24 r: Math.round(pulseColor.r * level + baseColor.r * (1 - level)), 25 g: Math.round(pulseColor.g * level + baseColor.g * (1 - level)), 26 b: Math.round(pulseColor.b * level + baseColor.b * (1 - level)) 27 }, 28 style = '#' + hex(color.r) + hex(color.g) + hex(color.b); 29 30 pulseElements.css('backgroundColor', style); 31 if (duration > pulseDuration) { 32 setTimeout(function() { 33 lastPulse = new Date().getTime(); 34 tick(); 35 }, pulseDelay); 36 } else { 37 setTimeout(tick, 50); 38 } 39 } 40 41 $provide.value('startPulse', function() { 42 setTimeout(function() { 43 lastPulse = new Date().getTime(); 44 tick(); 45 }, 2000); 46 }); 47 48 $locationProvider.html5Mode(true); 49 $locationProvider.hashPrefix('!'); 50 }) 51 52 .value('indent', function(text, spaces) { 53 if (!text) return text; 54 var lines = text.split(/\r?\n/); 55 var prefix = ' '.substr(0, spaces || 0); 56 var i; 57 58 // remove any leading blank lines 59 while (lines.length && lines[0].match(/^\s*$/)) lines.shift(); 60 // remove any trailing blank lines 61 while (lines.length && lines[lines.length - 1].match(/^\s*$/)) lines.pop(); 62 var minIndent = 999; 63 for (i = 0; i < lines.length; i++) { 64 var line = lines[0]; 65 var indent = line.match(/^\s*/)[0]; 66 if (indent !== line && indent.length < minIndent) { 67 minIndent = indent.length; 68 } 69 } 70 71 for (i = 0; i < lines.length; i++) { 72 lines[i] = prefix + lines[i].substring(minIndent); 73 } 74 lines.push(''); 75 return lines.join('\n'); 76 }) 77 78 .value('escape', function(text) { 79 return text.replace(/\&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"'); 80 }) 81 82 .factory('script', function() { 83 84 return { 85 angular: '<script src="https://ajax.googleapis.com/ajax/libs/angularjs/' + angular.version.full + '/angular.min.js"></script>\n', 86 resource: '<script src="https://ajax.googleapis.com/ajax/libs/angularjs/' + angular.version.full + '/angular-resource.min.js"></script>\n', 87 route: '<script src="https://ajax.googleapis.com/ajax/libs/angularjs/' + angular.version.full + '/angular-route.min.js"></script>\n', 88 firebase: '<script src="https://cdn.firebase.com/js/client/2.0.4/firebase.js"></script>\n <script src="https://cdn.firebase.com/libs/angularfire/0.9.0/angularfire.min.js"></script>\n' 89 }; 90 }) 91 92 .factory('fetchCode', function(indent) { 93 return function get(id, spaces) { 94 return indent(angular.element(document.getElementById(id)).html(), spaces); 95 }; 96 }) 97 98 .factory('formPostData', ['$document', function($document) { 99 return function(url, newWindow, fields) { 100 /** 101 * If the form posts to target="_blank", pop-up blockers can cause it not to work. 102 * If a user choses to bypass pop-up blocker one time and click the link, they will arrive at 103 * a new default plnkr, not a plnkr with the desired template. Given this undesired behavior, 104 * some may still want to open the plnk in a new window by opting-in via ctrl+click. The 105 * newWindow param allows for this possibility. 106 */ 107 var target = newWindow ? '_blank' : '_self'; 108 var form = angular.element('<form style="display: none;" method="post" action="' + url + '" target="' + target + '"></form>'); 109 angular.forEach(fields, function(value, name) { 110 var input = angular.element('<input type="hidden" name="' + name + '">'); 111 input.attr('value', value); 112 form.append(input); 113 }); 114 $document.find('body').append(form); 115 form[0].submit(); 116 form.remove(); 117 }; 118 }]) 119 120 .factory('templateBuilder', function(script) { 121 var COPYRIGHT = 'Copyright ' + (new Date()).getFullYear() + ' Google Inc. All Rights Reserved.\n' 122 + 'Use of this source code is governed by an MIT-style license that\n' 123 + 'can be found in the LICENSE file at https://angular.io/license'; 124 var COPYRIGHT_JS_CSS = '\n\n/*\n' + COPYRIGHT + '\n*/';
125 var COPYRIGHT_HTML = '\n\n<!-- \n' + COPYRIGHT + '\n-->'; 126 127 return { 128 createLocalDependencies: function(files) { 129 var head = []; 130 131 angular.forEach(files.split(' '), function(file, index) { 132 var filename = file.split(':')[0], 133 fileType = filename.split(/\./)[1]; 134 135 if (index === 0) return; 136 if (fileType == 'js') { 137 head.push(' <script src="' + filename + '"></script>\n'); 138 } else if (fileType == 'css') { 139 head.push(' <link rel="stylesheet" href="' + filename + '">\n'); 140 } 141 }); 142 143 return head; 144 }, 145 getIndexTemplate: function(deps) { 146 return '<!doctype html>\n' + 147 '<html ng-app__MODULE__>\n' + 148 ' <head>\n' + 149 ' ' + script.angular + 150 (deps.resource ? (' ' + script.resource.replace('></', '>\n </')) : '') + 151 (deps.route ? (' ' + script.route.replace('></', '>\n </')) : '') + 152 (deps.firebase ? (' ' + script.firebase) : '') + 153 '__HEAD__' + 154 ' </head>\n' + 155 ' <body>\n' + 156 '__BODY__' + 157 ' </body>\n' + 158 '</html>'; 159 }, 160 getCopyright: function(filename) { 161 switch (filename.substr(filename.lastIndexOf('.'))) { 162 case '.html': 163 return COPYRIGHT_HTML; 164 case '.js': 165 case '.css': 166 return COPYRIGHT_JS_CSS; 167 case '.md': 168 return COPYRIGHT; 169 } 170 return ''; 171 } 172 }; 173 }) 174 175 .directive('code', function() { 176 return {restrict: 'E', terminal: true}; 177 }) 178 179 .directive('appRun', function(fetchCode, $templateCache, $browser) { 180 return { 181 terminal: true, 182 link: function(scope, element, attrs) { 183 var modules = []; 184 185 modules.push(function($provide, $locationProvider) { 186 $provide.value('$templateCache', { 187 get: function(key) { 188 var value = $templateCache.get(key); 189 if (value) { 190 value = value.replace(/\#\//mg, '/'); 191 } 192 return value; 193 }, 194 put: function(key, value) { 195 // noop, we don't need the cache for anything in examples 196 return value; 197 } 198 }); 199 $provide.value('$anchorScroll', angular.noop); 200 $provide.value('$browser', $browser); 201 $locationProvider.html5Mode(true); 202 $locationProvider.hashPrefix('!'); 203 }); 204 if (attrs.module) { 205 modules.push(attrs.module); 206 } 207 208 element.html(fetchCode(attrs.appRun)); 209 element.bind('click', function(event) { 210 if (event.target.attributes.getNamedItem('ng-click')) { 211 event.preventDefault(); 212 } 213 }); 214 element.data('$injector', null); 215 angular.bootstrap(element, modules); 216 } 217 }; 218 }) 219 220 .directive('appSource', function(fetchCode, escape, $compile, $timeout, templateBuilder, $sce) { 221 return { 222 terminal: true, 223 scope: {}, 224 link: function(scope, element, attrs) { 225 var tabs = [], 226 annotation = attrs.annotate && angular.fromJson(fetchCode(attrs.annotate)) || {}; 227 228 element.css('clear', 'both'); 229 230 angular.forEach(attrs.appSource.split(' '), function(filename, index) { 231 var content; 232 233 if (index === 0) { 234 var head = templateBuilder.createLocalDependencies(attrs.appSource); 235 236 content = templateBuilder.getIndexTemplate(attrs); 237 content = content. 238 replace('__MODULE__', attrs.module ? '="' + attrs.module + '"' : ''). 239 replace('__HEAD__', head.join('')). 240 replace('__BODY__', fetchCode(filename, 4)); 241 } else { 242 content = fetchCode(filename); 243 } 244 245 // hack around incorrect tokenization 246 content = content.replace('.done-true', 'doneTrue'); 247 content = prettyPrintOne(escape(content), undefined, true); 248 // hack around incorrect tokenization 249 content = content.replace('doneTrue', '.done-true'); 250 251 var popovers = {}, 252 counter = 0; 253 254 angular.forEach(annotation[filename], function(text, key) { 255 counter++; 256 257 text = text.replace('{{', '{{').replace('}}', '}}'); 258
259 var regexp = new RegExp('(\\W|^)(' + key.replace(/([\W\-])/g, '\\$1') + ')(\\W|$)'); 260 261 scope['popover' + index + counter] = $sce.trustAsHtml(text); 262 263 content = content.replace(regexp, function(_, before, token, after) { 264 token = '__' + counter + '__'; 265 popovers[token] = 266 '<span ' + 267 'class="nocode code-annotation" ' + 268 'popover-append-to-body="true" ' + 269 'popover-trigger="mouseenter" ' + 270 'popover-title="' + escape(key) + '" ' + 271 'uib-popover-html="popover' + index + counter + '">' + 272 '<code>' + escape(key) + '</code>' + 273 '</span>'; 274 return before + token + after; 275 }); 276 }); 277 278 angular.forEach(popovers, function(text, token) { 279 content = content.replace(token, text); 280 }); 281 282 tabs.push( 283 '<uib-tab heading="' + (index ? filename : 'index.html') + '">\n' + 284 ' <pre class="prettyprint linenums nocode"><code>' + content +'</code></pre>\n' + 285 '</uib-tab>\n' 286 ); 287 }); 288 289 element.html( 290 '<uib-tabset>' + 291 tabs.join('') + 292 '</uib-tabset>'); 293 294 // Compile up the HTML to get the directives to kick-in 295 $compile(element.children())(scope); 296 $timeout(function() { 297 var annotationElements = element.find('span[uib-popover-html]'); 298 $compile(annotationElements)(scope); 299 }, 0); 300 } 301 }; 302 }) 303 304 .directive('plnkr', function(fetchCode, formPostData, script, templateBuilder) { 305 return { 306 template: '<button ng-click="plnkr.open($event)" class="btn btn-primary">' + 307 '<i class="icon-white icon-pencil"></i> ' + 308 'Edit Me' + 309 '</button>', 310 scope: {}, 311 bindToController: { 312 'files': '@plnkr', 313 'module': '@?module', 314 'resource': '@?', 315 'route': '@?', 316 'firebase': '@?', 317 }, 318 controllerAs: 'plnkr', 319 controller: function() { 320 321 this.$onInit = function() { 322 var ctrl = this; 323 324 var name = '', 325 bootstrapStylesheet = 'https://netdna.bootstrapcdn.com/twitter-bootstrap/2.0.4/css/bootstrap-combined.min.css', 326 plnkrFiles = []; 327 328 angular.forEach(ctrl.files.split(' '), function(filename, index) { 329 var content; 330 331 if (index === 0) { 332 var head = templateBuilder.createLocalDependencies(ctrl.files); 333 334 head.push(' <link rel="stylesheet" href="' + bootstrapStylesheet + '">\n'); 335 content = templateBuilder.getIndexTemplate({resource: ctrl.resource, route: ctrl.route, firebase: ctrl.firebase}); 336 content = content. 337 replace('__MODULE__', ctrl.module ? '="' + ctrl.module + '"' : ''). 338 replace('__HEAD__', head.join('')). 339 replace('__BODY__', fetchCode(filename, 4)); 340 } else { 341 content = fetchCode(filename); 342 } 343 344 content += templateBuilder.getCopyright(filename); 345 346 plnkrFiles.push({ 347 name: index === 0 ? 'index.html' : filename, // plnkr expects an index.html 348 content: content 349 }); 350 }); 351 352 ctrl.open = function(clickEvent) { 353 354 var newWindow = clickEvent.ctrlKey || clickEvent.metaKey; 355 356 var postData = { 357 'tags[0]': "angularjs", 358 'tags[1]': "example", 359 'private': true 360 }; 361 362 angular.forEach(plnkrFiles, function(file) { 363 postData['files[' + file.name + ']'] = file.content; 364 }); 365 366 postData.description = 'AngularJS Example: ' + name; 367 368 formPostData('https://plnkr.co/edit/?p=preview', newWindow, postData); 369 }; 370 }; 371 } 372 }; 373 }) 374 375 .directive('hint', function() { 376 return { 377 template: 378 '<em>Hint:</em> ' + 379 'hover over ' + 380 '<code ' + 381 'class="nocode code-annotation" ' + 382 'popover-append-to-body="true" ' + 383 'popover-trigger="mouseenter" ' + 384 'popover-title="Hover" ' + 385 'uib-popover="Place your mouse over highlighted areas in the code for explanations.">' + 386 'me' + 387 '</code>.' 388 }; 389 }) 390 391 .controller('AppController', function($scope, $uibModal, BRANCHES) { 392 $scope.BRANCHES = BRANCHES; 393 394 $scope.showDownloadModal = function() { 395 $uibModal.open({ 396 templateUrl: 'partials/download-modal.html', 397 windowClass: 'download-modal' 398 }); 399 }; 400 401 $scope.showVideo = function(videoUrl) { 402 $uibModal.open({ 403 templateUrl: 'partials/video-modal.html', 404 windowClass: 'video-modal', 405 controller: 'VideoController', 406 resolve: { 407 videoUrl: function() { return videoUrl; } 408 } 409 }); 410 }; 411 412 }) 413 414 .controller('DownloadController', function($scope, $sce, BRANCHES, BUILDS, DOWNLOAD_INFO) { 415 416 function getRelativeUrl(branch, build) { 417 switch (build.name) { 418 case 'Minified': 419 return branch.version + '/angular.min.js'; 420 case 'Uncompressed': 421 return branch.version + '/angular.js'; 422 case 'Zip': 423 return branch.version + '/angular-' + branch.version + '.zip'; 424 } 425 } 426 427 $scope.BRANCHES = BRANCHES; 428 $scope.BUILDS = BUILDS; 429 $scope.DOWNLOAD_INFO_TRUSTED = { 430 branchesInfo: $sce.trustAsHtml(DOWNLOAD_INFO.branchesInfo), 431 buildsInfo: $sce.trustAsHtml(DOWNLOAD_INFO.buildsInfo), 432 bowerInfo: $sce.trustAsHtml(DOWNLOAD_INFO.bowerInfo), 433 cdnInfo: $sce.trustAsHtml(DOWNLOAD_INFO.cdnInfo) 434 }; 435 436 $scope.currentBranch = $scope.BRANCHES[0]; 437 $scope.currentBuild = $scope.BUILDS[0]; 438 439 $scope.setBranch = function(branch) { 440 $scope.currentBranch = branch; 441 }; 442 443 $scope.setBuild = function(build) { 444 $scope.currentBuild = build; 445 }; 446 447 var BASE_CDN_URL = 'https://ajax.googleapis.com/ajax/libs/angularjs/'; 448 $scope.cdnUrl = function() { 449 if ($scope.currentBuild.name !== 'Zip') { 450 return BASE_CDN_URL + getRelativeUrl($scope.currentBranch, $scope.currentBuild); 451 } 452 }; 453 454 455 var BASE_CODE_ANGULAR_URL = 'https://code.angularjs.org/'; 456 $scope.downloadLink = function() { 457 return $scope.cdnUrl() || BASE_CODE_ANGULAR_URL + getRelativeUrl($scope.currentBranch, $scope.currentBuild); 458 }; 459 460 461 }) 462 463.controller('VideoController', function($scope, $timeout, $sce, videoUrl) { 464 465 // HACK: Trick YouTube to play HD by showing big and then resizing after 2500ms. 466 467 $scope.video = { 468 width: 1280, 469 height: 720, 470 allowfullscreen: true, 471 src: $sce.trustAsResourceUrl(videoUrl) 472 }; 473 474 $timeout(function() { 475 $scope.video.width = 970; 476 $scope.video.height = 556; 477 }, 2500); 478 479}) 480 481.run(function($rootScope, startPulse){ 482 $rootScope.version = angular.version; 483 484 // $('[rel=popover]'). 485 // popover(). 486 // pulse(); 487 // startPulse(); 488 // }); 489 490});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.