diff --git a/.firebaserc b/.firebaserc new file mode 100644 index 000000000..86b3ec1ba --- /dev/null +++ b/.firebaserc @@ -0,0 +1,5 @@ +{ + "projects": { + "default": "google.com:angular-sites" + } +} diff --git a/.gitignore b/.gitignore index 2ec8688d7..4bbdd612f 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,8 @@ node_modules/ old-yellow/ slides/gitFetchSite.log .DS_Store -build/ \ No newline at end of file +build/ +npm-debug.log + +src/css/angular.css +src/css/angular-topnav.css \ No newline at end of file diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 000000000..48082f72f --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +12 diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..ef6ab7bce --- /dev/null +++ b/.travis.yml @@ -0,0 +1,38 @@ +language: node_js +sudo: false +node_js: + - '12' + +cache: + yarn: true + +jobs: + include: + - stage: test + addons: + sauce_connect: + username: angular-ci + jwt: + secure: "NJ6eWX3YarBTf2LOjOXFhGfRQ90WQ4ZrukAHUxTyUc1o3AXURb3WRi1HzXFh4fnlliBuaCY85/B5oSctvVg+evAOfnqOPm0WN/AP4/PIY6Xicc3jJDmyaeHtfvq2hb6QwxOciM/Ht7WxPsmlIR0wLz98CwqrOPxltjj25E9pPK4=" + script: + - mkdir build + - travis_retry yarn run build copy test + - stage: deploy + # https://docs.travis-ci.com/user/conditional-builds-stages-jobs#Specifying-conditions + if: type != pull_request AND branch = master + script: + - mkdir build + # - "yarn run build copy dist" # dist adds the build to the dist folder + - "yarn run build copy" + deploy: + - provider: Firebase + # the upload folder for firebase is configured in /firebase.json + skip_cleanup: true + message: "Commit: $TRAVIS_COMMIT" + token: + # token generated by mjstaffa@gmail.com on 2017/10/13 + secure: "GRRRA+vodB/NvBW7BM4UJ9huwcE29pEHMSn6SJWIZtFjeoOpIUVocqQcOxYEDzJqwBxQmpAqejRxHzsNaPLVs+cdC7m0Tg+QH3pktyob3/kqwyOc0wm0WrinswXZ4CcOlFUZ2AGakQYO1QFZWRfoXIfW8WwNApTj/1p9Rc/xLvY=" + on: + repo: angular/angularjs.org + branch: + - master diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md new file mode 100644 index 000000000..0a3a12ece --- /dev/null +++ b/DEPLOYMENT.md @@ -0,0 +1,13 @@ +# Setting up Firebase + +- run `yarn add -g firebase-tools` + +# Manually deploying the contents of the build folder + +(You need to be logged in and have access to the project) + +- run `yarn run build` +- run `firebase deploy --only=hosting` + +# Generating a new Token for Travis +See https://docs.travis-ci.com/user/deployment/firebase/ \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..9aeac3e42 --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +The MIT License + +Copyright (c) 2021 Google LLC. http://angularjs.org + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + diff --git a/README.md b/README.md index 25c5b928c..e072577eb 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,22 @@ -The angularjs.org site is not designed to be used by third parties. -It is only kept here as part of our own deployment processes. +# AngularJS.org Home Page +The angularjs.org site is not designed to be used by third parties. +It is only kept here as part of our own deployment processes. If you want to have a go at hosting it yourself you can try running - ./build.sh + yarn run build -but we will not be providing support for doing this. +This will build the website in the `build` folder, which you can then serve with something like: -The site relies upon accessing numerous additional resources from all over the web. + yarn start -Hint: to access the AngularJS docs application rather than this site, which is only the homepage, - clone the main project and build the docs yourself... +and browse to http://localhost:8080 - git clone https://github.com/angular/angular.js.git - cd angular.js - npm install - grunt package webserver +**Note: we do not provide support for doing this.** -Then browse to http://localhost:8000/build/docs/api +The site relies upon accessing numerous additional resources from all over the web. + +Hint: to access the AngularJS docs application rather than this site, which is only the homepage, +clone the main project and build the docs yourself. +See https://github.com/angular/angular.js/blob/master/CONTRIBUTING.md diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..47aebc705 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,20 @@ +# Security Policy + +## Supported Versions + +| Version | Supported | Status | Comments | +| ----------- | ------------------ | --------------------- | ------------------------------------ | +| 1.8.x | :white_check_mark: | Long Term Support | See [Long Term Support policy][0] | +| 1.3.x-1.7.x | :x: | | | +| 1.2.x | :warning: | Security patches only | Last version to provide IE 8 support | +| <1.2.0 | :x: | | | + +## Reporting a Vulnerability + +Email us at [security@angularjs.org](mailto:security@angularjs.org) to report any potential security issues in AngularJS. + +Please [use the latest AngularJS possible](https://docs.angularjs.org/guide/security#use-the-latest-angularjs-possible) +and keep in mind the guidance around AngularJS' +[expression language](https://docs.angularjs.org/guide/security#angularjs-templates-and-expressions). + +[0]: https://docs.angularjs.org/misc/version-support-status#long-term-support diff --git a/build.sh b/build.sh deleted file mode 100755 index 51ff663bb..000000000 --- a/build.sh +++ /dev/null @@ -1,107 +0,0 @@ -#!/bin/sh -set -ex -shopt -s extglob - -CDN_REPLACE_FILES=( - build/index.html - build/js/download-data.js -) - -function copySrcToBuild { - rm -rf build/ - mkdir build/ - - cp -r src/ build/ -} - -# replaceInFile(file, findText, replaceText) -function replaceInFile { - sed -i .tmp "s/$2/$3/" $1 - rm $1.tmp -} - -function getCdnVersions { - CDN_VERSION_1_2=$(./get-cdn-version.sh 1.2) - CDN_VERSION_1_3=$(./get-cdn-version.sh 1.3) -} - -function replaceCdnVersionInFiles { - for FILE in "${CDN_REPLACE_FILES[@]}" - do - replaceInFile $FILE '${CDN_VERSION_1_2}' $CDN_VERSION_1_2 - replaceInFile $FILE '${CDN_VERSION_1_3}' $CDN_VERSION_1_3 - done -} - - - -function testBuildResult { - export ANGULAR_HOME_HOST='http://localhost:8100'; - export ANGULAR_DOWNLOAD_VERSIONS="$CDN_VERSION_1_2:1.2.x $CDN_VERSION_1_3:1.3.x" - export ANGULAR_VERSION="$CDN_VERSION_1_3" - export CHECK_SCRIPT_TAG="true" - - function killServer () { - kill $serverPid - } - - npm install . - ./node_modules/.bin/webdriver-manager update - - # Start basic webserver to serve the app - ./node_modules/.bin/http-server -p 8100 build/ & - serverPid=$! - - trap killServer EXIT - - ./node_modules/.bin/protractor protractorConf.js -} - -function moveBuildToDist { - branch=$(git rev-parse --abbrev-ref HEAD) - git checkout dist - rm -rf !(build) - cp -rf build/* . - rm -rf build - git add . -A - # ||true if we had no changes - git commit -m "update site from src" || true - git checkout $branch -} - -function parseArgs { - # defaults if no args are given - if (( $# == 0 )); then - DO_COPY=1 - DO_TEST=1 - fi - - # parse args - while (( $# > 0 )); do - case "$1" in - (copy) DO_COPY=1 ;; - (test) DO_TEST=1 ;; - (dist) DO_DIST=1 ;; - (*) echo "$0: error - unrecognized option $1" 1>&2; exit 1;; - esac - shift - done -} - -# -------------- -# main -parseArgs "$@" - -if [[ "$DO_COPY" ]]; then - copySrcToBuild - getCdnVersions - replaceCdnVersionInFiles -fi - -if [[ "$DO_TEST" ]]; then - testBuildResult -fi - -if [[ "$DO_DIST" ]]; then - moveBuildToDist -fi diff --git a/firebase.json b/firebase.json new file mode 100644 index 000000000..2ce4dc8f2 --- /dev/null +++ b/firebase.json @@ -0,0 +1,23 @@ +{ + "hosting": { + "public": "build", + "trailingSlash": false, + "ignore": [ + "/scss/", + "/*.php", + "/propagateClusterUpdate.js" + ], + "redirects": [ + { + "source": "/docs/api", + "destination": "https://docs.angularjs.org/api", + "type": 301 + }, + { + "source": "/@(docs|api)", + "destination": "https://docs.angularjs.org/api", + "type": 301 + } + ] + } +} diff --git a/get-cdn-version.sh b/get-cdn-version.sh deleted file mode 100755 index f9df2cc9d..000000000 --- a/get-cdn-version.sh +++ /dev/null @@ -1,97 +0,0 @@ -#! /bin/sh -set -e - - -# returns e.g. 1.2.8 -function getAngularVersions { - VERSION_PATTERN=$1 - # result of ls-remote - # e.g. 0f9a1c21e6d7c57dc02842efa9612a1a70993146 refs/tags/v1.2.8^{} - # e.g. a7a660b65bceb7c93579469047b332e040afdf5b refs/tags/v1.2.9 - git ls-remote https://github.com/angular/angular.js.git | while read line - do - if [[ $line =~ v([0-9].*[0-9])$ ]]; then - # a line like ...v1.2.9 - # remove 'v' at begin of tag - # e.g. v1.3.0 -> 1.3.0 - VERSION=${BASH_REMATCH[1]} - if [[ $VERSION == $VERSION_PATTERN ]]; then - log "found tag for $VERSION_PATTERN: $VERSION" - echo $VERSION - fi - fi - done -} - -function sortBySemVer { - - function sortBySortId() { - # e.g. 1.3.0.beta.1 - # e.g. 1.3.0.zzz.99 - sort -t"." -k1,1nr -k2,2nr -k3,3nr -k4,4r -k5,5nr - } - - function addSortId { - while read VERSION - do - SORT_ID=$VERSION - # add -zzz.99 to non pre release versions - # e.g. 1.3.0 -> 1.3.0-zzz.99 - if [[ $SORT_ID != *-* ]]; then - SORT_ID="$SORT_ID-zzz.99" - fi - # replace "-"" with "."" - # e.g. 1.3.0-beta.2 -> 1.3.0.beta.2 - SORT_ID=${SORT_ID//-/.} - - # e.g. 1.3.0.zzz.99:1.3.0 - # e.g. 1.3.0.beta.2:1.3.0-beta.2 - echo "$SORT_ID:$VERSION" - done - } - - function removeSortId { - while read line - do - # get the part after the first colon (i.e. remove sortId) - # e.g. 1.3.0.beta.99:1.3.0 -> 1.3.0 - echo ${line#*:} - done - } - - addSortId | sortBySortId | removeSortId -} - -function checkCDN { - while read VERSION - do - STATUS_CODE=$(curl http://ajax.googleapis.com/ajax/libs/angularjs/$VERSION/angular.min.js \ - --head --write-out '%{http_code}' -o /dev/null -silent) - - log "Checking version $VERSION on CDN: $STATUS_CODE" - if [[ $STATUS_CODE == "200" ]]; then - echo $VERSION - return - fi - done - log "No CDN version found" - exit 1 -} - -function log() { - echo "$@" >&2 -} - -# replaceInFile(file, findText, replaceText) -function replaceInFile { - sed -i .tmp "s/$2/$3/" $1 - rm $1.tmp -} - -BRANCH=$1 -if [[ ! BRANCH ]]; then - log "Usage: $0 , e.g. $0 1.2" - exit 1 -fi - -getAngularVersions "${BRANCH}.*" | sortBySemVer| checkCDN diff --git a/google385281288605d160.html b/google385281288605d160.html new file mode 100644 index 000000000..0e3614a19 --- /dev/null +++ b/google385281288605d160.html @@ -0,0 +1 @@ +google-site-verification: google385281288605d160.html \ No newline at end of file diff --git a/gulpfile.js b/gulpfile.js new file mode 100644 index 000000000..5481b07b3 --- /dev/null +++ b/gulpfile.js @@ -0,0 +1,45 @@ +const gulp = require('gulp'); +const sass = require('gulp-sass'); + + +/* +* CSS +* +*/ + +gulp.task('sass', function () { + return gulp.src('./src/scss/**/*.scss') + .pipe(sass({outputStyle: 'compressed'}) + .on('error', sass.logError)) + .pipe(gulp.dest('./src/css')); +}); + +gulp.task('sass-dev', function () { + return gulp.src('./src/scss/**/*.scss') + .pipe(sass({outputStyle: 'compressed'}) + .on('error', sass.logError)) + .pipe(gulp.dest('./src/css')) + .pipe(gulp.dest('./build/css')); +}); + +/* +* DEVELOPMENT +* +*/ + +gulp.task('dev', ['sass-dev'], function() { + gulp.watch('./src/scss/**/*', ['sass-dev']); + console.log('Watching Sass files for changes...'); +}); + + +/* +* PRODUCTION +* +*/ + +gulp.task('default', ['sass'], function() { + console.log('Building production assets'); +}); + +module.exports = gulp; diff --git a/package.json b/package.json index 7351e579b..dba8c78a8 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,29 @@ { "name": "angularjs.org", "version": "0.0.0", + "private": true, + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/angular/angularjs.org" + }, + "scripts": { + "build": "yarn && gulp && node scripts/build", + "build:dev": "gulp && node scripts/build copy", + "start": "http-server build/" + }, "devDependencies": { - "http-server": "*", - "protractor": "~0.24", - "selenium-webdriver": "~2.40.0" + "eslint": "^7.27.0", + "gulp": "^3.9.1", + "gulp-sass": "^4.1.0", + "http-server": "^0.12.3", + "protractor": "^7.0.0" + }, + "resolutions": { + "//1": "`natives@1.1.0` does not work with Node.js 10.x on Windows 10", + "//2": "(E.g. see https://github.com/gulpjs/gulp/issues/2162 and https://github.com/nodejs/node/issues/25132.)", + "natives": "1.1.6", + "//3": "`graceful-fs` needs to be pinned to support gulp 3, on Node v12+", + "graceful-fs": "^4.2.6" } -} \ No newline at end of file +} diff --git a/protractorConf.js b/protractorConf.js deleted file mode 100644 index e785d88c8..000000000 --- a/protractorConf.js +++ /dev/null @@ -1,18 +0,0 @@ -exports.config = { - seleniumServerJar: './node_modules/protractor/selenium/selenium-server-standalone-2.42.0.jar', - seleniumArgs: [], - baseUrl: process.env.ANGULAR_HOME_HOST || 'http://angularjs.org', - capabilities: { - 'browserName': 'chrome' - }, - specs: [ - 'test/angularjs.org.spec.js', - ], - jasmineNodeOpts: { - onComplete: null, - isVerbose: true, - showColors: false, - includeStackTrace: true, - defaultTimeoutInterval: 10000 - } -}; \ No newline at end of file diff --git a/protractorConfLocal.js b/protractorConfLocal.js new file mode 100644 index 000000000..ec7a1b7eb --- /dev/null +++ b/protractorConfLocal.js @@ -0,0 +1,9 @@ +var config = require('./protractorConfShared').config; + +config.capabilities = { + 'browserName': 'chrome' +}; + +config.directConnect = true; + +exports.config = config; \ No newline at end of file diff --git a/protractorConfShared.js b/protractorConfShared.js new file mode 100644 index 000000000..72308ef3c --- /dev/null +++ b/protractorConfShared.js @@ -0,0 +1,6 @@ +exports.config = { + baseUrl: process.env.ANGULAR_HOME_HOST || 'http://angularjs.org', + specs: [ + 'test/angularjs.org.spec.js', + ] +}; \ No newline at end of file diff --git a/protractorConfTravis.js b/protractorConfTravis.js new file mode 100644 index 000000000..4f448d5b3 --- /dev/null +++ b/protractorConfTravis.js @@ -0,0 +1,15 @@ +var config = require('./protractorConfShared').config; + +config.sauceUser = process.env.SAUCE_USERNAME; +config.sauceKey = process.env.SAUCE_ACCESS_KEY; + +config.capabilities = { + 'browserName': 'chrome', + 'version': 70, + 'build': process.env.TRAVIS_BUILD_NUMBER, + 'elementScrollBehavior': 1, + 'name': 'AngularJS.org E2E', + 'tunnel-identifier': process.env.TRAVIS_JOB_NUMBER +}; + +exports.config = config; diff --git a/scripts/.jshintrc b/scripts/.jshintrc new file mode 100644 index 000000000..6203bee69 --- /dev/null +++ b/scripts/.jshintrc @@ -0,0 +1,4 @@ +{ + "esversion": 6, + "node": true +} diff --git a/scripts/build.js b/scripts/build.js new file mode 100644 index 000000000..3c4d0f7bf --- /dev/null +++ b/scripts/build.js @@ -0,0 +1,200 @@ +'use strict'; + +// Imports +const exec = require('child_process').exec; +const fs = require('fs'); +const path = require('path'); + +const getCdnVersion = require('./get-cdn-version'); +const utils = require('./utils'); + +// Constants +const ROOT_DIR = '.'; +const DST_DIR = 'build'; +const SRC_DIR = 'src'; +const CDN_VERSIONS = ['1.2', '1.8']; +const CDN_REPLACE_FILES = ['index.html', 'js/download-data.js']; +const GIT_BRANCH_DIST = 'dist'; +const PTOR_CONF = process.env.TRAVIS ? 'protractorConfTravis.js' : 'protractorConfLocal.js'; +const PTOR_PORT = '8100'; +const PTOR_ENV = { + ANGULAR_HOME_HOST: `http://localhost:${PTOR_PORT}`, + ANGULAR_DOWNLOAD_VERSIONS: '-', + ANGULAR_VERSION: '-', + CHECK_SCRIPT_TAG: 'true' +}; + +// Variables - Private +const args = process.argv.slice(2); +const actions = parseArgs(args); + +// Run +_main(actions); + +// Functions - Definitions +function _main(actions) { + const callbacks = []; + + if (actions.copy) { + callbacks.push(copySource, getCdnVersions, updateProtractorEnv, replaceCdnVersionsInFiles); + } + + if (actions.test) { + callbacks.push(testBuild); + } + + if (actions.dist) { + callbacks.push(updateDist); + } + + return callbacks. + reduce((promise, cb) => promise.then(cb), Promise.resolve()). + catch(onError); +} + +function announce(message) { + const ruler = new Array(81).join('-'); + console.log(`${ruler}\n${message}\n`); +} + +function copySource() { + announce(`Copying source files from '${SRC_DIR}' to '${DST_DIR}'...`); + + return Promise.resolve(). + then(() => utils.removeDir(DST_DIR)). + then(() => utils.createDir(DST_DIR)). + then(() => utils.copyContent(SRC_DIR, DST_DIR)); +} + +function getCdnVersions() { + announce(`Getting the latest versions available on CDN...`); + + return Promise.all(CDN_VERSIONS.map(getCdnVersion)); +} + +function mapVersionsToPlaceholders(cdnVersions) { + return cdnVersions.reduce((map, v) => { + const tokens = v.split('.'); + map[v] = new RegExp(`\\\${CDN_VERSION_${tokens[0]}_${tokens[1]}}`, 'g'); + + return map; + }, {}); +} + +function onError(err) { + const cmd = `build${args.length ? ' ' + args.join(' ') : ''}`; + if (isFinite(err)) err = `Exit code: ${err}`; + + console.error(`ERROR (running '${cmd}'): ${err}\n ${err.stack || ''}`); + + process.exit(1); +} + +function parseArgs(args) { + var actions = { + copy: false, + test: false, + dist: false + }; + + if (!args.length) { + actions.copy = true; + actions.test = true; + } else { + args.forEach(arg => { + switch (arg) { + case 'copy': + case 'test': + case 'dist': + actions[arg] = true; + break; + default: + onError(`unrecognized option ${arg}`); + break; + } + }); + } + + return actions; +} + +function replaceCdnVersionsInFiles(cdnVersions) { + announce(`Replacing CDN versions in files (${CDN_REPLACE_FILES.join(', ')})...`); + + const versionMap = mapVersionsToPlaceholders(cdnVersions); + const replaceVersionsInFile = file => { + const filePath = path.join(DST_DIR, file); + return utils.replaceInFile(versionMap, filePath); + }; + + return Promise.all(CDN_REPLACE_FILES.map(replaceVersionsInFile)); +} + +function testBuild() { + announce(`Testing the current build (ENV: ${JSON.stringify(PTOR_ENV, null, 2)})...`); + + const protractorOptions = { + env: Object.assign(process.env, PTOR_ENV), + stdio: 'inherit' + }; + + const installCmd = `${utils.getExecutable('yarn', true)} install`; + const httpServerCmd = `${utils.getExecutable('http-server')} -p ${PTOR_PORT} ${DST_DIR}`; + const wdrManagerCmd = `${utils.getExecutable('webdriver-manager')} update`; + const protractorCmd = `${utils.getExecutable('protractor')} ${PTOR_CONF}`; + + let setupPromise = Promise.resolve(); + + if (!process.env.TRAVIS) { + setupPromise = chain(setupPromise, installCmd); + setupPromise = chain(setupPromise, wdrManagerCmd); + } + + const httpServerPromise = chain(setupPromise, httpServerCmd); + const protractorPromise = chain(setupPromise, protractorCmd, protractorOptions); + + const killHttpServer = () => httpServerPromise.$$killProcess(); + + return utils.finallyAsPromised(protractorPromise, killHttpServer); + + // Helpers + function chain(promise, cmd, options) { + let innerPromise; + + promise = promise.then(() => innerPromise = utils.spawnAsPromised(cmd, options)); + promise.$$killProcess = () => innerPromise && utils.killProcess(innerPromise.$$process); + + return promise; + } +} + +function updateDist() { + announce(`Updating '${GIT_BRANCH_DIST}' branch with the current build...`); + + return utils. + execAsPromised('git rev-parse --abbrev-ref HEAD'). + then(originalBranch => { + const restoreBranch = () => utils.spawnAsPromised(`git checkout ${originalBranch.trim()}`); + + const promise = Promise.resolve(). + then(() => utils.spawnAsPromised(`git checkout ${GIT_BRANCH_DIST}`)). + then(() => utils.keepOnly(DST_DIR)). + then(() => utils.copyContent(DST_DIR, ROOT_DIR)). + then(() => utils.removeDir(DST_DIR)). + then(() => utils.spawnAsPromised(`git add --all ${ROOT_DIR}`)). + then(() => utils.spawnAsPromised('git commit -m "update site from src"').catch(() => {})); + + return utils.finallyAsPromised(promise, restoreBranch); + }); +} + +function updateProtractorEnv(cdnVersions) { + announce(`Updating version-related environmental variables (for Protractor)...`); + + PTOR_ENV.ANGULAR_VERSION = cdnVersions[cdnVersions.length - 1]; + PTOR_ENV.ANGULAR_DOWNLOAD_VERSIONS = cdnVersions. + map((cdnVersion, idx) => `${cdnVersion}:${CDN_VERSIONS[idx]}.x`). + join(' '); + + return cdnVersions; +} diff --git a/scripts/get-cdn-version.js b/scripts/get-cdn-version.js new file mode 100644 index 000000000..128650af8 --- /dev/null +++ b/scripts/get-cdn-version.js @@ -0,0 +1,116 @@ +'use strict'; + +// Imports +const exec = require('child_process').exec; +const http = require('http'); + +const utils = require('./utils'); + +// Constants +const GIT_REMOTE = 'https://github.com/angular/angular.js.git'; +const VERSION_REGEXP = /^(\d+)\.(\d+)\.(\d+)(?:-?([^.\d]+)\.?(\d+))?$/; + +// Exports +module.exports = getCdnVersion; + +// Functions - Definitions +function checkCdn(versions) { + return new Promise((resolve, reject) => { + checkVersionOnCdn(0); + + // Helpers + function checkVersionOnCdn(idx) { + if (idx >= versions.length) reject('`checkCdn`: No version found on CDN'); + + const version = versions[idx]; + + const reqConfig = { + method: 'HEAD', + protocol: 'http:', + hostname: 'ajax.googleapis.com', + path: `/ajax/libs/angularjs/${version}/angular.min.js` + }; + const reqCallback = res => { + const statusCode = res.statusCode; + + console.log(` Checking version ${version} on CDN: ${statusCode}`); + if (statusCode === 200) return resolve(version); + + checkVersionOnCdn(idx + 1); + }; + + http.request(reqConfig, reqCallback).end(); + } + }); +} + +function getAngularVersions(version) { + // Sample output of `git ls-remote`: + // ... + // 92cb6eb5ef3a43ab569c80ccddd634c0f7a85e38 refs/tags/v1.5.2 + // f665968dafdc2e1f8fdd3ee466feecbdb137ee5d refs/tags/v1.5.2^{} + // cfffd1cd5607c0df03720614221c6b0e9c3e8189 refs/tags/v1.5.3 + // 514639b585affc218a6899f1b1755863647fa5a8 refs/tags/v1.5.3^{} + // ... + const versionRegExp = new RegExp(`v(${version.replace(/\./g, '\\.')}\\..*\\d)\\s*$`, 'i'); + + return utils. + execAsPromised(`git ls-remote --tags ${GIT_REMOTE}`). + then(extractMatchingVersions); + + //Helpers + function extractMatchingVersions(output) { + return output. + split('\n'). + map(line => versionRegExp.exec(line)). // '... refs/tags/vX.Y.Z' --> 'X.Y.Z' + filter(Boolean). + map(match => { + const v = match[1]; + console.log(` Found version for ${version}.*: ${v}`); + + return v; + }); + } +} + +function getCdnVersion(version) { + if (!version) return Promise.reject('`getCdnVersion`: No version specified'); + + return getAngularVersions(version). + then(sortBySemver). + then(checkCdn); +} + +function sortBySemver(versions) { + return versions. + map(parseVersion). + sort(sortReverse). + map(o => o.version); + + // Helpers + function parseVersion(version) { + const match = VERSION_REGEXP.exec(version); + const tokens = [ + /* major */ +match[1], + /* minor */ +match[2], + /* patch */ +match[3], + /* pre-1 */ match[4] || 'zzz', + /* pre-2 */ +(match[5] || 99) + ]; + + return {version, tokens}; + } + + function sortReverse(a, b) { + const tokensA = a.tokens; + const tokensB = b.tokens; + + for (let i = 0, ii = tokensA.length; i < ii; i++) { + const valueA = tokensA[i]; + const valueB = tokensB[i]; + + if (valueA < valueB) return +1; + if (valueA > valueB) return -1; + } + } +} diff --git a/scripts/utils.js b/scripts/utils.js new file mode 100644 index 000000000..2c6696e52 --- /dev/null +++ b/scripts/utils.js @@ -0,0 +1,247 @@ +'use strict'; + +// Imports +const childProcess = require('child_process'); +const fs = require('fs'); +const path = require('path'); + +// Variables - Private +const exec = childProcess.exec; +const spawn = childProcess.spawn; + +// Exports +module.exports = { + copyContent, + createDir, + execAsPromised, + finallyAsPromised, + getExecutable, + keepOnly, + killProcess, + removeDir, + removeFile, + replaceInFile, + spawnAsPromised, + traverseDir +}; + +// Functions - Definitions +function copyContent(srcDir, dstDir, notIgnoreHidden) { + return traverseDir(srcDir, fileCallback, {pre: dirCallback}); + + // Helpers + function dirCallback(dirPath) { + return (!notIgnoreHidden && isHidden(dirPath)) ? + Promise.resolve(true) : + createDir(dirPath.replace(srcDir, dstDir)); + } + + function fileCallback(filePath) { + return (!notIgnoreHidden && isHidden(filePath)) ? + Promise.resolve() : + new Promise((resolve, reject) => { + const dstPath = filePath.replace(srcDir, dstDir); + + fs.readFile(filePath, (err, data) => { + if (err) return reject(err); + + fs.writeFile(dstPath, data, err => err ? reject(err) : resolve()); + }); + }); + } +} + +function createDir(dir, failIfExists) { + return new Promise((resolve, reject) => { + fs.mkdir(dir, err => { + if (err && (failIfExists || (err.code !== 'EEXIST'))) reject(err); + + resolve(); + }); + }); +} + +function execAsPromised(cmd, options) { + let proc; + const promise = new Promise((resolve, reject) => { + proc = exec(cmd, options, (err, stdout) => { + if (err) return reject(err); + + resolve(stdout); + }); + }); + + promise.$$process = proc; + + return promise; +} + +function finallyAsPromised(promise, callback) { + return promise.then(runCallback('resolve'), runCallback('reject')); + + // Helpers + function runCallback(returnMethod) { + return arg => { + const onDone = () => Promise[returnMethod](arg); + + return Promise.resolve(callback()).then(onDone, onDone); + }; + } +} + +function getExecutable(name, isGlobal) { + const suffix = isWindows() ? '.cmd' : ''; + const prefix = isGlobal ? '' : path.join('node_modules', '.bin'); + + return `${path.join(prefix, name)}${suffix}`; +} + +function ignoreMissing(err) { + if (err.code === 'ENOENT') { + console.warn(`Ignoring error: ${err}\n ${err.stack || ''}`); + return Promise.resolve(); + } + + return Promise.reject(err); +} + +function isHidden(fileOrDir) { + return path.basename(fileOrDir)[0] === '.'; +} + +function isWindows() { + return process.platform === 'win32'; +} + +function keepOnly(fileOrDir, notIgnoreHidden) { + return new Promise((resolve, reject) => { + const absPath = path.resolve(fileOrDir); + const parentDir = path.dirname(absPath); + + fs.readdir(parentDir, (err, items) => { + if (err) return reject(err); + + let itemsToRemove = items.filter(item => path.resolve(item) !== absPath); + if (!notIgnoreHidden) itemsToRemove = itemsToRemove.filter(item => !isHidden(item)); + + Promise. + all(itemsToRemove.map(item => new Promise((resolve, reject) => { + fs.lstat(item, (err, stat) => { + if (err) return ignoreMissing(err).then(resolve, reject); + + const promise = stat.isDirectory() ? removeDir(item) : removeFile(item); + promise.catch(ignoreMissing).then(resolve, reject); + }); + }))). + then(resolve, reject); + }); + }); +} + +function killProcess(proc) { + const pid = proc.pid; + const cmd = (isWindows() ? 'taskkill /t /f /pid ' : 'kill ') + pid; + + return spawnAsPromised(cmd); +} + +function replaceInFile(valuePlaceholderMap, filePath) { + return new Promise((resolve, reject) => { + fs.readFile(filePath, 'utf-8', (err, text) => { + if (err) return reject(err); + + const newText = Object. + keys(valuePlaceholderMap). + reduce((text, value) => text.replace(valuePlaceholderMap[value], value), text); + + fs.writeFile(filePath, newText, err => err ? reject(err) : resolve()); + }); + }); +} + +function removeDir(dir) { + return traverseDir(dir, removeFile, removeEmptyDir, ignoreMissing). + catch(ignoreMissing); + + // Helpers + function removeEmptyDir(emptyDir) { + return new Promise((resolve, reject) => { + fs.rmdir(emptyDir, err => err ? reject(err) : resolve()); + }); + } +} + +function removeFile(file) { + return new Promise((resolve, reject) => { + fs.unlink(file, err => err ? reject(err) : resolve()); + }); +} + +function spawnAsPromised(cmd, options) { + let proc; + const promise = new Promise((resolve, reject) => { + const parsedCmd = parseCmd(cmd); + + const executable = parsedCmd.executable; + const args = parsedCmd.args; + options = options || {stdio: 'inherit'}; + + proc = spawn(executable, args, options). + on('error', reject). + on('exit', (code, signal) => (code !== 0) ? reject(code || signal) : resolve()); + }); + + promise.$$process = proc; + + return promise; + + // Helpers + function parseCmd(cmd) { + const tokens = Array.isArray(cmd) ? cmd : cmd. + split('"'). + reduce((arr, str, idx) => arr.concat((idx % 2) ? `"${str}"` : str.split(' ')), []). + filter(Boolean); + + return { + executable: tokens[0], + args: tokens.slice(1) + }; + } +} + +function traverseDir(dir, fileCallback, dirCallback, errInterceptor) { + const resolve = () => Promise.resolve(); + const reject = err => Promise.reject(err); + + if (!errInterceptor) errInterceptor = reject; + dirCallback = { + pre: dirCallback.pre || resolve, + post: (typeof dirCallback === 'function') ? dirCallback : (dirCallback.post || resolve) + }; + + return dirCallback.pre(dir). + then(skip => skip ? Promise.resolve() : new Promise((resolve, reject) => { + fs.readdir(dir, (err, items) => { + if (err) return errInterceptor(err).then(resolve, reject); + + Promise. + all(items.map(item => new Promise((resolve, reject) => { + const curPath = path.join(dir, item); + + fs.lstat(curPath, (err, stat) => { + if (err) return errInterceptor(err).then(resolve, reject); + + const promise = stat.isDirectory() ? + traverseDir(curPath, fileCallback, dirCallback, errInterceptor) : + fileCallback(curPath); + + promise. + catch(errInterceptor). + then(resolve, reject); + }); + }))). + then(resolve, reject); + }); + })). + then(() => dirCallback.post(dir)); +} diff --git a/src/css/bootstrap-responsive.css b/src/css/bootstrap-responsive.css deleted file mode 100644 index 0bc6de916..000000000 --- a/src/css/bootstrap-responsive.css +++ /dev/null @@ -1,686 +0,0 @@ -/*! - * Bootstrap Responsive v2.0.2 - * - * Copyright 2012 Twitter, Inc - * Licensed under the Apache License v2.0 - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Designed and built with all the love in the world @twitter by @mdo and @fat. - */ -.clearfix { - *zoom: 1; -} -.clearfix:before, -.clearfix:after { - display: table; - content: ""; -} -.clearfix:after { - clear: both; -} -.hide-text { - overflow: hidden; - text-indent: 100%; - white-space: nowrap; -} -.input-block-level { - display: block; - width: 100%; - min-height: 28px; - /* Make inputs at least the height of their button counterpart */ - - /* Makes inputs behave like true block-level elements */ - - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - -ms-box-sizing: border-box; - box-sizing: border-box; -} -.hidden { - display: none; - visibility: hidden; -} -.visible-phone { - display: none; -} -.visible-tablet { - display: none; -} -.visible-desktop { - display: block; -} -.hidden-phone { - display: block; -} -.hidden-tablet { - display: block; -} -.hidden-desktop { - display: none; -} -@media (max-width: 767px) { - .visible-phone { - display: block; - } - .hidden-phone { - display: none; - } - .hidden-desktop { - display: block; - } - .visible-desktop { - display: none; - } -} -@media (min-width: 768px) and (max-width: 979px) { - .visible-tablet { - display: block; - } - .hidden-tablet { - display: none; - } - .hidden-desktop { - display: block; - } - .visible-desktop { - display: none; - } -} -@media (max-width: 480px) { - .nav-collapse { - -webkit-transform: translate3d(0, 0, 0); - } - .page-header h1 small { - display: block; - line-height: 18px; - } - input[type="checkbox"], - input[type="radio"] { - border: 1px solid #ccc; - } - .form-horizontal .control-group > label { - float: none; - width: auto; - padding-top: 0; - text-align: left; - } - .form-horizontal .controls { - margin-left: 0; - } - .form-horizontal .control-list { - padding-top: 0; - } - .form-horizontal .form-actions { - padding-left: 10px; - padding-right: 10px; - } - .modal { - position: absolute; - top: 10px; - left: 10px; - right: 10px; - width: auto; - margin: 0; - } - .modal.fade.in { - top: auto; - } - .modal-header .close { - padding: 10px; - margin: -10px; - } - .carousel-caption { - position: static; - } -} -@media (max-width: 767px) { - body { - padding-left: 20px; - padding-right: 20px; - } - .navbar-fixed-top { - margin-left: -20px; - margin-right: -20px; - } - .container { - width: auto; - } - .row-fluid { - width: 100%; - } - .row { - margin-left: 0; - } - .row > [class*="span"], - .row-fluid > [class*="span"] { - float: none; - display: block; - width: auto; - margin: 0; - } - .thumbnails [class*="span"] { - width: auto; - } - input[class*="span"], - select[class*="span"], - textarea[class*="span"], - .uneditable-input { - display: block; - width: 100%; - min-height: 28px; - /* Make inputs at least the height of their button counterpart */ - - /* Makes inputs behave like true block-level elements */ - - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - -ms-box-sizing: border-box; - box-sizing: border-box; - } - .input-prepend input[class*="span"], - .input-append input[class*="span"] { - width: auto; - } -} -@media (min-width: 768px) and (max-width: 979px) { - .row { - margin-left: -20px; - *zoom: 1; - } - .row:before, - .row:after { - display: table; - content: ""; - } - .row:after { - clear: both; - } - [class*="span"] { - float: left; - margin-left: 20px; - } - .container, - .navbar-fixed-top .container, - .navbar-fixed-bottom .container { - width: 724px; - } - .span12 { - width: 724px; - } - .span11 { - width: 662px; - } - .span10 { - width: 600px; - } - .span9 { - width: 538px; - } - .span8 { - width: 476px; - } - .span7 { - width: 414px; - } - .span6 { - width: 352px; - } - .span5 { - width: 290px; - } - .span4 { - width: 228px; - } - .span3 { - width: 166px; - } - .span2 { - width: 104px; - } - .span1 { - width: 42px; - } - .offset12 { - margin-left: 764px; - } - .offset11 { - margin-left: 702px; - } - .offset10 { - margin-left: 640px; - } - .offset9 { - margin-left: 578px; - } - .offset8 { - margin-left: 516px; - } - .offset7 { - margin-left: 454px; - } - .offset6 { - margin-left: 392px; - } - .offset5 { - margin-left: 330px; - } - .offset4 { - margin-left: 268px; - } - .offset3 { - margin-left: 206px; - } - .offset2 { - margin-left: 144px; - } - .offset1 { - margin-left: 82px; - } - .row-fluid { - width: 100%; - *zoom: 1; - } - .row-fluid:before, - .row-fluid:after { - display: table; - content: ""; - } - .row-fluid:after { - clear: both; - } - .row-fluid > [class*="span"] { - float: left; - margin-left: 2.762430939%; - } - .row-fluid > [class*="span"]:first-child { - margin-left: 0; - } - .row-fluid > .span12 { - width: 99.999999993%; - } - .row-fluid > .span11 { - width: 91.436464082%; - } - .row-fluid > .span10 { - width: 82.87292817100001%; - } - .row-fluid > .span9 { - width: 74.30939226%; - } - .row-fluid > .span8 { - width: 65.74585634900001%; - } - .row-fluid > .span7 { - width: 57.182320438000005%; - } - .row-fluid > .span6 { - width: 48.618784527%; - } - .row-fluid > .span5 { - width: 40.055248616%; - } - .row-fluid > .span4 { - width: 31.491712705%; - } - .row-fluid > .span3 { - width: 22.928176794%; - } - .row-fluid > .span2 { - width: 14.364640883%; - } - .row-fluid > .span1 { - width: 5.801104972%; - } - input, - textarea, - .uneditable-input { - margin-left: 0; - } - input.span12, textarea.span12, .uneditable-input.span12 { - width: 714px; - } - input.span11, textarea.span11, .uneditable-input.span11 { - width: 652px; - } - input.span10, textarea.span10, .uneditable-input.span10 { - width: 590px; - } - input.span9, textarea.span9, .uneditable-input.span9 { - width: 528px; - } - input.span8, textarea.span8, .uneditable-input.span8 { - width: 466px; - } - input.span7, textarea.span7, .uneditable-input.span7 { - width: 404px; - } - input.span6, textarea.span6, .uneditable-input.span6 { - width: 342px; - } - input.span5, textarea.span5, .uneditable-input.span5 { - width: 280px; - } - input.span4, textarea.span4, .uneditable-input.span4 { - width: 218px; - } - input.span3, textarea.span3, .uneditable-input.span3 { - width: 156px; - } - input.span2, textarea.span2, .uneditable-input.span2 { - width: 94px; - } - input.span1, textarea.span1, .uneditable-input.span1 { - width: 32px; - } -} -@media (max-width: 979px) { - body { - padding-top: 0; - } - .navbar-fixed-top { - position: static; - margin-bottom: 18px; - } - .navbar-fixed-top .navbar-inner { - padding: 5px; - } - .navbar .container { - width: auto; - padding: 0; - } - .navbar .brand { - padding-left: 10px; - padding-right: 10px; - margin: 0 0 0 -5px; - } - .navbar .nav-collapse { - clear: left; - } - .navbar .nav { - float: none; - margin: 0 0 9px; - } - .navbar .nav > li { - float: none; - } - .navbar .nav > li > a { - margin-bottom: 2px; - } - .navbar .nav > .divider-vertical { - display: none; - } - .navbar .nav .nav-header { - color: #999999; - text-shadow: none; - } - .navbar .nav > li > a, - .navbar .dropdown-menu a { - padding: 6px 15px; - font-weight: bold; - color: #999999; - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - border-radius: 3px; - } - .navbar .dropdown-menu li + li a { - margin-bottom: 2px; - } - .navbar .nav > li > a:hover, - .navbar .dropdown-menu a:hover { - background-color: #222222; - } - .navbar .dropdown-menu { - position: static; - top: auto; - left: auto; - float: none; - display: block; - max-width: none; - margin: 0 15px; - padding: 0; - background-color: transparent; - border: none; - -webkit-border-radius: 0; - -moz-border-radius: 0; - border-radius: 0; - -webkit-box-shadow: none; - -moz-box-shadow: none; - box-shadow: none; - } - .navbar .dropdown-menu:before, - .navbar .dropdown-menu:after { - display: none; - } - .navbar .dropdown-menu .divider { - display: none; - } - .navbar-form, - .navbar-search { - float: none; - padding: 9px 15px; - margin: 9px 0; - border-top: 1px solid #222222; - border-bottom: 1px solid #222222; - -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); - -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); - box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); - } - .navbar .nav.pull-right { - float: none; - margin-left: 0; - } - .navbar-static .navbar-inner { - padding-left: 10px; - padding-right: 10px; - } - .btn-navbar { - display: block; - } - .nav-collapse { - overflow: hidden; - height: 0; - } -} -@media (min-width: 980px) { - .nav-collapse.collapse { - height: auto !important; - overflow: visible !important; - } -} -@media (min-width: 1200px) { - .row { - margin-left: -30px; - *zoom: 1; - } - .row:before, - .row:after { - display: table; - content: ""; - } - .row:after { - clear: both; - } - [class*="span"] { - float: left; - margin-left: 30px; - } - .container, - .navbar-fixed-top .container, - .navbar-fixed-bottom .container { - width: 1170px; - } - .span12 { - width: 1170px; - } - .span11 { - width: 1070px; - } - .span10 { - width: 970px; - } - .span9 { - width: 870px; - } - .span8 { - width: 770px; - } - .span7 { - width: 670px; - } - .span6 { - width: 570px; - } - .span5 { - width: 470px; - } - .span4 { - width: 370px; - } - .span3 { - width: 270px; - } - .span2 { - width: 170px; - } - .span1 { - width: 70px; - } - .offset12 { - margin-left: 1230px; - } - .offset11 { - margin-left: 1130px; - } - .offset10 { - margin-left: 1030px; - } - .offset9 { - margin-left: 930px; - } - .offset8 { - margin-left: 830px; - } - .offset7 { - margin-left: 730px; - } - .offset6 { - margin-left: 630px; - } - .offset5 { - margin-left: 530px; - } - .offset4 { - margin-left: 430px; - } - .offset3 { - margin-left: 330px; - } - .offset2 { - margin-left: 230px; - } - .offset1 { - margin-left: 130px; - } - .row-fluid { - width: 100%; - *zoom: 1; - } - .row-fluid:before, - .row-fluid:after { - display: table; - content: ""; - } - .row-fluid:after { - clear: both; - } - .row-fluid > [class*="span"] { - float: left; - margin-left: 2.564102564%; - } - .row-fluid > [class*="span"]:first-child { - margin-left: 0; - } - .row-fluid > .span12 { - width: 100%; - } - .row-fluid > .span11 { - width: 91.45299145300001%; - } - .row-fluid > .span10 { - width: 82.905982906%; - } - .row-fluid > .span9 { - width: 74.358974359%; - } - .row-fluid > .span8 { - width: 65.81196581200001%; - } - .row-fluid > .span7 { - width: 57.264957265%; - } - .row-fluid > .span6 { - width: 48.717948718%; - } - .row-fluid > .span5 { - width: 40.170940171000005%; - } - .row-fluid > .span4 { - width: 31.623931624%; - } - .row-fluid > .span3 { - width: 23.076923077%; - } - .row-fluid > .span2 { - width: 14.529914530000001%; - } - .row-fluid > .span1 { - width: 5.982905983%; - } - input, - textarea, - .uneditable-input { - margin-left: 0; - } - input.span12, textarea.span12, .uneditable-input.span12 { - width: 1160px; - } - input.span11, textarea.span11, .uneditable-input.span11 { - width: 1060px; - } - input.span10, textarea.span10, .uneditable-input.span10 { - width: 960px; - } - input.span9, textarea.span9, .uneditable-input.span9 { - width: 860px; - } - input.span8, textarea.span8, .uneditable-input.span8 { - width: 760px; - } - input.span7, textarea.span7, .uneditable-input.span7 { - width: 660px; - } - input.span6, textarea.span6, .uneditable-input.span6 { - width: 560px; - } - input.span5, textarea.span5, .uneditable-input.span5 { - width: 460px; - } - input.span4, textarea.span4, .uneditable-input.span4 { - width: 360px; - } - input.span3, textarea.span3, .uneditable-input.span3 { - width: 260px; - } - input.span2, textarea.span2, .uneditable-input.span2 { - width: 160px; - } - input.span1, textarea.span1, .uneditable-input.span1 { - width: 60px; - } - .thumbnails { - margin-left: -30px; - } - .thumbnails > li { - margin-left: 30px; - } -} diff --git a/src/css/bootstrap-responsive.min.css b/src/css/bootstrap-responsive.min.css deleted file mode 100644 index 60a47c949..000000000 --- a/src/css/bootstrap-responsive.min.css +++ /dev/null @@ -1,12 +0,0 @@ -.clearfix{*zoom:1;}.clearfix:before,.clearfix:after{display:table;content:"";} -.clearfix:after{clear:both;} -.hide-text{overflow:hidden;text-indent:100%;white-space:nowrap;} -.input-block-level{display:block;width:100%;min-height:28px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;} -.hidden{display:none;visibility:hidden;} -.visible-phone{display:none;} -.visible-tablet{display:none;} -.visible-desktop{display:block;} -.hidden-phone{display:block;} -.hidden-tablet{display:block;} -.hidden-desktop{display:none;} -@media (max-width:767px){.visible-phone{display:block;} .hidden-phone{display:none;} .hidden-desktop{display:block;} .visible-desktop{display:none;}}@media (min-width:768px) and (max-width:979px){.visible-tablet{display:block;} .hidden-tablet{display:none;} .hidden-desktop{display:block;} .visible-desktop{display:none;}}@media (max-width:480px){.nav-collapse{-webkit-transform:translate3d(0, 0, 0);} .page-header h1 small{display:block;line-height:18px;} input[type="checkbox"],input[type="radio"]{border:1px solid #ccc;} .form-horizontal .control-group>label{float:none;width:auto;padding-top:0;text-align:left;} .form-horizontal .controls{margin-left:0;} .form-horizontal .control-list{padding-top:0;} .form-horizontal .form-actions{padding-left:10px;padding-right:10px;} .modal{position:absolute;top:10px;left:10px;right:10px;width:auto;margin:0;}.modal.fade.in{top:auto;} .modal-header .close{padding:10px;margin:-10px;} .carousel-caption{position:static;}}@media (max-width:767px){body{padding-left:20px;padding-right:20px;} .navbar-fixed-top{margin-left:-20px;margin-right:-20px;} .container{width:auto;} .row-fluid{width:100%;} .row{margin-left:0;} .row>[class*="span"],.row-fluid>[class*="span"]{float:none;display:block;width:auto;margin:0;} .thumbnails [class*="span"]{width:auto;} input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input{display:block;width:100%;min-height:28px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;} .input-prepend input[class*="span"],.input-append input[class*="span"]{width:auto;}}@media (min-width:768px) and (max-width:979px){.row{margin-left:-20px;*zoom:1;}.row:before,.row:after{display:table;content:"";} .row:after{clear:both;} [class*="span"]{float:left;margin-left:20px;} .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:724px;} .span12{width:724px;} .span11{width:662px;} .span10{width:600px;} .span9{width:538px;} .span8{width:476px;} .span7{width:414px;} .span6{width:352px;} .span5{width:290px;} .span4{width:228px;} .span3{width:166px;} .span2{width:104px;} .span1{width:42px;} .offset12{margin-left:764px;} .offset11{margin-left:702px;} .offset10{margin-left:640px;} .offset9{margin-left:578px;} .offset8{margin-left:516px;} .offset7{margin-left:454px;} .offset6{margin-left:392px;} .offset5{margin-left:330px;} .offset4{margin-left:268px;} .offset3{margin-left:206px;} .offset2{margin-left:144px;} .offset1{margin-left:82px;} .row-fluid{width:100%;*zoom:1;}.row-fluid:before,.row-fluid:after{display:table;content:"";} .row-fluid:after{clear:both;} .row-fluid>[class*="span"]{float:left;margin-left:2.762430939%;} .row-fluid>[class*="span"]:first-child{margin-left:0;} .row-fluid > .span12{width:99.999999993%;} .row-fluid > .span11{width:91.436464082%;} .row-fluid > .span10{width:82.87292817100001%;} .row-fluid > .span9{width:74.30939226%;} .row-fluid > .span8{width:65.74585634900001%;} .row-fluid > .span7{width:57.182320438000005%;} .row-fluid > .span6{width:48.618784527%;} .row-fluid > .span5{width:40.055248616%;} .row-fluid > .span4{width:31.491712705%;} .row-fluid > .span3{width:22.928176794%;} .row-fluid > .span2{width:14.364640883%;} .row-fluid > .span1{width:5.801104972%;} input,textarea,.uneditable-input{margin-left:0;} input.span12, textarea.span12, .uneditable-input.span12{width:714px;} input.span11, textarea.span11, .uneditable-input.span11{width:652px;} input.span10, textarea.span10, .uneditable-input.span10{width:590px;} input.span9, textarea.span9, .uneditable-input.span9{width:528px;} input.span8, textarea.span8, .uneditable-input.span8{width:466px;} input.span7, textarea.span7, .uneditable-input.span7{width:404px;} input.span6, textarea.span6, .uneditable-input.span6{width:342px;} input.span5, textarea.span5, .uneditable-input.span5{width:280px;} input.span4, textarea.span4, .uneditable-input.span4{width:218px;} input.span3, textarea.span3, .uneditable-input.span3{width:156px;} input.span2, textarea.span2, .uneditable-input.span2{width:94px;} input.span1, textarea.span1, .uneditable-input.span1{width:32px;}}@media (max-width:979px){body{padding-top:0;} .navbar-fixed-top{position:static;margin-bottom:18px;} .navbar-fixed-top .navbar-inner{padding:5px;} .navbar .container{width:auto;padding:0;} .navbar .brand{padding-left:10px;padding-right:10px;margin:0 0 0 -5px;} .navbar .nav-collapse{clear:left;} .navbar .nav{float:none;margin:0 0 9px;} .navbar .nav>li{float:none;} .navbar .nav>li>a{margin-bottom:2px;} .navbar .nav>.divider-vertical{display:none;} .navbar .nav .nav-header{color:#999999;text-shadow:none;} .navbar .nav>li>a,.navbar .dropdown-menu a{padding:6px 15px;font-weight:bold;color:#999999;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;} .navbar .dropdown-menu li+li a{margin-bottom:2px;} .navbar .nav>li>a:hover,.navbar .dropdown-menu a:hover{background-color:#222222;} .navbar .dropdown-menu{position:static;top:auto;left:auto;float:none;display:block;max-width:none;margin:0 15px;padding:0;background-color:transparent;border:none;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;} .navbar .dropdown-menu:before,.navbar .dropdown-menu:after{display:none;} .navbar .dropdown-menu .divider{display:none;} .navbar-form,.navbar-search{float:none;padding:9px 15px;margin:9px 0;border-top:1px solid #222222;border-bottom:1px solid #222222;-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.1);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.1);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.1);} .navbar .nav.pull-right{float:none;margin-left:0;} .navbar-static .navbar-inner{padding-left:10px;padding-right:10px;} .btn-navbar{display:block;} .nav-collapse{overflow:hidden;height:0;}}@media (min-width:980px){.nav-collapse.collapse{height:auto !important;overflow:visible !important;}}@media (min-width:1200px){.row{margin-left:-30px;*zoom:1;}.row:before,.row:after{display:table;content:"";} .row:after{clear:both;} [class*="span"]{float:left;margin-left:30px;} .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:1170px;} .span12{width:1170px;} .span11{width:1070px;} .span10{width:970px;} .span9{width:870px;} .span8{width:770px;} .span7{width:670px;} .span6{width:570px;} .span5{width:470px;} .span4{width:370px;} .span3{width:270px;} .span2{width:170px;} .span1{width:70px;} .offset12{margin-left:1230px;} .offset11{margin-left:1130px;} .offset10{margin-left:1030px;} .offset9{margin-left:930px;} .offset8{margin-left:830px;} .offset7{margin-left:730px;} .offset6{margin-left:630px;} .offset5{margin-left:530px;} .offset4{margin-left:430px;} .offset3{margin-left:330px;} .offset2{margin-left:230px;} .offset1{margin-left:130px;} .row-fluid{width:100%;*zoom:1;}.row-fluid:before,.row-fluid:after{display:table;content:"";} .row-fluid:after{clear:both;} .row-fluid>[class*="span"]{float:left;margin-left:2.564102564%;} .row-fluid>[class*="span"]:first-child{margin-left:0;} .row-fluid > .span12{width:100%;} .row-fluid > .span11{width:91.45299145300001%;} .row-fluid > .span10{width:82.905982906%;} .row-fluid > .span9{width:74.358974359%;} .row-fluid > .span8{width:65.81196581200001%;} .row-fluid > .span7{width:57.264957265%;} .row-fluid > .span6{width:48.717948718%;} .row-fluid > .span5{width:40.170940171000005%;} .row-fluid > .span4{width:31.623931624%;} .row-fluid > .span3{width:23.076923077%;} .row-fluid > .span2{width:14.529914530000001%;} .row-fluid > .span1{width:5.982905983%;} input,textarea,.uneditable-input{margin-left:0;} input.span12, textarea.span12, .uneditable-input.span12{width:1160px;} input.span11, textarea.span11, .uneditable-input.span11{width:1060px;} input.span10, textarea.span10, .uneditable-input.span10{width:960px;} input.span9, textarea.span9, .uneditable-input.span9{width:860px;} input.span8, textarea.span8, .uneditable-input.span8{width:760px;} input.span7, textarea.span7, .uneditable-input.span7{width:660px;} input.span6, textarea.span6, .uneditable-input.span6{width:560px;} input.span5, textarea.span5, .uneditable-input.span5{width:460px;} input.span4, textarea.span4, .uneditable-input.span4{width:360px;} input.span3, textarea.span3, .uneditable-input.span3{width:260px;} input.span2, textarea.span2, .uneditable-input.span2{width:160px;} input.span1, textarea.span1, .uneditable-input.span1{width:60px;} .thumbnails{margin-left:-30px;} .thumbnails>li{margin-left:30px;}} diff --git a/src/css/docs.css b/src/css/docs.css index 0378f3910..6945256e8 100644 --- a/src/css/docs.css +++ b/src/css/docs.css @@ -68,7 +68,7 @@ background: linear-gradient(to bottom, rgba(221,221,221,1) 0%,rgba(225,225,225, filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#dddddd', endColorstr='#eeeeee',GradientType=0 ); /* IE6-9 */ background-color:white; - + } .hero-unit h1 { @@ -110,36 +110,6 @@ filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#dddddd', end margin:auto; } -.learn-link { - background: white; - border: 1px solid #e2e4e5; - border-radius: 100px; - box-shadow: 0 1px 0 rgba(0, 0, 0, 0.15); - color: #535353; - display: block; - font-size: 16px; - margin: 0 auto; - max-width: 280px; - padding: 0.7em 40px 0.7em 60px; - position: relative; - text-align: center; -} -.learn-link:hover, -.learn-link:focus { - text-decoration: none; -} -.learn-link-badge { - left: -5px; - position: absolute; - top: -5px; -} - -.google-follow { - font-family: 'Helvetica Neue', Arial, sans-serif; - font-size: 11px; - font-weight: bold; -} - img.AngularJS-large { width: 383px; height: 108px; @@ -204,6 +174,7 @@ img.cache { .well h1.ng-binding { margin: 0; + word-break: break-all; } .no-margins { @@ -264,7 +235,6 @@ img.cache { } .modal-footer > a { - float: left; padding-right: 20px; padding-top: 7px; } @@ -296,6 +266,15 @@ div.modal-body button { z-index: 1051; } +.stage-buttons .btn { + margin:5px!important; +} + +@media only screen and (max-width: 50em) { + .stage-container .btn { display:block; } + .stage-container .spacer { display:none; } +} + .jumbotron { box-shadow:inset 0 0 100px #000; padding:0; @@ -304,138 +283,86 @@ div.modal-body button { text-align:center; } -.jumbotron, -.jumbotron .gallery a { +.jumbotron { color:white; } +.ng2-beta { + font-family: "Roboto","Helvetica Neue Light","Helvetica Neue",Helvetica,Arial,"Lucida Grande",sans-serif; + border-radius: 10px; + margin-bottom: 10px; +} + +.ng2-beta img { + height: 100px; +} + .jumbotron h1 { font-family: 'Helvetica Neue', Arial, sans-serif; margin-top:0; } -.jumbotron-sections a { +.jumbotron-sections > div { display:inline-block; - padding:10px 20px; - overflow:auto; + padding:10px; + overflow:visible; color:white; -} - -.jumbotron-sections a.active { - background:black; - border-radius:10px; + min-width:300px; + text-align:left; + box-sizing:border-box; } .jumbotron-sections h2 { - padding:0 20px; - overflow:auto; - display:inline-block; - vertical-align:middle; + font-size:30px; } -.jumbotron-logo { - height:70px; - vertical-align:middle; +.jumbotron-sections p { + margin:3px 0 0; + padding:0; } - -.gallery-item { - box-sizing:border-box; - display:inline-block; - vertical-align:top; - width:340px; +.jumbotron-sections p.date { + color:#ccc; } -.gallery-item img { +.jumbotron-sections a { box-sizing:border-box; + display:table; + padding:20px; + width:100%; + color:white; } -.video-item-heading { - box-sizing:border-box; - font-weight:bold; - line-height:25px; - padding:20px 10px 0; - font-size:20px; - font-family:helvetica; +.jumbotron-sections a > * { + display:table-cell; + vertical-align:middle; } -.video-item { - padding:10px 5px; - margin:20px 20px 30px; - text-decoration:none!important; +.jumbotron-sections a img { + width:70px; } -.video-item-image { +.jumbotron-sections a { + text-decoration:none; + background:rgba(0,0,0,0.3); + margin:0 10px; border-radius:10px; - position:relative; - height:170px; - overflow:hidden; - border:5px solid rgba(0,0,0,0.4); -} - -.video-item:hover .video-item-image { - transition:0.2s linear all; - -webkit-transition:0.2s linear all; -} - -.ng-europe .video-item:hover .video-item-image { - border-color:#006FCC; -} - -.ng-europe .jumbotron-sections a.active, -.ng-europe .video-item:hover .video-item-image:after { - color:#006FCC; -} - -.ng-conf .video-item:hover .video-item-image { - border-color:#D32C25; } -.ng-conf .jumbotron-sections a.active, -.ng-conf .video-item:hover .video-item-image:after { - color:#D32C25; -} - -.ng-conf .video-item:hover .video-item-image:after { - color:#D32C25; -} - -.video-item-image img { - margin-top:-40px; - max-height:250px; +.jumbotron-sections a:hover { + background:black; + color: white; } -.video-item-image { - position:relative; +.jumbotron-logo img { + padding-right:20px; } -.video-item:hover img { - box-shadow:0 0 10px #006FCC; - cursor:pointer; +.jumbotron-logo, +.jumbotron-logo img { + height:70px; + width:70px; } -.video-item:hover .video-item-image:before { - position:absolute; - top:0; - left:0; - bottom:0; - right:0; - background:rgba(0,0,0,0.4); - content:""; -} - -.video-item:hover .video-item-image:after { - position:absolute; - top:50%; - left:50%; - width:100px; - height:100px; - margin-top:-50px; - margin-left:-50px; - font-family: "FontAwesome"; - line-height:100px; - font-size:80px; - content: "\f04b"; -} .jumbotron-header, .jumbotron-actions { @@ -444,7 +371,7 @@ div.modal-body button { } .jumbotron-header { - padding:20px; + padding:10px; } .jumbotron-actions { @@ -473,6 +400,10 @@ div.modal-body button { margin:0 20px 20px; } +.track-2-link { + margin: 3px 0; +} + .animated-item.ng-enter-stagger { -webkit-animation-delay:150ms; animation-delay:150ms; @@ -578,10 +509,16 @@ filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#eeeeee', end margin-top:20px; } -.ng-europe-logo { - float:left; - margin-right:50px; -} .about-container { padding:30px 0 50px; } + +.modal-header button.close { + background: 0 0; + border: 0; + opacity: 0.4; +} + +.code-annotation { + cursor: help; +} diff --git a/src/css/font-awesome.css b/src/css/font-awesome.css old mode 100755 new mode 100644 diff --git a/src/featured-videos.json b/src/featured-videos.json deleted file mode 100644 index edac73ba5..000000000 --- a/src/featured-videos.json +++ /dev/null @@ -1,121 +0,0 @@ -{"ng-conf": [ - { - "imageUrl":"https://i.ytimg.com/vi/r1A1VR0ibIQ/hqdefault.jpg", - "title":"Misˌko Hevery and Brad Green - Keynote", - "url":"https://www.youtube.com/watch?v=r1A1VR0ibIQ" - }, - { - "imageUrl":"https://i.ytimg.com/vi/h-SQvre_6qU/hqdefault.jpg", - "title":"Igor Minar - Angular === Community (Keynote)", - "url":"https://www.youtube.com/watch?v=h-SQvre_6qU" - }, - { - "imageUrl":"https://i.ytimg.com/vi/aQipuiTcn3U/hqdefault.jpg", - "title":"Julie Ralph - End to End Angular Testing with Protractor", - "url":"https://www.youtube.com/watch?v=aQipuiTcn3U" - }, - { - "imageUrl":"https://i.ytimg.com/vi/srt3OBP2kGc/hqdefault.jpg", - "title":"Angular Team Panel", - "url":"https://www.youtube.com/watch?v=srt3OBP2kGc" - }, - { - "imageUrl":"https://i.ytimg.com/vi/hC0MpgUoui4/hqdefault.jpg", - "title":"Lukas Rubbelke & Matias Niemela - Awesome Interfaces with AngularJS Animations", - "url":"https://www.youtube.com/watch?v=hC0MpgUoui4" - }, - { - "imageUrl":"https://i.ytimg.com/vi/_OGGsf1ZXMs/hqdefault.jpg", - "title":"Vojta Jina - Dependency Injection", - "url":"https://www.youtube.com/watch?v=_OGGsf1ZXMs" - } -], "ng-europe": [ - { - "description": "Slides: http://goo.gl/70sEsr", - "duration": "2094", - "id": "c5HSqDLfpW0", - "imageUrl": "https://i.ytimg.com/vi/c5HSqDLfpW0/hqdefault.jpg", - "url": "https://www.youtube.com/watch?v=c5HSqDLfpW0", - "title": "Keynote on the state of Angular by Igor Minar, Brad Green and Judy Tuan" - }, - { - "description": "In this keynote, Mi\u0161ko introduces AtScript to the world and talks about the future and past of AngularJS. Slides: http://goo.gl/pwk6Pb.", - "duration": "2136", - "id": "lGdnh8QSPPk", - "imageUrl": "https://i.ytimg.com/vi/lGdnh8QSPPk/hqdefault.jpg", - "url": "https://www.youtube.com/watch?v=lGdnh8QSPPk", - "title": "Mi\u0161ko Hevery - Keynote on AtScript" - }, - { - "description": "Igor Minar & Tobias Bosch present on Angular 2.0 Core by comparing it with Angular 1.3. Slides: http://goo.gl/ZyUU3Q.", - "duration": "1815", - "id": "gNmWybAyBHI", - "imageUrl": "https://i.ytimg.com/vi/gNmWybAyBHI/hqdefault.jpg", - "url": "https://www.youtube.com/watch?v=gNmWybAyBHI", - "title": "Angular 2.0 Core by Igor Minar & Tobias Bosch" - }, - { - "description": "Brian and Jeff tell you everything you need to know about Angular 1.3. Slides: http://goo.gl/pNmhAa.", - "duration": "1158", - "id": "ojMy6m_fcxc", - "imageUrl": "https://i.ytimg.com/vi/ojMy6m_fcxc/hqdefault.jpg", - "url": "https://www.youtube.com/watch?v=ojMy6m_fcxc", - "title": "Angular 1.3 by Jeff Cross & Brian Ford" - }, - { - "description": "The angular team answers questions from the crowd and Google Moderator on Angular 2.0, AtScript, Angular Material, the new router, ARIA, the path to migration and more.", - "duration": "3234", - "id": "g-x1QKriY90", - "imageUrl": "https://i.ytimg.com/vi/g-x1QKriY90/hqdefault.jpg", - "url": "https://www.youtube.com/watch?v=g-x1QKriY90", - "title": "Q&A with Angular team on 2.0, AtScript & more" - }, - { - "description": "slides: http://goo.gl/Htbhuw docs and demos: https://material.angularjs.org/", - "duration": "1527", - "id": "2qiyhkQVyxE", - "imageUrl": "https://i.ytimg.com/vi/2qiyhkQVyxE/hqdefault.jpg", - "url": "https://www.youtube.com/watch?v=2qiyhkQVyxE", - "title": "Material Design by Thomas Burleson & Max Lynch" - }, - { - "description": "Animations (sequencer, web animations) by Matias Niemel\u00e4 ak yearofmoo at ngeurope 2014. Slides: http://goo.gl/dknq6x.", - "duration": "1577", - "id": "3hktBbxFxSM", - "imageUrl": "https://i.ytimg.com/vi/3hktBbxFxSM/hqdefault.jpg", - "url": "https://www.youtube.com/watch?v=3hktBbxFxSM", - "title": "Animations (sequencer, web animations) by Matias Niemel\u00e4 aka yearofmoo" - }, - { - "duration": "1543", - "description": "Slides: http://goo.gl/yWqIzW", - "id": "XgmUkCISabc", - "title":"Protractor and the Testability API by Julie Ralph & Chirayu Krishnappa", - "url": "https://www.youtube.com/watch?v=XgmUkCISabc", - "imageUrl":"https://i.ytimg.com/vi/XgmUkCISabc/hqdefault.jpg" - }, - { - "description": "Rob tells all about the new router in AngularJS. Slides: http://goo.gl/33Jlb6.", - "duration": "1554", - "id": "h1P_Vh4gSQY", - "imageUrl": "https://i.ytimg.com/vi/h1P_Vh4gSQY/hqdefault.jpg", - "url": "https://www.youtube.com/watch?v=h1P_Vh4gSQY", - "title": "The new Router for AngularJS by Rob Eisenberg" - }, - { - "description": "There's a tendency for engineers to pick up a web development framework and then suddenly forget everything they've ever learned about software engineering. This talk will start by examining...", - "duration": "1768", - "id": "dmYDggEgU-s", - "imageUrl": "https://i.ytimg.com/vi/dmYDggEgU-s/hqdefault.jpg", - "url": "https://www.youtube.com/watch?v=dmYDggEgU-s", - "title": "AngularJS Accessibility by Marcy Sutton" - }, - { - "description": "Brian talks about the latest tooling in Angular and the new Batarang. Slides: http://goo.gl/Jb8V46.", - "duration": "896", - "id": "x8IWsjoCy-M", - "imageUrl": "https://i.ytimg.com/vi/x8IWsjoCy-M/hqdefault.jpg", - "url": "https://www.youtube.com/watch?v=x8IWsjoCy-M", - "title": "Tooling by Brian Ford" - } -]} diff --git a/src/img/AngularJS-large.png b/src/img/AngularJS-large.png index e71a01f76..a1037f225 100644 Binary files a/src/img/AngularJS-large.png and b/src/img/AngularJS-large.png differ diff --git a/src/img/angular-u-logo.png b/src/img/angular-u-logo.png new file mode 100644 index 000000000..a082b7431 Binary files /dev/null and b/src/img/angular-u-logo.png differ diff --git a/src/img/angularattack-logo.png b/src/img/angularattack-logo.png new file mode 100644 index 000000000..103b3aa60 Binary files /dev/null and b/src/img/angularattack-logo.png differ diff --git a/src/img/angularconnect-conf.png b/src/img/angularconnect-conf.png new file mode 100644 index 000000000..8e92684dc Binary files /dev/null and b/src/img/angularconnect-conf.png differ diff --git a/src/img/angularjs-for-header-only.svg b/src/img/angularjs-for-header-only.svg new file mode 100644 index 000000000..68689b6bb --- /dev/null +++ b/src/img/angularjs-for-header-only.svg @@ -0,0 +1,41 @@ + + + +]> + + + + + + + + + + + + + + + + + diff --git a/src/img/button-logo-grey.png b/src/img/button-logo-grey.png new file mode 100644 index 000000000..11e4becb3 Binary files /dev/null and b/src/img/button-logo-grey.png differ diff --git a/src/img/button-logo-grey@2x.png b/src/img/button-logo-grey@2x.png new file mode 100644 index 000000000..a31050dfd Binary files /dev/null and b/src/img/button-logo-grey@2x.png differ diff --git a/src/img/button-logo-white.png b/src/img/button-logo-white.png new file mode 100644 index 000000000..2469971ec Binary files /dev/null and b/src/img/button-logo-white.png differ diff --git a/src/img/button-logo-white@2x.png b/src/img/button-logo-white@2x.png new file mode 100644 index 000000000..89310648a Binary files /dev/null and b/src/img/button-logo-white@2x.png differ diff --git a/src/img/codeschool-badge.png b/src/img/codeschool-badge.png deleted file mode 100644 index f2a4b8f89..000000000 Binary files a/src/img/codeschool-badge.png and /dev/null differ diff --git a/src/img/google-black.png b/src/img/google-black.png deleted file mode 100644 index cd8575773..000000000 Binary files a/src/img/google-black.png and /dev/null differ diff --git a/src/img/google.png b/src/img/google.png deleted file mode 100644 index 119e24ad4..000000000 Binary files a/src/img/google.png and /dev/null differ diff --git a/src/img/new-ribbon.png b/src/img/new-ribbon.png new file mode 100644 index 000000000..364dc879f Binary files /dev/null and b/src/img/new-ribbon.png differ diff --git a/src/img/new-ribbon@2x.png b/src/img/new-ribbon@2x.png new file mode 100644 index 000000000..ac2f933c0 Binary files /dev/null and b/src/img/new-ribbon@2x.png differ diff --git a/src/img/ng-europe-logo-compact-on-white.png b/src/img/ng-europe-logo-compact-on-white.png deleted file mode 100755 index b4ef89f8a..000000000 Binary files a/src/img/ng-europe-logo-compact-on-white.png and /dev/null differ diff --git a/src/img/ng-europe-logo.png b/src/img/ng-europe-logo.png deleted file mode 100755 index a4cc258b3..000000000 Binary files a/src/img/ng-europe-logo.png and /dev/null differ diff --git a/src/img/ng-logo.png b/src/img/ng-logo.png new file mode 100644 index 000000000..666a58cef Binary files /dev/null and b/src/img/ng-logo.png differ diff --git a/src/img/ng-vegas-logo.png b/src/img/ng-vegas-logo.png new file mode 100644 index 000000000..00716dc73 Binary files /dev/null and b/src/img/ng-vegas-logo.png differ diff --git a/src/index.html b/src/index.html index 7068da930..fc52d5350 100644 --- a/src/index.html +++ b/src/index.html @@ -6,6 +6,7 @@ + + + + - - - + + + + ïŧŋ - + - +
- + +
-
-
- -
-
-

