├── .nvmrc
├── vendor
└── .gitkeep
├── tests
├── helpers
│ └── .gitkeep
├── unit
│ ├── .gitkeep
│ └── href-to-test.js
├── dummy
│ ├── app
│ │ ├── helpers
│ │ │ └── .gitkeep
│ │ ├── models
│ │ │ └── .gitkeep
│ │ ├── routes
│ │ │ └── .gitkeep
│ │ ├── components
│ │ │ ├── .gitkeep
│ │ │ ├── not-a-link.js
│ │ │ ├── a-link.js
│ │ │ ├── link-button.js
│ │ │ └── a-subclass-link.js
│ │ ├── templates
│ │ │ ├── components
│ │ │ │ ├── .gitkeep
│ │ │ │ └── component-with-a-link.hbs
│ │ │ ├── index.hbs
│ │ │ ├── pages
│ │ │ │ ├── first.hbs
│ │ │ │ └── second.hbs
│ │ │ ├── about.hbs
│ │ │ └── application.hbs
│ │ ├── styles
│ │ │ └── app.css
│ │ ├── resolver.js
│ │ ├── controllers
│ │ │ ├── application.js
│ │ │ └── about.js
│ │ ├── router.js
│ │ ├── app.js
│ │ └── index.html
│ ├── config
│ │ ├── optional-features.json
│ │ ├── targets.js
│ │ └── environment.js
│ └── public
│ │ └── robots.txt
├── .eslintrc.js
├── test-helper.js
├── integration
│ ├── component-test.js
│ └── href-to-test.js
├── index.html
└── acceptance
│ └── href-to-test.js
├── REPO_OWNER
├── .watchmanconfig
├── .bowerrc
├── app
├── helpers
│ └── href-to.js
└── instance-initializers
│ └── ember-href-to.js
├── .template-lintrc.js
├── index.js
├── config
├── environment.js
└── ember-try.js
├── .ember-cli
├── .eslintignore
├── .editorconfig
├── .gitignore
├── .npmignore
├── ember-cli-build.js
├── .github
└── workflows
│ └── ci.yml
├── testem.js
├── CONTRIBUTING.md
├── .eslintrc.js
├── addon
├── helpers
│ └── href-to.js
└── href-to.js
├── package.json
├── README.md
└── LICENSE.md
/.nvmrc:
--------------------------------------------------------------------------------
1 | 14
2 |
--------------------------------------------------------------------------------
/vendor/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/helpers/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/unit/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/REPO_OWNER:
--------------------------------------------------------------------------------
1 | team-frontend-tech
2 |
--------------------------------------------------------------------------------
/tests/dummy/app/helpers/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/dummy/app/models/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/dummy/app/routes/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/dummy/app/components/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/dummy/app/templates/components/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/dummy/app/templates/index.hbs:
--------------------------------------------------------------------------------
1 |
Index
2 |
--------------------------------------------------------------------------------
/tests/dummy/app/templates/pages/first.hbs:
--------------------------------------------------------------------------------
1 | First
2 |
--------------------------------------------------------------------------------
/tests/dummy/app/templates/pages/second.hbs:
--------------------------------------------------------------------------------
1 | Second
2 |
--------------------------------------------------------------------------------
/.watchmanconfig:
--------------------------------------------------------------------------------
1 | {
2 | "ignore_dirs": ["tmp", "dist"]
3 | }
4 |
--------------------------------------------------------------------------------
/tests/dummy/app/styles/app.css:
--------------------------------------------------------------------------------
1 | a.active { color: green; font-weight: bold; }
2 |
--------------------------------------------------------------------------------
/.bowerrc:
--------------------------------------------------------------------------------
1 | {
2 | "directory": "bower_components",
3 | "analytics": false
4 | }
5 |
--------------------------------------------------------------------------------
/tests/dummy/config/optional-features.json:
--------------------------------------------------------------------------------
1 | {
2 | "jquery-integration": true
3 | }
4 |
--------------------------------------------------------------------------------
/app/helpers/href-to.js:
--------------------------------------------------------------------------------
1 | export { default, hrefTo } from 'ember-href-to/helpers/href-to';
2 |
--------------------------------------------------------------------------------
/tests/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: {
3 | embertest: true
4 | }
5 | };
6 |
--------------------------------------------------------------------------------
/tests/dummy/public/robots.txt:
--------------------------------------------------------------------------------
1 | # http://www.robotstxt.org
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/.template-lintrc.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = {
4 | extends: 'recommended'
5 | };
6 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = {
4 | name: require('./package').name
5 | };
6 |
--------------------------------------------------------------------------------
/tests/dummy/app/resolver.js:
--------------------------------------------------------------------------------
1 | import Resolver from 'ember-resolver';
2 |
3 | export default Resolver;
4 |
--------------------------------------------------------------------------------
/tests/dummy/app/templates/components/component-with-a-link.hbs:
--------------------------------------------------------------------------------
1 | Go Home
2 |
--------------------------------------------------------------------------------
/config/environment.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = function(/* environment, appConfig */) {
4 | return { };
5 | };
6 |
--------------------------------------------------------------------------------
/tests/dummy/app/components/not-a-link.js:
--------------------------------------------------------------------------------
1 | import Component from '@ember/component';
2 |
3 | export default Component.extend({
4 | });
5 |
--------------------------------------------------------------------------------
/tests/dummy/app/components/a-link.js:
--------------------------------------------------------------------------------
1 | import { LinkComponent } from '@ember/legacy-built-in-components';
2 |
3 | export default LinkComponent.extend({
4 | });
5 |
--------------------------------------------------------------------------------
/tests/dummy/app/components/link-button.js:
--------------------------------------------------------------------------------
1 | import Component from '@ember/component';
2 |
3 | export default Component.extend({
4 |
5 | tagName: 'a',
6 |
7 | attributeBindings: ['href']
8 |
9 | });
10 |
--------------------------------------------------------------------------------
/tests/test-helper.js:
--------------------------------------------------------------------------------
1 | import Application from '../app';
2 | import config from '../config/environment';
3 | import { setApplication } from '@ember/test-helpers';
4 | import { start } from 'ember-qunit';
5 |
6 | setApplication(Application.create(config.APP));
7 |
8 | start();
9 |
--------------------------------------------------------------------------------
/tests/dummy/app/controllers/application.js:
--------------------------------------------------------------------------------
1 | import Controller from '@ember/controller';
2 |
3 | export default Controller.extend({
4 | dynamicParams: undefined,
5 |
6 | init() {
7 | this._super(...arguments);
8 | this.set('dynamicParams', ['pages.second']);
9 | },
10 | });
11 |
--------------------------------------------------------------------------------
/tests/dummy/app/components/a-subclass-link.js:
--------------------------------------------------------------------------------
1 | import ALink from './a-link';
2 |
3 | let LinkClass = ALink.extend({
4 | });
5 |
6 | LinkClass = LinkClass.extend({
7 | });
8 |
9 | LinkClass = LinkClass.extend({
10 | });
11 |
12 | LinkClass = LinkClass.extend({
13 | });
14 |
15 |
16 | export default LinkClass;
17 |
--------------------------------------------------------------------------------
/.ember-cli:
--------------------------------------------------------------------------------
1 | {
2 | /**
3 | Ember CLI sends analytics information by default. The data is completely
4 | anonymous, but there are times when you might want to disable this behavior.
5 |
6 | Setting `disableAnalytics` to true will prevent any data from being sent.
7 | */
8 | "disableAnalytics": false
9 | }
10 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | # unconventional js
2 | /blueprints/*/files/
3 | /vendor/
4 |
5 | # compiled output
6 | /dist/
7 | /tmp/
8 |
9 | # dependencies
10 | /bower_components/
11 | /node_modules/
12 |
13 | # misc
14 | /coverage/
15 | !.*
16 |
17 | # ember-try
18 | /.node_modules.ember-try/
19 | /bower.json.ember-try
20 | /package.json.ember-try
21 |
--------------------------------------------------------------------------------
/tests/dummy/app/controllers/about.js:
--------------------------------------------------------------------------------
1 | import Controller from '@ember/controller';
2 |
3 | export default Controller.extend({
4 | queryParams: ['section'],
5 | section: 'one',
6 | dynamic: 'hello',
7 | count: 0,
8 | actions: {
9 | increment: function() {
10 | this.incrementProperty('count');
11 | }
12 | }
13 | });
14 |
--------------------------------------------------------------------------------
/tests/dummy/config/targets.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const browsers = [
4 | 'last 1 Chrome versions',
5 | 'last 1 Firefox versions',
6 | 'last 1 Safari versions'
7 | ];
8 |
9 | const isCI = !!process.env.CI;
10 | const isProduction = process.env.EMBER_ENV === 'production';
11 |
12 | if (isCI || isProduction) {
13 | browsers.push('ie 11');
14 | }
15 |
16 | module.exports = {
17 | browsers
18 | };
19 |
--------------------------------------------------------------------------------
/tests/dummy/app/router.js:
--------------------------------------------------------------------------------
1 | import EmberRouter from '@ember/routing/router';
2 | import config from './config/environment';
3 |
4 | const Router = EmberRouter.extend({
5 | location: config.locationType,
6 | rootURL: config.rootURL
7 | });
8 |
9 | Router.map(function() {
10 | this.route('about');
11 | this.route('pages', function() {
12 | this.route('first');
13 | this.route('second');
14 | });
15 | });
16 |
17 | export default Router;
18 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig helps developers define and maintain consistent
2 | # coding styles between different editors and IDEs
3 | # editorconfig.org
4 |
5 | root = true
6 |
7 |
8 | [*]
9 | end_of_line = lf
10 | charset = utf-8
11 | trim_trailing_whitespace = true
12 | insert_final_newline = true
13 | indent_style = space
14 | indent_size = 2
15 |
16 | [*.hbs]
17 | insert_final_newline = false
18 |
19 | [*.{diff,md}]
20 | trim_trailing_whitespace = false
21 |
--------------------------------------------------------------------------------
/tests/dummy/app/app.js:
--------------------------------------------------------------------------------
1 | import Application from '@ember/application';
2 | import Resolver from './resolver';
3 | import loadInitializers from 'ember-load-initializers';
4 | import config from './config/environment';
5 |
6 | let App;
7 |
8 | App = Application.extend({
9 | modulePrefix: config.modulePrefix,
10 | podModulePrefix: config.podModulePrefix,
11 | Resolver
12 | });
13 |
14 | loadInitializers(App, config.modulePrefix);
15 |
16 | export default App;
17 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # compiled output
4 | /dist/
5 | /tmp/
6 |
7 | # dependencies
8 | /bower_components/
9 | /node_modules/
10 |
11 | # misc
12 | /.env*
13 | /.pnp*
14 | /.sass-cache
15 | /connect.lock
16 | /coverage/
17 | /libpeerconnection.log
18 | /npm-debug.log*
19 | /testem.log
20 | /yarn-error.log
21 |
22 | # ember-try
23 | /.node_modules.ember-try/
24 | /bower.json.ember-try
25 | /package.json.ember-try
26 |
--------------------------------------------------------------------------------
/tests/integration/component-test.js:
--------------------------------------------------------------------------------
1 | import { module, test } from 'qunit';
2 | import { setupRenderingTest } from 'ember-qunit';
3 | import hbs from 'htmlbars-inline-precompile';
4 | import { render, find } from '@ember/test-helpers';
5 |
6 | module('Integration | Component | component-with-a-link', function(hooks) {
7 | setupRenderingTest(hooks);
8 |
9 | test('should change colors', async function(assert) {
10 | await render(hbs`{{component-with-a-link}}`);
11 |
12 | assert.ok(find('a'), 'the link is rendered');
13 | });
14 | });
15 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | # compiled output
2 | /dist/
3 | /tmp/
4 |
5 | # dependencies
6 | /bower_components/
7 |
8 | # misc
9 | /.bowerrc
10 | /.editorconfig
11 | /.ember-cli
12 | /.env*
13 | /.eslintignore
14 | /.eslintrc.js
15 | /.gitignore
16 | /.template-lintrc.js
17 | /.travis.yml
18 | /.watchmanconfig
19 | /bower.json
20 | /config/ember-try.js
21 | /CONTRIBUTING.md
22 | /ember-cli-build.js
23 | /testem.js
24 | /tests/
25 | /yarn.lock
26 | .gitkeep
27 |
28 | # ember-try
29 | /.node_modules.ember-try/
30 | /bower.json.ember-try
31 | /package.json.ember-try
32 |
--------------------------------------------------------------------------------
/ember-cli-build.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const EmberAddon = require('ember-cli/lib/broccoli/ember-addon');
4 |
5 | module.exports = function(defaults) {
6 | var app = new EmberAddon(defaults, {
7 | // Add options here
8 | });
9 |
10 | /*
11 | This build file specifies the options for the dummy test app of this
12 | addon, located in `/tests/dummy`
13 | This build file does *not* influence how the addon or the app using it
14 | behave. You most likely want to be modifying `./index.js` or app's build file
15 | */
16 |
17 | const { maybeEmbroider } = require('@embroider/test-setup');
18 | return maybeEmbroider(app);
19 | };
20 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | push:
5 | branches:
6 | - "*"
7 | pull_request:
8 | branches:
9 | - "*"
10 |
11 | jobs:
12 | test:
13 | runs-on: ubuntu-latest
14 |
15 | steps:
16 | - uses: actions/checkout@v2
17 | - uses: actions/setup-node@v1
18 | with:
19 | node-version: 14.x
20 |
21 | - uses: actions/cache@v2
22 | with:
23 | path: "**/node_modules"
24 | key: ci-modules-${{ hashFiles('**/yarn.lock') }}
25 |
26 | - name: install dependencies
27 | run: yarn install --frozen-lockfile --non-interactive
28 |
29 | - name: test
30 | run: yarn test
31 |
--------------------------------------------------------------------------------
/testem.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "framework": "qunit",
3 | "test_page": "tests/index.html?hidepassed",
4 | "disable_watching": true,
5 | launch_in_ci: [
6 | 'Chrome'
7 | ],
8 | launch_in_dev: [
9 | 'Chrome'
10 | ],
11 | browser_args: {
12 | Chrome: {
13 | ci: [
14 | // --no-sandbox is needed when running Chrome inside a container
15 | process.env.CI ? '--no-sandbox' : null,
16 | '--headless',
17 | '--disable-gpu',
18 | '--disable-dev-shm-usage',
19 | '--disable-software-rasterizer',
20 | '--mute-audio',
21 | '--remote-debugging-port=0',
22 | '--window-size=1440,900'
23 | ].filter(Boolean)
24 | }
25 | }
26 | };
27 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # How To Contribute
2 |
3 | ## Installation
4 |
5 | * `git clone `
6 | * `cd my-addon`
7 | * `npm install`
8 |
9 | ## Linting
10 |
11 | * `npm run lint:hbs`
12 | * `npm run lint:js`
13 | * `npm run lint:js -- --fix`
14 |
15 | ## Running tests
16 |
17 | * `ember test` – Runs the test suite on the current Ember version
18 | * `ember test --server` – Runs the test suite in "watch mode"
19 | * `ember try:each` – Runs the test suite against multiple Ember versions
20 |
21 | ## Running the dummy application
22 |
23 | * `ember serve`
24 | * Visit the dummy application at [http://localhost:4200](http://localhost:4200).
25 |
26 | For more information on using ember-cli, visit [https://ember-cli.com/](https://ember-cli.com/).
--------------------------------------------------------------------------------
/tests/dummy/app/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Dummy
7 |
8 |
9 |
10 | {{content-for "head"}}
11 |
12 |
13 |
14 |
15 | {{content-for "head-footer"}}
16 |
17 |
18 | {{content-for "body"}}
19 |
20 |
21 |
22 |
23 | {{content-for "body-footer"}}
24 |
25 |
26 |
--------------------------------------------------------------------------------
/tests/dummy/app/templates/about.hbs:
--------------------------------------------------------------------------------
1 | About
2 |
3 | \{{link-to}}:
4 |
5 | [{{#link-to "about" (query-params section="one")}}One{{/link-to}}]
6 | [{{#link-to "about" (query-params section="two")}}Two{{/link-to}}]
7 | [{{#link-to "about" (query-params section=dynamic)}}Three{{/link-to}}]
8 |
9 |
10 | \{{href-to}}:
11 |
12 | [
One]
13 | [
Two]
14 | [
Three]
15 |
16 | dynamic QP value for Three link: {{input value=dynamic}}
17 |
18 |
19 |
20 | Section: {{section}}
21 |
22 |
23 |
24 | Action: Increment {{count}}
25 |
26 |
--------------------------------------------------------------------------------
/tests/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Dummy Tests
7 |
8 |
9 |
10 | {{content-for "head"}}
11 | {{content-for "test-head"}}
12 |
13 |
14 |
15 |
16 |
17 | {{content-for "head-footer"}}
18 | {{content-for "test-head-footer"}}
19 |
20 |
21 | {{content-for "body"}}
22 | {{content-for "test-body"}}
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | {{content-for "body-footer"}}
31 | {{content-for "test-body-footer"}}
32 |
33 |
34 |
--------------------------------------------------------------------------------
/tests/dummy/config/environment.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | module.exports = function(environment) {
4 | var ENV = {
5 | modulePrefix: "dummy",
6 | environment: environment,
7 | rootURL: "/",
8 | locationType: "auto",
9 | EmberENV: {
10 | FEATURES: {
11 | // Here you can enable experimental features on an ember canary build
12 | // e.g. 'with-controller': true
13 | }
14 | },
15 |
16 | APP: {
17 | // Here you can pass flags/options to your application instance
18 | // when it is created
19 | }
20 | };
21 |
22 | if (environment === "development") {
23 | // ENV.APP.LOG_RESOLVER = true;
24 | // ENV.APP.LOG_ACTIVE_GENERATION = true;
25 | ENV.APP.LOG_TRANSITIONS = true;
26 | // ENV.APP.LOG_TRANSITIONS_INTERNAL = true;
27 | // ENV.APP.LOG_VIEW_LOOKUPS = true;
28 | }
29 |
30 | if (environment === "test") {
31 | // Testem prefers this...
32 | ENV.locationType = "none";
33 |
34 | // keep test console output quieter
35 | ENV.APP.LOG_ACTIVE_GENERATION = false;
36 | ENV.APP.LOG_VIEW_LOOKUPS = false;
37 |
38 | ENV.APP.rootElement = "#ember-testing";
39 | ENV.APP.autoboot = false;
40 | }
41 |
42 | return ENV;
43 | };
44 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | parserOptions: {
4 | ecmaVersion: 2017,
5 | sourceType: 'module'
6 | },
7 | plugins: [
8 | 'ember'
9 | ],
10 | extends: [
11 | 'eslint:recommended',
12 | 'plugin:ember/recommended'
13 | ],
14 | env: {
15 | browser: true
16 | },
17 | rules: {
18 | },
19 | overrides: [
20 | // node files
21 | {
22 | files: [
23 | '.eslintrc.js',
24 | '.template-lintrc.js',
25 | 'ember-cli-build.js',
26 | 'index.js',
27 | 'testem.js',
28 | 'blueprints/*/index.js',
29 | 'config/**/*.js',
30 | 'tests/dummy/config/**/*.js'
31 | ],
32 | excludedFiles: [
33 | 'addon/**',
34 | 'addon-test-support/**',
35 | 'app/**',
36 | 'tests/dummy/app/**'
37 | ],
38 | parserOptions: {
39 | sourceType: 'script',
40 | ecmaVersion: 2015
41 | },
42 | env: {
43 | browser: false,
44 | node: true
45 | },
46 | plugins: ['node'],
47 | rules: Object.assign({}, require('eslint-plugin-node').configs.recommended.rules, {
48 | // add your custom rules and overrides for node files here
49 | })
50 | }
51 | ]
52 | };
53 |
--------------------------------------------------------------------------------
/app/instance-initializers/ember-href-to.js:
--------------------------------------------------------------------------------
1 | import HrefTo from 'ember-href-to/href-to';
2 |
3 | function closestLink(el) {
4 | if (el.closest) {
5 | return el.closest('a');
6 | } else {
7 | el = el.parentElement;
8 | while (el && el.tagName !== 'A') {
9 | el = el.parentElement;
10 | }
11 | return el;
12 | }
13 | }
14 | export default {
15 | name: 'ember-href-to',
16 | initialize(applicationInstance) {
17 | // we only want this to run in the browser, not in fastboot
18 | if (typeof(FastBoot) === "undefined") {
19 | let hrefToClickHandler = function _hrefToClickHandler(e) {
20 | let link = e.target.tagName === 'A' ? e.target : closestLink(e.target);
21 | if (link) {
22 | let hrefTo = new HrefTo(applicationInstance, e, link);
23 | hrefTo.maybeHandle();
24 | }
25 | };
26 |
27 | document.body.addEventListener('click', hrefToClickHandler);
28 |
29 | // Teardown on app destruction: clean up the event listener to avoid
30 | // memory leaks.
31 | applicationInstance.reopen({
32 | willDestroy() {
33 | document.body.removeEventListener('click', hrefToClickHandler);
34 | return this._super(...arguments);
35 | }
36 | });
37 | }
38 | }
39 | };
40 |
--------------------------------------------------------------------------------
/tests/dummy/app/templates/application.hbs:
--------------------------------------------------------------------------------
1 | \{{href-to}}
2 | A lightweight alternative to \{{link-to}}
3 |
4 | \{{link-to}}:
5 |
6 | [{{#link-to "index"}}Index{{/link-to}}]
7 | [{{#link-to "about"}}About{{/link-to}}]
8 | [{{#link-to "pages.first"}}First Page{{/link-to}}]
9 | [{{#link-to "pages.second"}}Second Page{{/link-to}}]
10 | [{{#link-to params=dynamicParams}}Second Page (with dynamic params){{/link-to}}]
11 |
12 |
13 |
14 | \{{href-to}}:
15 |
29 |
30 |
31 | {{outlet}}
32 |
--------------------------------------------------------------------------------
/addon/helpers/href-to.js:
--------------------------------------------------------------------------------
1 | import Helper from "@ember/component/helper";
2 | import { getOwner } from "@ember/application";
3 |
4 | export function hrefTo(routing, params) {
5 | return routing.generateURL(...getParamsForGenerateURL(params));
6 | }
7 |
8 | function getParamsForGenerateURL(params) {
9 | params = params.slice(); // create a copy
10 | let targetRouteName = params.shift(); // the first param is always the target route name
11 | let lastParam = params[params.length - 1]; // the last param might be queryParams
12 | let queryParams;
13 | if (lastParam && lastParam.isQueryParams) {
14 | queryParams = params.pop().values;
15 | } else {
16 | queryParams = {};
17 | }
18 | let models = params; // the remainder are the models
19 | return [targetRouteName, models, queryParams];
20 | }
21 |
22 | export default class HrefToHelper extends Helper {
23 | get routing() {
24 | return getOwner(this).lookup("service:-routing");
25 | }
26 |
27 | get router() {
28 | return getOwner(this).lookup("service:router");
29 | }
30 |
31 | init() {
32 | super.init();
33 |
34 | if (this.router && this.router.on) { // skip if the router service is mocked
35 | this.router.on("routeDidChange", this.recompute.bind(this));
36 | }
37 | }
38 |
39 | compute(params, namedArgs) {
40 | if (namedArgs.params) {
41 | return hrefTo(this.routing, namedArgs.params);
42 | } else {
43 | return hrefTo(this.routing, params);
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/config/ember-try.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const getChannelURL = require('ember-source-channel-url');
4 | const { embroiderSafe, embroiderOptimized } = require('@embroider/test-setup');
5 |
6 | module.exports = function() {
7 | return Promise.all([
8 | getChannelURL('release'),
9 | getChannelURL('beta'),
10 | getChannelURL('canary')
11 | ]).then((urls) => {
12 | return {
13 | useYarn: true,
14 | scenarios: [
15 | {
16 | name: 'ember-3.20',
17 | npm: {
18 | devDependencies: {
19 | 'ember-source': '~3.20.3'
20 | }
21 | }
22 | },
23 | {
24 | name: 'ember-release',
25 | npm: {
26 | devDependencies: {
27 | 'ember-source': urls[0]
28 | }
29 | }
30 | },
31 | {
32 | name: 'ember-beta',
33 | npm: {
34 | devDependencies: {
35 | 'ember-source': urls[1]
36 | }
37 | }
38 | },
39 | {
40 | name: 'ember-canary',
41 | npm: {
42 | devDependencies: {
43 | 'ember-source': urls[2]
44 | }
45 | }
46 | },
47 | embroiderSafe(),
48 | embroiderOptimized(),
49 | // The default `.travis.yml` runs this scenario via `npm test`,
50 | // not via `ember try`. It's still included here so that running
51 | // `ember try:each` manually or from a customized CI config will run it
52 | // along with all the other scenarios.
53 | {
54 | name: 'ember-default',
55 | npm: {
56 | devDependencies: {}
57 | }
58 | }
59 | ]
60 | };
61 | });
62 | };
63 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ember-href-to",
3 | "version": "5.0.6",
4 | "description": "A lightweight alternative to `{{link-to}}`",
5 | "scripts": {
6 | "lint:hbs": "ember-template-lint .",
7 | "lint:js": "eslint .",
8 | "start": "ember serve",
9 | "test": "ember test",
10 | "build": "ember build",
11 | "test:all": "ember try:each"
12 | },
13 | "engines": {
14 | "node": "12.* || 14.* || >= 16"
15 | },
16 | "devDependencies": {
17 | "@ember/jquery": "^1.1.0",
18 | "@ember/legacy-built-in-components": "^0.4.0",
19 | "@ember/optional-features": "^1.3.0",
20 | "@embroider/test-setup": "0.33.0",
21 | "broccoli-asset-rev": "^3.0.0",
22 | "ember-cli": "~3.20.0",
23 | "ember-cli-dependency-checker": "^3.2.0",
24 | "ember-cli-eslint": "5.1.0",
25 | "ember-cli-htmlbars": "^4.2.2",
26 | "ember-cli-inject-live-reload": "^2.0.2",
27 | "ember-cli-release": "^0.2.9",
28 | "ember-cli-sri": "^2.1.1",
29 | "ember-cli-uglify": "^3.0.0",
30 | "ember-disable-prototype-extensions": "^1.1.3",
31 | "ember-export-application-global": "^2.0.0",
32 | "ember-load-initializers": "^2.1.1",
33 | "ember-maybe-import-regenerator": "^0.1.6",
34 | "ember-qunit": "^4.6.0",
35 | "ember-resolver": "^7.0.0",
36 | "ember-source": "~3.28.5",
37 | "ember-source-channel-url": "^3.0.0",
38 | "ember-template-lint": "~2.4.1",
39 | "ember-try": "^1.4.0",
40 | "eslint-plugin-ember": "^7.7.2",
41 | "eslint-plugin-node": "^11.0.0",
42 | "loader.js": "^4.7.0",
43 | "qunit-dom": "^1.4.0"
44 | },
45 | "keywords": [
46 | "ember-addon"
47 | ],
48 | "repository": "https://github.com/intercom/ember-href-to",
49 | "license": "Apache-2.0",
50 | "author": "Intercom",
51 | "directories": {
52 | "doc": "doc",
53 | "test": "tests"
54 | },
55 | "dependencies": {
56 | "ember-cli-babel": "^7.13.2"
57 | },
58 | "ember-addon": {
59 | "configPath": "tests/dummy/config"
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/tests/integration/href-to-test.js:
--------------------------------------------------------------------------------
1 | import { module, test } from 'qunit';
2 | import { setupRenderingTest } from 'ember-qunit';
3 | import hbs from 'htmlbars-inline-precompile';
4 | import { render, find } from '@ember/test-helpers';
5 | import HrefTo from 'ember-href-to/href-to';
6 |
7 | function leftClickEvent() {
8 | return { which: 1, ctrlKey: false, metaKey: false };
9 | }
10 |
11 | module('Integration | HrefTo', function(hooks) {
12 | setupRenderingTest(hooks);
13 |
14 | hooks.beforeEach(function() {
15 | this.owner.lookup('router:main').setupRouter();
16 | })
17 |
18 |
19 | test(`#isNotLinkComponent should be true if the event target is not an instance of Em.LinkComponent`, async function(assert) {
20 | await render(hbs`{{not-a-link class='not-a-link'}}`);
21 |
22 | let event = leftClickEvent();
23 | event.target = find('.not-a-link');
24 |
25 | let hrefTo = new HrefTo(this.owner, event);
26 | assert.ok(hrefTo.isNotLinkComponent());
27 | });
28 |
29 | test(`#isNotLinkComponent should be false if the event target is a {{link-to}}`, async function(assert) {
30 | await render(hbs`{{link-to 'about' 'about' class='a-link'}}`);
31 |
32 | let event = leftClickEvent();
33 | event.target = find('.a-link');
34 |
35 | let hrefTo = new HrefTo(this.owner, event);
36 | assert.notOk(hrefTo.isNotLinkComponent());
37 | });
38 |
39 | test(`#isNotLinkComponent should be false if the event target is an instance of Em.LinkComponent`, async function(assert) {
40 | await render(hbs`{{a-link 'about' 'about' class='a-link'}}`);
41 |
42 | let event = leftClickEvent();
43 | event.target = find('.a-link');
44 |
45 | let hrefTo = new HrefTo(this.owner, event);
46 | assert.notOk(hrefTo.isNotLinkComponent());
47 | });
48 |
49 | test(`#isNotLinkComponent should be false if the event target is an instance of Em.LinkComponent (subclass)`, async function(assert) {
50 | await render(hbs`{{a-subclass-link 'about' 'about' class='a-link'}}`);
51 |
52 | let event = leftClickEvent();
53 | event.target = find('.a-link');
54 |
55 | let hrefTo = new HrefTo(this.owner, event);
56 | assert.notOk(hrefTo.isNotLinkComponent());
57 | });
58 | });
59 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ember-href-to
2 |
3 | A lightweight alternative to `{{link-to}}`. No components, no class bindings - just a bound anchor href and a click handler.
4 |
5 | [](https://github.com/intercom/ember-href-to/actions) [](http://emberobserver.com/addons/ember-href-to)
6 |
7 | ## Why use it?
8 |
9 | Every time you use a `{{link-to}}`, you create a component. This is usually fine, but in cases where you're creating many of these, performance can suffer. `{{href-to}}` simply creates a URL and is [12x faster](https://github.com/GavinJoyce/ember-performance/pull/1) than `{{link-to}}` in Ember 1.13.4.
10 |
11 | Questions? Ping me [@gavinjoyce](https://twitter.com/gavinjoyce)
12 |
13 | ## Installation
14 |
15 | This is an Ember CLI addon, to install:
16 |
17 | `ember install ember-href-to`
18 |
19 | ## Supported Ember Versions
20 |
21 | - v5.0.1. supports Ember versions >=3.1 & <3.27
22 | - It does not work in apps using Ember's modernized `LinkTo`, which was introduced in Ember 3.27 and built using Glimmer components. See [this comment](https://github.com/intercom/ember-href-to/pull/152#issuecomment-1882813145) for more info.
23 |
24 | ## Usage Instructions
25 |
26 | `{{href-to}}` has the same interface as [`{{link-to}}`](https://guides.emberjs.com/v2.16.0/templates/links/), you can use it to link to static and dynamic routes in your ember application:
27 |
28 | ```html
29 | Go Home
30 | View Contact 1
31 | View Contact 2
32 | You can also use query params
35 | You can also use fragment identifiers
38 |
39 | If you have a catchall route (this.route('catchall', { path: "/*" })), you
40 | need to add the attribute "data-href-to-ignore", otherwise you will always
41 | match it
42 |
43 | ```
44 |
45 | As `{{href-to}}` simply generates a URL, you won't get automatic `active` class bindings as you do with `{{link-to}}`. Clicking on a `{{href-to}}` URL will trigger a full router transition though:
46 |
47 | 
48 |
49 | ## Development Instructions
50 |
51 | - `git clone` this repository
52 | - `npm install`
53 | - `bower install`
54 |
55 | ### Running
56 |
57 | - `ember server`
58 | - Visit your app at http://localhost:4200.
59 |
--------------------------------------------------------------------------------
/addon/href-to.js:
--------------------------------------------------------------------------------
1 | export default class {
2 |
3 | constructor(applicationInstance, event, target = event.target) {
4 | this.applicationInstance = applicationInstance;
5 | this.event = event;
6 | this.target = target;
7 | let hrefAttr = this.target.attributes.href;
8 | this.url = hrefAttr && hrefAttr.value;
9 | }
10 |
11 | maybeHandle() {
12 | if (this.shouldHandle()) {
13 | this.handle();
14 | }
15 | }
16 |
17 | shouldHandle() {
18 | return this.isUnmodifiedLeftClick() &&
19 | this.isNotIgnored() &&
20 | this.hasNoTargetBlank() &&
21 | this.hasNoActionHelper() &&
22 | this.hasNoDownload() &&
23 | this.isNotLinkComponent() &&
24 | this.recognizeUrl();
25 | }
26 |
27 | handle() {
28 | let router = this._getRouter();
29 | router.transitionTo(this.getUrlWithoutRoot());
30 | this.event.preventDefault();
31 | }
32 |
33 | isUnmodifiedLeftClick() {
34 | let e = this.event;
35 |
36 | return (e.which === undefined || e.which === 1) && !e.ctrlKey && !e.metaKey;
37 | }
38 |
39 | hasNoTargetBlank() {
40 | let attr = this.target.attributes.target;
41 | return !attr || attr.value !== '_blank';
42 | }
43 |
44 | isNotIgnored() {
45 | return !this.target.attributes['data-href-to-ignore'];
46 | }
47 |
48 | hasNoActionHelper() {
49 | return !this.target.attributes['data-ember-action'];
50 | }
51 |
52 | hasNoDownload() {
53 | return !this.target.attributes.download;
54 | }
55 |
56 | isNotLinkComponent() {
57 | let isLinkComponent = false;
58 | let id = this.target.id;
59 | if (id) {
60 | let componentInstance = this.applicationInstance.lookup('-view-registry:main')[id];
61 | isLinkComponent = componentInstance && [componentInstance.constructor.toString(), componentInstance.constructor.superclass.toString()].includes('@ember/routing/link-component');
62 | }
63 |
64 | return !isLinkComponent;
65 | }
66 |
67 | recognizeUrl() {
68 | let url = this.url;
69 | let didRecognize = false;
70 |
71 | if (url) {
72 | let router = this._getRouter();
73 | let rootUrl = this._getRootUrl();
74 | let isInternal = url.indexOf(rootUrl) === 0;
75 | let urlWithoutRoot = this.getUrlWithoutRoot();
76 | let routerMicrolib = router._router._routerMicrolib || router._router.router;
77 |
78 | didRecognize = isInternal && routerMicrolib.recognizer.recognize(urlWithoutRoot);
79 | }
80 |
81 | return didRecognize;
82 | }
83 |
84 | getUrlWithoutRoot() {
85 | let url = this.url;
86 | let rootUrl = this._getRootUrl();
87 | return url.substr(rootUrl.length - 1);
88 | }
89 |
90 | _getRouter() {
91 | return this.applicationInstance.lookup('service:router');
92 | }
93 |
94 | _getRootUrl() {
95 | let router = this._getRouter();
96 | let rootURL = router.get('rootURL');
97 |
98 | if (rootURL.charAt(rootURL.length - 1) !== '/') {
99 | rootURL = rootURL + '/';
100 | }
101 |
102 | return rootURL;
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/tests/unit/href-to-test.js:
--------------------------------------------------------------------------------
1 | import { module, test } from 'qunit';
2 | import HrefTo from 'ember-href-to/href-to';
3 | import { hrefTo } from 'ember-href-to/helpers/href-to';
4 | import $ from 'jquery';
5 |
6 | module('Unit | HrefTo');
7 |
8 | const viewRegistry = {};
9 |
10 | function createHrefToForEvent(event) {
11 | let mockApplicationInstance = {
12 | lookup() {
13 | return viewRegistry;
14 | }
15 | };
16 |
17 | return new HrefTo(mockApplicationInstance, event);
18 | }
19 |
20 | function leftClickEvent() {
21 | return { which: 1, ctrlKey: false, metaKey: false };
22 | }
23 |
24 | function getClickEventOnEl(string) {
25 | let el = $(string)[0];
26 | let event = leftClickEvent();
27 | event.target = el;
28 |
29 | return event;
30 | }
31 |
32 | test('#isUnmodifiedLeftClick should be true for left clicks', function(assert) {
33 | let event = { which: 1, ctrlKey: false, metaKey: false, target: { attributes: {} } };
34 | let hrefTo = createHrefToForEvent(event);
35 |
36 | assert.ok(hrefTo.isUnmodifiedLeftClick());
37 | });
38 |
39 | test('#isUnmodifiedLeftClick should be false for right clicks', function(assert) {
40 | let event = { which: 2, ctrlKey: false, metaKey: false, target: { attributes: {} } };
41 | let hrefTo = createHrefToForEvent(event);
42 |
43 | assert.notOk(hrefTo.isUnmodifiedLeftClick());
44 | });
45 |
46 | test('#isNotIgnored should be false if [data-href-to-ignore] is present', function(assert) {
47 | let event = getClickEventOnEl("");
48 | let hrefTo = createHrefToForEvent(event);
49 |
50 | assert.notOk(hrefTo.isNotIgnored());
51 | });
52 |
53 | test('#isNotIgnored should be true if [data-href-to-ignore] is not present', function(assert) {
54 | let event = getClickEventOnEl("");
55 | let hrefTo = createHrefToForEvent(event);
56 |
57 | assert.ok(hrefTo.isNotIgnored());
58 | });
59 |
60 | test('#hasNoActionHelper should be false if [data-ember-action] is present', function(assert) {
61 | let event = getClickEventOnEl("");
62 | let hrefTo = createHrefToForEvent(event);
63 |
64 | assert.notOk(hrefTo.hasNoActionHelper());
65 | });
66 |
67 | test('#hasNoActionHelper should be true if [data-ember-action] is not present', function(assert) {
68 | let event = getClickEventOnEl("");
69 | let hrefTo = createHrefToForEvent(event);
70 |
71 | assert.ok(hrefTo.hasNoActionHelper());
72 | });
73 |
74 | test('#hasNoDownload should be false if [download] is present', function(assert) {
75 | let event = getClickEventOnEl("");
76 | let hrefTo = createHrefToForEvent(event);
77 |
78 | assert.notOk(hrefTo.hasNoDownload());
79 | });
80 |
81 | test('#hasNoDownload should be true if [download] is not present', function(assert) {
82 | let event = getClickEventOnEl("");
83 | let hrefTo = createHrefToForEvent(event);
84 |
85 | assert.ok(hrefTo.hasNoDownload());
86 | });
87 |
88 | test('#hasNoTargetBlank should be false if [target="_blank"] is present', function(assert) {
89 | let event = getClickEventOnEl("");
90 | let hrefTo = createHrefToForEvent(event);
91 |
92 | assert.notOk(hrefTo.hasNoTargetBlank());
93 | });
94 |
95 | test('#hasNoTargetBlank should be true if [target="_blank"] is not present', function(assert) {
96 | let event = getClickEventOnEl("");
97 | let hrefTo = createHrefToForEvent(event);
98 |
99 | assert.ok(hrefTo.hasNoTargetBlank());
100 | });
101 |
102 | test('#getUrlWithoutRoot should remove the rootUrl', function(assert) {
103 | let event = getClickEventOnEl("");
104 | let hrefTo = createHrefToForEvent(event);
105 |
106 | hrefTo._getRootUrl = () => '/a/';
107 | assert.equal(hrefTo.getUrlWithoutRoot(), '/inbox', 'the url shouldn\'t include the rootUrl');
108 |
109 | hrefTo._getRootUrl = () => '/';
110 | assert.equal(hrefTo.getUrlWithoutRoot(), '/a/inbox', 'the url shouldn\'t include the rootUrl');
111 | });
112 |
113 | test('hrefTo helper can be exported', function(assert) {
114 | assert.ok(hrefTo);
115 | })
116 |
--------------------------------------------------------------------------------
/tests/acceptance/href-to-test.js:
--------------------------------------------------------------------------------
1 | import $ from 'jquery';
2 | import { click, currentURL, visit, settled } from '@ember/test-helpers';
3 | import { setupApplicationTest } from 'ember-qunit';
4 | import { run } from '@ember/runloop';
5 |
6 | import { module, test } from 'qunit';
7 |
8 | function assertAnchorIsActive(selector, assert) {
9 | assert.ok($(selector).hasClass('active'));
10 | }
11 |
12 | module('Acceptance | href to', function(hooks) {
13 | setupApplicationTest(hooks);
14 |
15 | test('href renders and updates based on router.currentState', async function (assert) {
16 | await visit('/about');
17 |
18 | assert.equal($('#link-to-links a:contains(About)').attr('href'), '/about')
19 | assert.equal($('#href-to-links a:contains(About)').attr('href'), '/about')
20 |
21 | const aboutController = this.owner.lookup('controller:about');
22 |
23 | run(function() {
24 | aboutController.set('section', 'two');
25 | });
26 |
27 | await settled();
28 | assert.equal($('#link-to-links a:contains(About)').attr('href'), '/about?section=two')
29 | assert.equal($('#href-to-links a:contains(About)').attr('href'), '/about?section=two')
30 | });
31 |
32 | test('clicking a simple link-to', async function(assert) {
33 | await visit('/');
34 | await click($('#link-to-links a:contains(About)')[0]);
35 | assert.equal(currentURL(), '/about');
36 | assertAnchorIsActive('#link-to-links a:contains(About)', assert);
37 | });
38 |
39 | test('clicking a simple href-to', async function(assert) {
40 | await visit('/');
41 | await click($('#href-to-links a:contains(About)')[0]);
42 | assert.equal(currentURL(), '/about');
43 | assertAnchorIsActive('#link-to-links a:contains(About)', assert);
44 | });
45 |
46 | test('clicking a href-to with a params argument', async function(assert) {
47 | await visit('/');
48 | await click($('#href-to-links a:contains(Second Page (with dynamic params))')[0]);
49 | assert.equal(currentURL(), '/pages/second');
50 | });
51 |
52 | test('clicking a href-to with an inner element', async function(assert) {
53 | await visit('/');
54 | await click('#inner-span');
55 | assert.equal(currentURL(), '/pages/second');
56 | });
57 |
58 | test('it doesn\'t affect clicking on elements outside links', async function(assert) {
59 | await visit('/');
60 | await click('#href-to-links');
61 | assert.equal(currentURL(), '/');
62 | });
63 |
64 | test('clicking an anchor which has no href', async function(assert) {
65 | await visit('/');
66 | await click($('#href-to-links a:contains(An anchor with no href)')[0]);
67 | assert.equal(currentURL(), '/');
68 | });
69 |
70 | test('clicking a href-to to a nested route', async function(assert) {
71 | await visit('/');
72 | await click($('#href-to-links a:contains(Second Page)')[0]);
73 | assert.equal(currentURL(), '/pages/second');
74 | assertAnchorIsActive('#link-to-links a:contains(Second Page)', assert);
75 | });
76 |
77 | test('clicking a href-to with query params', async function(assert) {
78 | await visit('/');
79 | await click($('#href-to-links a:contains(About)')[0]);
80 | await click($('#about-href-to-links a:contains(Two)')[0]);
81 | assert.equal(currentURL(), '/about?section=two');
82 | assertAnchorIsActive('#link-to-links a:contains(About)', assert);
83 | assertAnchorIsActive('#about-link-to-links a:contains(Two)', assert);
84 | });
85 |
86 | test('clicking an action works', async function(assert) {
87 | await visit('/about');
88 | await click($('a:contains(Increment)')[0]);
89 | assert.equal($('#count').text(), '1');
90 | });
91 |
92 | test('clicking a href-to to should propagate events and prevent default ', async function(assert) {
93 | await visit('/');
94 |
95 | let event = document.createEvent('MouseEvents');
96 | event.initMouseEvent('click', true, true, window);
97 | let element = $('#href-to-links a:contains(About)')[0];
98 | let ancestor = document.querySelector('#href-to-links');
99 | ancestor.addEventListener('click', function(e) {
100 | assert.equal(event, e, 'should not stop propagation');
101 | });
102 | element.dispatchEvent(event);
103 | assert.equal(event.defaultPrevented, true, 'should prevent default');
104 | });
105 |
106 | test('clicking an ember component with href-to should work', async function(assert) {
107 | await visit('/');
108 | await click($('#href-to-links a:contains(A component with a bound href)')[0]);
109 |
110 | assert.equal(currentURL(), '/about');
111 | });
112 |
113 | test('[BUGFIX] it works with the `click` acceptance helper', async function(assert) {
114 | await visit('/');
115 | await click($('#href-to-links a:contains(About)')[0]);
116 | assert.equal(currentURL(), '/about');
117 | assertAnchorIsActive('#link-to-links a:contains(About)', assert);
118 | });
119 | });
120 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 |
179 | Copyright 2015 Intercom Inc
180 |
181 | Licensed under the Apache License, Version 2.0 (the "License");
182 | you may not use this file except in compliance with the License.
183 | You may obtain a copy of the License at
184 |
185 | http://www.apache.org/licenses/LICENSE-2.0
186 |
187 | Unless required by applicable law or agreed to in writing, software
188 | distributed under the License is distributed on an "AS IS" BASIS,
189 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
190 | See the License for the specific language governing permissions and
191 | limitations under the License.
192 |
--------------------------------------------------------------------------------