mno-enterprise-frontend 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (313) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +1 -0
  3. data/Rakefile +12 -0
  4. data/app/assets/images/favicon.ico +0 -0
  5. data/app/assets/images/mno_enterprise/impac/close-widget-pink.png +0 -0
  6. data/app/assets/images/mno_enterprise/impac/close-widget.png +0 -0
  7. data/app/assets/images/mno_enterprise/loader-32x32-bg-inverse.gif +0 -0
  8. data/app/assets/images/mno_enterprise/loader-32x32-bg-main.gif +0 -0
  9. data/app/assets/images/mno_enterprise/maestrano/logo-whitebg.png +0 -0
  10. data/app/assets/javascripts/mno_enterprise/angular/app.js.coffee.erb +173 -0
  11. data/app/assets/javascripts/mno_enterprise/angular/controllers/maestrano-controllers.js.coffee +4 -0
  12. data/app/assets/javascripts/mno_enterprise/angular/controllers/modals/modals-ctrl.js.coffee +62 -0
  13. data/app/assets/javascripts/mno_enterprise/angular/directives/dashboard/dashboard-account.js.coffee +157 -0
  14. data/app/assets/javascripts/mno_enterprise/angular/directives/dashboard/dashboard-app-deletion-request.js.coffee +62 -0
  15. data/app/assets/javascripts/mno_enterprise/angular/directives/dashboard/dashboard-apps-list.js.coffee +127 -0
  16. data/app/assets/javascripts/mno_enterprise/angular/directives/dashboard/dashboard-components.js.coffee +25 -0
  17. data/app/assets/javascripts/mno_enterprise/angular/directives/dashboard/dashboard-marketplace-app.js.coffee +77 -0
  18. data/app/assets/javascripts/mno_enterprise/angular/directives/dashboard/dashboard-marketplace.js.coffee +51 -0
  19. data/app/assets/javascripts/mno_enterprise/angular/directives/dashboard/dashboard-menu.js.coffee +174 -0
  20. data/app/assets/javascripts/mno_enterprise/angular/directives/dashboard/organization/arrears.js.coffee +70 -0
  21. data/app/assets/javascripts/mno_enterprise/angular/directives/dashboard/organization/billing.js.coffee +48 -0
  22. data/app/assets/javascripts/mno_enterprise/angular/directives/dashboard/organization/credit-card.js.coffee +142 -0
  23. data/app/assets/javascripts/mno_enterprise/angular/directives/dashboard/organization/index.js.coffee +61 -0
  24. data/app/assets/javascripts/mno_enterprise/angular/directives/dashboard/organization/invoices.js.coffee +42 -0
  25. data/app/assets/javascripts/mno_enterprise/angular/directives/dashboard/organization/members.js.coffee +280 -0
  26. data/app/assets/javascripts/mno_enterprise/angular/directives/dashboard/organization/settings.js.coffee +91 -0
  27. data/app/assets/javascripts/mno_enterprise/angular/directives/dashboard/organization/team-list.js.coffee +179 -0
  28. data/app/assets/javascripts/mno_enterprise/angular/directives/dashboard/organization/teams.js.coffee +268 -0
  29. data/app/assets/javascripts/mno_enterprise/angular/directives/maestrano-components/file-model.js.coffee +33 -0
  30. data/app/assets/javascripts/mno_enterprise/angular/directives/maestrano-components/maestrano-components.js.coffee +22 -0
  31. data/app/assets/javascripts/mno_enterprise/angular/directives/maestrano-components/mno-autostop-app.js.coffee +123 -0
  32. data/app/assets/javascripts/mno_enterprise/angular/directives/maestrano-components/mno-compile.js.coffee +18 -0
  33. data/app/assets/javascripts/mno_enterprise/angular/directives/maestrano-components/mno-currency-widget.js.coffee +43 -0
  34. data/app/assets/javascripts/mno_enterprise/angular/directives/maestrano-components/mno-current-currency.js.coffee +22 -0
  35. data/app/assets/javascripts/mno_enterprise/angular/directives/maestrano-components/mno-editable.js.coffee +68 -0
  36. data/app/assets/javascripts/mno_enterprise/angular/directives/maestrano-components/mno-flash-msg.js.coffee +40 -0
  37. data/app/assets/javascripts/mno_enterprise/angular/directives/maestrano-components/mno-helptext.js.coffee +38 -0
  38. data/app/assets/javascripts/mno_enterprise/angular/directives/maestrano-components/mno-loading-lounge.js.coffee +184 -0
  39. data/app/assets/javascripts/mno_enterprise/angular/directives/maestrano-components/mno-match.js.coffee +14 -0
  40. data/app/assets/javascripts/mno_enterprise/angular/directives/maestrano-components/mno-message-modal.js.coffee +56 -0
  41. data/app/assets/javascripts/mno_enterprise/angular/directives/maestrano-components/mno-notification-widget.js.coffee +192 -0
  42. data/app/assets/javascripts/mno_enterprise/angular/directives/maestrano-components/mno-partner-code-popup.js.coffee +74 -0
  43. data/app/assets/javascripts/mno_enterprise/angular/directives/maestrano-components/mno-partner-contact.js.coffee +59 -0
  44. data/app/assets/javascripts/mno_enterprise/angular/directives/maestrano-components/mno-password-strength.js.coffee +154 -0
  45. data/app/assets/javascripts/mno_enterprise/angular/directives/maestrano-components/mno-password.js.coffee +40 -0
  46. data/app/assets/javascripts/mno_enterprise/angular/directives/maestrano-components/mno-price-converter.js.coffee +49 -0
  47. data/app/assets/javascripts/mno_enterprise/angular/directives/maestrano-components/mno-scroll-to.js.coffee +14 -0
  48. data/app/assets/javascripts/mno_enterprise/angular/directives/maestrano-components/mno-typeahead.js.coffee +89 -0
  49. data/app/assets/javascripts/mno_enterprise/angular/directives/user-setup/components.js.coffee +7 -0
  50. data/app/assets/javascripts/mno_enterprise/angular/directives/user-setup/index.js.coffee +256 -0
  51. data/app/assets/javascripts/mno_enterprise/angular/filters/maestrano-app-by-category.js.coffee +5 -0
  52. data/app/assets/javascripts/mno_enterprise/angular/filters/maestrano-currency.js.coffee +30 -0
  53. data/app/assets/javascripts/mno_enterprise/angular/filters/maestrano-filters.js.coffee +7 -0
  54. data/app/assets/javascripts/mno_enterprise/angular/filters/maestrano-replace-underscore-with-space.js.coffee +4 -0
  55. data/app/assets/javascripts/mno_enterprise/angular/filters/maestrano-titleize.js.coffee +7 -0
  56. data/app/assets/javascripts/mno_enterprise/angular/filters/maestrano-truncate.js.coffee +24 -0
  57. data/app/assets/javascripts/mno_enterprise/angular/services/dashboard/maestrano-app-instance.js.coffee +15 -0
  58. data/app/assets/javascripts/mno_enterprise/angular/services/dashboard/maestrano-apps-document.js.coffee +83 -0
  59. data/app/assets/javascripts/mno_enterprise/angular/services/dashboard/maestrano-user.js.coffee +27 -0
  60. data/app/assets/javascripts/mno_enterprise/angular/services/dashboard/organization-svc.js.coffee +318 -0
  61. data/app/assets/javascripts/mno_enterprise/angular/services/dashboard/team-svc.js.coffee +180 -0
  62. data/app/assets/javascripts/mno_enterprise/angular/services/dashboard/tickets-svc.js.coffee +109 -0
  63. data/app/assets/javascripts/mno_enterprise/angular/services/impac-config.svc.js.coffee +35 -0
  64. data/app/assets/javascripts/mno_enterprise/angular/services/maestrano-apps-list-helper.js.coffee +337 -0
  65. data/app/assets/javascripts/mno_enterprise/angular/services/maestrano-assets.js.coffee.erb +21 -0
  66. data/app/assets/javascripts/mno_enterprise/angular/services/maestrano-current-currency.js.coffee +7 -0
  67. data/app/assets/javascripts/mno_enterprise/angular/services/maestrano-current-user-svc.js.coffee +99 -0
  68. data/app/assets/javascripts/mno_enterprise/angular/services/maestrano-exchange-rates.js.coffee +102 -0
  69. data/app/assets/javascripts/mno_enterprise/angular/services/maestrano-message-svc.js.coffee +71 -0
  70. data/app/assets/javascripts/mno_enterprise/angular/services/maestrano-miscellaneous.js.coffee.erb +46 -0
  71. data/app/assets/javascripts/mno_enterprise/angular/services/maestrano-modal.js.coffee +67 -0
  72. data/app/assets/javascripts/mno_enterprise/angular/services/maestrano-msg-bus.js.coffee +50 -0
  73. data/app/assets/javascripts/mno_enterprise/angular/services/maestrano-services.js.coffee +21 -0
  74. data/app/assets/javascripts/mno_enterprise/angular/services/maestrano-shopping-cart.js.coffee +361 -0
  75. data/app/assets/javascripts/mno_enterprise/angular/services/maestrano-utilities.js.coffee +61 -0
  76. data/app/assets/javascripts/mno_enterprise/angular/services/marketplace-svc.js.coffee +59 -0
  77. data/app/assets/javascripts/mno_enterprise/application.js +17 -0
  78. data/app/assets/javascripts/mno_enterprise/application_lib.js +14 -0
  79. data/app/assets/javascripts/mno_enterprise/lib/angular-really-click.js +17 -0
  80. data/app/assets/javascripts/mno_enterprise/lib/sortable.js +315 -0
  81. data/app/assets/javascripts/mno_enterprise/lib/ui-bootstrap-tpls-0.13.4.js +5836 -0
  82. data/app/assets/javascripts/mno_enterprise/lib/xeditable.js +1588 -0
  83. data/app/assets/javascripts/mno_enterprise/twitter/bootstrap.js +12 -0
  84. data/app/assets/javascripts/mno_enterprise/twitter/bootstrap/affix.js +162 -0
  85. data/app/assets/javascripts/mno_enterprise/twitter/bootstrap/alert.js +94 -0
  86. data/app/assets/javascripts/mno_enterprise/twitter/bootstrap/button.js +116 -0
  87. data/app/assets/javascripts/mno_enterprise/twitter/bootstrap/carousel.js +237 -0
  88. data/app/assets/javascripts/mno_enterprise/twitter/bootstrap/collapse.js +211 -0
  89. data/app/assets/javascripts/mno_enterprise/twitter/bootstrap/dropdown.js +161 -0
  90. data/app/assets/javascripts/mno_enterprise/twitter/bootstrap/modal.js +324 -0
  91. data/app/assets/javascripts/mno_enterprise/twitter/bootstrap/popover.js +113 -0
  92. data/app/assets/javascripts/mno_enterprise/twitter/bootstrap/scrollspy.js +175 -0
  93. data/app/assets/javascripts/mno_enterprise/twitter/bootstrap/tab.js +153 -0
  94. data/app/assets/javascripts/mno_enterprise/twitter/bootstrap/tooltip.js +472 -0
  95. data/app/assets/javascripts/mno_enterprise/twitter/bootstrap/transition.js +59 -0
  96. data/app/assets/javascripts/ng-performance/ngPerformance.html +109 -0
  97. data/app/assets/javascripts/ng-performance/ngPerformance.js +211 -0
  98. data/app/assets/stylesheets/mno_enterprise/application.css +13 -0
  99. data/app/assets/stylesheets/mno_enterprise/custom/accordion.less +90 -0
  100. data/app/assets/stylesheets/mno_enterprise/custom/alert.less +30 -0
  101. data/app/assets/stylesheets/mno_enterprise/custom/angular-animate.less +42 -0
  102. data/app/assets/stylesheets/mno_enterprise/custom/angular-material.less +56 -0
  103. data/app/assets/stylesheets/mno_enterprise/custom/bootstrap-on-juice.less +22 -0
  104. data/app/assets/stylesheets/mno_enterprise/custom/button.less +84 -0
  105. data/app/assets/stylesheets/mno_enterprise/custom/footer.less +44 -0
  106. data/app/assets/stylesheets/mno_enterprise/custom/form.less +191 -0
  107. data/app/assets/stylesheets/mno_enterprise/custom/header.less +281 -0
  108. data/app/assets/stylesheets/mno_enterprise/custom/impac-variables-override.less +65 -0
  109. data/app/assets/stylesheets/mno_enterprise/custom/modal.less +183 -0
  110. data/app/assets/stylesheets/mno_enterprise/custom/utilities.less +729 -0
  111. data/app/assets/stylesheets/mno_enterprise/custom/utility/app-presentation.less +77 -0
  112. data/app/assets/stylesheets/mno_enterprise/custom/utility/banners.less +145 -0
  113. data/app/assets/stylesheets/mno_enterprise/custom/utility/carousel.less +68 -0
  114. data/app/assets/stylesheets/mno_enterprise/custom/utility/helptext.less +40 -0
  115. data/app/assets/stylesheets/mno_enterprise/custom/utility/miscellaneous.less +134 -0
  116. data/app/assets/stylesheets/mno_enterprise/custom/utility/progress-bar.less +55 -0
  117. data/app/assets/stylesheets/mno_enterprise/custom/utility/step.less +101 -0
  118. data/app/assets/stylesheets/mno_enterprise/custom/utility/tabset.less +87 -0
  119. data/app/assets/stylesheets/mno_enterprise/custom/utility/testimonial.less +118 -0
  120. data/app/assets/stylesheets/mno_enterprise/custom/utility/text.less +23 -0
  121. data/app/assets/stylesheets/mno_enterprise/custom/views/apps.less +243 -0
  122. data/app/assets/stylesheets/mno_enterprise/custom/views/myspace.less +477 -0
  123. data/app/assets/stylesheets/mno_enterprise/custom/views/pages/confirmation_lounge.less +31 -0
  124. data/app/assets/stylesheets/mno_enterprise/custom/views/pages/home.less +57 -0
  125. data/app/assets/stylesheets/mno_enterprise/custom/views/pages/oauth-config-page.less +6 -0
  126. data/app/assets/stylesheets/mno_enterprise/custom/views/registration.less +84 -0
  127. data/app/assets/stylesheets/mno_enterprise/custom/views/star.less +18 -0
  128. data/app/assets/stylesheets/mno_enterprise/custom/views/templates/dashboard/bootstrap-wizard.less +63 -0
  129. data/app/assets/stylesheets/mno_enterprise/custom/views/templates/dashboard/dashboard-marketplace.less +187 -0
  130. data/app/assets/stylesheets/mno_enterprise/custom/views/templates/dashboard/dashboard-organization.less +149 -0
  131. data/app/assets/stylesheets/mno_enterprise/custom/views/templates/dashboard/dashboard-teams.less +119 -0
  132. data/app/assets/stylesheets/mno_enterprise/custom/views/templates/dashboard/support.less +42 -0
  133. data/app/assets/stylesheets/mno_enterprise/custom/views/templates/dashboard/tickets.less +80 -0
  134. data/app/assets/stylesheets/mno_enterprise/custom/views/templates/mno-editable.less +60 -0
  135. data/app/assets/stylesheets/mno_enterprise/custom/views/templates/notification-widget.less +49 -0
  136. data/app/assets/stylesheets/mno_enterprise/custom/views/templates/password-strength.less +28 -0
  137. data/app/assets/stylesheets/mno_enterprise/custom/views/templates/shopping-cart.less +333 -0
  138. data/app/assets/stylesheets/mno_enterprise/custom/views/templates/star-wizard.less +49 -0
  139. data/app/assets/stylesheets/mno_enterprise/custom/views/user-setup.less +10 -0
  140. data/app/assets/stylesheets/mno_enterprise/custom/xeditable.less +40 -0
  141. data/app/assets/stylesheets/mno_enterprise/font-awesome/font-awesome-regular.less +13 -0
  142. data/app/assets/stylesheets/mno_enterprise/font-awesome/font-awesome-sprockets.less +13 -0
  143. data/app/assets/stylesheets/mno_enterprise/font-awesome/font-awesome.less +16 -0
  144. data/app/assets/stylesheets/mno_enterprise/font-awesome/fonts/FontAwesome.otf +0 -0
  145. data/app/assets/stylesheets/mno_enterprise/font-awesome/fonts/fontawesome-webfont.eot +0 -0
  146. data/app/assets/stylesheets/mno_enterprise/font-awesome/fonts/fontawesome-webfont.svg +640 -0
  147. data/app/assets/stylesheets/mno_enterprise/font-awesome/fonts/fontawesome-webfont.ttf +0 -0
  148. data/app/assets/stylesheets/mno_enterprise/font-awesome/fonts/fontawesome-webfont.woff +0 -0
  149. data/app/assets/stylesheets/mno_enterprise/font-awesome/fonts/fontawesome-webfont.woff2 +0 -0
  150. data/app/assets/stylesheets/mno_enterprise/font-awesome/framework/animated.less +34 -0
  151. data/app/assets/stylesheets/mno_enterprise/font-awesome/framework/bordered-pulled.less +25 -0
  152. data/app/assets/stylesheets/mno_enterprise/font-awesome/framework/core.less +12 -0
  153. data/app/assets/stylesheets/mno_enterprise/font-awesome/framework/extras.less +2 -0
  154. data/app/assets/stylesheets/mno_enterprise/font-awesome/framework/fixed-width.less +6 -0
  155. data/app/assets/stylesheets/mno_enterprise/font-awesome/framework/icons.less +677 -0
  156. data/app/assets/stylesheets/mno_enterprise/font-awesome/framework/larger.less +13 -0
  157. data/app/assets/stylesheets/mno_enterprise/font-awesome/framework/list.less +19 -0
  158. data/app/assets/stylesheets/mno_enterprise/font-awesome/framework/mixins.less +26 -0
  159. data/app/assets/stylesheets/mno_enterprise/font-awesome/framework/rotated-flipped.less +20 -0
  160. data/app/assets/stylesheets/mno_enterprise/font-awesome/framework/stacked.less +20 -0
  161. data/app/assets/stylesheets/mno_enterprise/font-awesome/framework/variables.less +687 -0
  162. data/app/assets/stylesheets/mno_enterprise/main.less +132 -0
  163. data/app/assets/stylesheets/mno_enterprise/mixins.less +75 -0
  164. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/alerts.less +73 -0
  165. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/badges.less +65 -0
  166. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/bootstrap.less +50 -0
  167. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/breadcrumbs.less +26 -0
  168. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/button-groups.less +243 -0
  169. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/buttons.less +160 -0
  170. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/carousel.less +269 -0
  171. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/close.less +34 -0
  172. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/code.less +69 -0
  173. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/component-animations.less +34 -0
  174. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/dropdowns.less +214 -0
  175. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/forms.less +566 -0
  176. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/glyphicons.less +301 -0
  177. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/grid.less +84 -0
  178. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/input-groups.less +166 -0
  179. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/jumbotron.less +50 -0
  180. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/labels.less +64 -0
  181. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/list-group.less +124 -0
  182. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/media.less +61 -0
  183. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/mixins.less +39 -0
  184. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/mixins/alerts.less +14 -0
  185. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/mixins/background-variant.less +8 -0
  186. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/mixins/border-radius.less +18 -0
  187. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/mixins/buttons.less +52 -0
  188. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/mixins/center-block.less +7 -0
  189. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/mixins/clearfix.less +22 -0
  190. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/mixins/forms.less +85 -0
  191. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/mixins/gradients.less +59 -0
  192. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/mixins/grid-framework.less +91 -0
  193. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/mixins/grid.less +122 -0
  194. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/mixins/hide-text.less +21 -0
  195. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/mixins/image.less +33 -0
  196. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/mixins/labels.less +12 -0
  197. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/mixins/list-group.less +29 -0
  198. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/mixins/nav-divider.less +10 -0
  199. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/mixins/nav-vertical-align.less +9 -0
  200. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/mixins/opacity.less +8 -0
  201. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/mixins/pagination.less +23 -0
  202. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/mixins/panels.less +24 -0
  203. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/mixins/progress-bar.less +10 -0
  204. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/mixins/reset-filter.less +8 -0
  205. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/mixins/resize.less +6 -0
  206. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/mixins/responsive-visibility.less +15 -0
  207. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/mixins/size.less +10 -0
  208. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/mixins/tab-focus.less +9 -0
  209. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/mixins/table-row.less +28 -0
  210. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/mixins/text-emphasis.less +8 -0
  211. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/mixins/text-overflow.less +8 -0
  212. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/mixins/vendor-prefixes.less +227 -0
  213. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/modals.less +148 -0
  214. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/navbar.less +661 -0
  215. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/navs.less +244 -0
  216. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/normalize.less +427 -0
  217. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/pager.less +54 -0
  218. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/pagination.less +88 -0
  219. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/panels.less +265 -0
  220. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/popovers.less +135 -0
  221. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/print.less +107 -0
  222. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/progress-bars.less +87 -0
  223. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/responsive-embed.less +35 -0
  224. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/responsive-utilities.less +194 -0
  225. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/scaffolding.less +150 -0
  226. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/tables.less +234 -0
  227. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/theme.less +273 -0
  228. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/thumbnails.less +36 -0
  229. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/tooltip.less +103 -0
  230. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/type.less +302 -0
  231. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/utilities.less +56 -0
  232. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/variables.less +857 -0
  233. data/app/assets/stylesheets/mno_enterprise/twitter-bootstrap/wells.less +29 -0
  234. data/app/assets/stylesheets/mno_enterprise/variables.less +226 -0
  235. data/app/assets/templates/mno_enterprise/dashboard/account.html +131 -0
  236. data/app/assets/templates/mno_enterprise/dashboard/app_deletion.html +36 -0
  237. data/app/assets/templates/mno_enterprise/dashboard/apps_list.html +103 -0
  238. data/app/assets/templates/mno_enterprise/dashboard/horizontal_menu.html +85 -0
  239. data/app/assets/templates/mno_enterprise/dashboard/marketplace/index.html +60 -0
  240. data/app/assets/templates/mno_enterprise/dashboard/marketplace/show.html +133 -0
  241. data/app/assets/templates/mno_enterprise/dashboard/menu.html +161 -0
  242. data/app/assets/templates/mno_enterprise/dashboard/organization/arrears.html +37 -0
  243. data/app/assets/templates/mno_enterprise/dashboard/organization/billing.html +37 -0
  244. data/app/assets/templates/mno_enterprise/dashboard/organization/credit-card.html +156 -0
  245. data/app/assets/templates/mno_enterprise/dashboard/organization/index.full.html +181 -0
  246. data/app/assets/templates/mno_enterprise/dashboard/organization/index.html +173 -0
  247. data/app/assets/templates/mno_enterprise/dashboard/organization/invoices.html +27 -0
  248. data/app/assets/templates/mno_enterprise/dashboard/organization/members.html +288 -0
  249. data/app/assets/templates/mno_enterprise/dashboard/organization/settings.html +55 -0
  250. data/app/assets/templates/mno_enterprise/dashboard/teams/index.html +175 -0
  251. data/app/assets/templates/mno_enterprise/dashboard/teams/member-add-modal.html +60 -0
  252. data/app/assets/templates/mno_enterprise/dashboard/teams/member-removal-modal.html +35 -0
  253. data/app/assets/templates/mno_enterprise/dashboard/teams/team-add-modal.html +35 -0
  254. data/app/assets/templates/mno_enterprise/dashboard/teams/team-delete-modal.html +35 -0
  255. data/app/assets/templates/mno_enterprise/dashboard/teams/team-list.html +107 -0
  256. data/app/assets/templates/mno_enterprise/default_template.html +20 -0
  257. data/app/assets/templates/mno_enterprise/maestrano-components/autostop_app.html +42 -0
  258. data/app/assets/templates/mno_enterprise/maestrano-components/currency_widget.html +1 -0
  259. data/app/assets/templates/mno_enterprise/maestrano-components/editable.html +28 -0
  260. data/app/assets/templates/mno_enterprise/maestrano-components/loading_lounge.html +63 -0
  261. data/app/assets/templates/mno_enterprise/maestrano-components/modal-messages/congratulations.html +8 -0
  262. data/app/assets/templates/mno_enterprise/maestrano-components/modal-messages/default.html +1 -0
  263. data/app/assets/templates/mno_enterprise/maestrano-components/modal-messages/information.html +8 -0
  264. data/app/assets/templates/mno_enterprise/maestrano-components/notification-widget.html +31 -0
  265. data/app/assets/templates/mno_enterprise/maestrano-components/password.html +55 -0
  266. data/app/assets/templates/mno_enterprise/maestrano-components/star_wizard.html +254 -0
  267. data/app/assets/templates/mno_enterprise/maestrano-components/upload_spreadsheet.html +191 -0
  268. data/app/assets/templates/mno_enterprise/modals/new-organization-reseller-req.html +35 -0
  269. data/app/assets/templates/mno_enterprise/modals/new-organization.html +35 -0
  270. data/app/assets/templates/mno_enterprise/user-setup/index.html +312 -0
  271. data/app/controllers/mno_enterprise/invoices_controller.rb +22 -0
  272. data/app/controllers/mno_enterprise/pages_controller.rb +9 -0
  273. data/app/controllers/mno_enterprise/user_setup_controller.rb +11 -0
  274. data/app/views/layouts/mno_enterprise/_application_js.html.haml +17 -0
  275. data/app/views/layouts/mno_enterprise/_common_cdn_lib_js.html.haml +19 -0
  276. data/app/views/layouts/mno_enterprise/_footer_files.html.haml +5 -0
  277. data/app/views/layouts/mno_enterprise/_header_files.html.haml +31 -0
  278. data/app/views/layouts/mno_enterprise/_impersonate_header.html.haml +7 -0
  279. data/app/views/layouts/mno_enterprise/_public_page_header.html.haml +2 -0
  280. data/app/views/layouts/mno_enterprise/application.html.haml +18 -0
  281. data/app/views/layouts/mno_enterprise/application_dashboard.html.haml +22 -0
  282. data/app/views/layouts/mno_enterprise/third_party/_google_tag_container.html.haml +10 -0
  283. data/app/views/mno_enterprise/pages/app_access_unauthorized.html.haml +19 -0
  284. data/app/views/mno_enterprise/pages/app_logout.html.haml +18 -0
  285. data/app/views/mno_enterprise/pages/billing_details_required.html.haml +19 -0
  286. data/app/views/mno_enterprise/pages/loading.html.haml +3 -0
  287. data/app/views/mno_enterprise/pages/myspace.html.haml +6 -0
  288. data/app/views/mno_enterprise/provision/_provision_apps.html.haml +43 -0
  289. data/app/views/mno_enterprise/provision/_select_organization.html.haml +17 -0
  290. data/app/views/mno_enterprise/provision/new.html.haml +4 -0
  291. data/app/views/mno_enterprise/user_setup/index.html.haml +2 -0
  292. data/config/initializers/ng-annotate.rb +3 -0
  293. data/config/initializers/ng-performance.rb +3 -0
  294. data/config/routes.rb +19 -0
  295. data/lib/assets/javascripts/impac-angular.js +8719 -0
  296. data/lib/assets/stylesheets/impac-angular.less +2352 -0
  297. data/lib/mno-enterprise-frontend.rb +1 -0
  298. data/lib/mno_enterprise/frontend.rb +13 -0
  299. data/lib/mno_enterprise/frontend/assets_util.rb +47 -0
  300. data/lib/mno_enterprise/frontend/concerns/README.md +6 -0
  301. data/lib/mno_enterprise/frontend/concerns/controllers/pages_controller.rb +25 -0
  302. data/lib/mno_enterprise/frontend/engine.rb +13 -0
  303. data/spec/controllers/mno_enterprise/auth/confirmations_controller_spec.rb +84 -0
  304. data/spec/controllers/mno_enterprise/invoices_controller_spec.rb +34 -0
  305. data/spec/controllers/mno_enterprise/pages_controller_spec.rb +42 -0
  306. data/spec/controllers/mno_enterprise/user_setup_controller_spec.rb +33 -0
  307. data/spec/controllers/mno_enterprise/webhook/o_auth_controller_spec.rb +76 -0
  308. data/spec/rails_helper.rb +92 -0
  309. data/spec/routing/mno_enterprise/invoices_controller_routing_spec.rb +11 -0
  310. data/spec/routing/mno_enterprise/pages_controller_routing_spec.rb +18 -0
  311. data/spec/routing/mno_enterprise/user_setup_controller_routing_spec.rb +11 -0
  312. data/spec/spec_helper.rb +78 -0
  313. metadata +464 -0