HTML enhanced for web apps!

-
-
- - View on GitHub - - + +
-

Why AngularJS?

-

+

Why AngularJS?

+

HTML is great for declaring static documents, but it falters when we try to use it for declaring dynamic views in web-applications. AngularJS lets you extend HTML vocabulary for your application. The resulting environment is extraordinarily expressive, readable, and quick to develop.

+
-

Alternatives

-

+

Alternatives

+

Other frameworks deal with HTML’s shortcomings by either abstracting away HTML, CSS, and/or JavaScript or by providing an imperative way for manipulating the DOM. Neither of these address the root problem that HTML was not designed for dynamic views.

+
-

Extensibility

-

+

Extensibility

+

AngularJS is a toolset for building the framework most suited to your application development. It is fully extensible and works well with other libraries. Every feature can be modified or replaced to suit your unique development workflow and feature needs. @@ -181,65 +188,13 @@

Extensibility

-
- -
-
-

Loading...

-
- -
-
-

No videos were found by your search query

-
- -
- - - - -
-
-
-

The Basics

+

The Basics

- +

Watch as we build this app

@@ -251,29 +206,29 @@

Watch as we build this app

-

Add Some Control

+

Add Some Control

-

Data Binding

-

+

Data Binding

+

Data-binding is an automatic way of updating the view whenever the model changes, as well as updating the model whenever the view changes. This is awesome because it eliminates DOM manipulation from the list of things you have to worry about.

