From 9a70e8d8f7ab0833637da3fb00769f3f3bdefcc8 Mon Sep 17 00:00:00 2001
From: Piotr Sarnacki <drogus@gmail.com>
Date: Tue, 3 Feb 2015 09:53:42 +0100
Subject: [PATCH] Add the rest of ember-cli app

---
 .bowerrc                                    |   4 +
 .editorconfig                               |  33 ++++
 .ember-cli                                  |   9 +
 .gitignore                                  |  26 +--
 .jshintrc                                   |  32 ++++
 Brocfile.js                                 |  26 +++
 bower.json                                  |  20 +++
 config/environment.js                       |  69 ++++++++
 package.json                                |  39 ++++
 public/crossdomain.xml                      |  15 ++
 public/robots.txt                           |   2 +
 testem.json                                 |  11 ++
 tests/.jshintrc                             |  74 ++++++++
 tests/helpers/resolver.js                   |  11 ++
 tests/helpers/start-app.js                  |  19 ++
 tests/index.html                            |  33 ++++
 tests/test-helper.js                        |   6 +
 tests/unit/.gitkeep                         |   0
 tests/unit/initializers/auth-test.coffee    |  19 ++
 tests/unit/initializers/config-test.coffee  |  19 ++
 tests/unit/initializers/storage-test.coffee |  19 ++
 vendor/.gitkeep                             |   0
 vendor/ansiparse.js                         | 186 ++++++++++++++++++++
 23 files changed, 659 insertions(+), 13 deletions(-)
 create mode 100644 .bowerrc
 create mode 100644 .editorconfig
 create mode 100644 .ember-cli
 create mode 100644 .jshintrc
 create mode 100644 Brocfile.js
 create mode 100644 bower.json
 create mode 100644 config/environment.js
 create mode 100644 package.json
 create mode 100644 public/crossdomain.xml
 create mode 100644 public/robots.txt
 create mode 100644 testem.json
 create mode 100644 tests/.jshintrc
 create mode 100644 tests/helpers/resolver.js
 create mode 100644 tests/helpers/start-app.js
 create mode 100644 tests/index.html
 create mode 100644 tests/test-helper.js
 create mode 100644 tests/unit/.gitkeep
 create mode 100644 tests/unit/initializers/auth-test.coffee
 create mode 100644 tests/unit/initializers/config-test.coffee
 create mode 100644 tests/unit/initializers/storage-test.coffee
 create mode 100644 vendor/.gitkeep
 create mode 100644 vendor/ansiparse.js

diff --git a/.bowerrc b/.bowerrc
new file mode 100644
index 00000000..959e1696
--- /dev/null
+++ b/.bowerrc
@@ -0,0 +1,4 @@
+{
+  "directory": "bower_components",
+  "analytics": false
+}
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 00000000..2fe4874a
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,33 @@
+# EditorConfig helps developers define and maintain consistent
+# coding styles between different editors and IDEs
+# editorconfig.org
+
+root = true
+
+
+[*]
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+indent_style = space
+indent_size = 2
+
+[*.js]
+indent_style = space
+indent_size = 2
+
+[*.hbs]
+indent_style = space
+indent_size = 2
+
+[*.css]
+indent_style = space
+indent_size = 2
+
+[*.html]
+indent_style = space
+indent_size = 2
+
+[*.{diff,md}]
+trim_trailing_whitespace = false
diff --git a/.ember-cli b/.ember-cli
new file mode 100644
index 00000000..ee64cfed
--- /dev/null
+++ b/.ember-cli
@@ -0,0 +1,9 @@
+{
+  /**
+    Ember CLI sends analytics information by default. The data is completely
+    anonymous, but there are times when you might want to disable this behavior.
+
+    Setting `disableAnalytics` to true will prevent any data from being sent.
+  */
+  "disableAnalytics": false
+}
diff --git a/.gitignore b/.gitignore
index ec47c7b9..20b90e33 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,15 +1,15 @@
-/.bundle
-/config/travis.yml
+# compiled output
+/dist
 /tmp
-.sass-cache
-.localeapp/key
-/assets/scripts/config/locales.js
-.DS_Store
-*.sw[op]
 