@@ -0,0 +1,1588 @@
1
+ /*!
2
+ angular-xeditable - 0.1.8
3
+ Edit-in-place for angular.js
4
+ Build date: 2014-01-10
5
+ */
6
+ /**
7
+ * Angular-xeditable module
8
+ *
9
+ */
10
+ angular.module('xeditable', [])
11
+
12
+
13
+ /**
14
+ * Default options.
15
+ *
16
+ * @namespace editable-options
17
+ */
18
+ //todo: maybe better have editableDefaults, not options...
19
+ .value('editableOptions', {
20
+ /**
21
+ * Theme. Possible values `bs3`, `bs2`, `default`.
22
+ *
23
+ * @var {string} theme
24
+ * @memberOf editable-options
25
+ */
26
+ theme: 'default',
27
+ /**
28
+ * Whether to show buttons for single editalbe element.
29
+ * Possible values `right` (default), `no`.
30
+ *
31
+ * @var {string} buttons
32
+ * @memberOf editable-options
33
+ */
34
+ buttons: 'right',
35
+ /**
36
+ * Default value for `blur` attribute of single editable element.
37
+ * Can be `cancel|submit|ignore`.
38
+ *
39
+ * @var {string} blurElem
40
+ * @memberOf editable-options
41
+ */
42
+ blurElem: 'cancel',
43
+ /**
44
+ * Default value for `blur` attribute of editable form.
45
+ * Can be `cancel|submit|ignore`.
46
+ *
47
+ * @var {string} blurForm
48
+ * @memberOf editable-options
49
+ */
50
+ blurForm: 'ignore',
51
+ /**
52
+ * How input elements get activated. Possible values: `focus|select|none`.
53
+ *
54
+ * @var {string} activate
55
+ * @memberOf editable-options
56
+ */
57
+ activate: 'focus'
58
+
59
+ });
60
+ /*
61
+ Angular-ui bootstrap datepicker
62
+ http://angular-ui.github.io/bootstrap/#/datepicker
63
+ */
64
+ angular.module('xeditable').directive('editableBsdate', ['editableDirectiveFactory',
65
+ function(editableDirectiveFactory) {
66
+ return editableDirectiveFactory({
67
+ directiveName: 'editableBsdate',
68
+ inputTpl: '<input type="text">'
69
+ });
70
+ }]);
71
+ /*
72
+ Angular-ui bootstrap editable timepicker
73
+ http://angular-ui.github.io/bootstrap/#/timepicker
74
+ */
75
+ angular.module('xeditable').directive('editableBstime', ['editableDirectiveFactory',
76
+ function(editableDirectiveFactory) {
77
+ return editableDirectiveFactory({
78
+ directiveName: 'editableBstime',
79
+ inputTpl: '<timepicker></timepicker>',
80
+ render: function() {
81
+ this.parent.render.call(this);
82
+
83
+ // timepicker can't update model when ng-model set directly to it
84
+ // see: https://github.com/angular-ui/bootstrap/issues/1141
85
+ // so we wrap it into DIV
86
+ var div = angular.element('<div class="well well-small" style="display:inline-block;"></div>');
87
+
88
+ // move ng-model to wrapping div
89
+ div.attr('ng-model', this.inputEl.attr('ng-model'));
90
+ this.inputEl.removeAttr('ng-model');
91
+
92
+ // move ng-change to wrapping div
93
+ if(this.attrs.eNgChange) {
94
+ div.attr('ng-change', this.inputEl.attr('ng-change'));
95
+ this.inputEl.removeAttr('ng-change');
96
+ }
97
+
98
+ // wrap
99
+ this.inputEl.wrap(div);
100
+ }
101
+ });
102
+ }]);
103
+ //checkbox
104
+ angular.module('xeditable').directive('editableCheckbox', ['editableDirectiveFactory',
105
+ function(editableDirectiveFactory) {
106
+ return editableDirectiveFactory({
107
+ directiveName: 'editableCheckbox',
108
+ inputTpl: '<input type="checkbox">',
109
+ render: function() {
110
+ this.parent.render.call(this);
111
+ if(this.attrs.eTitle) {
112
+ this.inputEl.wrap('<label></label>');
113
+ this.inputEl.after(angular.element('<span></span>').text(this.attrs.eTitle));
114
+ }
115
+ },
116
+ autosubmit: function() {
117
+ var self = this;
118
+ self.inputEl.bind('change', function() {
119
+ setTimeout(function() {
120
+ self.scope.$apply(function() {
121
+ self.scope.$form.$submit();
122
+ });
123
+ }, 500);
124
+ });
125
+ }
126
+ });
127
+ }]);
128
+ // checklist
129
+ angular.module('xeditable').directive('editableChecklist', [
130
+ 'editableDirectiveFactory',
131
+ 'editableNgOptionsParser',
132
+ function(editableDirectiveFactory, editableNgOptionsParser) {
133
+ return editableDirectiveFactory({
134
+ directiveName: 'editableChecklist',
135
+ inputTpl: '<span></span>',
136
+ useCopy: true,
137
+ render: function() {
138
+ this.parent.render.call(this);
139
+ var parsed = editableNgOptionsParser(this.attrs.eNgOptions);
140
+ var html = '<label ng-repeat="'+parsed.ngRepeat+'">'+
141
+ '<input type="checkbox" checklist-model="$parent.$data" checklist-value="'+parsed.locals.valueFn+'">'+
142
+ '<span ng-bind="'+parsed.locals.displayFn+'"></span></label>';
143
+
144
+ this.inputEl.removeAttr('ng-model');
145
+ this.inputEl.removeAttr('ng-options');
146
+ this.inputEl.html(html);
147
+ }
148
+ });
149
+ }]);
150
+ /*
151
+ Input types: text|email|tel|number|url|search|color|date|datetime|time|month|week
152
+ */
153
+
154
+ (function() {
155
+
156
+ var types = 'text|email|tel|number|url|search|color|date|datetime|time|month|week'.split('|');
157
+
158
+ //todo: datalist
159
+
160
+ // generate directives
161
+ angular.forEach(types, function(type) {
162
+ var directiveName = 'editable'+type.charAt(0).toUpperCase() + type.slice(1);
163
+ angular.module('xeditable').directive(directiveName, ['editableDirectiveFactory',
164
+ function(editableDirectiveFactory) {
165
+ return editableDirectiveFactory({
166
+ directiveName: directiveName,
167
+ inputTpl: '<input type="'+type+'">'
168
+ });
169
+ }]);
170
+ });
171
+
172
+ //`range` is bit specific
173
+ angular.module('xeditable').directive('editableRange', ['editableDirectiveFactory',
174
+ function(editableDirectiveFactory) {
175
+ return editableDirectiveFactory({
176
+ directiveName: 'editableRange',
177
+ inputTpl: '<input type="range" id="range" name="range">',
178
+ render: function() {
179
+ this.parent.render.call(this);
180
+ this.inputEl.after('<output>{{$data}}</output>');
181
+ }
182
+ });
183
+ }]);
184
+
185
+ }());
186
+
187
+
188
+ // radiolist
189
+ angular.module('xeditable').directive('editableRadiolist', [
190
+ 'editableDirectiveFactory',
191
+ 'editableNgOptionsParser',
192
+ function(editableDirectiveFactory, editableNgOptionsParser) {
193
+ return editableDirectiveFactory({
194
+ directiveName: 'editableRadiolist',
195
+ inputTpl: '<span></span>',
196
+ render: function() {
197
+ this.parent.render.call(this);
198
+ var parsed = editableNgOptionsParser(this.attrs.eNgOptions);
199
+ var html = '<label ng-repeat="'+parsed.ngRepeat+'">'+
200
+ '<input type="radio" ng-model="$parent.$data" value="{{'+parsed.locals.valueFn+'}}">'+
201
+ '<span ng-bind="'+parsed.locals.displayFn+'"></span></label>';
202
+
203
+ this.inputEl.removeAttr('ng-model');
204
+ this.inputEl.removeAttr('ng-options');
205
+ this.inputEl.html(html);
206
+ },
207
+ autosubmit: function() {
208
+ var self = this;
209
+ self.inputEl.bind('change', function() {
210
+ setTimeout(function() {
211
+ self.scope.$apply(function() {
212
+ self.scope.$form.$submit();
213
+ });
214
+ }, 500);
215
+ });
216
+ }
217
+ });
218
+ }]);
219
+ //select
220
+ angular.module('xeditable').directive('editableSelect', ['editableDirectiveFactory',
221
+ function(editableDirectiveFactory) {
222
+ return editableDirectiveFactory({
223
+ directiveName: 'editableSelect',
224
+ inputTpl: '<select></select>',
225
+ autosubmit: function() {
226
+ var self = this;
227
+ self.inputEl.bind('change', function() {
228
+ self.scope.$apply(function() {
229
+ self.scope.$form.$submit();
230
+ });
231
+ });
232
+ }
233
+ });
234
+ }]);
235
+ //textarea
236
+ angular.module('xeditable').directive('editableTextarea', ['editableDirectiveFactory',
237
+ function(editableDirectiveFactory) {
238
+ return editableDirectiveFactory({
239
+ directiveName: 'editableTextarea',
240
+ inputTpl: '<textarea></textarea>',
241
+ addListeners: function() {
242
+ var self = this;
243
+ self.parent.addListeners.call(self);
244
+ // submit textarea by ctrl+enter even with buttons
245
+ if (self.single && self.buttons !== 'no') {
246
+ self.autosubmit();
247
+ }
248
+ },
249
+ autosubmit: function() {
250
+ var self = this;
251
+ self.inputEl.bind('keydown', function(e) {
252
+ if ((e.ctrlKey || e.metaKey) && (e.keyCode === 13)) {
253
+ self.scope.$apply(function() {
254
+ self.scope.$form.$submit();
255
+ });
256
+ }
257
+ });
258
+ }
259
+ });
260
+ }]);
261
+
262
+ /**
263
+ * EditableController class.
264
+ * Attached to element with `editable-xxx` directive.
265
+ *
266
+ * @namespace editable-element
267
+ */
268
+ /*
269
+ TODO: this file should be refactored to work more clear without closures!
270
+ */
271
+ angular.module('xeditable').factory('editableController',
272
+ ['$q', 'editableUtils',
273
+ function($q, editableUtils) {
274
+
275
+ //EditableController function
276
+ EditableController.$inject = ['$scope', '$attrs', '$element', '$parse', 'editableThemes', 'editableOptions', '$rootScope', '$compile', '$q'];
277
+ function EditableController($scope, $attrs, $element, $parse, editableThemes, editableOptions, $rootScope, $compile, $q) {
278
+ var valueGetter;
279
+
280
+ //if control is disabled - it does not participate in waiting process
281
+ var inWaiting;
282
+
283
+ var self = this;
284
+
285
+ self.scope = $scope;
286
+ self.elem = $element;
287
+ self.attrs = $attrs;
288
+ self.inputEl = null;
289
+ self.editorEl = null;
290
+ self.single = true;
291
+ self.error = '';
292
+ self.theme = editableThemes[editableOptions.theme] || editableThemes['default'];
293
+ self.parent = {};
294
+
295
+ //to be overwritten by directive
296
+ self.inputTpl = '';
297
+ self.directiveName = '';
298
+
299
+ // with majority of controls copy is not needed, but..
300
+ // copy MUST NOT be used for `select-multiple` with objects as items
301
+ // copy MUST be used for `checklist`
302
+ self.useCopy = false;
303
+
304
+ //runtime (defaults)
305
+ self.single = null;
306
+
307
+ /**
308
+ * Attributes defined with `e-*` prefix automatically transfered from original element to
309
+ * control.
310
+ * For example, if you set `<span editable-text="user.name" e-style="width: 100px"`>
311
+ * then input will appear as `<input style="width: 100px">`.
312
+ * See [demo](#text-customize).
313
+ *
314
+ * @var {any|attribute} e-*
315
+ * @memberOf editable-element
316
+ */
317
+
318
+ /**
319
+ * Whether to show ok/cancel buttons. Values: `right|no`.
320
+ * If set to `no` control automatically submitted when value changed.
321
+ * If control is part of form buttons will never be shown.
322
+ *
323
+ * @var {string|attribute} buttons
324
+ * @memberOf editable-element
325
+ */
326
+ self.buttons = 'right';
327
+ /**
328
+ * Action when control losses focus. Values: `cancel|submit|ignore`.
329
+ * Has sense only for single editable element.
330
+ * Otherwise, if control is part of form - you should set `blur` of form, not of individual element.
331
+ *
332
+ * @var {string|attribute} blur
333
+ * @memberOf editable-element
334
+ */
335
+ // no real `blur` property as it is transfered to editable form
336
+
337
+ //init
338
+ self.init = function(single) {
339
+ self.single = single;
340
+
341
+ self.name = $attrs.eName || $attrs[self.directiveName];
342
+ /*
343
+ if(!$attrs[directiveName] && !$attrs.eNgModel && ($attrs.eValue === undefined)) {
344
+ throw 'You should provide value for `'+directiveName+'` or `e-value` in editable element!';
345
+ }
346
+ */
347
+ if($attrs[self.directiveName]) {
348
+ valueGetter = $parse($attrs[self.directiveName]);
349
+ } else {
350
+ throw 'You should provide value for `'+self.directiveName+'` in editable element!';
351
+ }
352
+
353
+ // settings for single and non-single
354
+ if (!self.single) {
355
+ // hide buttons for non-single
356
+ self.buttons = 'no';
357
+ } else {
358
+ self.buttons = self.attrs.buttons || editableOptions.buttons;
359
+ }
360
+
361
+ //if name defined --> watch changes and update $data in form
362
+ if($attrs.eName) {
363
+ self.scope.$watch('$data', function(newVal){
364
+ self.scope.$form.$data[$attrs.eName] = newVal;
365
+ });
366
+ }
367
+
368
+ /**
369
+ * Called when control is shown.
370
+ * See [demo](#select-remote).
371
+ *
372
+ * @var {method|attribute} onshow
373
+ * @memberOf editable-element
374
+ */
375
+ if($attrs.onshow) {
376
+ self.onshow = function() {
377
+ return self.catchError($parse($attrs.onshow)($scope));
378
+ };
379
+ }
380
+
381
+ /**
382
+ * Called when control is hidden after both save or cancel.
383
+ *
384
+ * @var {method|attribute} onhide
385
+ * @memberOf editable-element
386
+ */
387
+ if($attrs.onhide) {
388
+ self.onhide = function() {
389
+ return $parse($attrs.onhide)($scope);
390
+ };
391
+ }
392
+
393
+ /**
394
+ * Called when control is cancelled.
395
+ *
396
+ * @var {method|attribute} oncancel
397
+ * @memberOf editable-element
398
+ */
399
+ if($attrs.oncancel) {
400
+ self.oncancel = function() {
401
+ return $parse($attrs.oncancel)($scope);
402
+ };
403
+ }
404
+
405
+ /**
406
+ * Called during submit before value is saved to model.
407
+ * See [demo](#onbeforesave).
408
+ *
409
+ * @var {method|attribute} onbeforesave
410
+ * @memberOf editable-element
411
+ */
412
+ if ($attrs.onbeforesave) {
413
+ self.onbeforesave = function() {
414
+ return self.catchError($parse($attrs.onbeforesave)($scope));
415
+ };
416
+ }
417
+
418
+ /**
419
+ * Called during submit after value is saved to model.
420
+ * See [demo](#onaftersave).
421
+ *
422
+ * @var {method|attribute} onaftersave
423
+ * @memberOf editable-element
424
+ */
425
+ if ($attrs.onaftersave) {
426
+ self.onaftersave = function() {
427
+ return self.catchError($parse($attrs.onaftersave)($scope));
428
+ };
429
+ }
430
+
431
+ // watch change of model to update editable element
432
+ // now only add/remove `editable-empty` class.
433
+ // Initially this method called with newVal = undefined, oldVal = undefined
434
+ // so no need initially call handleEmpty() explicitly
435
+ $scope.$parent.$watch($attrs[self.directiveName], function(newVal, oldVal) {
436
+ self.handleEmpty();
437
+ });
438
+ };
439
+
440
+ self.render = function() {
441
+ var theme = self.theme;
442
+
443
+ //build input
444
+ self.inputEl = angular.element(self.inputTpl);
445
+
446
+ //build controls
447
+ self.controlsEl = angular.element(theme.controlsTpl);
448
+ self.controlsEl.append(self.inputEl);
449
+
450
+ //build buttons
451
+ if(self.buttons !== 'no') {
452
+ self.buttonsEl = angular.element(theme.buttonsTpl);
453
+ self.submitEl = angular.element(theme.submitTpl);
454
+ self.cancelEl = angular.element(theme.cancelTpl);
455
+ self.buttonsEl.append(self.submitEl).append(self.cancelEl);
456
+ self.controlsEl.append(self.buttonsEl);
457
+
458
+ self.inputEl.addClass('editable-has-buttons');
459
+ }
460
+
461
+ //build error
462
+ self.errorEl = angular.element(theme.errorTpl);
463
+ self.controlsEl.append(self.errorEl);
464
+
465
+ //build editor
466
+ self.editorEl = angular.element(self.single ? theme.formTpl : theme.noformTpl);
467
+ self.editorEl.append(self.controlsEl);
468
+
469
+ // transfer `e-*|data-e-*|x-e-*` attributes
470
+ for(var k in $attrs.$attr) {
471
+ if(k.length <= 1) {
472
+ continue;
473
+ }
474
+ var transferAttr = false;
475
+ var nextLetter = k.substring(1, 2);
476
+
477
+ // if starts with `e` + uppercase letter
478
+ if(k.substring(0, 1) === 'e' && nextLetter === nextLetter.toUpperCase()) {
479
+ transferAttr = k.substring(1); // cut `e`
480
+ } else {
481
+ continue;
482
+ }
483
+
484
+ // exclude `form` and `ng-submit`,
485
+ if(transferAttr === 'Form' || transferAttr === 'NgSubmit') {
486
+ continue;
487
+ }
488
+
489
+ // convert back to lowercase style
490
+ transferAttr = transferAttr.substring(0, 1).toLowerCase() + editableUtils.camelToDash(transferAttr.substring(1));
491
+
492
+ // workaround for attributes without value (e.g. `multiple = "multiple"`)
493
+ var attrValue = ($attrs[k] === '') ? transferAttr : $attrs[k];
494
+
495
+ // set attributes to input
496
+ self.inputEl.attr(transferAttr, attrValue);
497
+ }
498
+
499
+ self.inputEl.addClass('editable-input');
500
+ self.inputEl.attr('ng-model', '$data');
501
+
502
+ // add directiveName class to editor, e.g. `editable-text`
503
+ self.editorEl.addClass(editableUtils.camelToDash(self.directiveName));
504
+
505
+ if(self.single) {
506
+ self.editorEl.attr('editable-form', '$form');
507
+ // transfer `blur` to form
508
+ self.editorEl.attr('blur', self.attrs.blur || (self.buttons === 'no' ? 'cancel' : editableOptions.blurElem));
509
+ }
510
+
511
+ //apply `postrender` method of theme
512
+ if(angular.isFunction(theme.postrender)) {
513
+ theme.postrender.call(self);
514
+ }
515
+
516
+ };
517
+
518
+ // with majority of controls copy is not needed, but..
519
+ // copy MUST NOT be used for `select-multiple` with objects as items
520
+ // copy MUST be used for `checklist`
521
+ self.setLocalValue = function() {
522
+ self.scope.$data = self.useCopy ?
523
+ angular.copy(valueGetter($scope.$parent)) :
524
+ valueGetter($scope.$parent);
525
+ };
526
+
527
+ //show
528
+ self.show = function() {
529
+ // set value of scope.$data
530
+ self.setLocalValue();
531
+
532
+ /*
533
+ Originally render() was inside init() method, but some directives polluting editorEl,
534
+ so it is broken on second openning.
535
+ Cloning is not a solution as jqLite can not clone with event handler's.
536
+ */
537
+ self.render();
538
+
539
+ // insert into DOM
540
+ $element.after(self.editorEl);
541
+
542
+ // compile (needed to attach ng-* events from markup)
543
+ $compile(self.editorEl)($scope);
544
+
545
+ // attach listeners (`escape`, autosubmit, etc)
546
+ self.addListeners();
547
+
548
+ // hide element
549
+ $element.addClass('editable-hide');
550
+
551
+ // onshow
552
+ return self.onshow();
553
+ };
554
+
555
+ //hide
556
+ self.hide = function() {
557
+ self.editorEl.remove();
558
+ $element.removeClass('editable-hide');
559
+
560
+ // onhide
561
+ return self.onhide();
562
+ };
563
+
564
+ // cancel
565
+ self.cancel = function() {
566
+ // oncancel
567
+ self.oncancel();
568
+ // don't call hide() here as it called in form's code
569
+ };
570
+
571
+ /*
572
+ Called after show to attach listeners
573
+ */
574
+ self.addListeners = function() {
575
+ // bind keyup for `escape`
576
+ self.inputEl.bind('keyup', function(e) {
577
+ if(!self.single) {
578
+ return;
579
+ }
580
+
581
+ // todo: move this to editable-form!
582
+ switch(e.keyCode) {
583
+ // hide on `escape` press
584
+ case 27:
585
+ self.scope.$apply(function() {
586
+ self.scope.$form.$cancel();
587
+ });
588
+ break;
589
+ }
590
+ });
591
+
592
+ // autosubmit when `no buttons`
593
+ if (self.single && self.buttons === 'no') {
594
+ self.autosubmit();
595
+ }
596
+
597
+ // click - mark element as clicked to exclude in document click handler
598
+ self.editorEl.bind('click', function(e) {
599
+ // ignore right/middle button click
600
+ if (e.which !== 1) {
601
+ return;
602
+ }
603
+
604
+ if (self.scope.$form.$visible) {
605
+ self.scope.$form._clicked = true;
606
+ }
607
+ });
608
+ };
609
+
610
+ // setWaiting
611
+ self.setWaiting = function(value) {
612
+ if (value) {
613
+ // participate in waiting only if not disabled
614
+ inWaiting = !self.inputEl.attr('disabled') &&
615
+ !self.inputEl.attr('ng-disabled') &&
616
+ !self.inputEl.attr('ng-enabled');
617
+ if (inWaiting) {
618
+ self.inputEl.attr('disabled', 'disabled');
619
+ if(self.buttonsEl) {
620
+ self.buttonsEl.find('button').attr('disabled', 'disabled');
621
+ }
622
+ }
623
+ } else {
624
+ if (inWaiting) {
625
+ self.inputEl.removeAttr('disabled');
626
+ if (self.buttonsEl) {
627
+ self.buttonsEl.find('button').removeAttr('disabled');
628
+ }
629
+ }
630
+ }
631
+ };
632
+
633
+ self.activate = function() {
634
+ setTimeout(function() {
635
+ var el = self.inputEl[0];
636
+ if (editableOptions.activate === 'focus' && el.focus) {
637
+ el.focus();
638
+ }
639
+ if (editableOptions.activate === 'select' && el.select) {
640
+ el.select();
641
+ }
642
+ }, 0);
643
+ };
644
+
645
+ self.setError = function(msg) {
646
+ if(!angular.isObject(msg)) {
647
+ $scope.$error = msg;
648
+ self.error = msg;
649
+ }
650
+ };
651
+
652
+ /*
653
+ Checks that result is string or promise returned string and shows it as error message
654
+ Applied to onshow, onbeforesave, onaftersave
655
+ */
656
+ self.catchError = function(result, noPromise) {
657
+ if (angular.isObject(result) && noPromise !== true) {
658
+ $q.when(result).then(
659
+ //success and fail handlers are equal
660
+ angular.bind(this, function(r) {
661
+ this.catchError(r, true);
662
+ }),
663
+ angular.bind(this, function(r) {
664
+ this.catchError(r, true);
665
+ })
666
+ );
667
+ //check $http error
668
+ } else if (noPromise && angular.isObject(result) && result.status &&
669
+ (result.status !== 200) && result.data && angular.isString(result.data)) {
670
+ this.setError(result.data);
671
+ //set result to string: to let form know that there was error
672
+ result = result.data;
673
+ } else if (angular.isString(result)) {
674
+ this.setError(result);
675
+ }
676
+ return result;
677
+ };
678
+
679
+ self.save = function() {
680
+ valueGetter.assign($scope.$parent, angular.copy(self.scope.$data));
681
+
682
+ // no need to call handleEmpty here as we are watching change of model value
683
+ // self.handleEmpty();
684
+ };
685
+
686
+ /*
687
+ attach/detach `editable-empty` class to element
688
+ */
689
+ self.handleEmpty = function() {
690
+ var val = valueGetter($scope.$parent);
691
+ var isEmpty = val === null || val === undefined || val === "" || (angular.isArray(val) && val.length === 0);
692
+ $element.toggleClass('editable-empty', isEmpty);
693
+ };
694
+
695
+ /*
696
+ Called when `buttons = "no"` to submit automatically
697
+ */
698
+ self.autosubmit = angular.noop;
699
+
700
+ self.onshow = angular.noop;
701
+ self.onhide = angular.noop;
702
+ self.oncancel = angular.noop;
703
+ self.onbeforesave = angular.noop;
704
+ self.onaftersave = angular.noop;
705
+ }
706
+
707
+ return EditableController;
708
+ }]);
709
+
710
+ /*
711
+ editableFactory is used to generate editable directives (see `/directives` folder)
712
+ Inside it does several things:
713
+ - detect form for editable element. Form may be one of three types:
714
+ 1. autogenerated form (for single editable elements)
715
+ 2. wrapper form (element wrapped by <form> tag)
716
+ 3. linked form (element has `e-form` attribute pointing to existing form)
717
+
718
+ - attach editableController to element
719
+
720
+ Depends on: editableController, editableFormFactory
721
+ */
722
+ angular.module('xeditable').factory('editableDirectiveFactory',
723
+ ['$parse', '$compile', 'editableThemes', '$rootScope', '$document', 'editableController', 'editableFormController',
724
+ function($parse, $compile, editableThemes, $rootScope, $document, editableController, editableFormController) {
725
+
726
+ //directive object
727
+ return function(overwrites) {
728
+ return {
729
+ restrict: 'A',
730
+ scope: true,
731
+ require: [overwrites.directiveName, '?^form'],
732
+ controller: editableController,
733
+ link: function(scope, elem, attrs, ctrl) {
734
+ // editable controller
735
+ var eCtrl = ctrl[0];
736
+
737
+ // form controller
738
+ var eFormCtrl;
739
+
740
+ // this variable indicates is element is bound to some existing form,
741
+ // or it's single element who's form will be generated automatically
742
+ // By default consider single element without any linked form.ß
743
+ var hasForm = false;
744
+
745
+ // element wrapped by form
746
+ if(ctrl[1]) {
747
+ eFormCtrl = ctrl[1];
748
+ hasForm = true;
749
+ } else if(attrs.eForm) { // element not wrapped by <form>, but we hane `e-form` attr
750
+ var getter = $parse(attrs.eForm)(scope);
751
+ if(getter) { // form exists in scope (above), e.g. editable column
752
+ eFormCtrl = getter;
753
+ hasForm = true;
754
+ } else { // form exists below or not exist at all: check document.forms
755
+ for(var i=0; i<$document[0].forms.length;i++){
756
+ if($document[0].forms[i].name === attrs.eForm) {
757
+ // form is below and not processed yet
758
+ eFormCtrl = null;
759
+ hasForm = true;
760
+ break;
761
+ }
762
+ }
763
+ }
764
+ }
765
+
766
+ /*
767
+ if(hasForm && !attrs.eName) {
768
+ throw 'You should provide `e-name` for editable element inside form!';
769
+ }
770
+ */
771
+
772
+ //check for `editable-form` attr in form
773
+ /*
774
+ if(eFormCtrl && ) {
775
+ throw 'You should provide `e-name` for editable element inside form!';
776
+ }
777
+ */
778
+
779
+ // store original props to `parent` before merge
780
+ angular.forEach(overwrites, function(v, k) {
781
+ if(eCtrl[k] !== undefined) {
782
+ eCtrl.parent[k] = eCtrl[k];
783
+ }
784
+ });
785
+
786
+ // merge overwrites to base editable controller
787
+ angular.extend(eCtrl, overwrites);
788
+
789
+ // init editable ctrl
790
+ eCtrl.init(!hasForm);
791
+
792
+ // publich editable controller as `$editable` to be referenced in html
793
+ scope.$editable = eCtrl;
794
+
795
+ // add `editable` class to element
796
+ elem.addClass('editable');
797
+
798
+ // hasForm
799
+ if(hasForm) {
800
+ if(eFormCtrl) {
801
+ scope.$form = eFormCtrl;
802
+ if(!scope.$form.$addEditable) {
803
+ throw 'Form with editable elements should have `editable-form` attribute.';
804
+ }
805
+ scope.$form.$addEditable(eCtrl);
806
+ } else {
807
+ // future form (below): add editable controller to buffer and add to form later
808
+ $rootScope.$$editableBuffer = $rootScope.$$editableBuffer || {};
809
+ $rootScope.$$editableBuffer[attrs.eForm] = $rootScope.$$editableBuffer[attrs.eForm] || [];
810
+ $rootScope.$$editableBuffer[attrs.eForm].push(eCtrl);
811
+ scope.$form = null; //will be re-assigned later
812
+ }
813
+ // !hasForm
814
+ } else {
815
+ // create editableform controller
816
+ scope.$form = editableFormController();
817
+ // add self to editable controller
818
+ scope.$form.$addEditable(eCtrl);
819
+
820
+ // if `e-form` provided, publish local $form in scope
821
+ if(attrs.eForm) {
822
+ scope.$parent[attrs.eForm] = scope.$form;
823
+ }
824
+
825
+ // bind click - if no external form defined
826
+ if(!attrs.eForm) {
827
+ elem.addClass('editable-click');
828
+ elem.bind('click', function(e) {
829
+ e.preventDefault();
830
+ e.editable = eCtrl;
831
+ scope.$apply(function(){
832
+ scope.$form.$show();
833
+ });
834
+ });
835
+ }
836
+ }
837
+
838
+ }
839
+ };
840
+ };
841
+ }]);
842
+
843
+ /*
844
+ Returns editableForm controller
845
+ */
846
+ angular.module('xeditable').factory('editableFormController',
847
+ ['$parse', '$document', '$rootScope', 'editablePromiseCollection', 'editableUtils',
848
+ function($parse, $document, $rootScope, editablePromiseCollection, editableUtils) {
849
+
850
+ // array of opened editable forms
851
+ var shown = [];
852
+
853
+ // bind click to body: cancel|submit|ignore forms
854
+ $document.bind('click', function(e) {
855
+ // ignore right/middle button click
856
+ if (e.which !== 1) {
857
+ return;
858
+ }
859
+
860
+ var toCancel = [];
861
+ var toSubmit = [];
862
+ for (var i=0; i<shown.length; i++) {
863
+
864
+ // exclude clicked
865
+ if (shown[i]._clicked) {
866
+ shown[i]._clicked = false;
867
+ continue;
868
+ }
869
+
870
+ // exclude waiting
871
+ if (shown[i].$waiting) {
872
+ continue;
873
+ }
874
+
875
+ if (shown[i]._blur === 'cancel') {
876
+ toCancel.push(shown[i]);
877
+ }
878
+
879
+ if (shown[i]._blur === 'submit') {
880
+ toSubmit.push(shown[i]);
881
+ }
882
+ }
883
+
884
+ if (toCancel.length || toSubmit.length) {
885
+ $rootScope.$apply(function() {
886
+ angular.forEach(toCancel, function(v){ v.$cancel(); });
887
+ angular.forEach(toSubmit, function(v){ v.$submit(); });
888
+ });
889
+ }
890
+ });
891
+
892
+
893
+ var base = {
894
+ $addEditable: function(editable) {
895
+ this.$editables.push(editable);
896
+
897
+ //'on' is not supported in angular 1.0.8
898
+ editable.elem.bind('$destroy', angular.bind(this, this.$removeEditable, editable));
899
+
900
+ //bind editable's local $form to self (if not bound yet, below form)
901
+ if (!editable.scope.$form) {
902
+ editable.scope.$form = this;
903
+ }
904
+
905
+ //if form already shown - call show() of new editable
906
+ if (this.$visible) {
907
+ editable.catchError(editable.show());
908
+ }
909
+ },
910
+
911
+ $removeEditable: function(editable) {
912
+ //arrayRemove
913
+ for(var i=0; i < this.$editables.length; i++) {
914
+ if(this.$editables[i] === editable) {
915
+ this.$editables.splice(i, 1);
916
+ return;
917
+ }
918
+ }
919
+ },
920
+
921
+ /**
922
+ * Shows form with editable controls.
923
+ *
924
+ * @method $show()
925
+ * @memberOf editable-form
926
+ */
927
+ $show: function() {
928
+ if (this.$visible) {
929
+ return;
930
+ }
931
+
932
+ this.$visible = true;
933
+
934
+ var pc = editablePromiseCollection();
935
+
936
+ //own show
937
+ pc.when(this.$onshow());
938
+
939
+ //clear errors
940
+ this.$setError(null, '');
941
+
942
+ //children show
943
+ angular.forEach(this.$editables, function(editable) {
944
+ pc.when(editable.show());
945
+ });
946
+
947
+ //wait promises and activate
948
+ pc.then({
949
+ onWait: angular.bind(this, this.$setWaiting),
950
+ onTrue: angular.bind(this, this.$activate),
951
+ onFalse: angular.bind(this, this.$activate),
952
+ onString: angular.bind(this, this.$activate)
953
+ });
954
+
955
+ // add to internal list of shown forms
956
+ // setTimeout needed to prevent closing right after opening (e.g. when trigger by button)
957
+ setTimeout(angular.bind(this, function() {
958
+ // clear `clicked` to get ready for clicks on visible form
959
+ this._clicked = false;
960
+ if(editableUtils.indexOf(shown, this) === -1) {
961
+ shown.push(this);
962
+ }
963
+ }), 0);
964
+ },
965
+
966
+ /**
967
+ * Sets focus on form field specified by `name`.
968
+ *
969
+ * @method $activate(name)
970
+ * @param {string} name name of field
971
+ * @memberOf editable-form
972
+ */
973
+ $activate: function(name) {
974
+ var i;
975
+ if (this.$editables.length) {
976
+ //activate by name
977
+ if (angular.isString(name)) {
978
+ for(i=0; i<this.$editables.length; i++) {
979
+ if (this.$editables[i].name === name) {
980
+ this.$editables[i].activate();
981
+ return;
982
+ }
983
+ }
984
+ }
985
+
986
+ //try activate error field
987
+ for(i=0; i<this.$editables.length; i++) {
988
+ if (this.$editables[i].error) {
989
+ this.$editables[i].activate();
990
+ return;
991
+ }
992
+ }
993
+
994
+ //by default activate first field
995
+ this.$editables[0].activate();
996
+ }
997
+ },
998
+
999
+ /**
1000
+ * Hides form with editable controls without saving.
1001
+ *
1002
+ * @method $hide()
1003
+ * @memberOf editable-form
1004
+ */
1005
+ $hide: function() {
1006
+ if (!this.$visible) {
1007
+ return;
1008
+ }
1009
+ this.$visible = false;
1010
+ // self hide
1011
+ this.$onhide();
1012
+ // children's hide
1013
+ angular.forEach(this.$editables, function(editable) {
1014
+ editable.hide();
1015
+ });
1016
+
1017
+ // remove from internal list of shown forms
1018
+ editableUtils.arrayRemove(shown, this);
1019
+ },
1020
+
1021
+ /**
1022
+ * Triggers `oncancel` event and calls `$hide()`.
1023
+ *
1024
+ * @method $cancel()
1025
+ * @memberOf editable-form
1026
+ */
1027
+ $cancel: function() {
1028
+ if (!this.$visible) {
1029
+ return;
1030
+ }
1031
+ // self cancel
1032
+ this.$oncancel();
1033
+ // children's cancel
1034
+ angular.forEach(this.$editables, function(editable) {
1035
+ editable.cancel();
1036
+ });
1037
+ // self hide
1038
+ this.$hide();
1039
+ },
1040
+
1041
+ $setWaiting: function(value) {
1042
+ this.$waiting = !!value;
1043
+ // we can't just set $waiting variable and use it via ng-disabled in children
1044
+ // because in editable-row form is not accessible
1045
+ angular.forEach(this.$editables, function(editable) {
1046
+ editable.setWaiting(!!value);
1047
+ });
1048
+ },
1049
+
1050
+ /**
1051
+ * Shows error message for particular field.
1052
+ *
1053
+ * @method $setError(name, msg)
1054
+ * @param {string} name name of field
1055
+ * @param {string} msg error message
1056
+ * @memberOf editable-form
1057
+ */
1058
+ $setError: function(name, msg) {
1059
+ angular.forEach(this.$editables, function(editable) {
1060
+ if(!name || editable.name === name) {
1061
+ editable.setError(msg);
1062
+ }
1063
+ });
1064
+ },
1065
+
1066
+ $submit: function() {
1067
+ if (this.$waiting) {
1068
+ return;
1069
+ }
1070
+
1071
+ //clear errors
1072
+ this.$setError(null, '');
1073
+
1074
+ //children onbeforesave
1075
+ var pc = editablePromiseCollection();
1076
+ angular.forEach(this.$editables, function(editable) {
1077
+ pc.when(editable.onbeforesave());
1078
+ });
1079
+
1080
+ /*
1081
+ onbeforesave result:
1082
+ - true/undefined: save data and close form
1083
+ - false: close form without saving
1084
+ - string: keep form open and show error
1085
+ */
1086
+ pc.then({
1087
+ onWait: angular.bind(this, this.$setWaiting),
1088
+ onTrue: angular.bind(this, checkSelf, true),
1089
+ onFalse: angular.bind(this, checkSelf, false),
1090
+ onString: angular.bind(this, this.$activate)
1091
+ });
1092
+
1093
+ //save
1094
+ function checkSelf(childrenTrue){
1095
+ var pc = editablePromiseCollection();
1096
+ pc.when(this.$onbeforesave());
1097
+ pc.then({
1098
+ onWait: angular.bind(this, this.$setWaiting),
1099
+ onTrue: childrenTrue ? angular.bind(this, this.$save) : angular.bind(this, this.$hide),
1100
+ onFalse: angular.bind(this, this.$hide),
1101
+ onString: angular.bind(this, this.$activate)
1102
+ });
1103
+ }
1104
+ },
1105
+
1106
+ $save: function() {
1107
+ // write model for each editable
1108
+ angular.forEach(this.$editables, function(editable) {
1109
+ editable.save();
1110
+ });
1111
+
1112
+ //call onaftersave of self and children
1113
+ var pc = editablePromiseCollection();
1114
+ pc.when(this.$onaftersave());
1115
+ angular.forEach(this.$editables, function(editable) {
1116
+ pc.when(editable.onaftersave());
1117
+ });
1118
+
1119
+ /*
1120
+ onaftersave result:
1121
+ - true/undefined/false: just close form
1122
+ - string: keep form open and show error
1123
+ */
1124
+ pc.then({
1125
+ onWait: angular.bind(this, this.$setWaiting),
1126
+ onTrue: angular.bind(this, this.$hide),
1127
+ onFalse: angular.bind(this, this.$hide),
1128
+ onString: angular.bind(this, this.$activate)
1129
+ });
1130
+ },
1131
+
1132
+ $onshow: angular.noop,
1133
+ $oncancel: angular.noop,
1134
+ $onhide: angular.noop,
1135
+ $onbeforesave: angular.noop,
1136
+ $onaftersave: angular.noop
1137
+ };
1138
+
1139
+ return function() {
1140
+ return angular.extend({
1141
+ $editables: [],
1142
+ /**
1143
+ * Form visibility flag.
1144
+ *
1145
+ * @var {bool} $visible
1146
+ * @memberOf editable-form
1147
+ */
1148
+ $visible: false,
1149
+ /**
1150
+ * Form waiting flag. It becomes `true` when form is loading or saving data.
1151
+ *
1152
+ * @var {bool} $waiting
1153
+ * @memberOf editable-form
1154
+ */
1155
+ $waiting: false,
1156
+ $data: {},
1157
+ _clicked: false,
1158
+ _blur: null
1159
+ }, base);
1160
+ };
1161
+ }]);
1162
+
1163
+ /**
1164
+ * EditableForm directive. Should be defined in <form> containing editable controls.
1165
+ * It add some usefull methods to form variable exposed to scope by `name="myform"` attribute.
1166
+ *
1167
+ * @namespace editable-form
1168
+ */
1169
+ angular.module('xeditable').directive('editableForm',
1170
+ ['$rootScope', '$parse', 'editableFormController', 'editableOptions',
1171
+ function($rootScope, $parse, editableFormController, editableOptions) {
1172
+ return {
1173
+ restrict: 'A',
1174
+ require: ['form'],
1175
+ //require: ['form', 'editableForm'],
1176
+ //controller: EditableFormController,
1177
+ compile: function() {
1178
+ return {
1179
+ pre: function(scope, elem, attrs, ctrl) {
1180
+ var form = ctrl[0];
1181
+ var eForm;
1182
+
1183
+ //if `editableForm` has value - publish smartly under this value
1184
+ //this is required only for single editor form that is created and removed
1185
+ if(attrs.editableForm) {
1186
+ if(scope[attrs.editableForm] && scope[attrs.editableForm].$show) {
1187
+ eForm = scope[attrs.editableForm];
1188
+ angular.extend(form, eForm);
1189
+ } else {
1190
+ eForm = editableFormController();
1191
+ scope[attrs.editableForm] = eForm;
1192
+ angular.extend(eForm, form);
1193
+ }
1194
+ } else { //just merge to form and publish if form has name
1195
+ eForm = editableFormController();
1196
+ angular.extend(form, eForm);
1197
+ }
1198
+
1199
+ //read editables from buffer (that appeared before FORM tag)
1200
+ var buf = $rootScope.$$editableBuffer;
1201
+ var name = form.$name;
1202
+ if(name && buf && buf[name]) {
1203
+ angular.forEach(buf[name], function(editable) {
1204
+ eForm.$addEditable(editable);
1205
+ });
1206
+ delete buf[name];
1207
+ }
1208
+ },
1209
+ post: function(scope, elem, attrs, ctrl) {
1210
+ var eForm;
1211
+
1212
+ if(attrs.editableForm && scope[attrs.editableForm] && scope[attrs.editableForm].$show) {
1213
+ eForm = scope[attrs.editableForm];
1214
+ } else {
1215
+ eForm = ctrl[0];
1216
+ }
1217
+
1218
+ /**
1219
+ * Called when form is shown.
1220
+ *
1221
+ * @var {method|attribute} onshow
1222
+ * @memberOf editable-form
1223
+ */
1224
+ if(attrs.onshow) {
1225
+ eForm.$onshow = angular.bind(eForm, $parse(attrs.onshow), scope);
1226
+ }
1227
+
1228
+ /**
1229
+ * Called when form hides after both save or cancel.
1230
+ *
1231
+ * @var {method|attribute} onhide
1232
+ * @memberOf editable-form
1233
+ */
1234
+ if(attrs.onhide) {
1235
+ eForm.$onhide = angular.bind(eForm, $parse(attrs.onhide), scope);
1236
+ }
1237
+
1238
+ /**
1239
+ * Called when form is cancelled.
1240
+ *
1241
+ * @var {method|attribute} oncancel
1242
+ * @memberOf editable-form
1243
+ */
1244
+ if(attrs.oncancel) {
1245
+ eForm.$oncancel = angular.bind(eForm, $parse(attrs.oncancel), scope);
1246
+ }
1247
+
1248
+ /**
1249
+ * Whether form initially rendered in shown state.
1250
+ *
1251
+ * @var {bool|attribute} shown
1252
+ * @memberOf editable-form
1253
+ */
1254
+ if(attrs.shown && $parse(attrs.shown)(scope)) {
1255
+ eForm.$show();
1256
+ }
1257
+
1258
+ /**
1259
+ * Action when form losses focus. Values: `cancel|submit|ignore`.
1260
+ * Default is `ignore`.
1261
+ *
1262
+ * @var {string|attribute} blur
1263
+ * @memberOf editable-form
1264
+ */
1265
+ eForm._blur = attrs.blur || editableOptions.blurForm;
1266
+
1267
+ // onbeforesave, onaftersave
1268
+ if(!attrs.ngSubmit && !attrs.submit) {
1269
+ /**
1270
+ * Called after all children `onbeforesave` callbacks but before saving form values
1271
+ * to model.
1272
+ * If at least one children callback returns `non-string` - it will not not be called.
1273
+ * See [editable-form demo](#editable-form) for details.
1274
+ *
1275
+ * @var {method|attribute} onbeforesave
1276
+ * @memberOf editable-form
1277
+ *
1278
+ */
1279
+ if(attrs.onbeforesave) {
1280
+ eForm.$onbeforesave = function() {
1281
+ return $parse(attrs.onbeforesave)(scope, {$data: eForm.$data});
1282
+ };
1283
+ }
1284
+
1285
+ /**
1286
+ * Called when form values are saved to model.
1287
+ * See [editable-form demo](#editable-form) for details.
1288
+ *
1289
+ * @var {method|attribute} onaftersave
1290
+ * @memberOf editable-form
1291
+ *
1292
+ */
1293
+ if(attrs.onaftersave) {
1294
+ eForm.$onaftersave = function() {
1295
+ return $parse(attrs.onaftersave)(scope, {$data: eForm.$data});
1296
+ };
1297
+ }
1298
+
1299
+ elem.bind('submit', function(event) {
1300
+ event.preventDefault();
1301
+ scope.$apply(function() {
1302
+ eForm.$submit();
1303
+ });
1304
+ });
1305
+ }
1306
+
1307
+
1308
+ // click - mark form as clicked to exclude in document click handler
1309
+ elem.bind('click', function(e) {
1310
+ // ignore right/middle button click
1311
+ if (e.which !== 1) {
1312
+ return;
1313
+ }
1314
+
1315
+ if (eForm.$visible) {
1316
+ eForm._clicked = true;
1317
+ }
1318
+ });
1319
+
1320
+ }
1321
+ };
1322
+ }
1323
+ };
1324
+ }]);
1325
+ /**
1326
+ * editablePromiseCollection
1327
+ *
1328
+ * Collect results of function calls. Shows waiting if there are promises.
1329
+ * Finally, applies callbacks if:
1330
+ * - onTrue(): all results are true and all promises resolved to true
1331
+ * - onFalse(): at least one result is false or promise resolved to false
1332
+ * - onString(): at least one result is string or promise rejected or promise resolved to string
1333
+ */
1334
+ angular.module('xeditable').factory('editablePromiseCollection', ['$q', function($q) {
1335
+
1336
+ function promiseCollection() {
1337
+ return {
1338
+ promises: [],
1339
+ hasFalse: false,
1340
+ hasString: false,
1341
+ when: function(result, noPromise) {
1342
+ if (result === false) {
1343
+ this.hasFalse = true;
1344
+ } else if (!noPromise && angular.isObject(result)) {
1345
+ this.promises.push($q.when(result));
1346
+ } else if (angular.isString(result)){
1347
+ this.hasString = true;
1348
+ } else { //result === true || result === undefined || result === null
1349
+ return;
1350
+ }
1351
+ },
1352
+ //callbacks: onTrue, onFalse, onString
1353
+ then: function(callbacks) {
1354
+ callbacks = callbacks || {};
1355
+ var onTrue = callbacks.onTrue || angular.noop;
1356
+ var onFalse = callbacks.onFalse || angular.noop;
1357
+ var onString = callbacks.onString || angular.noop;
1358
+ var onWait = callbacks.onWait || angular.noop;
1359
+
1360
+ var self = this;
1361
+
1362
+ if (this.promises.length) {
1363
+ onWait(true);
1364
+ $q.all(this.promises).then(
1365
+ //all resolved
1366
+ function(results) {
1367
+ onWait(false);
1368
+ //check all results via same `when` method (without checking promises)
1369
+ angular.forEach(results, function(result) {
1370
+ self.when(result, true);
1371
+ });
1372
+ applyCallback();
1373
+ },
1374
+ //some rejected
1375
+ function(error) {
1376
+ onWait(false);
1377
+ onString();
1378
+ }
1379
+ );
1380
+ } else {
1381
+ applyCallback();
1382
+ }
1383
+
1384
+ function applyCallback() {
1385
+ if (!self.hasString && !self.hasFalse) {
1386
+ onTrue();
1387
+ } else if (!self.hasString && self.hasFalse) {
1388
+ onFalse();
1389
+ } else {
1390
+ onString();
1391
+ }
1392
+ }
1393
+
1394
+ }
1395
+ };
1396
+ }
1397
+
1398
+ return promiseCollection;
1399
+
1400
+ }]);
1401
+
1402
+ /**
1403
+ * editableUtils
1404
+ */
1405
+ angular.module('xeditable').factory('editableUtils', [function() {
1406
+ return {
1407
+ indexOf: function (array, obj) {
1408
+ if (array.indexOf) return array.indexOf(obj);
1409
+
1410
+ for ( var i = 0; i < array.length; i++) {
1411
+ if (obj === array[i]) return i;
1412
+ }
1413
+ return -1;
1414
+ },
1415
+
1416
+ arrayRemove: function (array, value) {
1417
+ var index = this.indexOf(array, value);
1418
+ if (index >= 0) {
1419
+ array.splice(index, 1);
1420
+ }
1421
+ return value;
1422
+ },
1423
+
1424
+ // copy from https://github.com/angular/angular.js/blob/master/src/Angular.js
1425
+ camelToDash: function(str) {
1426
+ var SNAKE_CASE_REGEXP = /[A-Z]/g;
1427
+ return str.replace(SNAKE_CASE_REGEXP, function(letter, pos) {
1428
+ return (pos ? '-' : '') + letter.toLowerCase();
1429
+ });
1430
+ },
1431
+
1432
+ dashToCamel: function(str) {
1433
+ var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g;
1434
+ var MOZ_HACK_REGEXP = /^moz([A-Z])/;
1435
+ return str.
1436
+ replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) {
1437
+ return offset ? letter.toUpperCase() : letter;
1438
+ }).
1439
+ replace(MOZ_HACK_REGEXP, 'Moz$1');
1440
+ }
1441
+ };
1442
+ }]);
1443
+
1444
+ /**
1445
+ * editableNgOptionsParser
1446
+ *
1447
+ * see: https://github.com/angular/angular.js/blob/master/src/ng/directive/select.js#L131
1448
+ */
1449
+ angular.module('xeditable').factory('editableNgOptionsParser', [
1450
+ function() {
1451
+ //0000111110000000000022220000000000000000000000333300000000000000444444444444444000000000555555555555555000000066666666666666600000000000000007777000000000000000000088888
1452
+ var NG_OPTIONS_REGEXP = /^\s*(.*?)(?:\s+as\s+(.*?))?(?:\s+group\s+by\s+(.*))?\s+for\s+(?:([\$\w][\$\w]*)|(?:\(\s*([\$\w][\$\w]*)\s*,\s*([\$\w][\$\w]*)\s*\)))\s+in\s+(.*?)(?:\s+track\s+by\s+(.*?))?$/;
1453
+
1454
+ function parser(optionsExp) {
1455
+ var match;
1456
+
1457
+ if (! (match = optionsExp.match(NG_OPTIONS_REGEXP))) {
1458
+ throw 'ng-options parse error';
1459
+ }
1460
+
1461
+ var
1462
+ displayFn = match[2] || match[1],
1463
+ valueName = match[4] || match[6],
1464
+ keyName = match[5],
1465
+ groupByFn = match[3] || '',
1466
+ valueFn = match[2] ? match[1] : valueName,
1467
+ valuesFn = match[7],
1468
+ track = match[8],
1469
+ trackFn = track ? match[8] : null;
1470
+
1471
+ var ngRepeat;
1472
+ if (keyName === undefined) { // array
1473
+ ngRepeat = valueName + ' in ' + valuesFn;
1474
+ if (track !== undefined) {
1475
+ ngRepeat += ' track by '+trackFn;
1476
+ }
1477
+ } else { // object
1478
+ ngRepeat = '('+keyName+', '+valueName+') in '+valuesFn;
1479
+ }
1480
+
1481
+ // group not supported yet
1482
+ return {
1483
+ ngRepeat: ngRepeat,
1484
+ locals: {
1485
+ valueName: valueName,
1486
+ keyName: keyName,
1487
+ valueFn: valueFn,
1488
+ displayFn: displayFn
1489
+ }
1490
+ };
1491
+ }
1492
+
1493
+ return parser;
1494
+ }]);
1495
+
1496
+ /*
1497
+ Editable themes:
1498
+ - default
1499
+ - bootstrap 2
1500
+ - bootstrap 3
1501
+
1502
+ Note: in postrender() `this` is instance of editableController
1503
+ */
1504
+ angular.module('xeditable').factory('editableThemes', function() {
1505
+ var themes = {
1506
+ //default
1507
+ 'default': {
1508
+ formTpl: '<form class="editable-wrap"></form>',
1509
+ noformTpl: '<span class="editable-wrap"></span>',
1510
+ controlsTpl: '<span class="editable-controls"></span>',
1511
+ inputTpl: '',
1512
+ errorTpl: '<div class="editable-error" ng-show="$error" ng-bind="$error"></div>',
1513
+ buttonsTpl: '<span class="editable-buttons"></span>',
1514
+ submitTpl: '<button type="submit">save</button>',
1515
+ cancelTpl: '<button type="button" ng-click="$form.$cancel()">cancel</button>'
1516
+ },
1517
+
1518
+ //bs2
1519
+ 'bs2': {
1520
+ formTpl: '<form class="form-inline editable-wrap" role="form"></form>',
1521
+ noformTpl: '<span class="editable-wrap"></span>',
1522
+ controlsTpl: '<div class="editable-controls controls control-group" ng-class="{\'error\': $error}"></div>',
1523
+ inputTpl: '',
1524
+ errorTpl: '<div class="editable-error help-block" ng-show="$error" ng-bind="$error"></div>',
1525
+ buttonsTpl: '<span class="editable-buttons"></span>',
1526
+ submitTpl: '<button type="submit" class="btn btn-primary"><span class="icon-ok icon-white"></span></button>',
1527
+ cancelTpl: '<button type="button" class="btn" ng-click="$form.$cancel()">'+
1528
+ '<span class="icon-remove"></span>'+
1529
+ '</button>'
1530
+
1531
+ },
1532
+
1533
+ //bs3
1534
+ 'bs3': {
1535
+ formTpl: '<form class="form-inline editable-wrap" role="form"></form>',
1536
+ noformTpl: '<span class="editable-wrap"></span>',
1537
+ controlsTpl: '<div class="editable-controls form-group" ng-class="{\'has-error\': $error}"></div>',
1538
+ inputTpl: '',
1539
+ errorTpl: '<div class="editable-error help-block" ng-show="$error" ng-bind="$error"></div>',
1540
+ buttonsTpl: '<span class="editable-buttons"></span>',
1541
+ submitTpl: '<button type="submit" class="btn btn-primary"><span class="glyphicon glyphicon-ok"></span></button>',
1542
+ cancelTpl: '<button type="button" class="btn btn-default" ng-click="$form.$cancel()">'+
1543
+ '<span class="glyphicon glyphicon-remove"></span>'+
1544
+ '</button>',
1545
+
1546
+ //bs3 specific prop to change buttons class: btn-sm, btn-lg
1547
+ buttonsClass: '',
1548
+ //bs3 specific prop to change standard inputs class: input-sm, input-lg
1549
+ inputClass: '',
1550
+ postrender: function() {
1551
+ //apply `form-control` class to std inputs
1552
+ switch(this.directiveName) {
1553
+ case 'editableText':
1554
+ case 'editableSelect':
1555
+ case 'editableTextarea':
1556
+ case 'editableEmail':
1557
+ case 'editableTel':
1558
+ case 'editableNumber':
1559
+ case 'editableUrl':
1560
+ case 'editableSearch':
1561
+ case 'editableDate':
1562
+ case 'editableDatetime':
1563
+ case 'editableTime':
1564
+ case 'editableMonth':
1565
+ case 'editableWeek':
1566
+ this.inputEl.addClass('form-control');
1567
+ if(this.theme.inputClass) {
1568
+ // don`t apply `input-sm` and `input-lg` to select multiple
1569
+ // should be fixed in bs itself!
1570
+ if(this.inputEl.attr('multiple') &&
1571
+ (this.theme.inputClass === 'input-sm' || this.theme.inputClass === 'input-lg')) {
1572
+ break;
1573
+ }
1574
+ this.inputEl.addClass(this.theme.inputClass);
1575
+ }
1576
+ break;
1577
+ }
1578
+
1579
+ //apply buttonsClass (bs3 specific!)
1580
+ if(this.buttonsEl && this.theme.buttonsClass) {
1581
+ this.buttonsEl.find('button').addClass(this.theme.buttonsClass);
1582
+ }
1583
+ }
1584
+ }
1585
+ };
1586
+
1587
+ return themes;
1588
+ });