-

Controller

-

+

Controller

+

Controllers are the behavior behind the DOM elements. AngularJS lets you express the behavior in a clean readable form without the usual boilerplate of updating the DOM, registering callbacks or watching model changes.

-

Plain JavaScript

-

+

Plain JavaScript

+

Unlike other frameworks, there is no need to inherit from proprietary types in order to wrap the - model in accessors methods. Angular models are plain old JavaScript objects. This makes your code easy to + model in accessors methods. AngularJS models are plain old JavaScript objects. This makes your code easy to test, maintain, reuse, and again free from boilerplate.

@@ -282,10 +237,10 @@

Plain JavaScript

- +
-

Watch as we build this app

+

Watch as we build this app

@@ -295,91 +250,43 @@

Watch as we build this app

- -

Wire up a Backend

-
-
-

Deep Linking

-

- A deep link reflects where the user is in the app, this is useful so users can bookmark - and email links to locations within apps. Round trip apps get this automatically, but - AJAX apps by their nature do not. AngularJS combines the benefits of deep link with - desktop app-like behavior. -

-
-
-

Form Validation

-

- Client-side form validation is an important part of great user experience. - AngularJS lets you declare the validation rules of the form without having to write - JavaScript code. Write less code, go have beer sooner. -

-
-
-

Server Communication

