\n");
\ No newline at end of file
diff --git a/public/javascripts/app/templates/builds/list.hbs b/public/javascripts/app/templates/builds/list.hbs
new file mode 100644
index 00000000..eed2322d
--- /dev/null
+++ b/public/javascripts/app/templates/builds/list.hbs
@@ -0,0 +1 @@
+Ember.TEMPLATES['list']=Ember.Handlebars.compile("
\n");
\ No newline at end of file
diff --git a/public/javascripts/app/templates/builds/show.hbs b/public/javascripts/app/templates/builds/show.hbs
new file mode 100644
index 00000000..45a46890
--- /dev/null
+++ b/public/javascripts/app/templates/builds/show.hbs
@@ -0,0 +1 @@
+Ember.TEMPLATES['show']=Ember.Handlebars.compile("
\n");
\ No newline at end of file
diff --git a/public/javascripts/app/templates/jobs/list.hbs b/public/javascripts/app/templates/jobs/list.hbs
new file mode 100644
index 00000000..f452aaa5
--- /dev/null
+++ b/public/javascripts/app/templates/jobs/list.hbs
@@ -0,0 +1 @@
+Ember.TEMPLATES['list']=Ember.Handlebars.compile("
\n Allowed Failures are items in your build matrix that are allowed to\n fail without causing the entire build to be shown as failed. This lets you add\n in experimental and preparatory builds to test against versions or\n configurations that you are not ready to officially support.\n
\n
\n You can define allowed failures in the build matrix as follows:\n
\n
\n matrix:\n allow_failures:\n - rvm: ruby-head\n
\n
\n
\n{{/if}}\n\n");
\ No newline at end of file
diff --git a/public/javascripts/app/templates/jobs/log.hbs b/public/javascripts/app/templates/jobs/log.hbs
new file mode 100644
index 00000000..ea109365
--- /dev/null
+++ b/public/javascripts/app/templates/jobs/log.hbs
@@ -0,0 +1 @@
+Ember.TEMPLATES['log']=Ember.Handlebars.compile("{{! ugh ... }}\n{{#with jobs.firstObject}}\n
\n {{/if}}\n{{/with}}\n");
\ No newline at end of file
diff --git a/public/javascripts/app/templates/jobs/show.hbs b/public/javascripts/app/templates/jobs/show.hbs
new file mode 100644
index 00000000..5b16616d
--- /dev/null
+++ b/public/javascripts/app/templates/jobs/show.hbs
@@ -0,0 +1 @@
+Ember.TEMPLATES['show']=Ember.Handlebars.compile("
\n");
\ No newline at end of file
diff --git a/public/javascripts/app/templates/repositories/tabs.hbs b/public/javascripts/app/templates/repositories/tabs.hbs
new file mode 100644
index 00000000..3f37aa02
--- /dev/null
+++ b/public/javascripts/app/templates/repositories/tabs.hbs
@@ -0,0 +1 @@
+Ember.TEMPLATES['tabs']=Ember.Handlebars.compile("
([\\s\\S]*?)<\\/div>/g, '$1\\n');\n },\n location: function() {\n return window.location.hash;\n }\n };\n\n}).call(this);\n\n})();\n//@ sourceURL=log");minispade.register('model', "(function() {(function() {\n\n this.Travis.Model = DS.Model.extend({\n primaryKey: 'id',\n id: DS.attr('number'),\n refresh: function() {\n var id;\n id = this.get('id');\n if (id) {\n return Travis.app.store.adapter.find(Travis.app.store, this.constructor, id);\n }\n },\n update: function(attrs) {\n var _this = this;\n $.each(attrs, function(key, value) {\n if (key !== 'id') {\n return _this.set(key, value);\n }\n });\n return this;\n }\n });\n\n this.Travis.Model.reopenClass({\n load: function(attrs) {\n return Travis.app.store.load(this, attrs);\n }\n });\n\n}).call(this);\n\n})();\n//@ sourceURL=model");minispade.register('ticker', "(function() {(function() {\n\n this.Travis.Ticker = Ember.Object.extend({\n init: function() {\n this._super();\n return this.schedule();\n },\n tick: function() {\n var context,\n _this = this;\n context = this.get('context');\n this.get('targets').forEach(function(target) {\n target = context.get(target);\n if (!target) {\n return;\n }\n if (target.forEach) {\n return target.forEach(function(target) {\n return target.tick();\n });\n } else {\n return target.tick();\n }\n });\n return this.schedule();\n },\n schedule: function() {\n var _this = this;\n return Ember.run.later((function() {\n return _this.tick();\n }), Travis.app.TICK_INTERVAL);\n }\n });\n\n}).call(this);\n\n})();\n//@ sourceURL=ticker");minispade.register('foo', "(function() {window.Util = window.Util || {}\n\nwindow.Util.counter = (function () {\n var value = 0;\n\n return {\n getValue: function () {\n return value;\n },\n increment: function (i) {\n if (!i) i = 1;\n return value += i;\n },\n decrement: function (i) {\n if (!i) i = 1;\n return value -= i;\n },\n reset: function (i) {\n if (!i) i = 0;\n return value = i;\n }\n }\n}());\n\n})();\n//@ sourceURL=foo");minispade.register('i18n', "(function() {var I18n = I18n || {};\nI18n.translations = {\"ca\":{\"locales\":{\"en\":\"English\",\"es\":\"Español\",\"fr\":\"Français\",\"ja\":\"日本語\",\"nb\":\"Norsk Bokmål\",\"nl\":\"Nederlands\",\"pl\":\"Polski\",\"pt-BR\":\"português brasileiro\",\"ru\":\"Русский\"}},\"en\":{\"errors\":{\"messages\":{\"not_found\":\"not found\",\"already_confirmed\":\"was already confirmed\",\"not_locked\":\"was not locked\"}},\"devise\":{\"failure\":{\"unauthenticated\":\"You need to sign in or sign up before continuing.\",\"unconfirmed\":\"You have to confirm your account before continuing.\",\"locked\":\"Your account is locked.\",\"invalid\":\"Invalid email or password.\",\"invalid_token\":\"Invalid authentication token.\",\"timeout\":\"Your session expired, please sign in again to continue.\",\"inactive\":\"Your account was not activated yet.\"},\"sessions\":{\"signed_in\":\"Signed in successfully.\",\"signed_out\":\"Signed out successfully.\"},\"passwords\":{\"send_instructions\":\"You will receive an email with instructions about how to reset your password in a few minutes.\",\"updated\":\"Your password was changed successfully. You are now signed in.\"},\"confirmations\":{\"send_instructions\":\"You will receive an email with instructions about how to confirm your account in a few minutes.\",\"confirmed\":\"Your account was successfully confirmed. You are now signed in.\"},\"registrations\":{\"signed_up\":\"You have signed up successfully. If enabled, a confirmation was sent to your e-mail.\",\"updated\":\"You updated your account successfully.\",\"destroyed\":\"Bye! Your account was successfully cancelled. We hope to see you again soon.\"},\"unlocks\":{\"send_instructions\":\"You will receive an email with instructions about how to unlock your account in a few minutes.\",\"unlocked\":\"Your account was successfully unlocked. You are now signed in.\"},\"mailer\":{\"confirmation_instructions\":{\"subject\":\"Confirmation instructions\"},\"reset_password_instructions\":{\"subject\":\"Reset password instructions\"},\"unlock_instructions\":{\"subject\":\"Unlock Instructions\"}}},\"datetime\":{\"distance_in_words\":{\"hours_exact\":{\"one\":\"%{count} hour\",\"other\":\"%{count} hours\"},\"minutes_exact\":{\"one\":\"%{count} minute\",\"other\":\"%{count} minutes\"},\"seconds_exact\":{\"one\":\"%{count} second\",\"other\":\"%{count} seconds\"}}},\"workers\":\"Workers\",\"queue\":\"Queue\",\"no_job\":\"There are no jobs\",\"repositories\":{\"branch\":\"Branch\",\"image_url\":\"Image URL\",\"markdown\":\"Markdown\",\"textile\":\"Textile\",\"rdoc\":\"RDOC\",\"commit\":\"Commit\",\"message\":\"Message\",\"started_at\":\"Started\",\"duration\":\"Duration\",\"finished_at\":\"Finished\",\"tabs\":{\"current\":\"Current\",\"build_history\":\"Build History\",\"branches\":\"Branch Summary\",\"pull_requests\":\"Pull Requests\",\"build\":\"Build\",\"job\":\"Job\"}},\"build\":{\"job\":\"Job\",\"duration\":\"Duration\",\"finished_at\":\"Finished\"},\"jobs\":{\"messages\":{\"sponsored_by\":\"This test suite was run on a worker box sponsored by\"},\"build_matrix\":\"Build Matrix\",\"allowed_failures\":\"Allowed Failures\",\"author\":\"Author\",\"config\":\"Config\",\"compare\":\"Compare\",\"committer\":\"Committer\",\"branch\":\"Branch\",\"commit\":\"Commit\",\"message\":\"Message\",\"started_at\":\"Started\",\"duration\":\"Duration\",\"finished_at\":\"Finished\"},\"builds\":{\"name\":\"Build\",\"messages\":{\"sponsored_by\":\"This test suite was run on a worker box sponsored by\"},\"build_matrix\":\"Build Matrix\",\"allowed_failures\":\"Allowed Failures\",\"author\":\"Author\",\"config\":\"Config\",\"compare\":\"Compare\",\"committer\":\"Committer\",\"branch\":\"Branch\",\"commit\":\"Commit\",\"message\":\"Message\",\"started_at\":\"Started\",\"duration\":\"Duration\",\"finished_at\":\"Finished\",\"show_more\":\"Show more\"},\"layouts\":{\"top\":{\"home\":\"Home\",\"blog\":\"Blog\",\"docs\":\"Docs\",\"stats\":\"Stats\",\"github_login\":\"Sign in with Github\",\"profile\":\"Profile\",\"sign_out\":\"Sign Out\",\"admin\":\"Admin\"},\"application\":{\"fork_me\":\"Fork me on Github\",\"recent\":\"Recent\",\"search\":\"Search\",\"sponsers\":\"Sponsors\",\"sponsors_link\":\"See all of our amazing sponsors →\",\"my_repositories\":\"My Repositories\"},\"about\":{\"alpha\":\"This stuff is alpha.\",\"messages\":{\"alpha\":\"Please do not consider this a stable service. We're still far from that! More info here.\"},\"join\":\"Join us and help!\",\"mailing_list\":\"Mailing List\",\"repository\":\"Repository\",\"twitter\":\"Twitter\"},\"mobile\":{\"author\":\"Author\",\"build\":\"Build\",\"build_matrix\":\"Build Matrix\",\"commit\":\"Commit\",\"committer\":\"Committer\",\"compare\":\"Compare\",\"config\":\"Config\",\"duration\":\"Duration\",\"finished_at\":\"Finished at\",\"job\":\"Job\",\"log\":\"Log\"}},\"profiles\":{\"show\":{\"email\":\"Email\",\"github\":\"Github\",\"message\":{\"your_repos\":\" Flick the switches below to turn on the Travis service hook for your projects, then push to GitHub. \\n To test against multiple rubies, see\",\"config\":\"how to configure custom build options\"},\"messages\":{\"notice\":\"To get started, please read our Getting Started guide.\\n It will only take a couple of minutes.\"},\"token\":\"Token\",\"your_repos\":\"Your Repositories\",\"update\":\"Update\",\"update_locale\":\"Update\",\"your_locale\":\"Your Locale\"}},\"statistics\":{\"index\":{\"count\":\"Count\",\"repo_growth\":\"Repository Growth\",\"total_projects\":\"Total Projects/Repositories\",\"build_count\":\"Build Count\",\"last_month\":\"last month\",\"total_builds\":\"Total Builds\"}},\"locales\":{\"en\":\"English\",\"es\":\"Español\",\"fr\":\"Français\",\"ja\":\"日本語\",\"nb\":\"Norsk Bokmål\",\"pl\":\"Polski\",\"ru\":\"Русский\",\"nl\":\"Nederlands\",\"pt-BR\":\"português brasileiro\"}},\"es\":{\"datetime\":{\"distance_in_words\":{\"hours_exact\":{\"one\":\"%{count} hora\",\"other\":\"%{count} horas\"},\"minutes_exact\":{\"one\":\"%{count} minuto\",\"other\":\"%{count} minutos\"},\"seconds_exact\":{\"one\":\"%{count} segundo\",\"other\":\"%{count} segundos\"}}},\"workers\":\"Procesos\",\"queue\":\"Cola\",\"no_job\":\"No hay trabajos\",\"repositories\":{\"branch\":\"Rama\",\"image_url\":\"Imagen URL\",\"markdown\":\"Markdown\",\"textile\":\"Textile\",\"rdoc\":\"RDOC\",\"commit\":\"Commit\",\"message\":\"Mensaje\",\"started_at\":\"Iniciado\",\"duration\":\"Duración\",\"finished_at\":\"Finalizado\",\"tabs\":{\"current\":\"Actual\",\"build_history\":\"Histórico\",\"branches\":\"Ramas\",\"build\":\"Builds\",\"job\":\"Trabajo\"}},\"build\":{\"job\":\"Trabajo\",\"duration\":\"Duración\",\"finished_at\":\"Finalizado\"},\"jobs\":{\"messages\":{\"sponsored_by\":\"Esta serie de tests han sido ejecutados en una caja de Proceso patrocinada por\"},\"build_matrix\":\"Matriz de Builds\",\"allowed_failures\":\"Fallos Permitidos\",\"author\":\"Autor\",\"config\":\"Configuración\",\"compare\":\"Comparar\",\"committer\":\"Committer\",\"branch\":\"Rama\",\"commit\":\"Commit\",\"message\":\"Mensaje\",\"started_at\":\"Iniciado\",\"duration\":\"Duración\",\"finished_at\":\"Finalizado\",\"sponsored_by\":\"Patrocinado por\"},\"builds\":{\"name\":\"Build\",\"messages\":{\"sponsored_by\":\"Esta serie de tests han sido ejecutados en una caja de Proceso patrocinada por\"},\"build_matrix\":\"Matriz de Builds\",\"allowed_failures\":\"Fallos Permitidos\",\"author\":\"Autor\",\"config\":\"Configuración\",\"compare\":\"Comparar\",\"committer\":\"Committer\",\"branch\":\"Rama\",\"commit\":\"Commit\",\"message\":\"Mensaje\",\"started_at\":\"Iniciado\",\"duration\":\"Duración\",\"finished_at\":\"Finalizado\"},\"layouts\":{\"top\":{\"home\":\"Inicio\",\"blog\":\"Blog\",\"docs\":\"Documentación\",\"stats\":\"Estadísticas\",\"github_login\":\"Iniciar sesión con Github\",\"profile\":\"Perfil\",\"sign_out\":\"Desconectar\",\"admin\":\"Admin\"},\"application\":{\"fork_me\":\"Hazme un Fork en Github\",\"recent\":\"Reciente\",\"search\":\"Buscar\",\"sponsers\":\"Patrocinadores\",\"sponsors_link\":\"Ver todos nuestros patrocinadores →\",\"my_repositories\":\"Mis Repositorios\"},\"about\":{\"alpha\":\"Esto es alpha.\",\"messages\":{\"alpha\":\"Por favor no considereis esto un servicio estable. Estamos estamos aún lejos de ello! Más información aquí.\"},\"join\":\"Únetenos y ayudanos!\",\"mailing_list\":\"Lista de Correos\",\"repository\":\"Repositorio\",\"twitter\":\"Twitter\"}},\"profiles\":{\"show\":{\"email\":\"Correo electrónico\",\"github\":\"Github\",\"message\":{\"your_repos\":\" Activa los interruptores para inicial el Travis service hook para tus proyectos, y haz un Push en GitHub. \\n Para probar varias versiones de ruby, mira\",\"config\":\"como configurar tus propias opciones para el Build\"},\"messages\":{\"notice\":\"Para comenzar, por favor lee nuestra Guía de Inicio .\\n Solo tomará unos pocos minutos.\"},\"token\":\"Token\",\"your_repos\":\"Tus repositorios\",\"update\":\"Actualizar\",\"update_locale\":\"Actualizar\",\"your_locale\":\"Tu Idioma\"}},\"statistics\":{\"index\":{\"count\":\"Número\",\"repo_growth\":\"Crecimiento de Repositorios\",\"total_projects\":\"Total de Proyectos/Repositorios\",\"build_count\":\"Número de Builds\",\"last_month\":\"mes anterior\",\"total_builds\":\"Total de Builds\"}},\"locales\":{\"en\":\"English\",\"es\":\"Español\",\"fr\":\"Français\",\"ja\":\"日本語\",\"nb\":\"Norsk Bokmål\",\"pl\":\"Polski\",\"ru\":\"Русский\",\"nl\":\"Nederlands\",\"pt-BR\":\"português brasileiro\"}},\"fr\":{\"datetime\":{\"distance_in_words\":{\"hours_exact\":{\"one\":\"%{count} heure\",\"other\":\"%{count} heures\"},\"minutes_exact\":{\"one\":\"%{count} minute\",\"other\":\"%{count} minutes\"},\"seconds_exact\":{\"one\":\"%{count} seconde\",\"other\":\"%{count} secondes\"}}},\"workers\":\"Processus\",\"queue\":\"File\",\"no_job\":\"Pas de tâches\",\"repositories\":{\"branch\":\"Branche\",\"image_url\":\"Image\",\"markdown\":\"Markdown\",\"textile\":\"Textile\",\"rdoc\":\"RDOC\",\"commit\":\"Commit\",\"message\":\"Message\",\"started_at\":\"Commencé\",\"duration\":\"Durée\",\"finished_at\":\"Terminé\",\"tabs\":{\"current\":\"Actuel\",\"build_history\":\"Historique des tâches\",\"branches\":\"Résumé des branches\",\"build\":\"Construction\",\"job\":\"Tâche\"}},\"build\":{\"job\":\"Tâche\",\"duration\":\"Durée\",\"finished_at\":\"Terminé\"},\"jobs\":{\"messages\":{\"sponsored_by\":\"Cette série de tests a été exécutée sur une machine sponsorisée par\"},\"build_matrix\":\"Matrice des versions\",\"allowed_failures\":\"Échecs autorisés\",\"author\":\"Auteur\",\"config\":\"Config\",\"compare\":\"Comparer\",\"committer\":\"Committeur\",\"branch\":\"Branche\",\"commit\":\"Commit\",\"message\":\"Message\",\"started_at\":\"Commencé\",\"duration\":\"Durée\",\"finished_at\":\"Terminé\",\"sponsored_by\":\"Cette série de tests a été exécutée sur une machine sponsorisée par\"},\"builds\":{\"name\":\"Version\",\"messages\":{\"sponsored_by\":\"Cette série de tests a été exécutée sur une machine sponsorisée par\"},\"build_matrix\":\"Matrice des versions\",\"allowed_failures\":\"Échecs autorisés\",\"author\":\"Auteur\",\"config\":\"Config\",\"compare\":\"Comparer\",\"committer\":\"Committeur\",\"branch\":\"Branche\",\"commit\":\"Commit\",\"message\":\"Message\",\"started_at\":\"Commencé\",\"duration\":\"Durée\",\"finished_at\":\"Terminé\"},\"layouts\":{\"top\":{\"home\":\"Accueil\",\"blog\":\"Blog\",\"docs\":\"Documentation\",\"stats\":\"Statistiques\",\"github_login\":\"Connection Github\",\"profile\":\"Profil\",\"sign_out\":\"Déconnection\",\"admin\":\"Admin\"},\"application\":{\"fork_me\":\"Faites un Fork sur Github\",\"recent\":\"Récent\",\"search\":\"Chercher\",\"sponsers\":\"Sponsors\",\"sponsors_link\":\"Voir tous nos extraordinaire sponsors →\",\"my_repositories\":\"Mes dépôts\"},\"about\":{\"alpha\":\"Ceci est en alpha.\",\"messages\":{\"alpha\":\"S'il vous plaît ne considérez pas ce service comme étant stable. Nous sommes loin de ça! Plus d'infos ici.\"},\"join\":\"Joignez-vous à nous et aidez-nous!\",\"mailing_list\":\"Liste de distribution\",\"repository\":\"Dépôt\",\"twitter\":\"Twitter\"},\"mobile\":{\"author\":\"Auteur\",\"build\":\"Version\",\"build_matrix\":\"Matrice des versions\",\"commit\":\"Commit\",\"committer\":\"Committeur\",\"compare\":\"Comparer\",\"config\":\"Config\",\"duration\":\"Durée\",\"finished_at\":\"Terminé à\",\"job\":\"Tâche\",\"log\":\"Journal\"}},\"profiles\":{\"show\":{\"github\":\"Github\",\"message\":{\"your_repos\":\"Utilisez les boutons ci-dessous pour activer Travis sur vos projets puis déployez sur GitHub. \\nPour tester sur plus de versions de ruby, voir\",\"config\":\"comment configurer des options de version personnalisées\"},\"messages\":{\"notice\":\"Pour commencer, veuillez lire notre guide de démarrage.\\n Cela ne vous prendra que quelques minutes.\"},\"token\":\"Jeton\",\"your_repos\":\"Vos dépôts\",\"email\":\"Courriel\",\"update\":\"Modifier\",\"update_locale\":\"Modifier\",\"your_locale\":\"Votre langue\"}},\"statistics\":{\"index\":{\"count\":\"Décompte\",\"repo_growth\":\"Croissance de dépôt\",\"total_projects\":\"Total des projets/dépôts\",\"build_count\":\"Décompte des versions\",\"last_month\":\"mois dernier\",\"total_builds\":\"Total des versions\"}},\"admin\":{\"actions\":{\"create\":\"créer\",\"created\":\"créé\",\"delete\":\"supprimer\",\"deleted\":\"supprimé\",\"update\":\"mise à jour\",\"updated\":\"mis à jour\"},\"credentials\":{\"log_out\":\"Déconnection\"},\"delete\":{\"confirmation\":\"Oui, je suis sure\",\"flash_confirmation\":\"%{name} a été détruit avec succès\"},\"flash\":{\"error\":\"%{name} n'a pas pu être %{action}\",\"noaction\":\"Aucune action n'a été entreprise\",\"successful\":\"%{name} a réussi à %{action}\"},\"history\":{\"name\":\"Historique\",\"no_activity\":\"Aucune activité\",\"page_name\":\"Historique pour %{name}\"},\"list\":{\"add_new\":\"Ajouter un nouveau\",\"delete_action\":\"Supprimer\",\"delete_selected\":\"Supprimer la sélection\",\"edit_action\":\"Modifier\",\"search\":\"Rechercher\",\"select\":\"Sélectionner le %{name} à modifier\",\"select_action\":\"Sélectionner\",\"show_all\":\"Montrer tout\"},\"new\":{\"basic_info\":\"Information de base\",\"cancel\":\"Annuler\",\"chosen\":\"%{name} choisi\",\"chose_all\":\"Choisir tout\",\"clear_all\":\"Déselectionner tout\",\"many_chars\":\"caractères ou moins\",\"one_char\":\"caractère.\",\"optional\":\"Optionnel\",\"required\":\"Requis\",\"save\":\"Sauvegarder\",\"save_and_add_another\":\"Sauvegarder et en ajouter un autre\",\"save_and_edit\":\"Sauvegarder et modifier\",\"select_choice\":\"Faites vos choix et cliquez\"},\"dashboard\":{\"add_new\":\"Ajouter un nouveau\",\"last_used\":\"Dernière utilisation\",\"model_name\":\"Nom du modèle\",\"modify\":\"Modification\",\"name\":\"Tableau de bord\",\"pagename\":\"Administration du site\",\"records\":\"Enregistrements\",\"show\":\"Voir\",\"ago\":\"plus tôt\"}},\"home\":{\"name\":\"accueil\"},\"repository\":{\"duration\":\"Durée\"},\"devise\":{\"confirmations\":{\"confirmed\":\"Votre compte a été crée avec succès. Vous être maintenant connecté.\",\"send_instructions\":\"Vous allez recevoir un courriel avec les instructions de confirmation de votre compte dans quelques minutes.\"},\"failure\":{\"inactive\":\"Votre compte n'a pas encore été activé.\",\"invalid\":\"Adresse courriel ou mot de passe invalide.\",\"invalid_token\":\"Jeton d'authentification invalide.\",\"locked\":\"Votre compte est bloqué.\",\"timeout\":\"Votre session est expirée, veuillez vous reconnecter pour continuer.\",\"unauthenticated\":\"Vous devez vous connecter ou vous enregistrer afin de continuer\",\"unconfirmed\":\"Vous devez confirmer votre compte avant de continuer.\"},\"mailer\":{\"confirmation_instructions\":{\"subject\":\"Instructions de confirmations\"},\"reset_password_instructions\":{\"subject\":\"Instruction de remise à zéro du mot de passe\"},\"unlock_instructions\":{\"subject\":\"Instruction de débloquage\"}},\"passwords\":{\"send_instructions\":\"Vous recevrez un courriel avec les instructions de remise à zéro du mot de passe dans quelques minutes.\",\"updated\":\"Votre mot de passe a été changé avec succès. Vous êtes maintenant connecté.\"},\"registrations\":{\"destroyed\":\"Au revoir! Votre compte a été annulé avec succès. Nous espérons vous revoir bientôt.\",\"signed_up\":\"Vous êtes enregistré avec succès. Si activé, une confirmation vous a été envoyé par courriel.\",\"updated\":\"Votre compte a été mis a jour avec succès\"},\"sessions\":{\"signed_in\":\"Connecté avec succès\",\"signed_out\":\"Déconnecté avec succès\"},\"unlocks\":{\"send_instructions\":\"Vous recevrez un courriel contenant les instructions pour débloquer votre compte dans quelques minutes.\",\"unlocked\":\"Votre compte a été débloqué avec succès.\"}},\"errors\":{\"messages\":{\"already_confirmed\":\"étais déja confirmé\",\"not_found\":\"n'a pas été trouvé\",\"not_locked\":\"n'étais pas bloqué\"}},\"locales\":{\"en\":\"English\",\"es\":\"Español\",\"ja\":\"日本語\",\"ru\":\"Русский\",\"fr\":\"Français\",\"nb\":\"Norsk Bokmål\",\"pl\":\"Polski\",\"nl\":\"Nederlands\",\"pt-BR\":\"português brasileiro\"}},\"ja\":{\"workers\":\"ワーカー\",\"queue\":\"キュー\",\"no_job\":\"ジョブはありません\",\"repositories\":{\"branch\":\"ブランチ\",\"image_url\":\"画像URL\",\"markdown\":\".md\",\"textile\":\".textile\",\"rdoc\":\".rdoc\",\"commit\":\"コミット\",\"message\":\"メッセージ\",\"started_at\":\"開始時刻\",\"duration\":\"処理時間\",\"finished_at\":\"終了時刻\",\"tabs\":{\"current\":\"最新\",\"build_history\":\"ビルド履歴\",\"branches\":\"ブランチまとめ\",\"build\":\"ビルド\",\"job\":\"ジョブ\"}},\"build\":{\"job\":\"ジョブ\",\"duration\":\"処理時間\",\"finished_at\":\"終了時刻\"},\"jobs\":{\"messages\":{\"sponsored_by\":\"このテストは以下のスポンサーの協力で行いました。\"},\"build_matrix\":\"ビルドマトリクス\",\"allowed_failures\":\"失敗許容範囲内\",\"author\":\"制作者\",\"config\":\"設定\",\"compare\":\"比較\",\"committer\":\"コミット者\",\"branch\":\"ブランチ\",\"commit\":\"コミット\",\"message\":\"メッセージ\",\"started_at\":\"開始時刻\",\"duration\":\"処理時間\",\"finished_at\":\"終了時刻\"},\"builds\":{\"name\":\"ビルド\",\"messages\":{\"sponsored_by\":\"このテストは以下のスポンサーの協力で行いました。\"},\"build_matrix\":\"失敗許容範囲外\",\"allowed_failures\":\"失敗許容範囲内\",\"author\":\"制作者\",\"config\":\"設定\",\"compare\":\"比較\",\"committer\":\"コミット者\",\"branch\":\"ブランチ\",\"commit\":\"コミット\",\"message\":\"メッセージ\",\"started_at\":\"開始時刻\",\"duration\":\"処理時間\",\"finished_at\":\"終了時刻\"},\"layouts\":{\"about\":{\"alpha\":\"まだアルファですよ!\",\"join\":\"参加してみよう!\",\"mailing_list\":\"メールリスト\",\"messages\":{\"alpha\":\"Travis-ciは安定したサービスまで後一歩!詳しくはこちら\"},\"repository\":\"リポジトリ\",\"twitter\":\"ツイッター\"},\"application\":{\"fork_me\":\"Githubでフォークしよう\",\"my_repositories\":\"マイリポジトリ\",\"recent\":\"最近\",\"search\":\"検索\",\"sponsers\":\"スポンサー\",\"sponsors_link\":\"スポンサーをもっと見る →\"},\"top\":{\"blog\":\"ブログ\",\"docs\":\"Travisとは?\",\"github_login\":\"Githubでログイン\",\"home\":\"ホーム\",\"profile\":\"プロフィール\",\"sign_out\":\"ログアウト\",\"stats\":\"統計\",\"admin\":\"管理\"},\"mobile\":{\"author\":\"制作者\",\"build\":\"ビルド\",\"build_matrix\":\"ビルドマトリクス\",\"commit\":\"コミット\",\"committer\":\"コミット者\",\"compare\":\"比較\",\"config\":\"設定\",\"duration\":\"処理時間\",\"finished_at\":\"終了時刻\",\"job\":\"ジョブ\",\"log\":\"ログ\"}},\"profiles\":{\"show\":{\"github\":\"Github\",\"email\":\"メール\",\"message\":{\"config\":\"詳細設定\",\"your_repos\":\"以下のスイッチを設定し、Travis-ciを有効にします。Githubへプッシュしたらビルドは自動的に開始します。複数バーションや細かい設定はこちらへ:\"},\"messages\":{\"notice\":\"まずはTravisのはじめ方を参照してください。\"},\"token\":\"トークン\",\"your_repos\":\"リポジトリ\",\"update\":\"更新\",\"update_locale\":\"更新\",\"your_locale\":\"言語設定\"}},\"statistics\":{\"index\":{\"build_count\":\"ビルド数\",\"count\":\"数\",\"last_month\":\"先月\",\"repo_growth\":\"リポジトリ\",\"total_builds\":\"合計ビルド数\",\"total_projects\":\"合計リポジトリ\"}},\"locales\":{\"en\":\"English\",\"es\":\"Español\",\"fr\":\"Français\",\"ja\":\"日本語\",\"nb\":\"Norsk Bokmål\",\"pl\":\"Polski\",\"ru\":\"Русский\",\"nl\":\"Nederlands\",\"pt-BR\":\"português brasileiro\"}},\"nb\":{\"admin\":{\"actions\":{\"create\":\"opprett\",\"created\":\"opprettet\",\"delete\":\"slett\",\"deleted\":\"slettet\",\"update\":\"oppdater\",\"updated\":\"oppdatert\"},\"credentials\":{\"log_out\":\"Logg ut\"},\"dashboard\":{\"add_new\":\"Legg til ny\",\"ago\":\"siden\",\"last_used\":\"Sist brukt\",\"model_name\":\"Modell\",\"modify\":\"Rediger\",\"name\":\"Dashbord\",\"pagename\":\"Nettstedsadministrasjon\",\"records\":\"Oppføringer\",\"show\":\"Vis\"},\"delete\":{\"confirmation\":\"Ja, jeg er sikker\",\"flash_confirmation\":\"%{name} ble slettet\"},\"flash\":{\"error\":\"%{name} kunne ikke bli %{action}\",\"noaction\":\"Ingen handlinger ble utført\",\"successful\":\"%{name} ble %{action}\"},\"history\":{\"name\":\"Logg\",\"no_activity\":\"Ingen aktivitet\",\"page_name\":\"Logg for %{name}\"},\"list\":{\"add_new\":\"Legg til ny\",\"delete_action\":\"Slett\",\"delete_selected\":\"Slett valgte\",\"edit_action\":\"Rediger\",\"search\":\"Søk\",\"select\":\"Velg %{name} for å redigere\",\"select_action\":\"Velg\",\"show_all\":\"Vis alle \"},\"new\":{\"basic_info\":\"Basisinformasjon\",\"cancel\":\"Avbryt\",\"chosen\":\"Valgt %{name}\",\"chose_all\":\"Velg alle\",\"clear_all\":\"Fjern alle\",\"many_chars\":\"eller færre tegn.\",\"one_char\":\"tegn.\",\"optional\":\"Valgfri\",\"required\":\"Påkrevd\",\"save\":\"Lagre\",\"save_and_add_another\":\"Lagre og legg til ny\",\"save_and_edit\":\"Lagre og rediger\",\"select_choice\":\"Kryss av for dine valg og klikk\"}},\"build\":{\"duration\":\"Varighet\",\"finished_at\":\"Fullført\",\"job\":\"Jobb\"},\"builds\":{\"allowed_failures\":\"Tillatte feil\",\"author\":\"Forfatter\",\"branch\":\"Gren\",\"build_matrix\":\"Jobbmatrise\",\"commit\":\"Innsending\",\"committer\":\"Innsender\",\"compare\":\"Sammenlign\",\"config\":\"Oppsett\",\"duration\":\"Varighet\",\"finished_at\":\"Fullført\",\"message\":\"Beskrivelse\",\"messages\":{\"sponsored_by\":\"Denne testen ble kjørt på en maskin sponset av\"},\"name\":\"Jobb\",\"started_at\":\"Startet\"},\"datetime\":{\"distance_in_words\":{\"hours_exact\":{\"one\":\"%{count} time\",\"other\":\"%{count} timer\"},\"minutes_exact\":{\"one\":\"%{count} minutt\",\"other\":\"%{count} minutter\"},\"seconds_exact\":{\"one\":\"%{count} sekund\",\"other\":\"%{count} sekunder\"}}},\"devise\":{\"confirmations\":{\"confirmed\":\"Din konto er aktivert og du er nå innlogget.\",\"send_instructions\":\"Om noen få minutter så vil du få en e-post med informasjon om hvordan du bekrefter kontoen din.\"},\"failure\":{\"inactive\":\"Kontoen din har ikke blitt aktivert enda.\",\"invalid\":\"Ugyldig e-post eller passord.\",\"invalid_token\":\"Ugyldig autentiseringskode.\",\"locked\":\"Kontoen din er låst.\",\"timeout\":\"Du ble logget ut siden på grunn av mangel på aktivitet, vennligst logg inn på nytt.\",\"unauthenticated\":\"Du må logge inn eller registrere deg for å fortsette.\",\"unconfirmed\":\"Du må bekrefte kontoen din før du kan fortsette.\"},\"mailer\":{\"confirmation_instructions\":{\"subject\":\"Bekreftelsesinformasjon\"},\"reset_password_instructions\":{\"subject\":\"Instruksjoner for å få nytt passord\"},\"unlock_instructions\":{\"subject\":\"Opplåsningsinstruksjoner\"}},\"passwords\":{\"send_instructions\":\"Om noen få minutter så vil du få en epost med informasjon om hvordan du kan få et nytt passord.\",\"updated\":\"Passordet ditt ble endret, og du er logget inn.\"},\"registrations\":{\"destroyed\":\"Adjø! Kontoen din ble kansellert. Vi håper vi ser deg igjen snart.\",\"signed_up\":\"Du er nå registrert.\",\"updated\":\"Kontoen din ble oppdatert.\"},\"sessions\":{\"signed_in\":\"Du er nå logget inn.\",\"signed_out\":\"Du er nå logget ut.\"},\"unlocks\":{\"send_instructions\":\"Om noen få minutter så kommer du til å få en e-post med informasjon om hvordan du kan låse opp kontoen din.\",\"unlocked\":\"Kontoen din ble låst opp, og du er nå logget inn igjen.\"}},\"errors\":{\"messages\":{\"already_confirmed\":\"har allerede blitt bekreftet\",\"not_found\":\"ikke funnnet\",\"not_locked\":\"var ikke låst\"}},\"home\":{\"name\":\"hjem\"},\"jobs\":{\"allowed_failures\":\"Tillatte feil\",\"author\":\"Forfatter\",\"branch\":\"Gren\",\"build_matrix\":\"Jobbmatrise\",\"commit\":\"Innsending\",\"committer\":\"Innsender\",\"compare\":\"Sammenlign\",\"config\":\"Oppsett\",\"duration\":\"Varighet\",\"finished_at\":\"Fullført\",\"message\":\"Beskrivelse\",\"messages\":{\"sponsored_by\":\"Denne testserien ble kjørt på en maskin sponset av\"},\"started_at\":\"Startet\"},\"layouts\":{\"about\":{\"alpha\":\"Dette er alfa-greier.\",\"join\":\"Bli med og hjelp oss!\",\"mailing_list\":\"E-postliste\",\"messages\":{\"alpha\":\"Dette er ikke en stabil tjeneste. Vi har fremdeles et stykke igjen! Mer informasjon finner du her.\"},\"repository\":\"Kodelager\",\"twitter\":\"Twitter.\"},\"application\":{\"fork_me\":\"Se koden på Github\",\"my_repositories\":\"Mine kodelagre\",\"recent\":\"Nylig\",\"search\":\"Søk\",\"sponsers\":\"Sponsorer\",\"sponsors_link\":\"Se alle de flotte sponsorene våre →\"},\"mobile\":{\"author\":\"Forfatter\",\"build\":\"Jobb\",\"build_matrix\":\"Jobbmatrise\",\"commit\":\"Innsending\",\"committer\":\"Innsender\",\"compare\":\"Sammenlign\",\"config\":\"Oppsett\",\"duration\":\"Varighet\",\"finished_at\":\"Fullført\",\"job\":\"Jobb\",\"log\":\"Logg\"},\"top\":{\"admin\":\"Administrator\",\"blog\":\"Blogg\",\"docs\":\"Dokumentasjon\",\"github_login\":\"Logg inn med Github\",\"home\":\"Hjem\",\"profile\":\"Profil\",\"sign_out\":\"Logg ut\",\"stats\":\"Statistikk\"}},\"no_job\":\"Ingen jobber finnnes\",\"profiles\":{\"show\":{\"email\":\"E-post\",\"github\":\"Github\",\"message\":{\"config\":\"hvordan sette opp egne jobbinnstillinger\",\"your_repos\":\"Slå\\u0010 på Travis for prosjektene dine ved å dra i bryterne under, og send koden til Github. \\nFor å teste mot flere ruby-versjoner, se dokumentasjonen for\"},\"messages\":{\"notice\":\"For å komme i gang, vennligst les kom-i-gang-veivisereren vår. Det tar bare et par minutter.\"},\"token\":\"Kode\",\"update\":\"Oppdater\",\"update_locale\":\"Oppdater\",\"your_locale\":\"Ditt språk\",\"your_repos\":\"Dine kodelagre\"}},\"queue\":\"Kø\",\"repositories\":{\"branch\":\"Gren\",\"commit\":\"Innsender\",\"duration\":\"Varighet\",\"finished_at\":\"Fullført\",\"image_url\":\"Bilde-URL\",\"markdown\":\"Markdown\",\"message\":\"Beskrivelse\",\"rdoc\":\"RDOC\",\"started_at\":\"Startet\",\"tabs\":{\"branches\":\"Grensammendrag\",\"build\":\"Jobb\",\"build_history\":\"Jobblogg\",\"current\":\"Siste\",\"job\":\"Jobb\"},\"textile\":\"Textile\"},\"repository\":{\"duration\":\"Varighet\"},\"statistics\":{\"index\":{\"build_count\":\"Antall jobber\",\"count\":\"Antall\",\"last_month\":\"siste måned\",\"repo_growth\":\"Vekst i kodelager\",\"total_builds\":\"Totale jobber\",\"total_projects\":\"Antall prosjekter/kodelagre\"}},\"workers\":\"Arbeidere\",\"locales\":{\"en\":\"English\",\"es\":\"Español\",\"ja\":\"日本語\",\"ru\":\"Русский\",\"fr\":\"Français\",\"nb\":\"Norsk Bokmål\",\"pl\":\"Polski\",\"nl\":\"Nederlands\",\"pt-BR\":\"português brasileiro\"}},\"nl\":{\"admin\":{\"actions\":{\"create\":\"aanmaken\",\"created\":\"aangemaakt\",\"delete\":\"verwijderen\",\"deleted\":\"verwijderd\",\"update\":\"bijwerken\",\"updated\":\"bijgewerkt\"},\"credentials\":{\"log_out\":\"Afmelden\"},\"dashboard\":{\"add_new\":\"Nieuwe toevoegen\",\"ago\":\"geleden\",\"last_used\":\"Laatst gebruikt\",\"model_name\":\"Model naam\",\"modify\":\"Wijzigen\",\"pagename\":\"Site administratie\",\"show\":\"Laten zien\",\"records\":\"Gegevens\"},\"delete\":{\"confirmation\":\"Ja, ik ben zeker\",\"flash_confirmation\":\"%{name} is vernietigd\"},\"flash\":{\"error\":\"%{name} kon niet worden %{action}\",\"noaction\":\"Er zijn geen acties genomen\",\"successful\":\"%{name} is %{action}\"},\"history\":{\"name\":\"Geschiedenis\",\"no_activity\":\"Geen activiteit\",\"page_name\":\"Geschiedenis van %{name}\"},\"list\":{\"add_new\":\"Nieuwe toevoegen\",\"delete_action\":\"Verwijderen\",\"delete_selected\":\"Verwijder geselecteerden\",\"edit_action\":\"Bewerken\",\"search\":\"Zoeken\",\"select\":\"Selecteer %{name} om te bewerken\",\"select_action\":\"Selecteer\",\"show_all\":\"Laat allen zien\"},\"new\":{\"basic_info\":\"Basisinfo\",\"cancel\":\"Annuleren\",\"chosen\":\"%{name} gekozen\",\"chose_all\":\"Kies allen\",\"clear_all\":\"Deselecteer allen\",\"many_chars\":\"tekens of minder.\",\"one_char\":\"teken.\",\"optional\":\"Optioneel\",\"required\":\"Vereist\",\"save\":\"Opslaan\",\"save_and_add_another\":\"Opslaan en een nieuwe toevoegen\",\"save_and_edit\":\"Opslaan en bewerken\",\"select_choice\":\"Selecteer uw keuzes en klik\"}},\"build\":{\"duration\":\"Duur\",\"finished_at\":\"Voltooid\",\"job\":\"Taak\"},\"builds\":{\"allowed_failures\":\"Toegestane mislukkingen\",\"author\":\"Auteur\",\"branch\":\"Tak\",\"build_matrix\":\"Bouw Matrix\",\"compare\":\"Vergelijk\",\"config\":\"Configuratie\",\"duration\":\"Duur\",\"finished_at\":\"Voltooid\",\"message\":\"Bericht\",\"messages\":{\"sponsored_by\":\"Deze tests zijn gedraaid op een machine gesponsord door\"},\"name\":\"Bouw\",\"started_at\":\"Gestart\",\"commit\":\"Commit\",\"committer\":\"Committer\"},\"datetime\":{\"distance_in_words\":{\"hours_exact\":{\"one\":\"%{count} uur\",\"other\":\"%{count} uren\"},\"minutes_exact\":{\"one\":\"%{count} minuut\",\"other\":\"%{count} minuten\"},\"seconds_exact\":{\"one\":\"%{count} seconde\",\"other\":\"%{count} seconden\"}}},\"devise\":{\"confirmations\":{\"confirmed\":\"Uw account is bevestigd. U wordt nu ingelogd.\",\"send_instructions\":\"Binnen enkele minuten zal u een email ontvangen met instructies om uw account te bevestigen.\"},\"failure\":{\"inactive\":\"Uw account is nog niet geactiveerd.\",\"invalid\":\"Ongeldig email adres of wachtwoord.\",\"invalid_token\":\"Ongeldig authenticatie token.\",\"locked\":\"Uw account is vergrendeld.\",\"timeout\":\"Uw sessie is verlopen, gelieve opnieuw in te loggen om verder te gaan.\",\"unauthenticated\":\"U moet inloggen of u registeren voordat u verder gaat.\",\"unconfirmed\":\"U moet uw account bevestigen voordat u verder gaat.\"},\"mailer\":{\"confirmation_instructions\":{\"subject\":\"Bevestigings-instructies\"},\"reset_password_instructions\":{\"subject\":\"Wachtwoord herstel instructies\"},\"unlock_instructions\":{\"subject\":\"Ontgrendel-instructies\"}},\"passwords\":{\"send_instructions\":\"Binnen enkele minuten zal u een email krijgen met instructies om uw wachtwoord opnieuw in te stellen.\",\"updated\":\"Uw wachtwoord is veranderd. U wordt nu ingelogd.\"},\"registrations\":{\"destroyed\":\"Dag! Uw account is geannuleerd. We hopen u vlug terug te zien.\",\"signed_up\":\"Uw registratie is voltooid. Als het ingeschakeld is wordt een bevestiging naar uw email adres verzonden.\",\"updated\":\"Het bijwerken van uw account is gelukt.\"},\"sessions\":{\"signed_in\":\"Inloggen gelukt.\",\"signed_out\":\"Uitloggen gelukt.\"},\"unlocks\":{\"send_instructions\":\"Binnen enkele minuten zal u een email krijgen met instructies om uw account te ontgrendelen.\",\"unlocked\":\"Uw account is ontgrendeld. U wordt nu ingelogd.\"}},\"errors\":{\"messages\":{\"already_confirmed\":\"was al bevestigd\",\"not_found\":\"niet gevonden\",\"not_locked\":\"was niet vergrendeld\"}},\"jobs\":{\"allowed_failures\":\"Toegestane mislukkingen\",\"author\":\"Auteur\",\"branch\":\"Tak\",\"build_matrix\":\"Bouw matrix\",\"compare\":\"Vergelijk\",\"config\":\"Configuratie\",\"duration\":\"Duur\",\"finished_at\":\"Voltooid\",\"message\":\"Bericht\",\"messages\":{\"sponsored_by\":\"Deze testen zijn uitgevoerd op een machine gesponsord door\"},\"started_at\":\"Gestart\",\"commit\":\"Commit\",\"committer\":\"Committer\"},\"layouts\":{\"about\":{\"alpha\":\"Dit is in alfa-stadium.\",\"join\":\"Doe met ons mee en help!\",\"mailing_list\":\"Mailing lijst\",\"messages\":{\"alpha\":\"Gelieve deze service niet te beschouwen als stabiel. Daar zijn we nog lang niet! Meer info hier.\"},\"repository\":\"Repository\",\"twitter\":\"Twitter\"},\"application\":{\"fork_me\":\"Maak een fork op Github\",\"my_repositories\":\"Mijn repositories\",\"recent\":\"Recent\",\"search\":\"Zoeken\",\"sponsers\":\"Sponsors\",\"sponsors_link\":\"Bekijk al onze geweldige sponsors →\"},\"mobile\":{\"author\":\"Auteur\",\"build\":\"Bouw\",\"build_matrix\":\"Bouw matrix\",\"compare\":\"Vergelijk\",\"config\":\"Configuratie\",\"duration\":\"Duur\",\"finished_at\":\"Voltooid op\",\"job\":\"Taak\",\"commit\":\"Commit\",\"committer\":\"Committer\",\"log\":\"Logboek\"},\"top\":{\"admin\":\"Administratie\",\"blog\":\"Blog\",\"docs\":\"Documentatie\",\"github_login\":\"Inloggen met Github\",\"home\":\"Home\",\"profile\":\"Profiel\",\"sign_out\":\"Uitloggen\",\"stats\":\"Statistieken\"}},\"locales\":{\"en\":\"English\",\"es\":\"Español\",\"fr\":\"Français\",\"ja\":\"日本語\",\"nb\":\"Norsk Bokmål\",\"nl\":\"Nederlands\",\"pl\":\"Polski\",\"ru\":\"Русский\",\"pt-BR\":\"português brasileiro\"},\"no_job\":\"Er zijn geen taken\",\"profiles\":{\"show\":{\"email\":\"Email adres\",\"github\":\"Github\",\"message\":{\"config\":\"hoe eigen bouw-opties in te stellen\",\"your_repos\":\"Zet de schakelaars hieronder aan om de Travis hook voor uw projecten te activeren en push daarna naar Github \\nOm te testen tegen meerdere rubies, zie\"},\"messages\":{\"notice\":\"Om te beginnen kunt u onze startersgids lezen.\\\\n Het zal maar enkele minuten van uw tijd vergen.\"},\"update\":\"Bijwerken\",\"update_locale\":\"Bijwerken\",\"your_locale\":\"Uw taal\",\"your_repos\":\"Uw repositories\",\"token\":\"Token\"}},\"queue\":\"Wachtrij\",\"repositories\":{\"branch\":\"Tak\",\"duration\":\"Duur\",\"finished_at\":\"Voltooid\",\"image_url\":\"Afbeeldings URL\",\"message\":\"Bericht\",\"started_at\":\"Gestart\",\"tabs\":{\"branches\":\"Tak samenvatting\",\"build\":\"Bouw\",\"build_history\":\"Bouw geschiedenis\",\"current\":\"Huidig\",\"job\":\"Taak\"},\"commit\":\"Commit\",\"markdown\":\"Markdown\",\"rdoc\":\"RDOC\",\"textile\":\"Textile\"},\"repository\":{\"duration\":\"Duur\"},\"statistics\":{\"index\":{\"build_count\":\"Bouw aantal\",\"count\":\"Aantal\",\"last_month\":\"voorbije maand\",\"repo_growth\":\"Repository groei\",\"total_builds\":\"Bouw totaal\",\"total_projects\":\"Projecten/Repository totaal\"}},\"workers\":\"Machines\",\"home\":{\"name\":\"Hoofdpagina\"}},\"pl\":{\"datetime\":{\"distance_in_words\":{\"hours_exact\":{\"one\":\"%{count} godzina\",\"other\":\"%{count} godziny\"},\"minutes_exact\":{\"one\":\"%{count} minuta\",\"other\":\"%{count} minuty\"},\"seconds_exact\":{\"one\":\"%{count} sekunda\",\"other\":\"%{count} sekundy\"}}},\"workers\":\"Workers\",\"queue\":\"Kolejka\",\"no_job\":\"Brak zadań\",\"repositories\":{\"branch\":\"Gałąź\",\"image_url\":\"URL obrazka\",\"markdown\":\"Markdown\",\"textile\":\"Textile\",\"rdoc\":\"RDOC\",\"commit\":\"Commit\",\"message\":\"Opis\",\"started_at\":\"Rozpoczęto\",\"duration\":\"Czas trwania\",\"finished_at\":\"Zakończono\",\"tabs\":{\"current\":\"Aktualny\",\"build_history\":\"Historia Buildów\",\"branches\":\"Wszystkie Gałęzie\",\"build\":\"Build\",\"job\":\"Zadanie\"}},\"build\":{\"job\":\"Zadanie\",\"duration\":\"Czas trwania\",\"finished_at\":\"Zakończono\"},\"jobs\":{\"messages\":{\"sponsored_by\":\"Te testy zostały uruchomione na maszynie sponsorowanej przez\"},\"build_matrix\":\"Macierz Buildów\",\"allowed_failures\":\"Dopuszczalne Niepowodzenia\",\"author\":\"Autor\",\"config\":\"Konfiguracja\",\"compare\":\"Porównanie\",\"committer\":\"Committer\",\"branch\":\"Gałąź\",\"commit\":\"Commit\",\"message\":\"Opis\",\"started_at\":\"Rozpoczęto\",\"duration\":\"Czas trwania\",\"finished_at\":\"Zakończono\",\"sponsored_by\":\"Te testy zostały uruchomione na maszynie sponsorowanej przez\"},\"builds\":{\"name\":\"Build\",\"messages\":{\"sponsored_by\":\"Te testy zostały uruchomione na maszynie sponsorowanej przez\"},\"build_matrix\":\"Macierz Buildów\",\"allowed_failures\":\"Dopuszczalne Niepowodzenia\",\"author\":\"Autor\",\"config\":\"Konfiguracja\",\"compare\":\"Porównanie\",\"committer\":\"Komitujący\",\"branch\":\"Gałąź\",\"commit\":\"Commit\",\"message\":\"Opis\",\"started_at\":\"Rozpoczęto\",\"duration\":\"Czas trwania\",\"finished_at\":\"Zakończono\"},\"layouts\":{\"top\":{\"home\":\"Start\",\"blog\":\"Blog\",\"docs\":\"Dokumentacja\",\"stats\":\"Statystki\",\"github_login\":\"Zaloguj się przy pomocy Githuba\",\"profile\":\"Profil\",\"sign_out\":\"Wyloguj się\"},\"application\":{\"fork_me\":\"Fork me on Github\",\"recent\":\"Ostatnie\",\"search\":\"Wyniki\",\"sponsers\":\"Sponsorzy\",\"sponsors_link\":\"Zobacz naszych wszystkich wspaniałych sponsorów →\",\"my_repositories\":\"Moje repozytoria\"},\"about\":{\"alpha\":\"To wciąż jest wersja alpha.\",\"messages\":{\"alpha\":\"Proszę nie traktuj tego jako stabilnej usługi. Wciąż nam wiele do tego brakuje! Więcej informacji znajdziesz tutaj.\"},\"join\":\"Pomóż i dołącz do nas!\",\"mailing_list\":\"Lista mailingowa\",\"repository\":\"Repozytorium\",\"twitter\":\"Twitter\"},\"mobile\":{\"author\":\"Autor\",\"build\":\"Build\",\"build_matrix\":\"Macierz Buildów\",\"commit\":\"Commit\",\"committer\":\"Komitujący\",\"compare\":\"Porównianie\",\"config\":\"Konfiguracja\",\"duration\":\"Czas trwania\",\"finished_at\":\"Zakończono\",\"job\":\"Zadanie\",\"log\":\"Log\"}},\"profiles\":{\"show\":{\"email\":\"Email\",\"github\":\"Github\",\"message\":{\"your_repos\":\" Przesuń suwak poniżej, aby włączyć Travisa, dla twoich projektów, a następnie umieść swój kod na GitHubie. \\n Aby testować swój kod przy użyciu wielu wersji Rubiego, zobacz\",\"config\":\"jak skonfigurować niestandardowe opcje builda\"},\"messages\":{\"notice\":\"Aby zacząć, przeczytaj nasz Przewodnik .\\n Zajmie ci to tylko kilka minut.\"},\"token\":\"Token\",\"your_repos\":\"Twoje repozytoria\"}},\"statistics\":{\"index\":{\"count\":\"Ilość\",\"repo_growth\":\"Przyrost repozytoriów\",\"total_projects\":\"Łącznie projektów/repozytoriów\",\"build_count\":\"Liczba buildów\",\"last_month\":\"ostatni miesiąc\",\"total_builds\":\"Łącznie Buildów\"}},\"date\":{\"abbr_day_names\":[\"nie\",\"pon\",\"wto\",\"śro\",\"czw\",\"pią\",\"sob\"],\"abbr_month_names\":[\"sty\",\"lut\",\"mar\",\"kwi\",\"maj\",\"cze\",\"lip\",\"sie\",\"wrz\",\"paź\",\"lis\",\"gru\"],\"day_names\":[\"niedziela\",\"poniedziałek\",\"wtorek\",\"środa\",\"czwartek\",\"piątek\",\"sobota\"],\"formats\":{\"default\":\"%d-%m-%Y\",\"long\":\"%B %d, %Y\",\"short\":\"%d %b\"},\"month_names\":[\"styczeń\",\"luty\",\"marzec\",\"kwiecień\",\"maj\",\"czerwiec\",\"lipiec\",\"sierpień\",\"wrzesień\",\"październik\",\"listopad\",\"grudzień\"],\"order\":[\"day\",\"month\",\"year\"]},\"errors\":{\"format\":\"%{attribute} %{message}\",\"messages\":{\"accepted\":\"musi zostać zaakceptowane\",\"blank\":\"nie może być puste\"}},\"locales\":{\"en\":\"English\",\"es\":\"Español\",\"ja\":\"日本語\",\"ru\":\"Русский\",\"fr\":\"Français\",\"nb\":\"Norsk Bokmål\",\"pl\":\"Polski\",\"nl\":\"Nederlands\",\"pt-BR\":\"português brasileiro\"}},\"pt-BR\":{\"admin\":{\"actions\":{\"create\":\"criar\",\"created\":\"criado\",\"delete\":\"deletar\",\"deleted\":\"deletado\",\"update\":\"atualizar\",\"updated\":\"atualizado\"},\"credentials\":{\"log_out\":\"Deslogar\"},\"dashboard\":{\"add_new\":\"Adicionar novo\",\"ago\":\"atrás\",\"last_used\":\"Última utilização\",\"model_name\":\"Nome do modelo\",\"modify\":\"Modificar\",\"name\":\"Dashboard\",\"pagename\":\"Administração do site\",\"records\":\"Registros\",\"show\":\"Mostrar\"},\"delete\":{\"confirmation\":\"Sim, tenho certeza\",\"flash_confirmation\":\"%{name} foi destruído com sucesso\"},\"flash\":{\"error\":\"%{name} falhou ao %{action}\",\"noaction\":\"Nenhuma ação foi tomada\",\"successful\":\"%{name} foi %{action} com sucesso\"},\"history\":{\"name\":\"Histórico\",\"no_activity\":\"Nenhuma Atividade\",\"page_name\":\"Histórico para %{name}\"},\"list\":{\"add_new\":\"Adicionar novo\",\"delete_action\":\"Deletar\",\"delete_selected\":\"Deletar selecionados\",\"edit_action\":\"Editar\",\"search\":\"Buscar\",\"select\":\"Selecionar %{name} para editar\",\"select_action\":\"Selecionar\",\"show_all\":\"Mostrar todos\"},\"new\":{\"basic_info\":\"Informações básicas\",\"cancel\":\"Cancelar\",\"chosen\":\"Escolhido %{name}\",\"chose_all\":\"Escolher todos\",\"clear_all\":\"Limpar todos\",\"many_chars\":\"caracteres ou menos.\",\"one_char\":\"caractere.\",\"optional\":\"Opcional\",\"required\":\"Requerido\",\"save\":\"Salvar\",\"save_and_add_another\":\"Salvar e adicionar outro\",\"save_and_edit\":\"Salvar e alterar\",\"select_choice\":\"Selecione e clique\"}},\"build\":{\"duration\":\"Duração\",\"finished_at\":\"Concluído em\",\"job\":\"Trabalho\"},\"builds\":{\"allowed_failures\":\"Falhas Permitidas\",\"author\":\"Autor\",\"branch\":\"Branch\",\"build_matrix\":\"Matriz de Build\",\"commit\":\"Commit\",\"committer\":\"Committer\",\"compare\":\"Comparar\",\"config\":\"Config\",\"duration\":\"Duração\",\"finished_at\":\"Concluído em\",\"message\":\"Mensagem\",\"messages\":{\"sponsored_by\":\"Esta série de testes foi executada em uma caixa de processos patrocinada por\"},\"name\":\"Build\",\"started_at\":\"Iniciou em\"},\"datetime\":{\"distance_in_words\":{\"hours_exact\":{\"one\":\"%{count} hora\",\"other\":\"%{count} horas\"},\"minutes_exact\":{\"one\":\"%{count} minuto\",\"other\":\"%{count} minutos\"},\"seconds_exact\":{\"one\":\"%{count} segundo\",\"other\":\"%{count} segundos\"}}},\"devise\":{\"confirmations\":{\"confirmed\":\"Sua conta foi confirmada com sucesso. Você agora está logado.\",\"send_instructions\":\"Você receberá um email com instruções de como confirmar sua conta em alguns minutos.\"},\"failure\":{\"inactive\":\"Sua conta ainda não foi ativada.\",\"invalid\":\"Email ou senha inválidos.\",\"invalid_token\":\"Token de autenticação inválido.\",\"locked\":\"Sua conta está trancada.\",\"timeout\":\"Sua sessão expirou, por favor faça seu login novamente.\",\"unauthenticated\":\"Você precisa fazer o login ou cadastrar-se antes de continuar.\",\"unconfirmed\":\"Você precisa confirmar sua conta antes de continuar.\"},\"mailer\":{\"confirmation_instructions\":{\"subject\":\"Instruções de confirmação\"},\"reset_password_instructions\":{\"subject\":\"Instruções de atualização de senha\"},\"unlock_instructions\":{\"subject\":\"Instruções de destrancamento\"}},\"passwords\":{\"send_instructions\":\"Você receberá um email com instruções de como atualizar sua senha em alguns minutos.\",\"updated\":\"Sua senha foi alterada com sucesso. Você agora está logado.\"},\"registrations\":{\"destroyed\":\"Tchau! Sua conta foi cancelada com sucesso. Esperamos vê-lo novamente em breve!\",\"signed_up\":\"Você se cadastrou com sucesso. Se ativada, uma confirmação foi enviada para seu email.\",\"updated\":\"Você atualizou sua conta com sucesso.\"},\"sessions\":{\"signed_in\":\"Logado com sucesso.\",\"signed_out\":\"Deslogado com sucesso.\"},\"unlocks\":{\"send_instructions\":\"Você receberá um email com instruções de como destrancar sua conta em alguns minutos.\",\"unlocked\":\"Sua conta foi destrancada com sucesso. Você agora está logado.\"}},\"errors\":{\"messages\":{\"already_confirmed\":\"já foi confirmado\",\"not_found\":\"não encontrado\",\"not_locked\":\"não estava trancado\"}},\"home\":{\"name\":\"home\"},\"jobs\":{\"allowed_failures\":\"Falhas Permitidas\",\"author\":\"Autor\",\"branch\":\"Branch\",\"build_matrix\":\"Matriz de Build\",\"commit\":\"Commit\",\"committer\":\"Committer\",\"compare\":\"Comparar\",\"config\":\"Config\",\"duration\":\"Duração\",\"finished_at\":\"Concluído em\",\"message\":\"Mensagem\",\"messages\":{\"sponsored_by\":\"Esta série de testes foi executada em uma caixa de processos patrocinada por\"},\"started_at\":\"Iniciou em\"},\"layouts\":{\"about\":{\"alpha\":\"Isto é um alpha.\",\"join\":\"Junte-se à nós e ajude!\",\"mailing_list\":\"Lista de email\",\"messages\":{\"alpha\":\"Por favor, não considere isto um serviço estável. Estamos muito longe disso! Mais informações aqui.\"},\"repository\":\"Repositório\",\"twitter\":\"Twitter\"},\"application\":{\"fork_me\":\"Faça fork no Github\",\"my_repositories\":\"Meus Repositórios\",\"recent\":\"Recentes\",\"search\":\"Buscar\",\"sponsers\":\"Patrocinadores\",\"sponsors_link\":\"Conheça todos os nossos patrocinadores →\"},\"mobile\":{\"author\":\"Autor\",\"build\":\"Build\",\"build_matrix\":\"Matriz de Build\",\"commit\":\"Commit\",\"committer\":\"Committer\",\"compare\":\"Comparar\",\"config\":\"Config\",\"duration\":\"Duração\",\"finished_at\":\"Concluído em\",\"job\":\"Trabalho\",\"log\":\"Log\"},\"top\":{\"admin\":\"Admin\",\"blog\":\"Blog\",\"docs\":\"Documentação\",\"github_login\":\"Logue com o Github\",\"home\":\"Home\",\"profile\":\"Perfil\",\"sign_out\":\"Sair\",\"stats\":\"Estatísticas\"}},\"locales\":{\"en\":\"English\",\"es\":\"Español\",\"fr\":\"Français\",\"ja\":\"日本語\",\"nb\":\"Norsk Bokmål\",\"nl\":\"Nederlands\",\"pl\":\"Polski\",\"ru\":\"Русский\",\"pt-BR\":\"português brasileiro\"},\"no_job\":\"Não há trabalhos\",\"profiles\":{\"show\":{\"email\":\"Email\",\"github\":\"Github\",\"message\":{\"config\":\"como configurar opções de build\",\"your_repos\":\"Use os botões abaixo para ligar ou desligar o hook de serviço do Travis para seus projetos, e então, faça um push para o Github. Para testar com múltiplas versões do Ruby, leia\"},\"messages\":{\"notice\":\"Para começar, leia nosso Guia de início. Só leva alguns minutinhos.\"},\"token\":\"Token\",\"update\":\"Atualizar\",\"update_locale\":\"Atualizar\",\"your_locale\":\"Sua língua\",\"your_repos\":\"Seus Repositórios\"}},\"queue\":\"Fila\",\"repositories\":{\"branch\":\"Branch\",\"commit\":\"Commit\",\"duration\":\"Duração\",\"finished_at\":\"Concluído em\",\"image_url\":\"URL da imagem\",\"markdown\":\"Markdown\",\"message\":\"Mensagem\",\"rdoc\":\"RDOC\",\"started_at\":\"Iniciou em\",\"tabs\":{\"branches\":\"Sumário do Branch\",\"build\":\"Build\",\"build_history\":\"Histórico de Build\",\"current\":\"Atual\",\"job\":\"Trabalho\"},\"textile\":\"Textile\"},\"repository\":{\"duration\":\"Duração\"},\"statistics\":{\"index\":{\"build_count\":\"Número de Builds\",\"count\":\"Número\",\"last_month\":\"último mês\",\"repo_growth\":\"Crescimento de Repositório\",\"total_builds\":\"Total de Builds\",\"total_projects\":\"Total de Projetos/Repositórios\"}},\"workers\":\"Processos\"},\"ru\":{\"admin\":{\"actions\":{\"create\":\"создать\",\"created\":\"создано\",\"delete\":\"удалить\",\"deleted\":\"удалено\",\"update\":\"обновить\",\"updated\":\"обновлено\"},\"credentials\":{\"log_out\":\"Выход\"},\"dashboard\":{\"add_new\":\"Добавить\",\"ago\":\"назад\",\"last_used\":\"Использовалось в последний раз\",\"model_name\":\"Имя модели\",\"modify\":\"Изменить\",\"name\":\"Панель управления\",\"pagename\":\"Управление сайтом\",\"records\":\"Записи\",\"show\":\"Показать\"},\"delete\":{\"confirmation\":\"Да, я уверен\",\"flash_confirmation\":\"%{name} успешно удалено\"},\"history\":{\"name\":\"История\",\"no_activity\":\"Нет активности\",\"page_name\":\"История %{name}\"},\"list\":{\"add_new\":\"Добавить\",\"delete_action\":\"Удалить\",\"delete_selected\":\"Удалить выбранные\",\"edit_action\":\"Редактировать\",\"search\":\"Поиск\",\"select\":\"Для редактирования выберите %{name}\",\"select_action\":\"Выбрать\",\"show_all\":\"Показать все\"},\"new\":{\"basic_info\":\"Основная информация\",\"cancel\":\"Отмена\",\"chosen\":\"Выбрано %{name}\",\"chose_all\":\"Выбрать все\",\"clear_all\":\"Очистить все\",\"one_char\":\"символ.\",\"optional\":\"Необязательно\",\"required\":\"Обязательно\",\"save\":\"Сохранить\",\"save_and_add_another\":\"Сохранить и добавить другое\",\"save_and_edit\":\"Сохранить и продолжить редактирование\",\"select_choice\":\"Выберите и кликните\",\"many_chars\":\"символов или меньше.\"},\"flash\":{\"error\":\"%{name} не удалось %{action}\",\"noaction\":\"Никаких действий не произведено\",\"successful\":\"%{name} было успешно %{action}\"}},\"build\":{\"duration\":\"Длительность\",\"finished_at\":\"Завершен\",\"job\":\"Задача\"},\"builds\":{\"allowed_failures\":\"Допустимые неудачи\",\"author\":\"Автор\",\"branch\":\"Ветка\",\"build_matrix\":\"Матрица\",\"commit\":\"Коммит\",\"committer\":\"Коммитер\",\"compare\":\"Дифф\",\"config\":\"Конфигурация\",\"duration\":\"Длительность\",\"finished_at\":\"Завершен\",\"message\":\"Комментарий\",\"messages\":{\"sponsored_by\":\"Эта серия тестов была запущена на машине, спонсируемой\"},\"name\":\"Билд\",\"started_at\":\"Начало\"},\"datetime\":{\"distance_in_words\":{\"hours_exact\":{\"one\":\"%{count} час\",\"few\":\"%{count} часа\",\"many\":\"%{count} часов\",\"other\":\"%{count} часа\"},\"minutes_exact\":{\"one\":\"%{count} минута\",\"few\":\"%{count} минуты\",\"many\":\"%{count} минут\",\"other\":\"%{count} минуты\"},\"seconds_exact\":{\"one\":\"%{count} секунда\",\"few\":\"%{count} секунды\",\"many\":\"%{count} секунд\",\"other\":\"%{count} секунды\"}}},\"devise\":{\"confirmations\":{\"confirmed\":\"Ваш аккаунт успешно подтвержден. Приветствуем!\",\"send_instructions\":\"В течении нескольких минут вы получите электронное письмо с инструкциями для прохождения процедуры подтверждения аккаунта.\"},\"failure\":{\"inactive\":\"Ваш аккаунт еще не активирован.\",\"invalid\":\"Ошибка в адресе почты или пароле.\",\"invalid_token\":\"Неправильный токен аутентификации.\",\"locked\":\"Ваш аккаунт заблокирован.\",\"timeout\":\"Сессия окончена. Для продолжения работы войдите снова.\",\"unauthenticated\":\"Вам нужно войти или зарегистрироваться.\",\"unconfirmed\":\"Вы должны сначала подтвердить свой аккаунт.\"},\"mailer\":{\"confirmation_instructions\":{\"subject\":\"Инструкции для подтверждению аккаунта\"},\"reset_password_instructions\":{\"subject\":\"Инструкции для сброса пароля\"},\"unlock_instructions\":{\"subject\":\"Инструкции для разблокирования аккаунта\"}},\"passwords\":{\"send_instructions\":\"В течении нескольких минут вы получите электронное письмо с инструкциями для сброса пароля.\",\"updated\":\"Ваш пароль успешно изменен. Приветствуем!\"},\"registrations\":{\"destroyed\":\"Ваш аккаунт был успешно удален. Живите долго и процветайте!\",\"signed_up\":\"Вы успешно прошли регистрацию. Инструкции для подтверждения аккаунта отправлены на ваш электронный адрес.\",\"updated\":\"Аккаунт успешно обновлен.\"},\"sessions\":{\"signed_in\":\"Приветствуем!\",\"signed_out\":\"Удачи!\"},\"unlocks\":{\"send_instructions\":\"В течении нескольких минут вы получите электронное письмо с инструкциям для разблокировния аккаунта.\",\"unlocked\":\"Ваш аккаунт успешно разблокирован. Приветствуем!\"}},\"errors\":{\"messages\":{\"already_confirmed\":\"уже подтвержден\",\"not_found\":\"не найден\",\"not_locked\":\"не заблокирован\"}},\"home\":{\"name\":\"Главная\"},\"jobs\":{\"allowed_failures\":\"Допустимые неудачи\",\"author\":\"Автор\",\"branch\":\"Ветка\",\"build_matrix\":\"Матрица\",\"commit\":\"Коммит\",\"committer\":\"Коммитер\",\"compare\":\"Сравнение\",\"config\":\"Конфигурация\",\"duration\":\"Длительность\",\"finished_at\":\"Завершен\",\"message\":\"Комментарий\",\"messages\":{\"sponsored_by\":\"Эта серия тестов была запущена на машине спонсируемой\"},\"started_at\":\"Начало\"},\"layouts\":{\"about\":{\"alpha\":\"Это альфа-версия\",\"join\":\"Присоединяйтесь к нам и помогайте!\",\"mailing_list\":\"Лист рассылки\",\"messages\":{\"alpha\":\"Пожалуйста, не считайте данный сервис стабильным. Мы еще очень далеки от стабильности! Подробности\"},\"repository\":\"Репозиторий\",\"twitter\":\"Twitter\"},\"application\":{\"fork_me\":\"Fork me on Github\",\"my_repositories\":\"Мои репозитории\",\"recent\":\"Недавние\",\"search\":\"Поиск\",\"sponsers\":\"Спонсоры\",\"sponsors_link\":\"Список всех наших замечательных спонсоров →\"},\"mobile\":{\"author\":\"Автор\",\"build\":\"Сборка\",\"build_matrix\":\"Матрица сборок\",\"commit\":\"Коммит\",\"committer\":\"Коммитер\",\"compare\":\"Сравнение\",\"config\":\"Конфигурация\",\"duration\":\"Длительность\",\"finished_at\":\"Завершен\",\"job\":\"Задача\",\"log\":\"Журнал\"},\"top\":{\"admin\":\"Управление\",\"blog\":\"Блог\",\"docs\":\"Документация\",\"github_login\":\"Войти через Github\",\"home\":\"Главная\",\"profile\":\"Профиль\",\"sign_out\":\"Выход\",\"stats\":\"Статистика\"}},\"no_job\":\"Очередь пуста\",\"profiles\":{\"show\":{\"email\":\"Электронная почта\",\"github\":\"Github\",\"message\":{\"config\":\"как настроить специальные опции билда\",\"your_repos\":\"Используйте переключатели, чтобы включить Travis service hook для вашего проекта, а потом отправьте код на GitHub. \\nДля тестирования на нескольких версиях Ruby смотрите\"},\"messages\":{\"notice\":\"Перед началом, пожалуйста, прочтите Руководство для быстрого старта. Это займет всего несколько минут.\"},\"token\":\"Токен\",\"update\":\"Обновить\",\"update_locale\":\"Обновить\",\"your_locale\":\"Ваш язык\",\"your_repos\":\"Ваши репозитории\"}},\"queue\":\"Очередь\",\"repositories\":{\"branch\":\"Ветка\",\"commit\":\"Коммит\",\"duration\":\"Длительность\",\"finished_at\":\"Завершен\",\"image_url\":\"URL изображения\",\"markdown\":\"Markdown\",\"message\":\"Комментарий\",\"rdoc\":\"RDOC\",\"started_at\":\"Начало\",\"tabs\":{\"branches\":\"Статус веток\",\"build\":\"Билд\",\"build_history\":\"История\",\"current\":\"Текущий\",\"job\":\"Задача\"},\"textile\":\"Textile\"},\"repository\":{\"duration\":\"Длительность\"},\"statistics\":{\"index\":{\"build_count\":\"Количество билдов\",\"count\":\"Количество\",\"last_month\":\"прошлый месяц\",\"repo_growth\":\"Рост числа репозиториев\",\"total_builds\":\"Всего билдов\",\"total_projects\":\"Всего проектов/репозиториев\"}},\"workers\":\"Машины\",\"locales\":{\"en\":\"English\",\"es\":\"Español\",\"ja\":\"日本語\",\"ru\":\"Русский\",\"fr\":\"Français\",\"nb\":\"Norsk Bokmål\",\"pl\":\"Polski\",\"nl\":\"Nederlands\",\"pt-BR\":\"português brasileiro\"}}};\n\n\n})();\n//@ sourceURL=i18n");minispade.register('ext/ember/bound_helper', "(function() {// https://gist.github.com/2018185\n// For reference: https://github.com/wagenet/ember.js/blob/ac66dcb8a1cbe91d736074441f853e0da474ee6e/packages/ember-handlebars/lib/views/bound_property_view.js\nvar BoundHelperView = Ember.View.extend(Ember._Metamorph, {\n\n context: null,\n options: null,\n property: null,\n // paths of the property that are also observed\n propertyPaths: [],\n\n value: Ember.K,\n\n valueForRender: function() {\n var value = this.value(Ember.getPath(this.context, this.property), this.options);\n if (this.options.escaped) { value = Handlebars.Utils.escapeExpression(value); }\n return value;\n },\n\n render: function(buffer) {\n buffer.push(this.valueForRender());\n },\n\n valueDidChange: function() {\n if (this.morph.isRemoved()) { return; }\n this.morph.html(this.valueForRender());\n },\n\n didInsertElement: function() {\n this.valueDidChange();\n },\n\n init: function() {\n this._super();\n Ember.addObserver(this.context, this.property, this, 'valueDidChange');\n this.get('propertyPaths').forEach(function(propName) {\n Ember.addObserver(this.context, this.property + '.' + propName, this, 'valueDidChange');\n }, this);\n },\n\n destroy: function() {\n Ember.removeObserver(this.context, this.property, this, 'valueDidChange');\n this.get('propertyPaths').forEach(function(propName) {\n this.context.removeObserver(this.property + '.' + propName, this, 'valueDidChange');\n }, this);\n this._super();\n }\n\n});\n\nEmber.registerBoundHelper = function(name, func) {\n var propertyPaths = Array.prototype.slice.call(arguments, 2);\n Ember.Handlebars.registerHelper(name, function(property, options) {\n var data = options.data,\n view = data.view,\n ctx = this;\n\n var bindView = view.createChildView(BoundHelperView, {\n property: property,\n propertyPaths: propertyPaths,\n context: ctx,\n options: options.hash,\n value: func\n });\n\n view.appendChild(bindView);\n });\n};\n\n\n})();\n//@ sourceURL=ext/ember/bound_helper");
\ No newline at end of file
diff --git a/public/javascripts/vendor.js b/public/javascripts/vendor.js
new file mode 100644
index 00000000..e534bd86
--- /dev/null
+++ b/public/javascripts/vendor.js
@@ -0,0 +1,26088 @@
+/*jshint evil:true*/
+
+minispade = {
+ root: null,
+ modules: {},
+ loaded: {},
+
+ globalEval: function(data) {
+ if ( data ) {
+ // We use execScript on Internet Explorer
+ // We use an anonymous function so that context is window
+ // rather than jQuery in Firefox
+ ( window.execScript || function( data ) {
+ window[ "eval" ].call( window, data );
+ } )( data );
+ }
+ },
+
+ require: function(name) {
+ var loaded = minispade.loaded[name];
+ var mod = minispade.modules[name];
+
+ if (!loaded) {
+ if (mod) {
+ minispade.loaded[name] = true;
+
+ if (typeof mod === "string") {
+ this.globalEval(mod);
+ } else {
+ mod();
+ }
+ } else {
+ if (minispade.root && name.substr(0,minispade.root.length) !== minispade.root) {
+ return minispade.require(minispade.root+name);
+ } else {
+ throw "The module '" + name + "' could not be found";
+ }
+ }
+ }
+
+ return loaded;
+ },
+
+ requireAll: function(regex) {
+ for (var module in this.modules) {
+ if (!this.modules.hasOwnProperty(module)) { continue; }
+ if (regex && !regex.test(module)) { continue; }
+ minispade.require(module);
+ }
+ },
+
+ register: function(name, callback) {
+ minispade.modules[name] = callback;
+ }
+};
+/*! jQuery v1.7.2 jquery.com | jquery.org/license */
+(function(a,b){function cy(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cu(a){if(!cj[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){ck||(ck=c.createElement("iframe"),ck.frameBorder=ck.width=ck.height=0),b.appendChild(ck);if(!cl||!ck.createElement)cl=(ck.contentWindow||ck.contentDocument).document,cl.write((f.support.boxModel?"":"")+""),cl.close();d=cl.createElement(a),cl.body.appendChild(d),e=f.css(d,"display"),b.removeChild(ck)}cj[a]=e}return cj[a]}function ct(a,b){var c={};f.each(cp.concat.apply([],cp.slice(0,b)),function(){c[this]=a});return c}function cs(){cq=b}function cr(){setTimeout(cs,0);return cq=f.now()}function ci(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ch(){try{return new a.XMLHttpRequest}catch(b){}}function cb(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g0){if(c!=="border")for(;e=0===c})}function S(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function K(){return!0}function J(){return!1}function n(a,b,c){var d=b+"defer",e=b+"queue",g=b+"mark",h=f._data(a,d);h&&(c==="queue"||!f._data(a,e))&&(c==="mark"||!f._data(a,g))&&setTimeout(function(){!f._data(a,e)&&!f._data(a,g)&&(f.removeData(a,d,!0),h.fire())},0)}function m(a){for(var b in a){if(b==="data"&&f.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function l(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(k,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNumeric(d)?+d:j.test(d)?f.parseJSON(d):d}catch(g){}f.data(a,c,d)}else d=b}return d}function h(a){var b=g[a]={},c,d;a=a.split(/\s+/);for(c=0,d=a.length;c)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,n=/^[\],:{}\s]*$/,o=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,p=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,q=/(?:^|:|,)(?:\s*\[)+/g,r=/(webkit)[ \/]([\w.]+)/,s=/(opera)(?:.*version)?[ \/]([\w.]+)/,t=/(msie) ([\w.]+)/,u=/(mozilla)(?:.*? rv:([\w.]+))?/,v=/-([a-z]|[0-9])/ig,w=/^-ms-/,x=function(a,b){return(b+"").toUpperCase()},y=d.userAgent,z,A,B,C=Object.prototype.toString,D=Object.prototype.hasOwnProperty,E=Array.prototype.push,F=Array.prototype.slice,G=String.prototype.trim,H=Array.prototype.indexOf,I={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=m.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.7.2",length:0,size:function(){return this.length},toArray:function(){return F.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?E.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),A.add(a);return this},eq:function(a){a=+a;return a===-1?this.slice(a):this.slice(a,a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(F.apply(this,arguments),"slice",F.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:E,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j0)return;A.fireWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").off("ready")}},bindReady:function(){if(!A){A=e.Callbacks("once memory");if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",B,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",B),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&J()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a!=null&&a==a.window},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):I[C.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;try{if(a.constructor&&!D.call(a,"constructor")&&!D.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||D.call(a,d)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw new Error(a)},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(n.test(b.replace(o,"@").replace(p,"]").replace(q,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(c){if(typeof c!="string"||!c)return null;var d,f;try{a.DOMParser?(f=new DOMParser,d=f.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(g){d=b}(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&e.error("Invalid XML: "+c);return d},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(w,"ms-").replace(v,x)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i1?i.call(arguments,0):b,j.notifyWith(k,e)}}function l(a){return function(c){b[a]=arguments.length>1?i.call(arguments,0):c,--g||j.resolveWith(j,b)}}var b=i.call(arguments,0),c=0,d=b.length,e=Array(d),g=d,h=d,j=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred(),k=j.promise();if(d>1){for(;c
a",d=p.getElementsByTagName("*"),e=p.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=p.getElementsByTagName("input")[0],b={leadingWhitespace:p.firstChild.nodeType===3,tbody:!p.getElementsByTagName("tbody").length,htmlSerialize:!!p.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:p.className!=="t",enctype:!!c.createElement("form").enctype,html5Clone:c.createElement("nav").cloneNode(!0).outerHTML!=="<:nav>",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,pixelMargin:!0},f.boxModel=b.boxModel=c.compatMode==="CSS1Compat",i.checked=!0,b.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,b.optDisabled=!h.disabled;try{delete p.test}catch(r){b.deleteExpando=!1}!p.addEventListener&&p.attachEvent&&p.fireEvent&&(p.attachEvent("onclick",function(){b.noCloneEvent=!1}),p.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),b.radioValue=i.value==="t",i.setAttribute("checked","checked"),i.setAttribute("name","t"),p.appendChild(i),j=c.createDocumentFragment(),j.appendChild(p.lastChild),b.checkClone=j.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=i.checked,j.removeChild(i),j.appendChild(p);if(p.attachEvent)for(n in{submit:1,change:1,focusin:1})m="on"+n,o=m in p,o||(p.setAttribute(m,"return;"),o=typeof p[m]=="function"),b[n+"Bubbles"]=o;j.removeChild(p),j=g=h=p=i=null,f(function(){var d,e,g,h,i,j,l,m,n,q,r,s,t,u=c.getElementsByTagName("body")[0];!u||(m=1,t="padding:0;margin:0;border:",r="position:absolute;top:0;left:0;width:1px;height:1px;",s=t+"0;visibility:hidden;",n="style='"+r+t+"5px solid #000;",q="
"&&!s?p.childNodes:[];for(i=t.length-1;i>=0;--i)f.nodeName(t[i],"tbody")&&!t[i].childNodes.length&&t[i].parentNode.removeChild(t[i])}!f.support.leadingWhitespace&&X.test(l)&&p.insertBefore(b.createTextNode(X.exec(l)[0]),p.firstChild),l=p.childNodes,p&&(p.parentNode.removeChild(p),q.length>0&&(r=q[q.length-1],r&&r.parentNode&&r.parentNode.removeChild(r)))}var u;if(!f.support.appendChecked)if(l[0]&&typeof (u=l.length)=="number")for(i=0;i1)},f.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=by(a,"opacity");return c===""?"1":c}return a.style.opacity}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":f.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!!a&&a.nodeType!==3&&a.nodeType!==8&&!!a.style){var g,h,i=f.camelCase(c),j=a.style,k=f.cssHooks[i];c=f.cssProps[i]||i;if(d===b){if(k&&"get"in k&&(g=k.get(a,!1,e))!==b)return g;return j[c]}h=typeof d,h==="string"&&(g=bu.exec(d))&&(d=+(g[1]+1)*+g[2]+parseFloat(f.css(a,c)),h="number");if(d==null||h==="number"&&isNaN(d))return;h==="number"&&!f.cssNumber[i]&&(d+="px");if(!k||!("set"in k)||(d=k.set(a,d))!==b)try{j[c]=d}catch(l){}}},css:function(a,c,d){var e,g;c=f.camelCase(c),g=f.cssHooks[c],c=f.cssProps[c]||c,c==="cssFloat"&&(c="float");if(g&&"get"in g&&(e=g.get(a,!0,d))!==b)return e;if(by)return by(a,c)},swap:function(a,b,c){var d={},e,f;for(f in b)d[f]=a.style[f],a.style[f]=b[f];e=c.call(a);for(f in b)a.style[f]=d[f];return e}}),f.curCSS=f.css,c.defaultView&&c.defaultView.getComputedStyle&&(bz=function(a,b){var c,d,e,g,h=a.style;b=b.replace(br,"-$1").toLowerCase(),(d=a.ownerDocument.defaultView)&&(e=d.getComputedStyle(a,null))&&(c=e.getPropertyValue(b),c===""&&!f.contains(a.ownerDocument.documentElement,a)&&(c=f.style(a,b))),!f.support.pixelMargin&&e&&bv.test(b)&&bt.test(c)&&(g=h.width,h.width=c,c=e.width,h.width=g);return c}),c.documentElement.currentStyle&&(bA=function(a,b){var c,d,e,f=a.currentStyle&&a.currentStyle[b],g=a.style;f==null&&g&&(e=g[b])&&(f=e),bt.test(f)&&(c=g.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),g.left=b==="fontSize"?"1em":f,f=g.pixelLeft+"px",g.left=c,d&&(a.runtimeStyle.left=d));return f===""?"auto":f}),by=bz||bA,f.each(["height","width"],function(a,b){f.cssHooks[b]={get:function(a,c,d){if(c)return a.offsetWidth!==0?bB(a,b,d):f.swap(a,bw,function(){return bB(a,b,d)})},set:function(a,b){return bs.test(b)?b+"px":b}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return bq.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=f.isNumeric(b)?"alpha(opacity="+b*100+")":"",g=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&f.trim(g.replace(bp,""))===""){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bp.test(g)?g.replace(bp,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){return f.swap(a,{display:"inline-block"},function(){return b?by(a,"margin-right"):a.style.marginRight})}})}),f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style&&a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)}),f.each({margin:"",padding:"",border:"Width"},function(a,b){f.cssHooks[a+b]={expand:function(c){var d,e=typeof c=="string"?c.split(" "):[c],f={};for(d=0;d<4;d++)f[a+bx[d]+b]=e[d]||e[d-2]||e[0];return f}}});var bC=/%20/g,bD=/\[\]$/,bE=/\r?\n/g,bF=/#.*$/,bG=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bH=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bI=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,bJ=/^(?:GET|HEAD)$/,bK=/^\/\//,bL=/\?/,bM=/
+
+ And associate it by name using a view's `templateName` property:
+
+ AView = Ember.View.extend({
+ templateName: 'some-template'
+ })
+
+ Using a value for `templateName` that does not have a Handlebars template with a
+ matching `data-template-name` attribute will throw an error.
+
+ Assigning a value to both `template` and `templateName` properties will throw an error.
+
+ For views classes that may have a template later defined (e.g. as the block portion of a `{{view}}`
+ Handlebars helper call in another template or in a subclass), you can provide a `defaultTemplate`
+ property set to compiled template function. If a template is not later provided for the view
+ instance the `defaultTemplate` value will be used:
+
+ AView = Ember.View.extend({
+ defaultTemplate: Ember.Handlebars.compile('I was the default'),
+ template: null,
+ templateName: null
+ })
+
+ Will result in instances with an HTML representation of:
+
+
I was the default
+
+ If a `template` or `templateName` is provided it will take precedence over `defaultTemplate`:
+
+ AView = Ember.View.extend({
+ defaultTemplate: Ember.Handlebars.compile('I was the default')
+ })
+
+ aView = AView.create({
+ template: Ember.Handlebars.compile('I was the template, not default')
+ })
+
+ Will result in the following HTML representation when rendered:
+
+
I was the template, not default
+
+ ## Layouts
+ Views can have a secondary template that wraps their main template. Like
+ primary templates, layouts can be any function that accepts an optional context
+ parameter and returns a string of HTML that will be inserted inside view's tag. Views whose HTML
+ element is self closing (e.g. ``) cannot have a layout and this property will be ignored.
+
+ Most typically in Ember a layout will be a compiled Ember.Handlebars template.
+
+ A view's layout can be set directly with the `layout` property or reference an
+ existing Handlebars template by name with the `layoutName` property.
+
+ A template used as a layout must contain a single use of the Handlebars `{{yield}}`
+ helper. The HTML contents of a view's rendered `template` will be inserted at this location:
+
+ AViewWithLayout = Ember.View.extend({
+ layout: Ember.Handlebars.compile("
{{yield}}
")
+ template: Ember.Handlebars.compile("I got wrapped"),
+ })
+
+ Will result in view instances with an HTML representation of:
+
+
+
+ I got wrapped
+
+
+
+ See `Handlebars.helpers.yield` for more information.
+
+ ## Responding to Browser Events
+ Views can respond to user-initiated events in one of three ways: method implementation,
+ through an event manager, and through `{{action}}` helper use in their template or layout.
+
+ ### Method Implementation
+ Views can respond to user-initiated events by implementing a method that matches the
+ event name. A `jQuery.Event` object will be passed as the argument to this method.
+
+ AView = Ember.View.extend({
+ click: function(event){
+ // will be called when when an instance's
+ // rendered element is clicked
+ }
+ })
+
+ ### Event Managers
+ Views can define an object as their `eventManager` property. This object can then
+ implement methods that match the desired event names. Matching events that occur
+ on the view's rendered HTML or the rendered HTML of any of its DOM descendants
+ will trigger this method. A `jQuery.Event` object will be passed as the first
+ argument to the method and an `Ember.View` object as the second. The `Ember.View`
+ will be the view whose rendered HTML was interacted with. This may be the view with
+ the `eventManager` property or one of its descendent views.
+
+ AView = Ember.View.extend({
+ eventManager: Ember.Object.create({
+ doubleClick: function(event, view){
+ // will be called when when an instance's
+ // rendered element or any rendering
+ // of this views's descendent
+ // elements is clicked
+ }
+ })
+ })
+
+
+ An event defined for an event manager takes precedence over events of the same
+ name handled through methods on the view.
+
+
+ AView = Ember.View.extend({
+ mouseEnter: function(event){
+ // will never trigger.
+ },
+ eventManager: Ember.Object.create({
+ mouseEnter: function(event, view){
+ // takes presedence over AView#mouseEnter
+ }
+ })
+ })
+
+ Similarly a view's event manager will take precedence for events of any views
+ rendered as a descendent. A method name that matches an event name will not be called
+ if the view instance was rendered inside the HTML representation of a view that has
+ an `eventManager` property defined that handles events of the name. Events not handled
+ by the event manager will still trigger method calls on the descendent.
+
+ OuterView = Ember.View.extend({
+ eventManager: Ember.Object.create({
+ template: Ember.Handlebars.compile("outer {{#view InnerView}}inner{{/view}} outer"),
+ mouseEnter: function(event, view){
+ // view might be instance of either
+ // OutsideView or InnerView depending on
+ // where on the page the user interaction occured
+ }
+ })
+ })
+
+ InnerView = Ember.View.extend({
+ click: function(event){
+ // will be called if rendered inside
+ // an OuterView because OuterView's
+ // eventManager doesn't handle click events
+ },
+ mouseEnter: function(event){
+ // will never be called if rendered inside
+ // an OuterView.
+ }
+ })
+
+ ### Handlebars `{{action}}` Helper
+ See `Handlebars.helpers.action`.
+
+ ### Event Names
+ Possible events names for any of the responding approaches described above are:
+
+ Touch events: 'touchStart', 'touchMove', 'touchEnd', 'touchCancel'
+
+ Keyboard events: 'keyDown', 'keyUp', 'keyPress'
+
+ Mouse events: 'mouseDown', 'mouseUp', 'contextMenu', 'click', 'doubleClick', 'mouseMove',
+ 'focusIn', 'focusOut', 'mouseEnter', 'mouseLeave'
+
+ Form events: 'submit', 'change', 'focusIn', 'focusOut', 'input'
+
+ HTML5 drag and drop events: 'dragStart', 'drag', 'dragEnter', 'dragLeave', 'drop', 'dragEnd'
+
+ ## Handlebars `{{view}}` Helper
+ Other `Ember.View` instances can be included as part of a view's template by using the `{{view}}`
+ Handlebars helper. See `Handlebars.helpers.view` for additional information.
+
+ @extends Ember.Object
+*/
+Ember.View = Ember.Object.extend(Ember.Evented,
+/** @scope Ember.View.prototype */ {
+
+ /** @private */
+ concatenatedProperties: ['classNames', 'classNameBindings', 'attributeBindings'],
+
+ /**
+ @type Boolean
+ @default true
+ @constant
+ */
+ isView: true,
+
+ // ..........................................................
+ // TEMPLATE SUPPORT
+ //
+
+ /**
+ The name of the template to lookup if no template is provided.
+
+ Ember.View will look for a template with this name in this view's
+ `templates` object. By default, this will be a global object
+ shared in `Ember.TEMPLATES`.
+
+ @type String
+ @default null
+ */
+ templateName: null,
+
+ /**
+ The name of the layout to lookup if no layout is provided.
+
+ Ember.View will look for a template with this name in this view's
+ `templates` object. By default, this will be a global object
+ shared in `Ember.TEMPLATES`.
+
+ @type String
+ @default null
+ */
+ layoutName: null,
+
+ /**
+ The hash in which to look for `templateName`.
+
+ @type Ember.Object
+ @default Ember.TEMPLATES
+ */
+ templates: Ember.TEMPLATES,
+
+ /**
+ The template used to render the view. This should be a function that
+ accepts an optional context parameter and returns a string of HTML that
+ will be inserted into the DOM relative to its parent view.
+
+ In general, you should set the `templateName` property instead of setting
+ the template yourself.
+
+ @field
+ @type Function
+ */
+ template: Ember.computed(function(key, value) {
+ if (value !== undefined) { return value; }
+
+ var templateName = get(this, 'templateName'),
+ template = this.templateForName(templateName, 'template');
+
+ return template || get(this, 'defaultTemplate');
+ }).property('templateName').cacheable(),
+
+ /**
+ The controller managing this view. If this property is set, it will be
+ made available for use by the template.
+
+ @type Object
+ */
+ controller: Ember.computed(function(key, value) {
+ var parentView;
+
+ if (arguments.length === 2) {
+ return value;
+ } else {
+ parentView = get(this, 'parentView');
+ return parentView ? get(parentView, 'controller') : null;
+ }
+ }).property().cacheable(),
+
+ /**
+ A view may contain a layout. A layout is a regular template but
+ supersedes the `template` property during rendering. It is the
+ responsibility of the layout template to retrieve the `template`
+ property from the view (or alternatively, call `Handlebars.helpers.yield`,
+ `{{yield}}`) to render it in the correct location.
+
+ This is useful for a view that has a shared wrapper, but which delegates
+ the rendering of the contents of the wrapper to the `template` property
+ on a subclass.
+
+ @field
+ @type Function
+ */
+ layout: Ember.computed(function(key, value) {
+ if (arguments.length === 2) { return value; }
+
+ var layoutName = get(this, 'layoutName'),
+ layout = this.templateForName(layoutName, 'layout');
+
+ return layout || get(this, 'defaultLayout');
+ }).property('layoutName').cacheable(),
+
+ templateForName: function(name, type) {
+ if (!name) { return; }
+
+ var templates = get(this, 'templates'),
+ template = get(templates, name);
+
+ if (!template) {
+ throw new Ember.Error(fmt('%@ - Unable to find %@ "%@".', [this, type, name]));
+ }
+
+ return template;
+ },
+
+ /**
+ The object from which templates should access properties.
+
+ This object will be passed to the template function each time the render
+ method is called, but it is up to the individual function to decide what
+ to do with it.
+
+ By default, this will be the view itself.
+
+ @type Object
+ */
+ context: Ember.computed(function(key, value) {
+ if (arguments.length === 2) {
+ set(this, '_context', value);
+ return value;
+ } else {
+ return get(this, '_context');
+ }
+ }).cacheable(),
+
+ /**
+ @private
+
+ Private copy of the view's template context. This can be set directly
+ by Handlebars without triggering the observer that causes the view
+ to be re-rendered.
+ */
+ _context: Ember.computed(function(key, value) {
+ var parentView, controller;
+
+ if (arguments.length === 2) {
+ return value;
+ }
+
+ if (VIEW_PRESERVES_CONTEXT) {
+ if (controller = get(this, 'controller')) {
+ return controller;
+ }
+
+ parentView = get(this, '_parentView');
+ if (parentView) {
+ return get(parentView, '_context');
+ }
+ }
+
+ return this;
+ }).cacheable(),
+
+ /**
+ If a value that affects template rendering changes, the view should be
+ re-rendered to reflect the new value.
+
+ @private
+ */
+ _displayPropertyDidChange: Ember.observer(function() {
+ this.rerender();
+ }, 'context', 'controller'),
+
+ /**
+ If the view is currently inserted into the DOM of a parent view, this
+ property will point to the parent of the view.
+
+ @type Ember.View
+ @default null
+ */
+ parentView: Ember.computed(function() {
+ var parent = get(this, '_parentView');
+
+ if (parent && parent.isVirtual) {
+ return get(parent, 'parentView');
+ } else {
+ return parent;
+ }
+ }).property('_parentView').volatile(),
+
+ _parentView: null,
+
+ // return the current view, not including virtual views
+ concreteView: Ember.computed(function() {
+ if (!this.isVirtual) { return this; }
+ else { return get(this, 'parentView'); }
+ }).property('_parentView').volatile(),
+
+ /**
+ If false, the view will appear hidden in DOM.
+
+ @type Boolean
+ @default null
+ */
+ isVisible: true,
+
+ /**
+ Array of child views. You should never edit this array directly.
+ Instead, use appendChild and removeFromParent.
+
+ @private
+ @type Array
+ @default []
+ */
+ childViews: childViewsProperty,
+
+ _childViews: [],
+
+ /**
+ When it's a virtual view, we need to notify the parent that their
+ childViews will change.
+ */
+ _childViewsWillChange: Ember.beforeObserver(function() {
+ if (this.isVirtual) {
+ var parentView = get(this, 'parentView');
+ if (parentView) { Ember.propertyWillChange(parentView, 'childViews'); }
+ }
+ }, 'childViews'),
+
+ /**
+ When it's a virtual view, we need to notify the parent that their
+ childViews did change.
+ */
+ _childViewsDidChange: Ember.observer(function() {
+ if (this.isVirtual) {
+ var parentView = get(this, 'parentView');
+ if (parentView) { Ember.propertyDidChange(parentView, 'childViews'); }
+ }
+ }, 'childViews'),
+
+ /**
+ Return the nearest ancestor that is an instance of the provided
+ class.
+
+ @param {Class} klass Subclass of Ember.View (or Ember.View itself)
+ @returns Ember.View
+ */
+ nearestInstanceOf: function(klass) {
+ var view = get(this, 'parentView');
+
+ while (view) {
+ if(view instanceof klass) { return view; }
+ view = get(view, 'parentView');
+ }
+ },
+
+ /**
+ Return the nearest ancestor that has a given property.
+
+ @param {String} property A property name
+ @returns Ember.View
+ */
+ nearestWithProperty: function(property) {
+ var view = get(this, 'parentView');
+
+ while (view) {
+ if (property in view) { return view; }
+ view = get(view, 'parentView');
+ }
+ },
+
+ /**
+ Return the nearest ancestor whose parent is an instance of
+ `klass`.
+
+ @param {Class} klass Subclass of Ember.View (or Ember.View itself)
+ @returns Ember.View
+ */
+ nearestChildOf: function(klass) {
+ var view = get(this, 'parentView');
+
+ while (view) {
+ if(get(view, 'parentView') instanceof klass) { return view; }
+ view = get(view, 'parentView');
+ }
+ },
+
+ /**
+ Return the nearest ancestor that is an Ember.CollectionView
+
+ @returns Ember.CollectionView
+ */
+ collectionView: Ember.computed(function() {
+ return this.nearestInstanceOf(Ember.CollectionView);
+ }).cacheable(),
+
+ /**
+ Return the nearest ancestor that is a direct child of
+ an Ember.CollectionView
+
+ @returns Ember.View
+ */
+ itemView: Ember.computed(function() {
+ return this.nearestChildOf(Ember.CollectionView);
+ }).cacheable(),
+
+ /**
+ Return the nearest ancestor that has the property
+ `content`.
+
+ @returns Ember.View
+ */
+ contentView: Ember.computed(function() {
+ return this.nearestWithProperty('content');
+ }).cacheable(),
+
+ /**
+ @private
+
+ When the parent view changes, recursively invalidate
+ collectionView, itemView, and contentView
+ */
+ _parentViewDidChange: Ember.observer(function() {
+ if (this.isDestroying) { return; }
+
+ this.invokeRecursively(function(view) {
+ view.propertyDidChange('collectionView');
+ view.propertyDidChange('itemView');
+ view.propertyDidChange('contentView');
+ });
+
+ if (getPath(this, 'parentView.controller') && !get(this, 'controller')) {
+ this.notifyPropertyChange('controller');
+ }
+ }, '_parentView'),
+
+ _controllerDidChange: Ember.observer(function() {
+ if (this.isDestroying) { return; }
+
+ this.forEachChildView(function(view) {
+ view.propertyDidChange('controller');
+ });
+ }, 'controller'),
+
+ cloneKeywords: function() {
+ var templateData = get(this, 'templateData');
+
+ var keywords = templateData ? Ember.copy(templateData.keywords) : {};
+ keywords.view = get(this, 'concreteView');
+ keywords.controller = get(this, 'controller');
+
+ return keywords;
+ },
+
+ /**
+ Called on your view when it should push strings of HTML into a
+ Ember.RenderBuffer. Most users will want to override the `template`
+ or `templateName` properties instead of this method.
+
+ By default, Ember.View will look for a function in the `template`
+ property and invoke it with the value of `context`. The value of
+ `context` will be the view's controller unless you override it.
+
+ @param {Ember.RenderBuffer} buffer The render buffer
+ */
+ render: function(buffer) {
+ // If this view has a layout, it is the responsibility of the
+ // the layout to render the view's template. Otherwise, render the template
+ // directly.
+ var template = get(this, 'layout') || get(this, 'template');
+
+ if (template) {
+ var context = get(this, '_context');
+ var keywords = this.cloneKeywords();
+
+ var data = {
+ view: this,
+ buffer: buffer,
+ isRenderData: true,
+ keywords: keywords
+ };
+
+ // Invoke the template with the provided template context, which
+ // is the view by default. A hash of data is also passed that provides
+ // the template with access to the view and render buffer.
+
+ Ember.assert('template must be a function. Did you mean to call Ember.Handlebars.compile("...") or specify templateName instead?', typeof template === 'function');
+ // The template should write directly to the render buffer instead
+ // of returning a string.
+ var output = template(context, { data: data });
+
+ // If the template returned a string instead of writing to the buffer,
+ // push the string onto the buffer.
+ if (output !== undefined) { buffer.push(output); }
+ }
+ },
+
+ invokeForState: function(name) {
+ var stateName = this.state, args, fn;
+
+ // try to find the function for the state in the cache
+ if (fn = invokeForState[stateName][name]) {
+ args = a_slice.call(arguments);
+ args[0] = this;
+
+ return fn.apply(this, args);
+ }
+
+ // otherwise, find and cache the function for this state
+ var parent = this, states = parent.states, state;
+
+ while (states) {
+ state = states[stateName];
+
+ while (state) {
+ fn = state[name];
+
+ if (fn) {
+ invokeForState[stateName][name] = fn;
+
+ args = a_slice.call(arguments, 1);
+ args.unshift(this);
+
+ return fn.apply(this, args);
+ }
+
+ state = state.parentState;
+ }
+
+ states = states.parent;
+ }
+ },
+
+ /**
+ Renders the view again. This will work regardless of whether the
+ view is already in the DOM or not. If the view is in the DOM, the
+ rendering process will be deferred to give bindings a chance
+ to synchronize.
+
+ If children were added during the rendering process using `appendChild`,
+ `rerender` will remove them, because they will be added again
+ if needed by the next `render`.
+
+ In general, if the display of your view changes, you should modify
+ the DOM element directly instead of manually calling `rerender`, which can
+ be slow.
+ */
+ rerender: function() {
+ return this.invokeForState('rerender');
+ },
+
+ clearRenderedChildren: function() {
+ var lengthBefore = this.lengthBeforeRender,
+ lengthAfter = this.lengthAfterRender;
+
+ // If there were child views created during the last call to render(),
+ // remove them under the assumption that they will be re-created when
+ // we re-render.
+
+ // VIEW-TODO: Unit test this path.
+ var childViews = get(this, '_childViews');
+ for (var i=lengthAfter-1; i>=lengthBefore; i--) {
+ if (childViews[i]) { childViews[i].destroy(); }
+ }
+ },
+
+ /**
+ @private
+
+ Iterates over the view's `classNameBindings` array, inserts the value
+ of the specified property into the `classNames` array, then creates an
+ observer to update the view's element if the bound property ever changes
+ in the future.
+ */
+ _applyClassNameBindings: function() {
+ var classBindings = get(this, 'classNameBindings'),
+ classNames = get(this, 'classNames'),
+ elem, newClass, dasherizedClass;
+
+ if (!classBindings) { return; }
+
+ // Loop through all of the configured bindings. These will be either
+ // property names ('isUrgent') or property paths relative to the view
+ // ('content.isUrgent')
+ a_forEach(classBindings, function(binding) {
+
+ // Variable in which the old class value is saved. The observer function
+ // closes over this variable, so it knows which string to remove when
+ // the property changes.
+ var oldClass, property;
+
+ // Set up an observer on the context. If the property changes, toggle the
+ // class name.
+ var observer = function() {
+ // Get the current value of the property
+ newClass = this._classStringForProperty(binding);
+ elem = this.$();
+
+ // If we had previously added a class to the element, remove it.
+ if (oldClass) {
+ elem.removeClass(oldClass);
+ // Also remove from classNames so that if the view gets rerendered,
+ // the class doesn't get added back to the DOM.
+ classNames.removeObject(oldClass);
+ }
+
+ // If necessary, add a new class. Make sure we keep track of it so
+ // it can be removed in the future.
+ if (newClass) {
+ elem.addClass(newClass);
+ oldClass = newClass;
+ } else {
+ oldClass = null;
+ }
+ };
+
+ // Get the class name for the property at its current value
+ dasherizedClass = this._classStringForProperty(binding);
+
+ if (dasherizedClass) {
+ // Ensure that it gets into the classNames array
+ // so it is displayed when we render.
+ classNames.push(dasherizedClass);
+
+ // Save a reference to the class name so we can remove it
+ // if the observer fires. Remember that this variable has
+ // been closed over by the observer.
+ oldClass = dasherizedClass;
+ }
+
+ // Extract just the property name from bindings like 'foo:bar'
+ property = binding.split(':')[0];
+ addObserver(this, property, observer);
+ }, this);
+ },
+
+ /**
+ Iterates through the view's attribute bindings, sets up observers for each,
+ then applies the current value of the attributes to the passed render buffer.
+
+ @param {Ember.RenderBuffer} buffer
+ */
+ _applyAttributeBindings: function(buffer) {
+ var attributeBindings = get(this, 'attributeBindings'),
+ attributeValue, elem, type;
+
+ if (!attributeBindings) { return; }
+
+ a_forEach(attributeBindings, function(binding) {
+ var split = binding.split(':'),
+ property = split[0],
+ attributeName = split[1] || property;
+
+ // Create an observer to add/remove/change the attribute if the
+ // JavaScript property changes.
+ var observer = function() {
+ elem = this.$();
+ attributeValue = get(this, property);
+
+ Ember.View.applyAttributeBindings(elem, attributeName, attributeValue);
+ };
+
+ addObserver(this, property, observer);
+
+ // Determine the current value and add it to the render buffer
+ // if necessary.
+ attributeValue = get(this, property);
+ Ember.View.applyAttributeBindings(buffer, attributeName, attributeValue);
+ }, this);
+ },
+
+ /**
+ @private
+
+ Given a property name, returns a dasherized version of that
+ property name if the property evaluates to a non-falsy value.
+
+ For example, if the view has property `isUrgent` that evaluates to true,
+ passing `isUrgent` to this method will return `"is-urgent"`.
+ */
+ _classStringForProperty: function(property) {
+ var split = property.split(':'),
+ className = split[1];
+
+ property = split[0];
+
+ // TODO: Remove this `false` when the `getPath` globals support is removed
+ var val = Ember.getPath(this, property, false);
+ if (val === undefined && Ember.isGlobalPath(property)) {
+ val = Ember.getPath(window, property);
+ }
+
+ // If the value is truthy and we're using the colon syntax,
+ // we should return the className directly
+ if (!!val && className) {
+ return className;
+
+ // If value is a Boolean and true, return the dasherized property
+ // name.
+ } else if (val === true) {
+ // Normalize property path to be suitable for use
+ // as a class name. For exaple, content.foo.barBaz
+ // becomes bar-baz.
+ var parts = property.split('.');
+ return Ember.String.dasherize(parts[parts.length-1]);
+
+ // If the value is not false, undefined, or null, return the current
+ // value of the property.
+ } else if (val !== false && val !== undefined && val !== null) {
+ return val;
+
+ // Nothing to display. Return null so that the old class is removed
+ // but no new class is added.
+ } else {
+ return null;
+ }
+ },
+
+ // ..........................................................
+ // ELEMENT SUPPORT
+ //
+
+ /**
+ Returns the current DOM element for the view.
+
+ @field
+ @type DOMElement
+ */
+ element: Ember.computed(function(key, value) {
+ if (value !== undefined) {
+ return this.invokeForState('setElement', value);
+ } else {
+ return this.invokeForState('getElement');
+ }
+ }).property('_parentView').cacheable(),
+
+ /**
+ Returns a jQuery object for this view's element. If you pass in a selector
+ string, this method will return a jQuery object, using the current element
+ as its buffer.
+
+ For example, calling `view.$('li')` will return a jQuery object containing
+ all of the `li` elements inside the DOM element of this view.
+
+ @param {String} [selector] a jQuery-compatible selector string
+ @returns {Ember.CoreQuery} the CoreQuery object for the DOM node
+ */
+ $: function(sel) {
+ return this.invokeForState('$', sel);
+ },
+
+ /** @private */
+ mutateChildViews: function(callback) {
+ var childViews = get(this, '_childViews'),
+ idx = get(childViews, 'length'),
+ view;
+
+ while(--idx >= 0) {
+ view = childViews[idx];
+ callback.call(this, view, idx);
+ }
+
+ return this;
+ },
+
+ /** @private */
+ forEachChildView: function(callback) {
+ var childViews = get(this, '_childViews');
+
+ if (!childViews) { return this; }
+
+ var len = get(childViews, 'length'),
+ view, idx;
+
+ for(idx = 0; idx < len; idx++) {
+ view = childViews[idx];
+ callback.call(this, view);
+ }
+
+ return this;
+ },
+
+ /**
+ Appends the view's element to the specified parent element.
+
+ If the view does not have an HTML representation yet, `createElement()`
+ will be called automatically.
+
+ Note that this method just schedules the view to be appended; the DOM
+ element will not be appended to the given element until all bindings have
+ finished synchronizing.
+
+ This is not typically a function that you will need to call directly
+ when building your application. You might consider using Ember.ContainerView
+ instead. If you do need to use appendTo, be sure that the target element you
+ are providing is associated with an Ember.Application and does not have an
+ ancestor element that is associated with an Ember view.
+
+ @param {String|DOMElement|jQuery} A selector, element, HTML string, or jQuery object
+ @returns {Ember.View} receiver
+ */
+ appendTo: function(target) {
+ Ember.assert("You cannot append to an existing Ember.View. Consider using Ember.ContainerView instead.", !Ember.$(target).is('.ember-view') && !Ember.$(target).parents().is('.ember-view'));
+
+ // Schedule the DOM element to be created and appended to the given
+ // element after bindings have synchronized.
+ this._insertElementLater(function() {
+ this.$().appendTo(target);
+ });
+
+ return this;
+ },
+
+ /**
+ Replaces the content of the specified parent element with this view's element.
+ If the view does not have an HTML representation yet, `createElement()`
+ will be called automatically.
+
+ Note that this method just schedules the view to be appended; the DOM
+ element will not be appended to the given element until all bindings have
+ finished synchronizing
+
+ @param {String|DOMElement|jQuery} A selector, element, HTML string, or jQuery object
+ @returns {Ember.View} received
+ */
+ replaceIn: function(target) {
+ Ember.assert("You cannot replace an existing Ember.View. Consider using Ember.ContainerView instead.", !Ember.$(target).is('.ember-view') && !Ember.$(target).parents().is('.ember-view'));
+
+ this._insertElementLater(function() {
+ Ember.$(target).empty();
+ this.$().appendTo(target);
+ });
+
+ return this;
+ },
+
+ /**
+ @private
+
+ Schedules a DOM operation to occur during the next render phase. This
+ ensures that all bindings have finished synchronizing before the view is
+ rendered.
+
+ To use, pass a function that performs a DOM operation..
+
+ Before your function is called, this view and all child views will receive
+ the `willInsertElement` event. After your function is invoked, this view
+ and all of its child views will receive the `didInsertElement` event.
+
+ view._insertElementLater(function() {
+ this.createElement();
+ this.$().appendTo('body');
+ });
+
+ @param {Function} fn the function that inserts the element into the DOM
+ */
+ _insertElementLater: function(fn) {
+ this._lastInsert = Ember.guidFor(fn);
+ Ember.run.schedule('render', this, this.invokeForState, 'insertElement', fn);
+ },
+
+ /**
+ Appends the view's element to the document body. If the view does
+ not have an HTML representation yet, `createElement()` will be called
+ automatically.
+
+ Note that this method just schedules the view to be appended; the DOM
+ element will not be appended to the document body until all bindings have
+ finished synchronizing.
+
+ @returns {Ember.View} receiver
+ */
+ append: function() {
+ return this.appendTo(document.body);
+ },
+
+ /**
+ Removes the view's element from the element to which it is attached.
+
+ @returns {Ember.View} receiver
+ */
+ remove: function() {
+ // What we should really do here is wait until the end of the run loop
+ // to determine if the element has been re-appended to a different
+ // element.
+ // In the interim, we will just re-render if that happens. It is more
+ // important than elements get garbage collected.
+ this.destroyElement();
+ this.invokeRecursively(function(view) {
+ view.clearRenderedChildren();
+ });
+ },
+
+ /**
+ The ID to use when trying to locate the element in the DOM. If you do not
+ set the elementId explicitly, then the view's GUID will be used instead.
+ This ID must be set at the time the view is created.
+
+ @type String
+ @readOnly
+ */
+ elementId: Ember.computed(function(key, value) {
+ return value !== undefined ? value : Ember.guidFor(this);
+ }).cacheable(),
+
+ /**
+ @private
+
+ TODO: Perhaps this should be removed from the production build somehow.
+ */
+ _elementIdDidChange: Ember.beforeObserver(function() {
+ throw "Changing a view's elementId after creation is not allowed.";
+ }, 'elementId'),
+
+ /**
+ Attempts to discover the element in the parent element. The default
+ implementation looks for an element with an ID of elementId (or the view's
+ guid if elementId is null). You can override this method to provide your
+ own form of lookup. For example, if you want to discover your element
+ using a CSS class name instead of an ID.
+
+ @param {DOMElement} parentElement The parent's DOM element
+ @returns {DOMElement} The discovered element
+ */
+ findElementInParentElement: function(parentElem) {
+ var id = "#" + get(this, 'elementId');
+ return Ember.$(id)[0] || Ember.$(id, parentElem)[0];
+ },
+
+ /**
+ Creates a new renderBuffer with the passed tagName. You can override this
+ method to provide further customization to the buffer if needed. Normally
+ you will not need to call or override this method.
+
+ @returns {Ember.RenderBuffer}
+ */
+ renderBuffer: function(tagName) {
+ tagName = tagName || get(this, 'tagName');
+
+ // Explicitly check for null or undefined, as tagName
+ // may be an empty string, which would evaluate to false.
+ if (tagName === null || tagName === undefined) {
+ tagName = 'div';
+ }
+
+ return Ember.RenderBuffer(tagName);
+ },
+
+ /**
+ Creates a DOM representation of the view and all of its
+ child views by recursively calling the `render()` method.
+
+ After the element has been created, `didInsertElement` will
+ be called on this view and all of its child views.
+
+ @returns {Ember.View} receiver
+ */
+ createElement: function() {
+ if (get(this, 'element')) { return this; }
+
+ var buffer = this.renderToBuffer();
+ set(this, 'element', buffer.element());
+
+ return this;
+ },
+
+ /**
+ Called when a view is going to insert an element into the DOM.
+ */
+ willInsertElement: Ember.K,
+
+ /**
+ Called when the element of the view has been inserted into the DOM.
+ Override this function to do any set up that requires an element in the
+ document body.
+ */
+ didInsertElement: Ember.K,
+
+ /**
+ Called when the view is about to rerender, but before anything has
+ been torn down. This is a good opportunity to tear down any manual
+ observers you have installed based on the DOM state
+ */
+ willRerender: Ember.K,
+
+ /**
+ Run this callback on the current view and recursively on child views.
+
+ @private
+ */
+ invokeRecursively: function(fn) {
+ fn.call(this, this);
+
+ this.forEachChildView(function(view) {
+ view.invokeRecursively(fn);
+ });
+ },
+
+ /**
+ Invalidates the cache for a property on all child views.
+ */
+ invalidateRecursively: function(key) {
+ this.forEachChildView(function(view) {
+ view.propertyDidChange(key);
+ });
+ },
+
+ /**
+ @private
+
+ Invokes the receiver's willInsertElement() method if it exists and then
+ invokes the same on all child views.
+
+ NOTE: In some cases this was called when the element existed. This no longer
+ works so we let people know. We can remove this warning code later.
+ */
+ _notifyWillInsertElement: function() {
+ this.invokeRecursively(function(view) {
+ view.trigger('willInsertElement');
+ });
+ },
+
+ /**
+ @private
+
+ Invokes the receiver's didInsertElement() method if it exists and then
+ invokes the same on all child views.
+ */
+ _notifyDidInsertElement: function() {
+ this.invokeRecursively(function(view) {
+ view.trigger('didInsertElement');
+ });
+ },
+
+ /**
+ @private
+
+ Invokes the receiver's willRerender() method if it exists and then
+ invokes the same on all child views.
+ */
+ _notifyWillRerender: function() {
+ this.invokeRecursively(function(view) {
+ view.trigger('willRerender');
+ });
+ },
+
+ /**
+ Destroys any existing element along with the element for any child views
+ as well. If the view does not currently have a element, then this method
+ will do nothing.
+
+ If you implement willDestroyElement() on your view, then this method will
+ be invoked on your view before your element is destroyed to give you a
+ chance to clean up any event handlers, etc.
+
+ If you write a willDestroyElement() handler, you can assume that your
+ didInsertElement() handler was called earlier for the same element.
+
+ Normally you will not call or override this method yourself, but you may
+ want to implement the above callbacks when it is run.
+
+ @returns {Ember.View} receiver
+ */
+ destroyElement: function() {
+ return this.invokeForState('destroyElement');
+ },
+
+ /**
+ Called when the element of the view is going to be destroyed. Override
+ this function to do any teardown that requires an element, like removing
+ event listeners.
+ */
+ willDestroyElement: function() {},
+
+ /**
+ @private
+
+ Invokes the `willDestroyElement` callback on the view and child views.
+ */
+ _notifyWillDestroyElement: function() {
+ this.invokeRecursively(function(view) {
+ view.trigger('willDestroyElement');
+ });
+ },
+
+ /** @private (nodoc) */
+ _elementWillChange: Ember.beforeObserver(function() {
+ this.forEachChildView(function(view) {
+ Ember.propertyWillChange(view, 'element');
+ });
+ }, 'element'),
+
+ /**
+ @private
+
+ If this view's element changes, we need to invalidate the caches of our
+ child views so that we do not retain references to DOM elements that are
+ no longer needed.
+
+ @observes element
+ */
+ _elementDidChange: Ember.observer(function() {
+ this.forEachChildView(function(view) {
+ Ember.propertyDidChange(view, 'element');
+ });
+ }, 'element'),
+
+ /**
+ Called when the parentView property has changed.
+
+ @function
+ */
+ parentViewDidChange: Ember.K,
+
+ /**
+ @private
+
+ Invoked by the view system when this view needs to produce an HTML
+ representation. This method will create a new render buffer, if needed,
+ then apply any default attributes, such as class names and visibility.
+ Finally, the `render()` method is invoked, which is responsible for
+ doing the bulk of the rendering.
+
+ You should not need to override this method; instead, implement the
+ `template` property, or if you need more control, override the `render`
+ method.
+
+ @param {Ember.RenderBuffer} buffer the render buffer. If no buffer is
+ passed, a default buffer, using the current view's `tagName`, will
+ be used.
+ */
+ renderToBuffer: function(parentBuffer, bufferOperation) {
+ var buffer;
+
+ Ember.run.sync();
+
+ // Determine where in the parent buffer to start the new buffer.
+ // By default, a new buffer will be appended to the parent buffer.
+ // The buffer operation may be changed if the child views array is
+ // mutated by Ember.ContainerView.
+ bufferOperation = bufferOperation || 'begin';
+
+ // If this is the top-most view, start a new buffer. Otherwise,
+ // create a new buffer relative to the original using the
+ // provided buffer operation (for example, `insertAfter` will
+ // insert a new buffer after the "parent buffer").
+ if (parentBuffer) {
+ var tagName = get(this, 'tagName');
+ if (tagName === null || tagName === undefined) {
+ tagName = 'div';
+ }
+
+ buffer = parentBuffer[bufferOperation](tagName);
+ } else {
+ buffer = this.renderBuffer();
+ }
+
+ this.buffer = buffer;
+ this.transitionTo('inBuffer', false);
+
+ this.lengthBeforeRender = get(get(this, '_childViews'), 'length');
+
+ this.beforeRender(buffer);
+ this.render(buffer);
+ this.afterRender(buffer);
+
+ this.lengthAfterRender = get(get(this, '_childViews'), 'length');
+
+ return buffer;
+ },
+
+ beforeRender: function(buffer) {
+ this.applyAttributesToBuffer(buffer);
+ },
+
+ afterRender: Ember.K,
+
+ /**
+ @private
+ */
+ applyAttributesToBuffer: function(buffer) {
+ // Creates observers for all registered class name and attribute bindings,
+ // then adds them to the element.
+ this._applyClassNameBindings();
+
+ // Pass the render buffer so the method can apply attributes directly.
+ // This isn't needed for class name bindings because they use the
+ // existing classNames infrastructure.
+ this._applyAttributeBindings(buffer);
+
+
+ a_forEach(get(this, 'classNames'), function(name){ buffer.addClass(name); });
+ buffer.id(get(this, 'elementId'));
+
+ var role = get(this, 'ariaRole');
+ if (role) {
+ buffer.attr('role', role);
+ }
+
+ if (get(this, 'isVisible') === false) {
+ buffer.style('display', 'none');
+ }
+ },
+
+ // ..........................................................
+ // STANDARD RENDER PROPERTIES
+ //
+
+ /**
+ Tag name for the view's outer element. The tag name is only used when
+ an element is first created. If you change the tagName for an element, you
+ must destroy and recreate the view element.
+
+ By default, the render buffer will use a `
` tag for views.
+
+ @type String
+ @default null
+ */
+
+ // We leave this null by default so we can tell the difference between
+ // the default case and a user-specified tag.
+ tagName: null,
+
+ /**
+ The WAI-ARIA role of the control represented by this view. For example, a
+ button may have a role of type 'button', or a pane may have a role of
+ type 'alertdialog'. This property is used by assistive software to help
+ visually challenged users navigate rich web applications.
+
+ The full list of valid WAI-ARIA roles is available at:
+ http://www.w3.org/TR/wai-aria/roles#roles_categorization
+
+ @type String
+ @default null
+ */
+ ariaRole: null,
+
+ /**
+ Standard CSS class names to apply to the view's outer element. This
+ property automatically inherits any class names defined by the view's
+ superclasses as well.
+
+ @type Array
+ @default ['ember-view']
+ */
+ classNames: ['ember-view'],
+
+ /**
+ A list of properties of the view to apply as class names. If the property
+ is a string value, the value of that string will be applied as a class
+ name.
+
+ // Applies the 'high' class to the view element
+ Ember.View.create({
+ classNameBindings: ['priority']
+ priority: 'high'
+ });
+
+ If the value of the property is a Boolean, the name of that property is
+ added as a dasherized class name.
+
+ // Applies the 'is-urgent' class to the view element
+ Ember.View.create({
+ classNameBindings: ['isUrgent']
+ isUrgent: true
+ });
+
+ If you would prefer to use a custom value instead of the dasherized
+ property name, you can pass a binding like this:
+
+ // Applies the 'urgent' class to the view element
+ Ember.View.create({
+ classNameBindings: ['isUrgent:urgent']
+ isUrgent: true
+ });
+
+ This list of properties is inherited from the view's superclasses as well.
+
+ @type Array
+ @default []
+ */
+ classNameBindings: [],
+
+ /**
+ A list of properties of the view to apply as attributes. If the property is
+ a string value, the value of that string will be applied as the attribute.
+
+ // Applies the type attribute to the element
+ // with the value "button", like
+ Ember.View.create({
+ attributeBindings: ['type'],
+ type: 'button'
+ });
+
+ If the value of the property is a Boolean, the name of that property is
+ added as an attribute.
+
+ // Renders something like
+
+ Removing a view
+
+ aContainer.get('childViews') // [aContainer.aView, aContainer.bView]
+ aContainer.get('childViews').removeObject(aContainer.get('bView'))
+ aContainer.get('childViews') // [aContainer.aView]
+
+ Will result in the following HTML
+
+
+
A
+
+
+
+ Similarly, adding a child view is accomplished by adding `Ember.View` instances to the
+ container's `childViews` property.
+
+ Given an empty `` the following code
+
+ aContainer = Ember.ContainerView.create({
+ classNames: ['the-container'],
+ childViews: ['aView', 'bView'],
+ aView: Ember.View.create({
+ template: Ember.Handlebars.compile("A")
+ }),
+ bView: Ember.View.create({
+ template: Ember.Handlebars.compile("B")
+ })
+ })
+
+ aContainer.appendTo('body')
+
+ Results in the HTML
+
+
+
A
+
B
+
+
+ Adding a view
+
+ AnotherViewClass = Ember.View.extend({
+ template: Ember.Handlebars.compile("Another view")
+ })
+
+ aContainer.get('childViews') // [aContainer.aView, aContainer.bView]
+ aContainer.get('childViews').pushObject(AnotherViewClass.create())
+ aContainer.get('childViews') // [aContainer.aView, aContainer.bView, ]
+
+ Will result in the following HTML
+
+
+
A
+
B
+
Another view
+
+
+
+ Direct manipulation of childViews presence or absence in the DOM via calls to
+ `remove` or `removeFromParent` or calls to a container's `removeChild` may not behave
+ correctly.
+
+ Calling `remove()` on a child view will remove the view's HTML, but it will remain as part of its
+ container's `childView`s property.
+
+ Calling `removeChild()` on the container will remove the passed view instance from the container's
+ `childView`s but keep its HTML within the container's rendered view.
+
+ Calling `removeFromParent()` behaves as expected but should be avoided in favor of direct
+ manipulation of a container's `childViews` property.
+
+ aContainer = Ember.ContainerView.create({
+ classNames: ['the-container'],
+ childViews: ['aView', 'bView'],
+ aView: Ember.View.create({
+ template: Ember.Handlebars.compile("A")
+ }),
+ bView: Ember.View.create({
+ template: Ember.Handlebars.compile("B")
+ })
+ })
+
+ aContainer.appendTo('body')
+
+ Results in the HTML
+
+
+
A
+
B
+
+
+ Calling `aContainer.get('aView').removeFromParent()` will result in the following HTML
+
+
+
B
+
+
+ And the `Ember.View` instance stored in `aContainer.aView` will be removed from `aContainer`'s
+ `childViews` array.
+
+ ## Templates and Layout
+ A `template`, `templateName`, `defaultTemplate`, `layout`, `layoutName` or `defaultLayout`
+ property on a container view will not result in the template or layout being rendered.
+ The HTML contents of a `Ember.ContainerView`'s DOM representation will only be the rendered HTML
+ of its child views.
+
+ ## Binding a View to Display
+
+ If you would like to display a single view in your ContainerView, you can set its `currentView`
+ property. When the `currentView` property is set to a view instance, it will be added to the
+ ContainerView's `childViews` array. If the `currentView` property is later changed to a
+ different view, the new view will replace the old view. If `currentView` is set to `null`, the
+ last `currentView` will be removed.
+
+ This functionality is useful for cases where you want to bind the display of a ContainerView to
+ a controller or state manager. For example, you can bind the `currentView` of a container to
+ a controller like this:
+
+ // Controller
+ App.appController = Ember.Object.create({
+ view: Ember.View.create({
+ templateName: 'person_template'
+ })
+ });
+
+ // Handlebars template
+ {{view Ember.ContainerView currentViewBinding="App.appController.view"}}
+
+ @extends Ember.View
+*/
+
+Ember.ContainerView = Ember.View.extend({
+
+ init: function() {
+ var childViews = get(this, 'childViews');
+ Ember.defineProperty(this, 'childViews', childViewsProperty);
+
+ this._super();
+
+ var _childViews = get(this, '_childViews');
+
+ forEach(childViews, function(viewName, idx) {
+ var view;
+
+ if ('string' === typeof viewName) {
+ view = get(this, viewName);
+ view = this.createChildView(view);
+ set(this, viewName, view);
+ } else {
+ view = this.createChildView(viewName);
+ }
+
+ _childViews[idx] = view;
+ }, this);
+
+ // Make the _childViews array observable
+ Ember.A(_childViews);
+
+ // Sets up an array observer on the child views array. This
+ // observer will detect when child views are added or removed
+ // and update the DOM to reflect the mutation.
+ get(this, 'childViews').addArrayObserver(this, {
+ willChange: 'childViewsWillChange',
+ didChange: 'childViewsDidChange'
+ });
+ },
+
+ /**
+ Instructs each child view to render to the passed render buffer.
+
+ @param {Ember.RenderBuffer} buffer the buffer to render to
+ @private
+ */
+ render: function(buffer) {
+ this.forEachChildView(function(view) {
+ view.renderToBuffer(buffer);
+ });
+ },
+
+ /**
+ When the container view is destroyed, tear down the child views
+ array observer.
+
+ @private
+ */
+ willDestroy: function() {
+ get(this, 'childViews').removeArrayObserver(this, {
+ willChange: 'childViewsWillChange',
+ didChange: 'childViewsDidChange'
+ });
+
+ this._super();
+ },
+
+ /**
+ When a child view is removed, destroy its element so that
+ it is removed from the DOM.
+
+ The array observer that triggers this action is set up in the
+ `renderToBuffer` method.
+
+ @private
+ @param {Ember.Array} views the child views array before mutation
+ @param {Number} start the start position of the mutation
+ @param {Number} removed the number of child views removed
+ **/
+ childViewsWillChange: function(views, start, removed) {
+ if (removed === 0) { return; }
+
+ var changedViews = views.slice(start, start+removed);
+ this.initializeViews(changedViews, null, null);
+
+ this.invokeForState('childViewsWillChange', views, start, removed);
+ },
+
+ /**
+ When a child view is added, make sure the DOM gets updated appropriately.
+
+ If the view has already rendered an element, we tell the child view to
+ create an element and insert it into the DOM. If the enclosing container view
+ has already written to a buffer, but not yet converted that buffer into an
+ element, we insert the string representation of the child into the appropriate
+ place in the buffer.
+
+ @private
+ @param {Ember.Array} views the array of child views afte the mutation has occurred
+ @param {Number} start the start position of the mutation
+ @param {Number} removed the number of child views removed
+ @param {Number} the number of child views added
+ */
+ childViewsDidChange: function(views, start, removed, added) {
+ var len = get(views, 'length');
+
+ // No new child views were added; bail out.
+ if (added === 0) return;
+
+ var changedViews = views.slice(start, start+added);
+ this.initializeViews(changedViews, this, get(this, 'templateData'));
+
+ // Let the current state handle the changes
+ this.invokeForState('childViewsDidChange', views, start, added);
+ },
+
+ initializeViews: function(views, parentView, templateData) {
+ forEach(views, function(view) {
+ set(view, '_parentView', parentView);
+
+ if (!get(view, 'templateData')) {
+ set(view, 'templateData', templateData);
+ }
+ });
+ },
+
+ /**
+ Schedules a child view to be inserted into the DOM after bindings have
+ finished syncing for this run loop.
+
+ @param {Ember.View} view the child view to insert
+ @param {Ember.View} prev the child view after which the specified view should
+ be inserted
+ @private
+ */
+ _scheduleInsertion: function(view, prev) {
+ if (prev) {
+ prev.domManager.after(prev, view);
+ } else {
+ this.domManager.prepend(this, view);
+ }
+ },
+
+ currentView: null,
+
+ _currentViewWillChange: Ember.beforeObserver(function() {
+ var childViews = get(this, 'childViews'),
+ currentView = get(this, 'currentView');
+
+ if (currentView) {
+ childViews.removeObject(currentView);
+ }
+ }, 'currentView'),
+
+ _currentViewDidChange: Ember.observer(function() {
+ var childViews = get(this, 'childViews'),
+ currentView = get(this, 'currentView');
+
+ if (currentView) {
+ childViews.pushObject(currentView);
+ }
+ }, 'currentView')
+});
+
+// Ember.ContainerView extends the default view states to provide different
+// behavior for childViewsWillChange and childViewsDidChange.
+Ember.ContainerView.states = {
+ parent: Ember.View.states,
+
+ inBuffer: {
+ childViewsDidChange: function(parentView, views, start, added) {
+ var buffer = parentView.buffer,
+ startWith, prev, prevBuffer, view;
+
+ // Determine where to begin inserting the child view(s) in the
+ // render buffer.
+ if (start === 0) {
+ // If views were inserted at the beginning, prepend the first
+ // view to the render buffer, then begin inserting any
+ // additional views at the beginning.
+ view = views[start];
+ startWith = start + 1;
+ view.renderToBuffer(buffer, 'prepend');
+ } else {
+ // Otherwise, just insert them at the same place as the child
+ // views mutation.
+ view = views[start - 1];
+ startWith = start;
+ }
+
+ for (var i=startWith; i` and the following code:
+
+
+ someItemsView = Ember.CollectionView.create({
+ classNames: ['a-collection'],
+ content: ['A','B','C'],
+ itemViewClass: Ember.View.extend({
+ template: Ember.Handlebars.compile("the letter: {{view.content}}")
+ })
+ })
+
+ someItemsView.appendTo('body')
+
+ Will result in the following HTML structure
+
+
+
the letter: A
+
the letter: B
+
the letter: C
+
+
+ ## Automatic matching of parent/child tagNames
+
+ Setting the `tagName` property of a `CollectionView` to any of
+ "ul", "ol", "table", "thead", "tbody", "tfoot", "tr", or "select" will result
+ in the item views receiving an appropriately matched `tagName` property.
+
+
+ Given an empty `` and the following code:
+
+ anUndorderedListView = Ember.CollectionView.create({
+ tagName: 'ul',
+ content: ['A','B','C'],
+ itemViewClass: Ember.View.extend({
+ template: Ember.Handlebars.compile("the letter: {{view.content}}")
+ })
+ })
+
+ anUndorderedListView.appendTo('body')
+
+ Will result in the following HTML structure
+
+
+
the letter: A
+
the letter: B
+
the letter: C
+
+
+ Additional tagName pairs can be provided by adding to `Ember.CollectionView.CONTAINER_MAP `
+
+ Ember.CollectionView.CONTAINER_MAP['article'] = 'section'
+
+
+ ## Empty View
+ You can provide an `Ember.View` subclass to the `Ember.CollectionView` instance as its
+ `emptyView` property. If the `content` property of a `CollectionView` is set to `null`
+ or an empty array, an instance of this view will be the `CollectionView`s only child.
+
+ aListWithNothing = Ember.CollectionView.create({
+ classNames: ['nothing']
+ content: null,
+ emptyView: Ember.View.extend({
+ template: Ember.Handlebars.compile("The collection is empty")
+ })
+ })
+
+ aListWithNothing.appendTo('body')
+
+ Will result in the following HTML structure
+
+
')
+ })
+
+ viewStates = Ember.StateManager.create({
+ showingPeople: Ember.ViewState.create({
+ view: ContactListView
+ }),
+ showingPhotos: Ember.ViewState.create({
+ view: PhotoListView
+ })
+ })
+
+ viewStates.transitionTo('showingPeople')
+
+ The above code will change the rendered HTML from
+
+
+
+ to
+
+
+
+
People
+
+
+
+ Changing the current state via `transitionTo` from `showingPeople` to
+ `showingPhotos` will remove the `showingPeople` view and add the `showingPhotos` view:
+
+ viewStates.transitionTo('showingPhotos')
+
+ will change the rendered HTML to
+
+
+
+
Photos
+
+
+
+
+ When entering nested `ViewState`s, each state's view will be draw into the the StateManager's
+ `rootView` or `rootElement` as siblings.
+
+
+ ContactListView = Ember.View.extend({
+ classNames: ['my-contacts-css-class'],
+ template: Ember.Handlebars.compile('
+
+
+
+ ViewState views are added and removed from their StateManager's view via their
+ `enter` and `exit` methods. If you need to override these methods, be sure to call
+ `_super` to maintain the adding and removing behavior:
+
+ viewStates = Ember.StateManager.create({
+ aState: Ember.ViewState.create({
+ view: Ember.View.extend({}),
+ enter: function(manager){
+ // calling _super ensures this view will be
+ // properly inserted
+ this._super(manager);
+
+ // now you can do other things
+ }
+ })
+ })
+
+ ## Managing Multiple Sections of A Page With States
+ Multiple StateManagers can be combined to control multiple areas of an application's rendered views.
+ Given the following HTML body:
+
+
+
" ],
+ map: [ 1, "" ],
+ _default: [ 0, "", "" ]
+ };
+
+ /**
+ * Given a parent node and some HTML, generate a set of nodes. Return the first
+ * node, which will allow us to traverse the rest using nextSibling.
+ *
+ * We need to do this because innerHTML in IE does not really parse the nodes.
+ **/
+ var firstNodeFor = function(parentNode, html) {
+ var arr = wrapMap[parentNode.tagName.toLowerCase()] || wrapMap._default;
+ var depth = arr[0], start = arr[1], end = arr[2];
+
+ if (needsShy) { html = ''+html; }
+
+ var element = document.createElement('div');
+ element.innerHTML = start + html + end;
+
+ for (var i=0; i<=depth; i++) {
+ element = element.firstChild;
+ }
+
+ // Look for to remove it.
+ if (needsShy) {
+ var shyElement = element;
+
+ // Sometimes we get nameless elements with the shy inside
+ while (shyElement.nodeType === 1 && !shyElement.nodeName) {
+ shyElement = shyElement.firstChild;
+ }
+
+ // At this point it's the actual unicode character.
+ if (shyElement.nodeType === 3 && shyElement.nodeValue.charAt(0) === "\u00AD") {
+ shyElement.nodeValue = shyElement.nodeValue.slice(1);
+ }
+ }
+
+ return element;
+ };
+
+ /**
+ * In some cases, Internet Explorer can create an anonymous node in
+ * the hierarchy with no tagName. You can create this scenario via:
+ *
+ * div = document.createElement("div");
+ * div.innerHTML = "
­
hi
";
+ * div.firstChild.firstChild.tagName //=> ""
+ *
+ * If our script markers are inside such a node, we need to find that
+ * node and use *it* as the marker.
+ **/
+ var realNode = function(start) {
+ while (start.parentNode.tagName === "") {
+ start = start.parentNode;
+ }
+
+ return start;
+ };
+
+ /**
+ * When automatically adding a tbody, Internet Explorer inserts the
+ * tbody immediately before the first
. Other browsers create it
+ * before the first node, no matter what.
+ *
+ * This means the the following code:
+ *
+ * div = document.createElement("div");
+ * div.innerHTML = "
+
+
+ ### parentView setting
+
+ The `parentView` property of the new `Ember.View` instance created through `{{view}}`
+ will be set to the `Ember.View` instance of the template where `{{view}}` was called.
+
+ aView = Ember.View.create({
+ template: Ember.Handlebars.compile("{{#view}} my parent: {{parentView.elementId}} {{/view}}")
+ })
+
+ aView.appendTo('body')
+
+ Will result in HTML structure:
+
+
+
+ my parent: ember1
+
+
+
+ ### Setting CSS id and class attributes
+
+ The HTML `id` attribute can be set on the `{{view}}`'s resulting element with the `id` option.
+ This option will _not_ be passed to `Ember.View.create`.
+
+
+
+ Results in the following HTML structure:
+
+
+
+ hello.
+
+
+
+ The HTML `class` attribute can be set on the `{{view}}`'s resulting element with
+ the `class` or `classNameBindings` options. The `class` option
+ will directly set the CSS `class` attribute and will not be passed to
+ `Ember.View.create`. `classNameBindings` will be passed to `create` and use
+ `Ember.View`'s class name binding functionality:
+
+
+
+ Results in the following HTML structure:
+
+
+
+ hello.
+
+
+
+ ### Supplying a different view class
+ `{{view}}` can take an optional first argument before its supplied options to specify a
+ path to a custom view class.
+
+
+
+ The first argument can also be a relative path. Ember will search for the view class
+ starting at the `Ember.View` of the template where `{{view}}` was used as the root object:
+
+ MyApp = Ember.Application.create({})
+ MyApp.OuterView = Ember.View.extend({
+ innerViewClass: Ember.View.extend({
+ classNames: ['a-custom-view-class-as-property']
+ }),
+ template: Ember.Handlebars.compile('{{#view "innerViewClass"}} hi {{/view}}')
+ })
+
+ MyApp.OuterView.create().appendTo('body')
+
+Will result in the following HTML:
+
+
+
+ ### Blockless Use
+ If you provide an `itemViewClass` option that has its own `template` you can omit
+ the block.
+
+ The following template:
+
+
+
+ And application code
+
+ App = Ember.Application.create()
+ App.items = [
+ Ember.Object.create({name: 'Dave'}),
+ Ember.Object.create({name: 'Mary'}),
+ Ember.Object.create({name: 'Sara'})
+ ]
+
+ App.AnItemView = Ember.View.extend({
+ template: Ember.Handlebars.compile("Greetings {{content.name}}")
+ })
+
+ Will result in the HTML structure below
+
+
+
Greetings Dave
+
Greetings Mary
+
Greetings Sara
+
+
+ ### Specifying a CollectionView subclass
+ By default the `{{collection}}` helper will create an instance of `Ember.CollectionView`.
+ You can supply a `Ember.CollectionView` subclass to the helper by passing it
+ as the first argument:
+
+
+
+
+ ### Forwarded `item.*`-named Options
+ As with the `{{view}}`, helper options passed to the `{{collection}}` will be set on
+ the resulting `Ember.CollectionView` as properties. Additionally, options prefixed with
+ `item` will be applied to the views rendered for each item (note the camelcasing):
+
+
+
+ Will result in the following HTML structure:
+
+
+
+ Clicking "click me" will trigger the `anActionName` method of the `aView` object with a
+ `jQuery.Event` object as its argument. The `jQuery.Event` object will be extended to include
+ a `view` property that is set to the original view interacted with (in this case the `aView` object).
+
+ ### Event Propagation
+
+ Events triggered through the action helper will automatically have
+ `.preventDefault()` and `.stopPropagation()` called on them. You do not need
+ to do so in your event handlers, and you do not need to return `false`.
+
+ If you need the default handler to trigger, or if you need propagation,
+ you should either register your own event handler, or use event methods on
+ your view class.
+
+ ### Specifying an Action Target
+
+ A `target` option can be provided to change which object will receive the method call. This option must be
+ a string representing a path to an object:
+
+
+
+ Clicking "click me" in the rendered HTML of the above template will trigger the
+ `anActionName` method of the object at `MyApplication.someObject`. The first argument
+ to this method will be a `jQuery.Event` extended to include a `view` property that is
+ set to the original view interacted with.
+
+ A path relative to the template's `Ember.View` instance can also be used as a target:
+
+
+
+ Clicking "click me" in the rendered HTML of the above template will trigger the
+ `anActionName` method of the view's parent view.
+
+ The `{{action}}` helper is `Ember.StateManager` aware. If the target of
+ the action is an `Ember.StateManager` instance `{{action}}` will use the `send`
+ functionality of StateManagers. The documentation for `Ember.StateManager` has additional
+ information about this use.
+
+ If an action's target does not implement a method that matches the supplied action name
+ an error will be thrown.
+
+
+
+ With the following application code
+
+ AView = Ember.View.extend({
+ templateName; 'a-template',
+ // note: no method 'aMethodNameThatIsMissing'
+ anActionName: function(event){}
+ })
+
+ aView = AView.create()
+ aView.appendTo('body')
+
+ Will throw `Uncaught TypeError: Cannot call method 'call' of undefined` when "click me" is clicked.
+
+
+ ### Specifying DOM event type
+ By default the `{{action}}` helper registers for DOM `click` events. You can supply an
+ `on` option to the helper to specify a different DOM event name:
+
+
+
+ See `Ember.EventDispatcher` for a list of acceptable DOM event names.
+
+ Because `{{action}}` depends on Ember's event dispatch system it will only function if
+ an `Ember.EventDispatcher` instance is available. An `Ember.EventDispatcher` instance
+ will be created when a new `Ember.Application` is created. Having an instance of
+ `Ember.Application` will satisfy this requirement.
+
+ ### Specifying a context
+ By default the `{{action}}` helper passes the current Handlebars context along in the
+ `jQuery.Event` object. You may specify an alternative object to pass as the context by
+ providing a property path:
+
+
+
+ @name Handlebars.helpers.action
+ @param {String} actionName
+ @param {Hash} options
+*/
+EmberHandlebars.registerHelper('action', function(actionName, options) {
+ var hash = options.hash,
+ eventName = hash.on || "click",
+ view = options.data.view,
+ target, context, controller;
+
+ view = get(view, 'concreteView');
+
+ if (hash.target) {
+ target = getPath(this, hash.target, options);
+ } else if (controller = options.data.keywords.controller) {
+ target = get(controller, 'target');
+ }
+
+ target = target || view;
+
+ // console.log(hash.context)
+ // debugger
+
+ context = hash.context ? getPath(this, hash.context, options) : options.contexts[0];
+
+ var output = [], url;
+
+ if (hash.href && target.urlForEvent) {
+ url = target.urlForEvent(actionName, context);
+ output.push('href="' + url + '"');
+ }
+
+ var actionId = ActionHelper.registerAction(actionName, eventName, target, view, context);
+ output.push('data-ember-action="' + actionId + '"');
+
+ return new EmberHandlebars.SafeString(output.join(" "));
+});
+
+})();
+
+
+
+(function() {
+var get = Ember.get, set = Ember.set;
+
+/**
+
+ When used in a Handlebars template that is assigned to an `Ember.View` instance's
+ `layout` property Ember will render the layout template first, inserting the view's
+ own rendered output at the `{{ yield }}` location.
+
+ An empty `` and the following application code:
+
+ AView = Ember.View.extend({
+ classNames: ['a-view-with-layout'],
+ layout: Ember.Handlebars.compile('
{{ yield }}
'),
+ template: Ember.Handlebars.compile('I am wrapped')
+ })
+
+ aView = AView.create()
+ aView.appendTo('body')
+
+ Will result in the following HTML output:
+
+
+