-/public/images
-/public/scripts
-/public/styles/app.css
-/public/styles/dashboard.css
-/public/version
-vendor/bundle
+# dependencies
+/node_modules
+/bower_components
+
+# misc
+/.sass-cache
+/connect.lock
+/coverage/*
+/libpeerconnection.log
+npm-debug.log
+testem.log
diff --git a/.jshintrc b/.jshintrc
new file mode 100644
index 00000000..08096eff
--- /dev/null
+++ b/.jshintrc
@@ -0,0 +1,32 @@
+{
+  "predef": [
+    "document",
+    "window",
+    "-Promise"
+  ],
+  "browser": true,
+  "boss": true,
+  "curly": true,
+  "debug": false,
+  "devel": true,
+  "eqeqeq": true,
+  "evil": true,
+  "forin": false,
+  "immed": false,
+  "laxbreak": false,
+  "newcap": true,
+  "noarg": true,
+  "noempty": false,
+  "nonew": false,
+  "nomen": false,
+  "onevar": false,
+  "plusplus": false,
+  "regexp": false,
+  "undef": true,
+  "sub": true,
+  "strict": false,
+  "white": false,
+  "eqnull": true,
+  "esnext": true,
+  "unused": true
+}
diff --git a/Brocfile.js b/Brocfile.js
new file mode 100644
index 00000000..dad08e20
--- /dev/null
+++ b/Brocfile.js
@@ -0,0 +1,26 @@
+/* global require, module */
+
+var EmberApp = require('ember-cli/lib/broccoli/ember-app');
+
+var app = new EmberApp();
+
+app.import('bower_components/jquery-timeago/jquery.timeago.js');
+app.import('bower_components/visibilityjs/lib/visibility.core.js');
+app.import('bower_components/visibilityjs/lib/visibility.timers.js');
+app.import('bower_components/JavaScript-MD5/js/md5.js');
+app.import('vendor/ansiparse.js');
+app.import('bower_components/moment/moment.js');
+// Use `app.import` to add additional libraries to the generated
+// output files.
+//
+// If you need to use different assets in different
+// environments, specify an object as the first parameter. That
+// object's keys should be the environment name and the values
+// should be the asset to use in that environment.
+//
+// If the library that you are including contains AMD or ES6
+// modules that you would like to import into your application
+// please specify an object with the list of modules as keys
+// along with the exports of each module as its value.
+
+module.exports = app.toTree();
diff --git a/bower.json b/bower.json
new file mode 100644
index 00000000..c1b4d7d9
--- /dev/null
+++ b/bower.json
@@ -0,0 +1,20 @@
+{
+  "name": "travis",
+  "dependencies": {
+    "handlebars": "2.0.0",
+    "jquery": "^1.11.1",
+    "ember": "1.9.1",
+    "ember-data": "1.0.0-beta.12",
+    "ember-resolver": "~0.1.11",
+    "loader.js": "ember-cli/loader.js#1.0.1",
+    "ember-cli-shims": "ember-cli/ember-cli-shims#0.0.3",
+    "ember-cli-test-loader": "ember-cli/ember-cli-test-loader#0.1.0",
+    "ember-load-initializers": "ember-cli/ember-load-initializers#0.0.2",
+    "ember-qunit": "0.1.8",
+    "ember-qunit-notifications": "0.0.5",
+    "qunit": "~1.17.1",
+    "visibilityjs": "~1.2.1",
+    "JavaScript-MD5": "~1.1.0",
+    "moment": "~2.9.0"
+  }
+}
diff --git a/config/environment.js b/config/environment.js
new file mode 100644
index 00000000..dbc3c1c9
--- /dev/null
+++ b/config/environment.js
@@ -0,0 +1,69 @@
+/* jshint node: true */
+
+module.exports = function(environment) {
+  var ENV = {
+    modulePrefix: 'travis',
+    environment: environment,
+    baseURL: '/',
+    locationType: 'auto',
+    EmberENV: {
+      FEATURES: {
+        // Here you can enable experimental features on an ember canary build
+        // e.g. 'with-controller': true
+      }
+    },
+
+    APP: {
+      // Here you can pass flags/options to your application instance
+      // when it is created
+    }
+  };
+
+  if (environment === 'development') {
+    // ENV.APP.LOG_RESOLVER = true;
+    // ENV.APP.LOG_ACTIVE_GENERATION = true;
+    // ENV.APP.LOG_TRANSITIONS = true;
+    // ENV.APP.LOG_TRANSITIONS_INTERNAL = true;
+    // ENV.APP.LOG_VIEW_LOOKUPS = true;
+  }
+
+  if (environment === 'test') {
+    // Testem prefers this...
+    ENV.baseURL = '/';
+    ENV.locationType = 'none';
+
+    // keep test console output quieter
+    ENV.APP.LOG_ACTIVE_GENERATION = false;
+    ENV.APP.LOG_VIEW_LOOKUPS = false;
+
+    ENV.APP.rootElement = '#ember-testing';
+  }
+
+  if (environment === 'production') {
+
+  }
+
+  ENV.endpoints = {
+    ssh_key: false,
+    caches: false
+  };
+  ENV.pro = false;
+  ENV.pusher = {};
+  ENV.intervals = { updateTimes: 1000, times: -1 };
+  ENV.api_endpoint = 'https://api.travis-ci.org';
+
+  ENV.contentSecurityPolicy = {
+    'default-src': "'none'",
+    // TODO: for some reason unsafe-eval is needed when I use collection helper,
+    //       we should probably remove it at some point
+    'script-src': "'self' 'unsafe-eval'",
+    'font-src': "'self'",
+    'connect-src': "'self' https://api.travis-ci.org",
+    'img-src': "'self' data: https://www.gravatar.com http://www.gravatar.com",
+    'style-src': "'self'",
+    'media-src': "'self'",
+    'frame-src': "'self' https://api.travis-ci.org"
+  }
+
+  return ENV;
+};
diff --git a/package.json b/package.json
new file mode 100644
index 00000000..beecb008
--- /dev/null
+++ b/package.json
@@ -0,0 +1,39 @@
+{
+  "name": "travis",
+  "version": "0.0.0",
+  "description": "Small description for travis goes here",
+  "private": true,
+  "directories": {
+    "doc": "doc",
+    "test": "tests"
+  },
+  "scripts": {
+    "start": "ember server",
+    "build": "ember build",
+    "test": "ember test"
+  },
+  "repository": "",
+  "engines": {
+    "node": ">= 0.10.0"
+  },
+  "author": "",
+  "license": "MIT",
+  "devDependencies": {
+    "broccoli-asset-rev": "^2.0.0",
+    "ember-cli": "0.1.11",
+    "ember-cli-6to5": "^3.0.0",
+    "ember-cli-app-version": "0.3.0",
+    "ember-cli-coffeescript": "0.7.0",
+    "ember-cli-content-security-policy": "0.3.0",
+    "ember-cli-dependency-checker": "0.0.7",
+    "ember-cli-htmlbars": "^0.6.0",
+    "ember-cli-ic-ajax": "0.1.1",
+    "ember-cli-inject-live-reload": "^1.3.0",
+    "ember-cli-qunit": "0.3.0",
+    "ember-cli-uglify": "1.0.1",
+    "ember-data": "1.0.0-beta.12",
+    "ember-export-application-global": "^1.0.0",
+    "express": "^4.8.5",
+    "glob": "^4.0.5"
+  }
+}
diff --git a/public/crossdomain.xml b/public/crossdomain.xml
new file mode 100644
index 00000000..29a035d7
--- /dev/null
+++ b/public/crossdomain.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0"?>
+<!DOCTYPE cross-domain-policy SYSTEM "http://www.adobe.com/xml/dtds/cross-domain-policy.dtd">
+<cross-domain-policy>
+    <!-- Read this: www.adobe.com/devnet/articles/crossdomain_policy_file_spec.html -->
+
+    <!-- Most restrictive policy: -->
+    <site-control permitted-cross-domain-policies="none"/>
+
+    <!-- Least restrictive policy: -->
+    <!--
+    <site-control permitted-cross-domain-policies="all"/>
+    <allow-access-from domain="*" to-ports="*" secure="false"/>
+    <allow-http-request-headers-from domain="*" headers="*" secure="false"/>
+    -->
+</cross-domain-policy>
diff --git a/public/robots.txt b/public/robots.txt
new file mode 100644
index 00000000..5debfa4d
--- /dev/null
+++ b/public/robots.txt
@@ -0,0 +1,2 @@
+# http://www.robotstxt.org
+User-agent: *
diff --git a/testem.json b/testem.json
new file mode 100644
index 00000000..42a4ddb2
--- /dev/null
+++ b/testem.json
@@ -0,0 +1,11 @@
+{
+  "framework": "qunit",
+  "test_page": "tests/index.html?hidepassed",
+  "launch_in_ci": [
+    "PhantomJS"
+  ],
+  "launch_in_dev": [
+    "PhantomJS",
+    "Chrome"
+  ]
+}
diff --git a/tests/.jshintrc b/tests/.jshintrc
new file mode 100644
index 00000000..6ebf71a0
--- /dev/null
+++ b/tests/.jshintrc
@@ -0,0 +1,74 @@
+{
+  "predef": [
+    "document",
+    "window",
+    "location",
+    "setTimeout",
+    "$",
+    "-Promise",
+    "QUnit",
+    "define",
+    "console",
+    "equal",
+    "notEqual",
+    "notStrictEqual",
+    "test",
+    "asyncTest",
+    "testBoth",
+    "testWithDefault",
+    "raises",
+    "throws",
+    "deepEqual",
+    "start",
+    "stop",
+    "ok",
+    "strictEqual",
+    "module",
+    "moduleFor",
+    "moduleForComponent",
+    "moduleForModel",
+    "process",
+    "expect",
+    "visit",
+    "exists",
+    "fillIn",
+    "click",
+    "keyEvent",
+    "triggerEvent",
+    "find",
+    "findWithAssert",
+    "wait",
+    "DS",
+    "isolatedContainer",
+    "startApp",
+    "andThen",
+    "currentURL",
+    "currentPath",
+    "currentRouteName"
+  ],
+  "node": false,
+  "browser": false,
+  "boss": true,
+  "curly": false,
+  "debug": false,
+  "devel": false,
+  "eqeqeq": true,
+  "evil": true,
+  "forin": false,
+  "immed": false,
+  "laxbreak": false,
+  "newcap": true,
+  "noarg": true,
+  "noempty": false,
+  "nonew": false,
+  "nomen": false,
+  "onevar": false,
+  "plusplus": false,
+  "regexp": false,
+  "undef": true,
+  "sub": true,
+  "strict": false,
+  "white": false,
+  "eqnull": true,
+  "esnext": true
+}
diff --git a/tests/helpers/resolver.js b/tests/helpers/resolver.js
new file mode 100644
index 00000000..28f4ece4
--- /dev/null
+++ b/tests/helpers/resolver.js
@@ -0,0 +1,11 @@
+import Resolver from 'ember/resolver';
+import config from '../../config/environment';
+
+var resolver = Resolver.create();
+
+resolver.namespace = {
+  modulePrefix: config.modulePrefix,
+  podModulePrefix: config.podModulePrefix
+};
+
+export default resolver;
diff --git a/tests/helpers/start-app.js b/tests/helpers/start-app.js
new file mode 100644
index 00000000..16cc7c39
--- /dev/null
+++ b/tests/helpers/start-app.js
@@ -0,0 +1,19 @@
+import Ember from 'ember';
+import Application from '../../app';
+import Router from '../../router';
+import config from '../../config/environment';
+
+export default function startApp(attrs) {
+  var application;
+
+  var attributes = Ember.merge({}, config.APP);
+  attributes = Ember.merge(attributes, attrs); // use defaults, but you can override;
+
+  Ember.run(function() {
+    application = Application.create(attributes);
+    application.setupForTesting();
+    application.injectTestHelpers();
+  });
+
+  return application;
+}
diff --git a/tests/index.html b/tests/index.html
new file mode 100644
index 00000000..86513a02
--- /dev/null
+++ b/tests/index.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <title>Travis Tests</title>
+    <meta name="description" content="">
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+
+    {{content-for 'head'}}
+    {{content-for 'test-head'}}
+
+    <link rel="stylesheet" href="assets/vendor.css">
+    <link rel="stylesheet" href="assets/travis.css">
+    <link rel="stylesheet" href="assets/test-support.css">
+
+    {{content-for 'head-footer'}}
+    {{content-for 'test-head-footer'}}
+  </head>
+  <body>
+
+    {{content-for 'body'}}
+    {{content-for 'test-body'}}
+    <script src="assets/vendor.js"></script>
+    <script src="assets/test-support.js"></script>
+    <script src="assets/travis.js"></script>
+    <script src="testem.js"></script>
+    <script src="assets/test-loader.js"></script>
+
+    {{content-for 'body-footer'}}
+    {{content-for 'test-body-footer'}}
+  </body>
+</html>
diff --git a/tests/test-helper.js b/tests/test-helper.js
new file mode 100644
index 00000000..e6cfb70f
--- /dev/null
+++ b/tests/test-helper.js
@@ -0,0 +1,6 @@
+import resolver from './helpers/resolver';
+import {
+  setResolver
+} from 'ember-qunit';
+
+setResolver(resolver);
diff --git a/tests/unit/.gitkeep b/tests/unit/.gitkeep
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/unit/initializers/auth-test.coffee b/tests/unit/initializers/auth-test.coffee
new file mode 100644
index 00000000..f8c70675
--- /dev/null
+++ b/tests/unit/initializers/auth-test.coffee
@@ -0,0 +1,19 @@
+`import Ember from 'ember'`
+`import { initialize } from 'travis/initializers/auth'`
+
+container = null
+application = null
+
+module 'AuthInitializer',
+  setup: ->
+    Ember.run ->
+      application = Ember.Application.create()
+      container = application.__container__
+      application.deferReadiness()
+
+# Replace this with your real tests.
+test 'it works', ->
+  initialize container, application
+
+  # you would normally confirm the results of the initializer here
+  ok true
diff --git a/tests/unit/initializers/config-test.coffee b/tests/unit/initializers/config-test.coffee
new file mode 100644
index 00000000..faea5eb8
--- /dev/null
+++ b/tests/unit/initializers/config-test.coffee
@@ -0,0 +1,19 @@
+`import Ember from 'ember'`
+`import { initialize } from 'travis/initializers/config'`
+
+container = null
+application = null
+
+module 'ConfigInitializer',
+  setup: ->
+    Ember.run ->
+      application = Ember.Application.create()
+      container = application.__container__
+      application.deferReadiness()
+
+# Replace this with your real tests.
+test 'it works', ->
+  initialize container, application
+
+  # you would normally confirm the results of the initializer here
+  ok true
diff --git a/tests/unit/initializers/storage-test.coffee b/tests/unit/initializers/storage-test.coffee
new file mode 100644
index 00000000..81c9a132
--- /dev/null
+++ b/tests/unit/initializers/storage-test.coffee
@@ -0,0 +1,19 @@
+`import Ember from 'ember'`
+`import { initialize } from 'travis/initializers/storage'`
+
+container = null
+application = null
+
+module 'StorageInitializer',
+  setup: ->
+    Ember.run ->
+      application = Ember.Application.create()
+      container = application.__container__
+      application.deferReadiness()
+
+# Replace this with your real tests.
+test 'it works', ->
+  initialize container, application
+
+  # you would normally confirm the results of the initializer here
+  ok true
diff --git a/vendor/.gitkeep b/vendor/.gitkeep
new file mode 100644
index 00000000..e69de29b
diff --git a/vendor/ansiparse.js b/vendor/ansiparse.js
new file mode 100644
index 00000000..692247d7
--- /dev/null
+++ b/vendor/ansiparse.js
@@ -0,0 +1,186 @@
+ansiparse = function (str) {
+  //
+  // I'm terrible at writing parsers.
+  //
+  var matchingControl = null,
+      matchingData = null,
+      matchingText = '',
+      ansiState = [],
+      result = [],
+      state = {},
+      eraseChar;
+
+  //
+  // General workflow for this thing is:
+  // \033\[33mText
+  // |     |  |
+  // |     |  matchingText
+  // |     matchingData
+  // matchingControl
+  //
+  // In further steps we hope it's all going to be fine. It usually is.
+  //
+
+  //
+  // Erases a char from the output
+  //
+  eraseChar = function () {
+    var index, text;
+    if (matchingText.length) {
+      matchingText = matchingText.substr(0, matchingText.length - 1);
+    }
+    else if (result.length) {
+      index = result.length - 1;
+      text = result[index].text;
+      if (text.length === 1) {
+        //
+        // A result bit was fully deleted, pop it out to simplify the final output
+        //
+        result.pop();
+      }
+      else {
+        result[index].text = text.substr(0, text.length - 1);
+      }
+    }
+  };
+
+  for (var i = 0; i < str.length; i++) {
+    if (matchingControl != null) {
+      if (matchingControl == '\033' && str[i] == '\[') {
+        //
+        // We've matched full control code. Lets start matching formating data.
+        //
+
+        //
+        // "emit" matched text with correct state
+        //
+        if (matchingText) {
+          state.text = matchingText;
+          result.push(state);
+          state = {};
+          matchingText = "";
+        }
+
+        matchingControl = null;
+        matchingData = '';
+      }
+      else {
+        //
+        // We failed to match anything - most likely a bad control code. We
+        // go back to matching regular strings.
+        //
+        matchingText += matchingControl + str[i];
+        matchingControl = null;
+      }
+      continue;
+    }
+    else if (matchingData != null) {
+      if (str[i] == ';') {
+        //
+        // `;` separates many formatting codes, for example: `\033[33;43m`
+        // means that both `33` and `43` should be applied.
+        //
+        // TODO: this can be simplified by modifying state here.
+        //
+        ansiState.push(matchingData);
+        matchingData = '';
+      }
+      else if (str[i] == 'm') {
+        //
+        // `m` finished whole formatting code. We can proceed to matching
+        // formatted text.
+        //
+        ansiState.push(matchingData);
+        matchingData = null;
+        matchingText = '';
+
+        //
+        // Convert matched formatting data into user-friendly state object.
+        //
+        // TODO: DRY.
+        //
+        ansiState.forEach(function (ansiCode) {
+          if (ansiparse.foregroundColors[ansiCode]) {
+            state.foreground = ansiparse.foregroundColors[ansiCode];
+          }
+          else if (ansiparse.backgroundColors[ansiCode]) {
+            state.background = ansiparse.backgroundColors[ansiCode];
+          }
+          else if (ansiCode == 39) {
+            delete state.foreground;
+          }
+          else if (ansiCode == 49) {
+            delete state.background;
+          }
+          else if (ansiparse.styles[ansiCode]) {
+            state[ansiparse.styles[ansiCode]] = true;
+          }
+          else if (ansiCode == 22) {
+            state.bold = false;
+          }
+          else if (ansiCode == 23) {
+            state.italic = false;
+          }
+          else if (ansiCode == 24) {
+            state.underline = false;
+          }
+        });
+        ansiState = [];
+      }
+      else {
+        matchingData += str[i];
+      }
+      continue;
+    }
+
+    if (str[i] == '\033') {
+      matchingControl = str[i];
+    }
+    else if (str[i] == '\u0008') {
+      eraseChar();
+    }
+    else {
+      matchingText += str[i];
+    }
+  }
+
+  if (matchingText) {
+    state.text = matchingText + (matchingControl ? matchingControl : '');
+    result.push(state);
+  }
+  return result;
+}
+
+ansiparse.foregroundColors = {
+  '30': 'black',
+  '31': 'red',
+  '32': 'green',
+  '33': 'yellow',
+  '34': 'blue',
+  '35': 'magenta',
+  '36': 'cyan',
+  '37': 'white',
+  '90': 'grey'
+};
+
+ansiparse.backgroundColors = {
+  '40': 'black',
+  '41': 'red',
+  '42': 'green',
+  '43': 'yellow',
+  '44': 'blue',
+  '45': 'magenta',
+  '46': 'cyan',
+  '47': 'white'
+};
+
+ansiparse.styles = {
+  '1': 'bold',
+  '3': 'italic',
+  '4': 'underline'
+};
+
+if (typeof module == "object" && typeof window == "undefined") {
+  module.exports = ansiparse;
+}
+