-

- AngularJS provides built-in services on top of XHR as well as various other backends - using third party libraries. Promises further simplify your code - by handling asynchronous return of data. In this example, we use the AngularFire - library to wire up a Firebase backend to a simple Angular app. -

-
-
-
-
-
- - -
- -
-
-
- - - -

Create Components

+

Create Components

-

Directives

-

- Directives is a unique and powerful feature available only in Angular. Directives let +

Directives

+

+ Directives are a unique and powerful feature available in AngularJS. Directives let you invent new HTML syntax, specific to your application.

-

Reusable Components

-

+

Reusable Components

+

We use directives to create reusable components. A component allows you to hide complex DOM structure, CSS, and behavior. This lets you focus either on what the application does or how the application looks separately.

-

Localization

-

- An important part of serious apps is localization. Angular's locale aware filters and +

Localization

+

+ An important part of serious apps is localization. AngularJS's locale aware filters and stemming directives give you building blocks to make your application available in all locales.

-
+
- +

Locale: US

@@ -389,23 +296,40 @@

Locale: SK

- - -

Embed and Inject

+

Navigation, Forms and Backends

-

Embeddable

-

- AngularJS works great with other technologies. Add as much or as little of AngularJS to - an existing page as you like. Many other frameworks require full commitment. This page - has multiple AngularJS applications embedded in it. Because AngularJS has no global - state multiple apps can run on a single page without the use of iframes. We - encourage you to view-source and look around. +

Deep Linking

+

+ A deep link reflects where the user is in the app. This is useful so users can bookmark + and email links to locations within the app. Round trip apps get this automatically, but + AJAX apps by their nature do not. AngularJS combines the benefits of deep linking with + desktop app-like behavior. +

+
+
+

Form Validation

+

+ Client-side form validation is an important part of a great user experience. + AngularJS lets you declare the validation rules of the form without having to write + JavaScript code. Write less code, go have beer sooner.

-

Injectable

-

+

Server Communication

+

+ AngularJS provides built-in services on top of XHR as well as various other backends + using third party libraries. Promises further simplify your code + by handling asynchronous return of data. +

+
+
+ +

Testability Built-in

+
+
+

Injectable

+

The dependency injection in AngularJS allows you to declaratively describe how your application is wired. This means that your application needs no main() method which is usually an unmaintainable mess. Dependency injection is also a core to @@ -413,9 +337,9 @@

Injectable

replaced.

-
-

Testable

-

+

+

Testable

+

AngularJS was designed from ground up to be testable. It encourages behavior-view separation, comes pre-bundled with mocks, and takes full advantage of dependency injection. It also comes with end-to-end scenario runner which eliminates test flakiness @@ -429,11 +353,11 @@

Testable

Back to top

-

Super-powered by Google ÂĐ2010-2014

+

Super-powered by Google ÂĐ2010-2021

Code licensed under the The MIT License. Documentation licensed under CC BY 3.0. + href="https://creativecommons.org/licenses/by/4.0/">CC BY 4.0.

@@ -472,46 +396,49 @@

Hello {{yourName}}!

-
- - - - - - - -
- - - - + + + - + - + - + \n'); + } else if (fileType == 'css') { + head.push(' \n'); + } + }); + + return head; + }, + getIndexTemplate: function(deps) { + return '\n' + + '\n' + + ' \n' + + ' ' + script.angular + + (deps.resource ? (' ' + script.resource.replace('>\n \n \n' + + ' \n' + + '__BODY__' + + ' \n' + + ''; + }, + getCopyright: function(filename) { + switch (filename.substr(filename.lastIndexOf('.'))) { + case '.html': + return COPYRIGHT_HTML; + case '.js': + case '.css': + return COPYRIGHT_JS_CSS; + case '.md': + return COPYRIGHT; + } + return ''; + } + }; + }) + .directive('code', function() { return {restrict: 'E', terminal: true}; }) @@ -116,6 +193,7 @@ angular.module('homepage', ['ngAnimate', 'ui.bootstrap', 'download-data']) }, put: function(key, value) { // noop, we don't need the cache for anything in examples + return value; } }); $provide.value('$anchorScroll', angular.noop); @@ -139,29 +217,13 @@ angular.module('homepage', ['ngAnimate', 'ui.bootstrap', 'download-data']) }; }) - .directive('appSource', function(fetchCode, escape, script, $compile, $timeout) { + .directive('appSource', function(fetchCode, escape, $compile, $timeout, templateBuilder, $sce) { return { terminal: true, - scope: true, + scope: {}, link: function(scope, element, attrs) { var tabs = [], - annotation = attrs.annotate && angular.fromJson(fetchCode(attrs.annotate)) || {}, - TEMPLATE = { - 'index.html': - '\n' + - '\n' + - ' \n' + - ' ' + script.angular + - (attrs.resource ? (' ' + script.resource.replace('>\n \n \n' + - ' \n' + - '__BODY__' + - ' \n' + - '' - }; + annotation = attrs.annotate && angular.fromJson(fetchCode(attrs.annotate)) || {}; element.css('clear', 'both'); @@ -169,20 +231,9 @@ angular.module('homepage', ['ngAnimate', 'ui.bootstrap', 'download-data']) var content; if (index === 0) { - var head = []; + var head = templateBuilder.createLocalDependencies(attrs.appSource); - angular.forEach(attrs.appSource.split(' '), function(tab, index) { - var filename = tab.split(':')[0], - fileType = filename.split(/\./)[1]; - - if (index === 0) return; - if (fileType == 'js') { - head.push(' \n'); - } else if (fileType == 'css') { - head.push(' \n'); - } - }); - content = TEMPLATE['index.html']; + content = templateBuilder.getIndexTemplate(attrs); content = content. replace('__MODULE__', attrs.module ? '="' + attrs.module + '"' : ''). replace('__HEAD__', head.join('')). @@ -201,19 +252,24 @@ angular.module('homepage', ['ngAnimate', 'ui.bootstrap', 'download-data']) counter = 0; angular.forEach(annotation[filename], function(text, key) { + counter++; text = text.replace('{{', '{{').replace('}}', '}}'); var regexp = new RegExp('(\\W|^)(' + key.replace(/([\W\-])/g, '\\$1') + ')(\\W|$)'); + scope['popover' + index + counter] = $sce.trustAsHtml(text); + content = content.replace(regexp, function(_, before, token, after) { - token = "__" + (counter++) + "__"; + token = '__' + counter + '__'; popovers[token] = - '' + escape(key) + '' + + '' + + '' + escape(key) + '' + ''; return before + token + after; }); @@ -224,132 +280,126 @@ angular.module('homepage', ['ngAnimate', 'ui.bootstrap', 'download-data']) }); tabs.push( - '\n' + + '\n' + '
' + content +'
\n' + - '
\n' + '\n' ); }); element.html( - '' + + '' + tabs.join('') + - ''); - // element.find('[rel=popover]').popover().pulse(); + ''); // Compile up the HTML to get the directives to kick-in $compile(element.children())(scope); $timeout(function() { - var annotationElements = element.find('span[popover-html-unsafe]'); + var annotationElements = element.find('span[uib-popover-html]'); $compile(annotationElements)(scope); }, 0); } }; }) - .directive('jsFiddle', function(fetchCode, escape, script) { + .directive('plnkr', function(fetchCode, formPostData, script, templateBuilder) { return { - terminal: true, - link: function(scope, element, attr) { - var name = '', - stylesheet = '\n', - fields = { - html: '', - css: '', - js: '' - }; + template: '', + scope: {}, + bindToController: { + 'files': '@plnkr', + 'module': '@?module', + 'resource': '@?', + 'route': '@?', + 'firebase': '@?', + }, + controllerAs: 'plnkr', + controller: function() { + + this.$onInit = function() { + var ctrl = this; + + var name = '', + bootstrapStylesheet = 'https://netdna.bootstrapcdn.com/twitter-bootstrap/2.0.4/css/bootstrap-combined.min.css', + plnkrFiles = []; + + angular.forEach(ctrl.files.split(' '), function(filename, index) { + var content; - angular.forEach(attr.jsFiddle.split(' '), function(file, index) { - var fileType = file.split('.')[1]; - - if (fileType == 'html') { if (index === 0) { - fields[fileType] += - '
\n' + - fetchCode(file, 2); + var head = templateBuilder.createLocalDependencies(ctrl.files); + + head.push(' \n'); + content = templateBuilder.getIndexTemplate({resource: ctrl.resource, route: ctrl.route, firebase: ctrl.firebase}); + content = content. + replace('__MODULE__', ctrl.module ? '="' + ctrl.module + '"' : ''). + replace('__HEAD__', head.join('')). + replace('__BODY__', fetchCode(filename, 4)); } else { - fields[fileType] += '\n\n\n \n' + - ' \n'; + content = fetchCode(filename); } - } else { - fields[fileType] += fetchCode(file) + '\n'; - } - }); - fields.html += '
\n'; + content += templateBuilder.getCopyright(filename); - element.html( - '
' + - hiddenField('title', 'AngularJS Example: ' + name) + - hiddenField('css', ' \n' + - stylesheet + - script.angular + - (attr.resource ? script.resource : '') + - (attr.route ? script.route : '') + - (attr.firebase ? script.firebase : '') + - '