diff --git a/Gemfile b/Gemfile index eea36d384..c0d4b144e 100644 --- a/Gemfile +++ b/Gemfile @@ -109,8 +109,8 @@ group :test do end group :development, :test do - gem 'terminal-notifier' - gem 'terminal-notifier-guard' + # gem 'terminal-notifier' + # gem 'terminal-notifier-guard' # Call 'byebug' anywhere in the code to stop execution and get a debugger console gem 'byebug' diff --git a/Gemfile.lock b/Gemfile.lock index f51bed510..325952d07 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -561,8 +561,6 @@ GEM i18n json (>= 1.4.3) temple (0.7.6) - terminal-notifier (1.6.3) - terminal-notifier-guard (1.6.4) terminal-table (1.5.2) therubyracer (0.12.2) libv8 (~> 3.16.14.0) @@ -683,8 +681,6 @@ DEPENDENCIES smart_listing spring spring-commands-rspec - terminal-notifier - terminal-notifier-guard therubyracer timecop turbolinks diff --git a/app/assets/images/keywords/faciliter.png b/app/assets/images/keywords/faciliter.png index 35d0da530..020ba4339 100644 Binary files a/app/assets/images/keywords/faciliter.png and b/app/assets/images/keywords/faciliter.png differ diff --git a/app/assets/images/keywords/profiter.png b/app/assets/images/keywords/profiter.png index 682ea1723..0e1f016ba 100644 Binary files a/app/assets/images/keywords/profiter.png and b/app/assets/images/keywords/profiter.png differ diff --git a/app/assets/images/landing_background.jpg b/app/assets/images/landing_background.jpg index 3f25fb159..f7ddc8c98 100644 Binary files a/app/assets/images/landing_background.jpg and b/app/assets/images/landing_background.jpg differ diff --git a/app/assets/javascripts/admin.js b/app/assets/javascripts/admin.js index 6b66994cc..8cf2e6467 100644 --- a/app/assets/javascripts/admin.js +++ b/app/assets/javascripts/admin.js @@ -1,5 +1,10 @@ -$(document).on('page:load', destroy_action); -$(document).ready(destroy_action); +$(document).on('page:load', init_admin); +$(document).ready(init_admin); + +function init_admin(){ + destroy_action(); + on_change_type_de_champ_select(); +} function destroy_action(){ $(".delete").on('click', function(){ @@ -15,4 +20,19 @@ function destroy_action(){ $("#liste_gestionnaire #libelle").on('click', function(){ setTimeout(destroy_action, 500); }); +} + +function on_change_type_de_champ_select (){ + + $("select.form-control.type_champ").on('change', function(e){ + + parent = $(this).parent().parent() + + if (this.value === 'header_section') { + parent.addClass('header_section') + } + else { + parent.removeClass('header_section') + } + }) } \ No newline at end of file diff --git a/app/assets/javascripts/admin_procedures_modal.js b/app/assets/javascripts/admin_procedures_modal.js new file mode 100644 index 000000000..38112328c --- /dev/null +++ b/app/assets/javascripts/admin_procedures_modal.js @@ -0,0 +1,98 @@ +$(document).on('page:load', init_path_modal); +$(document).ready(init_path_modal); + +function init_path_modal() { + path_modal_action(); + path_validation_action(); + path_type_init(); + path_validation($("input[id='procedure_path']")); +} + +function path_modal_action() { + $('#publishModal').on('show.bs.modal', function (event) { + $("#publishModal .modal-body .table .tr_content").hide(); + + var button = $(event.relatedTarget) // Button that triggered the modal + var modal_title = button.data('modal_title'); // Extract info from data-* attributes + var modal_index = button.data('modal_index'); // Extract info from data-* attributes + + var modal = $(this) + modal.find('#publishModal_title').html(modal_title); + $("#publishModal .modal-body .table #"+modal_index).show(); + }) +} + +function path_validation_action() { + $("input[id='procedure_path']").keyup(function (key) { + if (key.keyCode != 13) + path_validation(this); + }); +} + +function togglePathMessage(valid, mine) { + $('#path_messages .message').hide(); + + if (valid === true && mine === true) { + $('#path_is_mine').show(); + } else if (valid === true && mine === false) { + $('#path_is_not_mine').show(); + } else if (valid === false && mine === null) { + $('#path_is_invalid').show(); + } + + if ((valid && mine === null) || mine === true) + $('#publishModal #publish').removeAttr('disabled') + else + $('#publishModal #publish').attr('disabled', 'disabled') +} + +function path_validation(el) { + var valid = validatePath($(el).val()); + toggleErrorClass(el, valid); + togglePathMessage(valid, null); +} + +function validatePath(path) { + var re = /^[a-z0-9_]{3,30}$/; + return re.test(path); +} + +function path_type_init() { + display = 'label'; + + var bloodhound = new Bloodhound({ + datumTokenizer: Bloodhound.tokenizers.obj.whitespace(display), + queryTokenizer: Bloodhound.tokenizers.whitespace, + + remote: { + url: '/admin/procedures/path_list?request=%QUERY', + wildcard: '%QUERY' + } + }); + bloodhound.initialize(); + + $("#procedure_path").typeahead({ + minLength: 1 + }, { + display: display, + source: bloodhound, + templates: { + empty: 'Ce lien est disponible !', + suggestion: Handlebars.compile("
{{label}}
") + }, + limit: 5 + }); + + $('#procedure_path').bind('typeahead:select', function(ev, suggestion) { + togglePathMessage(true, suggestion['mine']); + }); +} + +function transfer_errors_message(show) { + if(show){ + $("#not_found_admin").slideDown(100) + } + else { + $("#not_found_admin").slideUp(100) + } +} \ No newline at end of file diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index fa22bb903..288d060c0 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -29,6 +29,7 @@ //= require franceconnect //= require bootstrap-wysihtml5 //= require bootstrap-wysihtml5/locales/fr-FR +//= require handlebars //= require typeahead.bundle $(document).on('page:load', scroll_to); diff --git a/app/assets/javascripts/carte/carte.js b/app/assets/javascripts/carte/carte.js index 1e9ba0514..b7ec0ea4b 100644 --- a/app/assets/javascripts/carte/carte.js +++ b/app/assets/javascripts/carte/carte.js @@ -156,7 +156,7 @@ function add_event_search_address() { }); $("#search_by_address input[type='address']").keypress(function (e) { - if (e.which == 13) + if (e.keyCode == 13) get_address_point($(this).val()); }); } \ No newline at end of file diff --git a/app/assets/javascripts/dossiers.js b/app/assets/javascripts/dossiers.js index 86764fb01..0307b5a63 100644 --- a/app/assets/javascripts/dossiers.js +++ b/app/assets/javascripts/dossiers.js @@ -31,4 +31,9 @@ function error_form_siret(invalid_siret){ function reset_form_siret(){ $("input[type='submit']").removeClass('btn-danger').addClass('btn-success').val('Valider'); $("#dossier_siret").removeClass('input-error'); +} + +function toggle_etape_1(){ + $('.row.etape.etape_1 .etapes_menu #logos').toggle(100); + $('.row.etape.etape_1 .etapes_informations #description_procedure').toggle(100); } \ No newline at end of file diff --git a/app/assets/stylesheets/admin_procedures_modal.scss b/app/assets/stylesheets/admin_procedures_modal.scss new file mode 100644 index 000000000..320752d3c --- /dev/null +++ b/app/assets/stylesheets/admin_procedures_modal.scss @@ -0,0 +1,18 @@ +.path_mine_false { + color: red +} + +#path_messages { + .message { + display: none + } +} + +#publishModal { + .twitter-typeahead { + width: 300px; + } + .tt-menu { + width: 300px; + } +} \ No newline at end of file diff --git a/app/assets/stylesheets/admin_type_de_champ.scss b/app/assets/stylesheets/admin_type_de_champ.scss new file mode 100644 index 000000000..d8b9d1a7b --- /dev/null +++ b/app/assets/stylesheets/admin_type_de_champ.scss @@ -0,0 +1,18 @@ +.header_section{ + background-color: rgb(245,245,245); + margin-top: 20px; + margin-bottom: 10px; + margin-left: 0; + margin-right: 0; + text-align:center; + padding-bottom: 8px; + + .form-group.description { + display: none; + } + + .form-group.mandatory { + display: none; + } + +} \ No newline at end of file diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index 92fa141f8..dedae8dcd 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -164,10 +164,6 @@ textarea#description { width: 100%; } -input#nom_projet { - width: 100%; -} - .logo_fc_small { max-width: 27px; } diff --git a/app/assets/stylesheets/carte.scss b/app/assets/stylesheets/carte.scss index 274d502ea..f1d7d4dbb 100644 --- a/app/assets/stylesheets/carte.scss +++ b/app/assets/stylesheets/carte.scss @@ -132,4 +132,13 @@ table { .info h4 { margin: 0 0 5px; color: #777; -} \ No newline at end of file +} + +#search_by_address { + .twitter-typeahead { + width: 555px; + } + .tt-menu { + width: 555px; + } +} diff --git a/app/assets/stylesheets/description.scss b/app/assets/stylesheets/description.scss index 6bef57d7f..6c0845c46 100644 --- a/app/assets/stylesheets/description.scss +++ b/app/assets/stylesheets/description.scss @@ -7,6 +7,10 @@ } } +.page-header{ + border-bottom: 1px solid #CCCCCC !important; +} + .input-error { color: darkred !important; border-color: darkred !important @@ -21,11 +25,25 @@ } } +.type_champ-header_section { + @extend .col-md-12; + @extend .col-lg-12; + + margin-bottom: -25px; +} + .type_champ-address { @extend .col-md-6; @extend .col-lg-6; - input[type='address'] { + .twitter-typeahead { + width: 100%; + input { + width: 100%; + display: block !important; + } + } + .tt-menu { width: 100%; } } @@ -44,6 +62,11 @@ @extend .col-lg-3; } +.type_champ-yes_no { + @extend .col-md-3; + @extend .col-lg-3; +} + .type_champ-phone { @extend .col-md-2; @extend .col-lg-2; diff --git a/app/assets/stylesheets/typeahead.scss b/app/assets/stylesheets/typeahead.scss index e29aff51f..8cda8e19b 100644 --- a/app/assets/stylesheets/typeahead.scss +++ b/app/assets/stylesheets/typeahead.scss @@ -1,5 +1,5 @@ + .tt-menu { - width: 555px; padding: 8px 0; background-color: #fff; border: 1px solid #ccc; @@ -18,10 +18,6 @@ line-height: 24px; } -.twitter-typeahead { - width: 555px; -} - .tt-suggestion:hover { cursor: pointer; color: #fff; diff --git a/app/controllers/admin/procedures_controller.rb b/app/controllers/admin/procedures_controller.rb index 2698c8dca..1f6539c6d 100644 --- a/app/controllers/admin/procedures_controller.rb +++ b/app/controllers/admin/procedures_controller.rb @@ -7,9 +7,9 @@ class Admin::ProceduresController < AdminController def index @procedures = smart_listing_create :procedures, - current_administrateur.procedures.where(published: true, archived: false).order(created_at: :desc), - partial: "admin/procedures/list", - array: true + current_administrateur.procedures.where(published: true, archived: false).order(created_at: :desc), + partial: "admin/procedures/list", + array: true active_class end @@ -36,7 +36,6 @@ class Admin::ProceduresController < AdminController render 'index' end - def show @facade = AdminProceduresShowFacades.new @procedure.decorate end @@ -87,22 +86,78 @@ class Admin::ProceduresController < AdminController end def publish - change_status({published: true}) + procedure = current_administrateur.procedures.find(params[:procedure_id]) + + new_procedure_path = ProcedurePath.new( + { + path: params[:procedure_path], + procedure: procedure, + administrateur: procedure.administrateur + }) + if new_procedure_path.validate + new_procedure_path.delete + else + flash.alert = 'Lien de la procédure invalide' + return redirect_to admin_procedures_path + end + + procedure_path = ProcedurePath.find_by_path(params[:procedure_path]) + if procedure_path + if procedure_path.administrateur_id == current_administrateur.id + procedure_path.procedure.archive + else + @mine = false + return render '/admin/procedures/publish', formats: 'js' + end + end + + procedure.publish!(params[:procedure_path]) + + flash.notice = "Procédure publiée" + render js: "window.location = '#{admin_procedures_path}'" + + rescue ActiveRecord::RecordNotFound + flash.alert = 'Procédure inéxistante' + redirect_to admin_procedures_path + end + + def transfer + admin = Administrateur.find_by_email(params[:email_admin]) + + return render '/admin/procedures/transfer', formats: 'js', status: 404 if admin.nil? + + procedure = current_administrateur.procedures.find(params[:procedure_id]) + clone_procedure = procedure.clone + + clone_procedure.administrateur = admin + clone_procedure.save + + flash.now.notice = "La procédure a correctement été cloné vers le nouvel administrateur." + + render '/admin/procedures/transfer', formats: 'js', status: 200 end def archive - change_status({archived: params[:archive]}) + procedure = current_administrateur.procedures.find(params[:procedure_id]) + procedure.archive + + flash.notice = "Procédure archivée" + redirect_to admin_procedures_path + + rescue ActiveRecord::RecordNotFound + flash.alert = 'Procédure inéxistante' + redirect_to admin_procedures_path end def clone - @procedure = current_administrateur.procedures.find(params[:procedure_id]) + procedure = current_administrateur.procedures.find(params[:procedure_id]) - new_procedure = @procedure.clone + new_procedure = procedure.clone if new_procedure flash.notice = 'Procédure clonée' redirect_to edit_admin_procedure_path(id: new_procedure.id) else - flash.now.alert = @procedure.errors.full_messages.join('
').html_safe + flash.now.alert = procedure.errors.full_messages.join('
').html_safe render 'index' end @@ -123,6 +178,12 @@ class Admin::ProceduresController < AdminController @draft_class = 'active' end + def path_list + render json: ProcedurePath.where("path LIKE '%#{params[:request]}%'").pluck(:path, :administrateur_id).inject([]) { + |acc, value| acc.push({label: value.first, mine: value.second == current_administrateur.id}) + }.to_json + end + private def create_procedure_params @@ -132,16 +193,4 @@ class Admin::ProceduresController < AdminController def create_module_api_carto_params params.require(:procedure).require(:module_api_carto_attributes).permit(:id, :use_api_carto, :quartiers_prioritaires, :cadastre) end - - def change_status(status_options) - @procedure = current_administrateur.procedures.find(params[:procedure_id]) - @procedure.update_attributes(status_options) - - flash.notice = 'Procédure éditée' - redirect_to admin_procedures_path - - rescue ActiveRecord::RecordNotFound - flash.alert = 'Procédure inéxistante' - redirect_to admin_procedures_path - end end diff --git a/app/controllers/backoffice/dossiers_controller.rb b/app/controllers/backoffice/dossiers_controller.rb index 6f3e8b453..edf67db28 100644 --- a/app/controllers/backoffice/dossiers_controller.rb +++ b/app/controllers/backoffice/dossiers_controller.rb @@ -54,12 +54,20 @@ class Backoffice::DossiersController < ApplicationController render 'show' end + def follow + follow = current_gestionnaire.toggle_follow_dossier params[:dossier_id] + + flash.notice = (follow.class == Follow ? 'Dossier suivi' : 'Dossier relaché') + redirect_to request.referer + end + private def dossiers_to_display {'a_traiter' => waiting_for_gestionnaire, 'en_attente' => waiting_for_user, - 'termine' => termine}[@liste] + 'termine' => termine, + 'suivi' => suivi}[@liste] end def waiting_for_gestionnaire @@ -77,10 +85,16 @@ class Backoffice::DossiersController < ApplicationController @termine ||= current_gestionnaire.dossiers_filter.termine end + def suivi + @suivi_class = (@liste == 'suivi' ? 'active' : '') + @suivi ||= current_gestionnaire.dossiers_follow + end + def total_dossiers_per_state @dossiers_a_traiter_total = waiting_for_gestionnaire.count @dossiers_en_attente_total = waiting_for_user.count @dossiers_termine_total = termine.count + @dossiers_suivi_total = suivi.count end def create_dossier_facade dossier_id diff --git a/app/controllers/commentaires_controller.rb b/app/controllers/commentaires_controller.rb index 4a0b75662..bea003766 100644 --- a/app/controllers/commentaires_controller.rb +++ b/app/controllers/commentaires_controller.rb @@ -26,6 +26,10 @@ class CommentairesController < ApplicationController saved = @commentaire.save unless flash.alert if is_gestionnaire? + unless current_gestionnaire.follow? @commentaire.dossier + current_gestionnaire.toggle_follow_dossier @commentaire.dossier + end + NotificationMailer.new_answer(@commentaire.dossier).deliver_now! if saved redirect_to url_for(controller: 'backoffice/dossiers', action: :show, id: params['dossier_id']) elsif current_user.email != @commentaire.dossier.user.email diff --git a/app/controllers/users/description_controller.rb b/app/controllers/users/description_controller.rb index dfe9672d3..b3956eaa6 100644 --- a/app/controllers/users/description_controller.rb +++ b/app/controllers/users/description_controller.rb @@ -102,6 +102,6 @@ class Users::DescriptionController < UsersController private def create_params - params.permit(:nom_projet) + params.permit() end end diff --git a/app/controllers/users/dossiers_controller.rb b/app/controllers/users/dossiers_controller.rb index 50a5d9c9d..c96ed1cfb 100644 --- a/app/controllers/users/dossiers_controller.rb +++ b/app/controllers/users/dossiers_controller.rb @@ -2,7 +2,7 @@ class Users::DossiersController < UsersController include SmartListing::Helper::ControllerExtensions helper SmartListing::Helper - before_action :authenticate_user! + before_action :authenticate_user!, except: :commencer before_action :check_siret, only: :siret_informations before_action only: [:show] do @@ -22,6 +22,16 @@ class Users::DossiersController < UsersController total_dossiers_per_state end + def commencer + unless params[:procedure_path].nil? + procedure = ProcedurePath.where(path: params[:procedure_path]).first!.procedure + end + + redirect_to new_users_dossier_path(procedure_id: procedure.id) + rescue ActiveRecord::RecordNotFound + error_procedure + end + def new procedure = Procedure.where(archived: false, published: true).find(params[:procedure_id]) @@ -49,12 +59,16 @@ class Users::DossiersController < UsersController update_current_user_siret! siret - DossierService.new(@facade.dossier, siret, current_user.france_connect_information).dossier_informations! + dossier = DossierService.new(@facade.dossier, siret, current_user.france_connect_information).dossier_informations! + + if dossier.entreprise.nil? || dossier.etablissement.nil? + return errors_valid_siret + end @facade = facade params[:dossier_id] render '/dossiers/new_siret', formats: 'js' - rescue RestClient::ResourceNotFound + rescue RestClient::ResourceNotFound, RestClient::BadRequest errors_valid_siret rescue ActiveRecord::RecordNotFound @@ -169,4 +183,5 @@ class Users::DossiersController < UsersController def facade id = params[:id] DossierFacades.new id, current_user.email end + end diff --git a/app/decorators/procedure_decorator.rb b/app/decorators/procedure_decorator.rb index b0b38bea2..df848570d 100644 --- a/app/decorators/procedure_decorator.rb +++ b/app/decorators/procedure_decorator.rb @@ -2,7 +2,7 @@ class ProcedureDecorator < Draper::Decorator delegate_all def lien - h.new_users_dossiers_url(procedure_id: id) + h.commencer_url(procedure_path: path) unless path.nil? end def created_at_fr @@ -11,7 +11,7 @@ class ProcedureDecorator < Draper::Decorator def logo_img return 'logo-tps.png' if logo.blank? - logo + File.join(STORAGE_URL, File.basename(logo.path)) end def geographic_information module_api_carto diff --git a/app/facades/dossier_facades.rb b/app/facades/dossier_facades.rb index 55d4728da..2291d608f 100644 --- a/app/facades/dossier_facades.rb +++ b/app/facades/dossier_facades.rb @@ -45,4 +45,12 @@ class DossierFacades def invites @dossier.invites end + + def commentaires_files + PieceJustificative.where(dossier_id: @dossier.id, type_de_piece_justificative_id: nil) + end + + def followers + Gestionnaire.joins(:follows).where("follows.dossier_id=#{@dossier.id}") + end end \ No newline at end of file diff --git a/app/models/dossier.rb b/app/models/dossier.rb index 80e212454..4e0f13f8c 100644 --- a/app/models/dossier.rb +++ b/app/models/dossier.rb @@ -18,6 +18,7 @@ class Dossier < ActiveRecord::Base has_many :cadastres, dependent: :destroy has_many :commentaires, dependent: :destroy has_many :invites, dependent: :destroy + has_many :follows belongs_to :procedure belongs_to :user @@ -29,7 +30,6 @@ class Dossier < ActiveRecord::Base after_save :build_default_champs, if: Proc.new { procedure_id_changed? } - validates :nom_projet, presence: true, allow_blank: false, allow_nil: true validates :user, presence: true WAITING_FOR_GESTIONNAIRE = %w(initiated updated submitted) @@ -160,7 +160,6 @@ class Dossier < ActiveRecord::Base query_string_start_with = "#{word}%" composed_scope = composed_scope.where( - dossiers[:nom_projet].matches(query_string).or\ users[:email].matches(query_string).or\ etablissements[:siret].matches(query_string_start_with).or\ entreprises[:raison_sociale].matches(query_string)) @@ -199,4 +198,12 @@ class Dossier < ActiveRecord::Base update_attributes(autorisation_donnees: false) end + + def total_follow + follows.size + end + + def total_commentaire + self.commentaires.size + end end diff --git a/app/models/follow.rb b/app/models/follow.rb new file mode 100644 index 000000000..d554d8fe6 --- /dev/null +++ b/app/models/follow.rb @@ -0,0 +1,6 @@ +class Follow < ActiveRecord::Base + belongs_to :gestionnaire + belongs_to :dossier + + validates_uniqueness_of :gestionnaire_id, :scope => :dossier_id +end \ No newline at end of file diff --git a/app/models/gestionnaire.rb b/app/models/gestionnaire.rb index e01fe47a2..128783937 100644 --- a/app/models/gestionnaire.rb +++ b/app/models/gestionnaire.rb @@ -7,12 +7,34 @@ class Gestionnaire < ActiveRecord::Base has_many :assign_to, dependent: :destroy has_many :procedures, through: :assign_to has_many :dossiers, through: :procedures + has_many :follows def dossiers_filter dossiers.where(procedure_id: procedure_filter_list) end + def dossiers_follow + dossiers.joins(:follows).where("follows.gestionnaire_id = #{id}") + end + def procedure_filter_list procedure_filter.empty? ? procedures.pluck(:id) : procedure_filter end + + def toggle_follow_dossier dossier_id + dossier = dossier_id + dossier = Dossier.find(dossier_id) unless dossier_id.class == Dossier + + Follow.create!(dossier: dossier, gestionnaire: self) + rescue ActiveRecord::RecordInvalid + Follow.where(dossier: dossier, gestionnaire: self).delete_all + rescue ActiveRecord::RecordNotFound + nil + end + + def follow? dossier_id + dossier_id = dossier_id.id if dossier_id.class == Dossier + + Follow.where(gestionnaire_id: id, dossier_id: dossier_id).any? + end end diff --git a/app/models/procedure.rb b/app/models/procedure.rb index de66b000e..cd90371f7 100644 --- a/app/models/procedure.rb +++ b/app/models/procedure.rb @@ -3,6 +3,8 @@ class Procedure < ActiveRecord::Base has_many :types_de_champ, dependent: :destroy has_many :dossiers + has_one :procedure_path, dependent: :destroy + has_one :module_api_carto, dependent: :destroy belongs_to :administrateur @@ -21,6 +23,14 @@ class Procedure < ActiveRecord::Base validates :libelle, presence: true, allow_blank: false, allow_nil: false validates :description, presence: true, allow_blank: false, allow_nil: false + def path + procedure_path.path unless procedure_path.nil? + end + + def default_path + libelle.downcase.gsub(/[^a-z0-9\-_]/,"_").gsub(/_*$/, '').gsub(/_+/, '_') + end + def types_de_champ_ordered types_de_champ.order(:order_place) end @@ -65,4 +75,17 @@ class Procedure < ActiveRecord::Base return procedure if procedure.save end + def publish!(path) + self.update_attributes!({ published: true, archived: false }) + ProcedurePath.create!(path: path, procedure: self, administrateur: self.administrateur) + end + + def archive + self.procedure_path.destroy! if self.path + self.update_attributes!({ archived: true }) + end + + def total_dossier + self.dossiers.where.not(state: :draft).size + end end diff --git a/app/models/procedure_path.rb b/app/models/procedure_path.rb new file mode 100644 index 000000000..579544c12 --- /dev/null +++ b/app/models/procedure_path.rb @@ -0,0 +1,8 @@ +class ProcedurePath < ActiveRecord::Base + validates :path, procedure_path_format: true, presence: true, allow_blank: false, allow_nil: false + validates :administrateur_id, presence: true, allow_blank: false, allow_nil: false + validates :procedure_id, presence: true, allow_blank: false, allow_nil: false + + belongs_to :procedure + belongs_to :administrateur +end \ No newline at end of file diff --git a/app/models/type_de_champ.rb b/app/models/type_de_champ.rb index 3f3bdf1f4..e4df31ea0 100644 --- a/app/models/type_de_champ.rb +++ b/app/models/type_de_champ.rb @@ -9,7 +9,9 @@ class TypeDeChamp < ActiveRecord::Base civilite: 'civilite', email: 'email', phone: 'phone', - address: 'address' + address: 'address', + yes_no: 'yes_no', + header_section: 'header_section' } belongs_to :procedure diff --git a/app/serializers/dossier_serializer.rb b/app/serializers/dossier_serializer.rb index 90b391f66..3364c8928 100644 --- a/app/serializers/dossier_serializer.rb +++ b/app/serializers/dossier_serializer.rb @@ -1,10 +1,11 @@ class DossierSerializer < ActiveModel::Serializer attributes :id, - :nom_projet, :created_at, :updated_at, :archived, - :mandataire_social + :mandataire_social, + :state, + :total_commentaire has_one :entreprise has_one :etablissement diff --git a/app/serializers/dossiers_serializer.rb b/app/serializers/dossiers_serializer.rb index 2a4f084a1..0179a0667 100644 --- a/app/serializers/dossiers_serializer.rb +++ b/app/serializers/dossiers_serializer.rb @@ -1,5 +1,4 @@ class DossiersSerializer < ActiveModel::Serializer attributes :id, - :nom_projet, :updated_at end \ No newline at end of file diff --git a/app/serializers/procedure_serializer.rb b/app/serializers/procedure_serializer.rb index e21025a37..69d3bf307 100644 --- a/app/serializers/procedure_serializer.rb +++ b/app/serializers/procedure_serializer.rb @@ -7,7 +7,8 @@ class ProcedureSerializer < ActiveModel::Serializer :organisation, :direction, :archived, - :geographic_information + :geographic_information, + :total_dossier has_one :geographic_information, serializer: ModuleApiCartoSerializer diff --git a/app/uploaders/remote_downloader.rb b/app/uploaders/remote_downloader.rb index 987971f86..6d0d07e1b 100644 --- a/app/uploaders/remote_downloader.rb +++ b/app/uploaders/remote_downloader.rb @@ -1,11 +1,9 @@ class RemoteDownloader - DEST_URL = "https://storage.apientreprise.fr/" + CarrierWave::Uploader::Base.fog_directory + '/' - def initialize(filename) @filename = filename end def url - @url ||= File.join(DEST_URL, @filename) + @url ||= File.join(STORAGE_URL, @filename) end end diff --git a/app/validators/procedure_path_format_validator.rb b/app/validators/procedure_path_format_validator.rb new file mode 100644 index 000000000..2d677c37e --- /dev/null +++ b/app/validators/procedure_path_format_validator.rb @@ -0,0 +1,12 @@ +class ProcedurePathFormatValidator < ActiveModel::Validator + + def path_regex + /^[a-z0-9_]{3,30}$/ + end + + def validate(record) + return false if record.path.blank? + record.errors[:path] << "Path invalide" unless path_regex.match(record.path) + end + +end diff --git a/app/views/admin/procedures/_head.html.haml b/app/views/admin/procedures/_head.html.haml index 185fd711e..f3ca2c1b7 100644 --- a/app/views/admin/procedures/_head.html.haml +++ b/app/views/admin/procedures/_head.html.haml @@ -1,6 +1,6 @@ %h2.text-info -unless @procedure.logo.blank? - = image_tag @procedure.logo, style: 'width: 30px' + = image_tag @procedure.decorate.logo_img, style: 'width: 30px' =@procedure.libelle %br diff --git a/app/views/admin/procedures/_informations.html.haml b/app/views/admin/procedures/_informations.html.haml index c71686181..6be1a248f 100644 --- a/app/views/admin/procedures/_informations.html.haml +++ b/app/views/admin/procedures/_informations.html.haml @@ -15,7 +15,7 @@ .col-md-6.col-lg-6 %h4 Logo de la procédure - unless @procedure.logo.blank? - = image_tag @procedure.logo, {style: 'height: 40px; display: inline; margin-right: 6px', id: 'preview_procedure_logo'} + = image_tag @procedure.decorate.logo_img, {style: 'height: 40px; display: inline; margin-right: 6px', id: 'preview_procedure_logo'} = f.file_field(:logo, accept: 'image/png, image/jpg, image/jpeg', style: 'display:inline') %div{style:'margin-top:5px'} diff --git a/app/views/admin/procedures/_list.html.haml b/app/views/admin/procedures/_list.html.haml index ed729fc6d..459585388 100644 --- a/app/views/admin/procedures/_list.html.haml +++ b/app/views/admin/procedures/_list.html.haml @@ -3,7 +3,7 @@ %thead %th#ID= smart_listing.sortable 'ID', 'id' %th#libelle= smart_listing.sortable 'Libellé', 'libelle' - - unless @draft_class + - if @active_class %th#lien Lien %th#created_at= smart_listing.sortable 'Date création', 'created_at' %th#lien Actions @@ -11,10 +11,10 @@ - @procedures.each do |procedure| - procedure = procedure.decorate %tr - %td.col-md-1.col-lg-1= procedure.id + %td= procedure.id %td.col-md-6.col-lg-6 = link_to(procedure.libelle, "/admin/procedures/#{procedure.id}") - - unless @draft_class + - if @active_class %td= link_to procedure.lien, procedure.lien %td = procedure.created_at_fr diff --git a/app/views/admin/procedures/_modal_publish.html.haml b/app/views/admin/procedures/_modal_publish.html.haml new file mode 100644 index 000000000..a241ae444 --- /dev/null +++ b/app/views/admin/procedures/_modal_publish.html.haml @@ -0,0 +1,48 @@ +#publishModal.modal.fade{"aria-labelledby" => "myModalLabel", :role => "dialog", :tabindex => "-1"} + .modal-dialog.modal-lg{:role => "document"} + = form_tag admin_procedure_publish_path(procedure_id: @procedure.id), method: :put, remote: true do + .modal-content + .modal-header + %button.close{"aria-label" => "Close", "data-dismiss" => "modal", :type => "button"} + %span{"aria-hidden" => "true"} × + %h4#myModalLabel.modal-title + =@procedure.archived?? 'Réactiver' : 'Publier' + la procédure + %span#publishModal_title + .modal-body + Vous vous apprêtez à + =@procedure.archived?? 'republier' : 'publier' + votre procédure au public. + - unless @procedure.archived? + %b + Elle ne pourra plus être modifiée à l'issue de cette publication. + %br + Afin de faciliter l'accès à la procédure, vous êtes invité à personnaliser l'adresse d'accès si vous le souhaitez. + %br + .form-group + %br + %h4 Lien de la procédure + %p.center + ="#{root_url}commencer/" + = text_field_tag('procedure_path', @procedure.default_path, + id: 'procedure_path', + placeholder: 'Chemin vers la procédure', + class:'form-control', + maxlength: 30, + style: 'width: 300px; display: inline') + #path_messages + #path_is_mine.text-warning.center.message + Ce lien est déjà utilisé par une de vos procédure. + %br + Si vous voulez l'utiliser, l'ancienne procédure sera archivée (plus accessible du public). + #path_is_not_mine.text-danger.center.message + Ce lien est déjà utilisé par une procédure. + %br + Vous ne pouvez pas l'utiliser car il appartient à un autre administrateur. + #path_is_invalid.text-danger.center.message + Ce lien n'est pas valide. Il doit comporter au moins 3 caractères et seuls les caractères a-z, 0-9 et '_' sont autorisés. + .modal-footer + = submit_tag "#{@procedure.archived?? 'Réactiver' : 'Publier'}", class: %w(btn btn btn-success), + id: 'publish', + disabled: :disabled + = button_tag 'Annuler', class: %w(btn btn btn-default), id: 'cancel', data: { dismiss: 'modal' } diff --git a/app/views/admin/procedures/_modal_transfer.html.haml b/app/views/admin/procedures/_modal_transfer.html.haml new file mode 100644 index 000000000..caa28a31e --- /dev/null +++ b/app/views/admin/procedures/_modal_transfer.html.haml @@ -0,0 +1,23 @@ +#transferModal.modal.fade{"aria-labelledby" => "TransferProcedureModal", :role => "dialog", :tabindex => "-1"} + .modal-dialog.modal-md{:role => "document"} + = form_tag admin_procedure_transfer_path(procedure_id: @procedure.id), method: :post, remote: true do + .modal-content + .modal-header + %button.close{"aria-label" => "Close", "data-dismiss" => "modal", :type => "button"} + %span{"aria-hidden" => "true"} × + %h4#myModalLabel.modal-title + Petit transfert de procédure entre administrateur + .modal-body + %p + Cette fonctionnalité vous permet de transmettre un clone de votre procédure à un autre administrateur. + %div{style:'margin-top:20px'} + = text_field_tag :email_admin, '', {class: 'form-control', + type: 'email', + placeholder: 'Email administrateur cible', + maxlength: 30, + style: 'width: 300px; margin-left:auto; margin-right:auto'} + %div#not_found_admin.center.text-danger{style:'display:none; margin-top: 10px'} + Cet administrateur n'existe pas. + .modal-footer + = submit_tag "Envoyer", class: 'btn btn-success' + = button_tag 'Annuler', class: %w(btn btn btn-default), id: 'cancel', data: { dismiss: 'modal' } diff --git a/app/views/admin/procedures/publish.js.erb b/app/views/admin/procedures/publish.js.erb new file mode 100644 index 000000000..04a6b2229 --- /dev/null +++ b/app/views/admin/procedures/publish.js.erb @@ -0,0 +1 @@ +<%= "togglePathMessage(true, #{@mine})" %> \ No newline at end of file diff --git a/app/views/admin/procedures/show.html.haml b/app/views/admin/procedures/show.html.haml index 8626a87e2..494969bcf 100644 --- a/app/views/admin/procedures/show.html.haml +++ b/app/views/admin/procedures/show.html.haml @@ -1,27 +1,31 @@ #procedure_show =render partial: 'head', locals: {active: 'Informations'} - -if ! @facade.procedure.published? - = form_tag admin_procedure_publish_path(procedure_id: @facade.procedure.id, publish: true), method: :put, style:'float: right; margin-top: 10px' do - %button#archive.btn.btn-small.btn-success.text-info{type: :button} - %i.fa.fa-eraser - Publier - #confirm - %button#valid.btn.btn-small.btn-success{type: :submit} - %i.fa.fa-check - Valider - %button#cancel.btn.btn-small.btn-danger{type: :button} - %i.fa.fa-remove - Annuler + -unless @facade.procedure.published? + %a#publish.btn.btn-success{"data-target" => "#publishModal", "data-toggle" => "modal", :type => "button", style:'float: right; margin-top: 10px'} + %i.fa.fa-eraser + Publier - -else + =render partial: '/admin/procedures/modal_publish' + + %a#transfer.btn.btn-small.btn-default{"data-target" => "#transferModal", "data-toggle" => "modal", :type => "button", style:'float: right; margin-top: 10px'} + %i.fa.fa-exchange + Transférer + + =render partial: '/admin/procedures/modal_transfer' + + -if @facade.procedure.archived? + %a#reenable.btn.btn-small.btn-default.text-info{"data-target" => "#publishModal", "data-toggle" => "modal", :type => "button", style:'float: right; margin-top: 10px'} + %i.fa.fa-eraser + Réactiver + + =render partial: '/admin/procedures/modal_publish' + + -elsif @facade.procedure.published? = form_tag admin_procedure_archive_path(procedure_id: @facade.procedure.id, archive: !@facade.procedure.archived?), method: :put, style:'float: right; margin-top: 10px' do %button#archive.btn.btn-small.btn-default.text-info{type: :button} %i.fa.fa-eraser - - if @facade.procedure.archived - = 'Réactiver' - - else - = 'Archiver' + = 'Archiver' #confirm %button#valid.btn.btn-small.btn-success{type: :submit} %i.fa.fa-check @@ -38,7 +42,10 @@ %div %h3 Lien procédure %div{style:'margin-left:3%'} - -if @facade.procedure.published? + -if @facade.procedure.archived? + %b + Cette procédure a été archivée et n'est plus accessible par le public. + -elsif @facade.procedure.published? = @facade.procedure.lien -else %b diff --git a/app/views/admin/procedures/transfer.js.erb b/app/views/admin/procedures/transfer.js.erb new file mode 100644 index 000000000..d57f2f48f --- /dev/null +++ b/app/views/admin/procedures/transfer.js.erb @@ -0,0 +1,11 @@ +<%- if response.status == 404 %> + transfer_errors_message(true); +<%- else %> + <% flash.each do |type, message| %> + $("#flash_message").html("
<%= message.html_safe %>
") + <% end %> + <% flash.clear %> + transfer_errors_message(false); + $("#email_admin").val(''); + $("button#cancel").click(); +<%- end %> \ No newline at end of file diff --git a/app/views/admin/types_de_champ/_fields.html.haml b/app/views/admin/types_de_champ/_fields.html.haml index 42118cdd3..4103a2329 100644 --- a/app/views/admin/types_de_champ/_fields.html.haml +++ b/app/views/admin/types_de_champ/_fields.html.haml @@ -1,15 +1,18 @@ = f.fields_for :types_de_champ, types_de_champ, remote: true do |ff| - .form-inline - .form-group + .form-inline{class:"#{ff.object.object.type_champ == 'header_section' ? 'header_section' : ''}"} + .form-group.libelle %h4 Libellé = ff.text_field :libelle, class: 'form-control libelle', placeholder: 'Libellé' - .form-group + + .form-group.type %h4 Type = ff.select :type_champ, TypeDeChamp.type_champs, {}, {class: 'form-control type_champ'} - .form-group + + .form-group.description %h4 Description = ff.text_area :description, class: 'form-control description', placeholder: 'Description' - .form-group + + .form-group.mandatory %h4 Obligatoire ? .center = ff.check_box :mandatory, placeholder: 'Obligatoire ?' diff --git a/app/views/admin/types_de_champ/show.js.erb b/app/views/admin/types_de_champ/show.js.erb index f8e69b515..259d609e3 100644 --- a/app/views/admin/types_de_champ/show.js.erb +++ b/app/views/admin/types_de_champ/show.js.erb @@ -1,4 +1,5 @@ <% flash.each do |type, message| %> $("#flash_message").html("
<%= message.html_safe %>
").children().fadeOut(5000) <% end %> -$('#liste_champ').html("<%= escape_javascript(render partial: 'form', locals: { procedure: @procedure, types_de_champ: @types_de_champ } ) %>"); \ No newline at end of file +$('#liste_champ').html("<%= escape_javascript(render partial: 'form', locals: { procedure: @procedure, types_de_champ: @types_de_champ } ) %>"); +on_change_type_de_champ_select (); \ No newline at end of file diff --git a/app/views/backoffice/dossiers/_follow_action.html.haml b/app/views/backoffice/dossiers/_follow_action.html.haml new file mode 100644 index 000000000..2a87b49eb --- /dev/null +++ b/app/views/backoffice/dossiers/_follow_action.html.haml @@ -0,0 +1,4 @@ +- if current_gestionnaire.follow?(@facade.dossier.id) + = link_to('Quitter'.html_safe, backoffice_dossier_follow_path(dossier_id: @facade.dossier.id), 'data-method' => :put, class: 'btn btn-md btn-danger', id: "suivre_dossier_#{@facade.dossier.id}") +-else + = link_to('Suivre', backoffice_dossier_follow_path(dossier_id: @facade.dossier.id), 'data-method' => :put, class: 'btn btn-md btn-primary', id: "suivre_dossier_#{@facade.dossier.id}") diff --git a/app/views/backoffice/dossiers/_followers.html.haml b/app/views/backoffice/dossiers/_followers.html.haml new file mode 100644 index 000000000..8274055d4 --- /dev/null +++ b/app/views/backoffice/dossiers/_followers.html.haml @@ -0,0 +1,14 @@ +%h3 Personnes suivant l'activité de ce dossier + +%br +.row + .col-md-4.col-lg-4 + - if @facade.followers.size > 0 + %ul + - @facade.followers.each do |follower| + %li + = follower.email + - else + Aucune personne ne suit ce dossier + + diff --git a/app/views/backoffice/dossiers/_list.html.haml b/app/views/backoffice/dossiers/_list.html.haml index 176b093f7..6307fff8c 100644 --- a/app/views/backoffice/dossiers/_list.html.haml +++ b/app/views/backoffice/dossiers/_list.html.haml @@ -1,19 +1,28 @@ - unless smart_listing.empty? %table.table - %thead.row - %th.col-md-4.col-lg-4= smart_listing.sortable 'Procédure', 'procedure.libelle' - %th.col-md-4.col-lg-4= smart_listing.sortable 'Dossier', 'nom_projet' - %th.col-md-2.col-lg-2= smart_listing.sortable 'État', 'state' - %th.col-md-2.col-lg-2= smart_listing.sortable 'Date de mise à jour', 'updated_at' + %thead + %th= smart_listing.sortable 'Procédure', 'procedure.libelle' + %th= smart_listing.sortable 'Raison sociale', 'entreprise.raison_sociale' + %th= smart_listing.sortable 'État', 'state' + %th= smart_listing.sortable 'Date de mise à jour', 'updated_at' + %th.center Actions + %th.center Abonnés - @dossiers.each do |dossier| - dossier = dossier.decorate %tr - %td= dossier.procedure.libelle - %td - = link_to(dossier.nom_projet, "/backoffice/dossiers/#{dossier.id}") + %td.col-md-4.col-lg-4= dossier.procedure.libelle + %td.col-md-4.col-lg-4 + = link_to(dossier.entreprise.raison_sociale, "/backoffice/dossiers/#{dossier.id}") %td= dossier.display_state %td= dossier.last_update + %td.center + - if current_gestionnaire.follow?(dossier.id) + = link_to('Quitter'.html_safe, backoffice_dossier_follow_path(dossier_id: dossier.id), 'data-method' => :put, class: 'btn-sm btn-danger', id: "suivre_dossier_#{dossier.id}") + -else + = link_to('Suivre', backoffice_dossier_follow_path(dossier_id: dossier.id), 'data-method' => :put, class: 'btn-sm btn-primary', id: "suivre_dossier_#{dossier.id}") + %td.center{style:"color: #{dossier.total_follow == 0 ? 'red' : ''}"} + = dossier.total_follow = smart_listing.paginate = smart_listing.pagination_per_page_links diff --git a/app/views/backoffice/dossiers/_onglets.html.haml b/app/views/backoffice/dossiers/_onglets.html.haml index 7ee1ae1a3..589b12fce 100644 --- a/app/views/backoffice/dossiers/_onglets.html.haml +++ b/app/views/backoffice/dossiers/_onglets.html.haml @@ -17,6 +17,13 @@ .badge.progress-bar-info =@dossiers_en_attente_total + %li{ class: (@suivi_class) } + %a{:href => "#{url_for backoffice_dossiers_path(liste: 'suivi')}"} + %h5.text-warning + ="Suivi" + .badge.progress-bar-warning + =@dossiers_suivi_total + %li{ class: (@termine_class) } %a{:href => "#{url_for backoffice_dossiers_path(liste: 'termine')}"} %h5.text-success diff --git a/app/views/backoffice/dossiers/search.html.haml b/app/views/backoffice/dossiers/search.html.haml index d55850ae2..9d01e31b5 100644 --- a/app/views/backoffice/dossiers/search.html.haml +++ b/app/views/backoffice/dossiers/search.html.haml @@ -8,16 +8,14 @@ %h4 = "Dossier N°#{@dossier.id}" %tr - %td.col-md-2.col-lg-1 + %td.col-md-1.col-lg-1 = @dossier.id - %td.col-md-4.col-lg-3 - = link_to(@dossier.nom_projet, "/backoffice/dossiers/#{@dossier.id}") - %td.col-md-2.col-lg-3 - = @dossier.entreprise.raison_sociale - %td.col-md-4.col-lg-2 - = @dossier.user.email + %td.col-md-4.col-lg-4 + = @dossier.procedure.libelle + %td.col-md-4.col-lg-4 + = link_to(@dossier.entreprise.raison_sociale, "/backoffice/dossiers/#{@dossier.id}") %td.col-md-2.col-lg-2 - = @dossier.etablissement.siret + = @dossier.user.email %td.col-md-1.col-lg-1{class: @dossier.state_color_class} = @dossier.display_state %br @@ -29,21 +27,18 @@ - elsif !@dossiers_search.empty? %table.table %tr - %th.col-md-2.col-lg-1 ID dossier - %th.col-md-4.col-lg-3 Dossier - %th.col-md-2.col-lg-3 Raison Sociale - %th.col-md-4.col-lg-2 Email contact - %th.col-md-2.col-lg-2 SIRET + %th.col-md-1.col-lg-1 ID dossier + %th.col-md-4.col-lg-4 Procédure + %th.col-md-4.col-lg-4 Raison Sociale + %th.col-md-2.col-lg-2 Email contact %th.col-md-1.col-lg-1 État - @dossiers_search.each do |dossier| %tr %td= dossier.id - %td - = link_to(dossier.nom_projet, "/backoffice/dossiers/#{dossier.id}") - %td= dossier.entreprise.raison_sociale + %td= dossier.procedure.libelle + %td= link_to(dossier.entreprise.raison_sociale, "/backoffice/dossiers/#{dossier.id}") %td= dossier.user.email - %td= dossier.etablissement.siret %td{class: dossier.state_color_class}= dossier.display_state .pagination diff --git a/app/views/backoffice/dossiers/show.html.haml b/app/views/backoffice/dossiers/show.html.haml index e24c2b978..caba91adb 100644 --- a/app/views/backoffice/dossiers/show.html.haml +++ b/app/views/backoffice/dossiers/show.html.haml @@ -6,6 +6,8 @@ %h3{:class => 'text-success'} = @facade.dossier.display_state + = render partial: 'follow_action' + = render partial: '/dossiers/infos_entreprise' = render partial: '/dossiers/infos_dossier' @@ -16,17 +18,27 @@ %li{role: "presentation", class: "active"} %a{href: "#commentaires", 'aria-controls' => "commentaires", role: "tab", 'data-toggle' => "tab"} Commentaires + %li{role: "presentation"} + %a{href: "#commentaires_files", 'aria-controls' => "commentaires_files", role: "tab", 'data-toggle' => "tab"} + Fichiers %li{role: "presentation"} %a{href: "#invites", 'aria-controls' => "invites", role: "tab", 'data-toggle' => "tab"} Invités + %li{role: "presentation"} + %a{href: "#followers", 'aria-controls' => "followers", role: "tab", 'data-toggle' => "tab"} + Abonnés %div{class: "tab-content"} %div{role: "tabpanel", class: "tab-pane fade in active", id:"commentaires"} %h3 Flux de commentaires %br = render partial: '/users/recapitulatif/commentaires_flux' + %div{role: "tabpanel", class: "tab-pane fade", id:"commentaires_files"} + = render partial: '/dossiers/commentaires_files' %div{role: "tabpanel", class: "tab-pane fade", id:"invites"} = render partial: '/dossiers/invites' + %div{role: "tabpanel", class: "tab-pane fade", id:"followers"} + = render partial: 'followers' %br %br \ No newline at end of file diff --git a/app/views/dossiers/_commentaires_files.html.haml b/app/views/dossiers/_commentaires_files.html.haml new file mode 100644 index 000000000..53a22cc06 --- /dev/null +++ b/app/views/dossiers/_commentaires_files.html.haml @@ -0,0 +1,23 @@ +%h3 Fichiers des commentaires + +%br +- if @facade.commentaires_files.size > 0 + %table.table + %thead + %th.col-md-3 + Email + %th.col-md-2 + Date + %th.col-md-6 + Fichier + + + - @facade.commentaires_files.each do |file| + %tr + %td= file.user.nil? ? 'Accompagnateur' : file.user.email + %td= file.created_at.localtime + %td= link_to file.original_filename, file.content_url, style:'color: green', target: '_blank' +- else + %h4.text-primary + Pas de fichier dans le flux de commentaires. + diff --git a/app/views/dossiers/_infos_dossier.html.haml b/app/views/dossiers/_infos_dossier.html.haml index f916f5dcb..6e814043f 100644 --- a/app/views/dossiers/_infos_dossier.html.haml +++ b/app/views/dossiers/_infos_dossier.html.haml @@ -1,9 +1,7 @@ #infos_dossier %div.row .col-lg-6.col-md-6 - %h3.text-info - = @facade.dossier.nom_projet - %h4 + %h3 = @facade.dossier.procedure.libelle - if @facade.dossier.mandataire_social && gestionnaire_signed_in? diff --git a/app/views/dossiers/_show.html.haml b/app/views/dossiers/_show.html.haml index 3b273f41d..4874bedb6 100644 --- a/app/views/dossiers/_show.html.haml +++ b/app/views/dossiers/_show.html.haml @@ -8,14 +8,7 @@ -#- if @facade.procedure.module_api_carto.use_api_carto? -# .row.etape.etape_3 - -# .etape.etapes_menu.col-md-3.col-lg-3 - -# %h3 - -# 3 - Ma zone d'intervention - -# .etape.etapes_informations.col-md-9.col-lg-9 + -# = render partial: '/dossiers/etapes/etape3' -# -#.row.etape.etape_4 - -# .etape.etapes_menu.col-md-3.col-lg-3 - -# %h3 - -# = "#{@facade.procedure.module_api_carto.use_api_carto? ? '4' : '3'} - Mon dossier" - -# .etape.etapes_informations.col-md-9.col-lg-9 - + -# = render partial: '/dossiers/etapes/etape4' diff --git a/app/views/dossiers/etapes/_etape1.html.haml b/app/views/dossiers/etapes/_etape1.html.haml index 917e08b0f..9b3a10025 100644 --- a/app/views/dossiers/etapes/_etape1.html.haml +++ b/app/views/dossiers/etapes/_etape1.html.haml @@ -2,7 +2,8 @@ %h3 Ma procédure %br - .center + + #logos.center{class: (@facade.entreprise.nil? ? '' : 'mask')} - if @facade.procedure.euro_flag #euro_flag.flag =image_tag('drapeau_europe.png') @@ -15,5 +16,5 @@ %h2#titre_procedure.text-info = @facade.procedure.libelle - %p{style:'width: 95%;'} + %p#description_procedure{style:'width: 95%;', class: (@facade.entreprise.nil? ? '' : 'mask')} = h @facade.procedure.description.html_safe \ No newline at end of file diff --git a/app/views/dossiers/etapes/_etape2.html.haml b/app/views/dossiers/etapes/_etape2.html.haml index cb14a694c..2e8f161e9 100644 --- a/app/views/dossiers/etapes/_etape2.html.haml +++ b/app/views/dossiers/etapes/_etape2.html.haml @@ -22,6 +22,7 @@ = f.hidden_field :dossier_id, value: @facade.dossier.id = f.submit 'Valider', class: %w(btn btn-lg btn-success), data: { disable_with: "Recherche en cours ..." } - else + %br #recap_info_entreprise = render partial: '/dossiers/infos_entreprise' diff --git a/app/views/dossiers/etapes/_etape3.html.haml b/app/views/dossiers/etapes/_etape3.html.haml new file mode 100644 index 000000000..5e5735480 --- /dev/null +++ b/app/views/dossiers/etapes/_etape3.html.haml @@ -0,0 +1,6 @@ +.etape.etapes_menu.col-md-3.col-lg-3 + %h3 + Ma zone d'intervention + +.etape.etapes_informations.col-md-9.col-lg-9 + .row diff --git a/app/views/dossiers/etapes/_etape4.html.haml b/app/views/dossiers/etapes/_etape4.html.haml new file mode 100644 index 000000000..64636c231 --- /dev/null +++ b/app/views/dossiers/etapes/_etape4.html.haml @@ -0,0 +1,6 @@ +.etape.etapes_menu.col-md-3.col-lg-3 + %h3 + Mon dossier + +.etape.etapes_informations.col-md-9.col-lg-9 + .row diff --git a/app/views/dossiers/new_siret.js.erb b/app/views/dossiers/new_siret.js.erb index 803581a3c..c3d7503cf 100644 --- a/app/views/dossiers/new_siret.js.erb +++ b/app/views/dossiers/new_siret.js.erb @@ -1,7 +1,14 @@ -$('.row.etape.etape_2').html("<%= escape_javascript(render partial: '/dossiers/etapes/etape2', locals: { facade: @facade } ) %>"); -the_terms(); - -<% unless flash.empty? %> +<% if flash.empty? %> +$('.row.etape.etape_2').hide(300, render_new_siret); +$('.row.etape.etape_2').slideDown(400, the_terms); +toggle_etape_1(); +<% else %> error_form_siret('<%= invalid_siret %>'); <% end %> -<% flash.clear %> \ No newline at end of file + +<% flash.clear %> + + +function render_new_siret(){ + $('.row.etape.etape_2').html("<%= escape_javascript(render partial: '/dossiers/etapes/etape2', locals: { facade: @facade } ) %>"); +} \ No newline at end of file diff --git a/app/views/gestionnaires/passwords/new.html.haml b/app/views/gestionnaires/passwords/new.html.haml index 9c15a8ca9..eed941e40 100644 --- a/app/views/gestionnaires/passwords/new.html.haml +++ b/app/views/gestionnaires/passwords/new.html.haml @@ -1,5 +1,6 @@ = devise_error_messages! +%br #form_login = image_tag('logo-tps.png') %br diff --git a/app/views/invite_mailer/invite_user.text.erb b/app/views/invite_mailer/invite_user.text.erb index 12d8125c9..57827303c 100644 --- a/app/views/invite_mailer/invite_user.text.erb +++ b/app/views/invite_mailer/invite_user.text.erb @@ -1,7 +1,6 @@ Bonjour <%= @invite.email %> L'utilisateur <%= @invite.email_sender %> souhaite que vous participiez à l'élaboration d'un dossier sur la plateforme TPS. -Ce dossier se nomme : <%= @invite.dossier.nom_projet %> Pour le consulter, merci de suivre ce lien : <%= users_dossiers_invite_url(@invite.id) %> diff --git a/app/views/users/description/_champs.html.haml b/app/views/users/description/_champs.html.haml index df175b482..3d6d5387f 100644 --- a/app/views/users/description/_champs.html.haml +++ b/app/views/users/description/_champs.html.haml @@ -8,6 +8,10 @@ = '*' %input{type: 'hidden', name:"champs['#{champ.id}']", id: "champs_#{champ.id}", value: ''} %input{type: 'checkbox', style:'margin-left: 15px;', name:"champs['#{champ.id}']", id: "champs_#{champ.id}", checked: ('checked' if champ.value == 'on')} + + - elsif champ.type_champ == 'header_section' + =render partial: 'users/description/champs/header_section', locals: {champ: champ} + -else %h4 = champ.libelle @@ -23,6 +27,9 @@ - elsif champ.type_champ == 'datetime' =render partial: 'users/description/champs/datetime', locals: {champ: champ} + - elsif champ.type_champ == 'yes_no' + =render partial: 'users/description/champs/yes_no', locals: {champ: champ} + -else %input.form-control{name:"champs['#{champ.id}']", placeholder: champ.libelle, diff --git a/app/views/users/description/_show.html.haml b/app/views/users/description/_show.html.haml index c0db91dc6..463ac15e8 100644 --- a/app/views/users/description/_show.html.haml +++ b/app/views/users/description/_show.html.haml @@ -3,15 +3,8 @@ = @dossier.procedure.libelle %h3 Votre dossier - %br - -#TODO use form_for = form_tag(url_for({controller: 'users/description', action: :create, dossier_id: @dossier.id}), class: 'form-inline', method: 'POST', multipart: true) do - %div - .row - .col-md-12 - %h4 Libellé pour votre dossier * - = text_field_tag :nom_projet, @dossier.nom_projet, placeholder: 'Nom du projet', class: 'form-control' #liste_champs -unless @champs.nil? diff --git a/app/views/users/description/champs/_header_section.html.haml b/app/views/users/description/champs/_header_section.html.haml new file mode 100644 index 000000000..5ff3eae62 --- /dev/null +++ b/app/views/users/description/champs/_header_section.html.haml @@ -0,0 +1,2 @@ +%h3.text-primary.page-header + =champ.libelle \ No newline at end of file diff --git a/app/views/users/description/champs/_yes_no.html.haml b/app/views/users/description/champs/_yes_no.html.haml new file mode 100644 index 000000000..9224f98f5 --- /dev/null +++ b/app/views/users/description/champs/_yes_no.html.haml @@ -0,0 +1,7 @@ +%label.radio-inline + = radio_button_tag "champs['#{champ.id}']", "true", champ.value == 'true' + Oui + +%label.radio-inline + = radio_button_tag "champs['#{champ.id}']", "false", champ.value == 'false' + Non \ No newline at end of file diff --git a/app/views/users/dossiers/_list.html.haml b/app/views/users/dossiers/_list.html.haml index eee042954..3f788cee0 100644 --- a/app/views/users/dossiers/_list.html.haml +++ b/app/views/users/dossiers/_list.html.haml @@ -2,7 +2,7 @@ %table.table %thead %th.col-md-4.col-lg-4= smart_listing.sortable 'Procédure', 'procedure.libelle' - %th.col-md-4.col-lg-4= smart_listing.sortable 'Nom du Projet', 'nom_projet' + %th.col-md-4.col-lg-4= smart_listing.sortable 'Raison sociale', 'entreprise.raison_sociale' %th.col-md-2.col-lg-2= smart_listing.sortable 'État', 'state' %th.col-md-2.col-lg-2= smart_listing.sortable 'Date de mise à jour', 'updated_at' - @dossiers.each do |dossier| @@ -15,8 +15,8 @@ %td = dossier.procedure.libelle %td - = link_to(dossier.nom_projet, users_dossiers_invite_path(id: invite.id)) unless invite.nil? - = link_to(dossier.nom_projet, users_dossier_recapitulatif_path(dossier)) if invite.nil? + = link_to(dossier.entreprise.raison_sociale, users_dossiers_invite_path(id: invite.id)) unless invite.nil? + = link_to(dossier.entreprise.raison_sociale, users_dossier_recapitulatif_path(dossier)) if invite.nil? %td{id: "dossier_#{dossier.id}_state"}= dossier.display_state %td= dossier.last_update diff --git a/app/views/users/passwords/new.html.haml b/app/views/users/passwords/new.html.haml index 4ffd7bc17..6504a7295 100644 --- a/app/views/users/passwords/new.html.haml +++ b/app/views/users/passwords/new.html.haml @@ -28,6 +28,7 @@ = devise_error_messages! +%br #form_login = image_tag('logo-tps.png') %br diff --git a/app/views/welcome_mailer/welcome_email.text.erb b/app/views/welcome_mailer/welcome_email.text.erb index 59e4ef9e6..57984af05 100644 --- a/app/views/welcome_mailer/welcome_email.text.erb +++ b/app/views/welcome_mailer/welcome_email.text.erb @@ -2,12 +2,12 @@ Bienvenue sur la plateforme TPS Nous vous remercions de vous être inscrit sur TPS. Pour mémoire, voici quelques informations utiles : - URL : https://tps.apientreprise.fr + URL : <%= root_url %>> Login : <%= @user.email %> Oubli de mot de passe, pas de problème : - https://tps.apientreprise.fr/users/password/new + <%= new_user_password_url %> Bonne journée, diff --git a/config/deploy.rb b/config/deploy.rb index 82ee49d66..4350ccd30 100644 --- a/config/deploy.rb +++ b/config/deploy.rb @@ -72,7 +72,8 @@ set :shared_paths, [ "config/unicorn.rb", "config/initializers/raven.rb", 'config/france_connect.yml', - 'config/initializers/mailjet.rb' + 'config/initializers/mailjet.rb', + 'config/initializers/storage_url.rb' ] diff --git a/config/environments/development.rb b/config/environments/development.rb index 0d42d3db7..66984e511 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -36,7 +36,7 @@ Rails.application.configure do # Raises helpful error messages. config.assets.raise_runtime_errors = true - config.action_mailer.delivery_method = :mailjet + config.action_mailer.delivery_method = :test config.action_mailer.default_url_options = { :host => 'localhost:3000' } # Raises error for missing translations diff --git a/config/initializers/carrierwave.rb b/config/initializers/carrierwave.rb index 35813733a..dcb948fc1 100644 --- a/config/initializers/carrierwave.rb +++ b/config/initializers/carrierwave.rb @@ -26,6 +26,8 @@ CarrierWave.configure do |config| if Rails.env.production? config.fog_directory = "tps" + elsif Rails.env.development? + config.fog_directory= "test_local" else config.fog_directory = "tps_dev" end diff --git a/config/initializers/storage_url.rb b/config/initializers/storage_url.rb new file mode 100644 index 000000000..0a1cf0f93 --- /dev/null +++ b/config/initializers/storage_url.rb @@ -0,0 +1 @@ +STORAGE_URL = "https://storage.apientreprise.fr/" + CarrierWave::Uploader::Base.fog_directory + '/' \ No newline at end of file diff --git a/config/locales/models/dossier/fr.yml b/config/locales/models/dossier/fr.yml index 37b811618..6c7eaab74 100644 --- a/config/locales/models/dossier/fr.yml +++ b/config/locales/models/dossier/fr.yml @@ -4,8 +4,6 @@ fr: dossier: 'Dossier' attributes: dossier: - nom_projet: 'Le nom du projet' - description: 'La description' montant_projet: 'Le montant du projet' montant_aide_demande: "Le montant d'aide demandée" date_previsionnelle: "La date de début prévisionnelle" diff --git a/config/routes.rb b/config/routes.rb index 98b331bc3..cc657c43d 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -89,6 +89,7 @@ Rails.application.routes.draw do get 'sign_in' => '/administrateurs/sessions#new' get 'procedures/archived' => 'procedures#archived' get 'procedures/draft' => 'procedures#draft' + get 'procedures/path_list' => 'procedures#path_list' get 'profile' => 'profile#show', as: :profile resources :procedures do @@ -103,6 +104,7 @@ Rails.application.routes.draw do put 'archive' => 'procedures#archive', as: :archive put 'publish' => 'procedures#publish', as: :publish + post 'transfer' => 'procedures#transfer', as: :transfer put 'clone' => 'procedures#clone', as: :clone resource :accompagnateurs, only: [:show, :update] @@ -142,6 +144,8 @@ Rails.application.routes.draw do post 'close' => 'dossiers#close' post 'invites' => '/invites#create' + + put 'follow' => 'dossiers#follow' end resources :commentaires, only: [:create] @@ -161,5 +165,9 @@ Rails.application.routes.draw do end end + namespace :commencer do + get '/:procedure_path' => '/users/dossiers#commencer' + end + apipie end diff --git a/db/migrate/20160622081322_add_procedure_path_mapping_table.rb b/db/migrate/20160622081322_add_procedure_path_mapping_table.rb new file mode 100644 index 000000000..9d3eecc1f --- /dev/null +++ b/db/migrate/20160622081322_add_procedure_path_mapping_table.rb @@ -0,0 +1,18 @@ +class AddProcedurePathMappingTable < ActiveRecord::Migration + class ProcedurePath < ActiveRecord::Base + end + + def change + create_table :procedure_paths do |t| + t.string :path, limit: 30, null: true, unique: true, index: true + t.integer :procedure_id, unique: true, null: true + t.integer :administrateur_id, unique: true, null: true + end + add_foreign_key :procedure_paths, :procedures + add_foreign_key :procedure_paths, :administrateurs + + Procedure.all.each do |procedure| + ProcedurePath.create(path: "#{procedure.id}", procedure_id: procedure.id, administrateur_id: procedure.administrateur.id) + end + end +end diff --git a/db/migrate/20160718124741_gestionnaire_can_follow_dossier.rb b/db/migrate/20160718124741_gestionnaire_can_follow_dossier.rb new file mode 100644 index 000000000..15247e65f --- /dev/null +++ b/db/migrate/20160718124741_gestionnaire_can_follow_dossier.rb @@ -0,0 +1,8 @@ +class GestionnaireCanFollowDossier < ActiveRecord::Migration + def change + create_table :follows do |t| + t.belongs_to :gestionnaire, index: true + t.belongs_to :dossier, index: true + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 630778df7..1dacf946f 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20160622081321) do +ActiveRecord::Schema.define(version: 20160718124741) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -168,6 +168,14 @@ ActiveRecord::Schema.define(version: 20160622081321) do t.integer "etablissement_id" end + create_table "follows", force: :cascade do |t| + t.integer "gestionnaire_id" + t.integer "dossier_id" + end + + add_index "follows", ["dossier_id"], name: "index_follows_on_dossier_id", using: :btree + add_index "follows", ["gestionnaire_id"], name: "index_follows_on_gestionnaire_id", using: :btree + create_table "france_connect_informations", force: :cascade do |t| t.string "gender" t.string "given_name" @@ -226,6 +234,14 @@ ActiveRecord::Schema.define(version: 20160622081321) do add_index "pieces_justificatives", ["type_de_piece_justificative_id"], name: "index_pieces_justificatives_on_type_de_piece_justificative_id", using: :btree + create_table "procedure_paths", force: :cascade do |t| + t.string "path", limit: 30 + t.integer "procedure_id" + t.integer "administrateur_id" + end + + add_index "procedure_paths", ["path"], name: "index_procedure_paths_on_path", using: :btree + create_table "procedures", force: :cascade do |t| t.string "libelle" t.string "description" @@ -304,4 +320,6 @@ ActiveRecord::Schema.define(version: 20160622081321) do add_foreign_key "cerfas", "dossiers" add_foreign_key "commentaires", "dossiers" add_foreign_key "dossiers", "users" + add_foreign_key "procedure_paths", "administrateurs" + add_foreign_key "procedure_paths", "procedures" end diff --git a/spec/controllers/admin/procedures_controller_spec.rb b/spec/controllers/admin/procedures_controller_spec.rb index 6bd380b79..c6ddc9462 100644 --- a/spec/controllers/admin/procedures_controller_spec.rb +++ b/spec/controllers/admin/procedures_controller_spec.rb @@ -77,7 +77,7 @@ describe Admin::ProceduresController, type: :controller do subject end - it { expect { subject }.to change{Procedure.count}.by(-1) } + it { expect { subject }.to change { Procedure.count }.by(-1) } end context 'when procedure is published' do @@ -263,31 +263,116 @@ describe Admin::ProceduresController, type: :controller do end end + describe 'PUT #publish' do + let(:procedure) { create(:procedure, administrateur: admin) } + let(:procedure2) { create(:procedure, :published, administrateur: admin) } + let(:procedure3) { create(:procedure, :published) } + + context 'when admin is the owner of the procedure' do + before do + put :publish, procedure_id: procedure.id, procedure_path: procedure_path + procedure.reload + procedure2.reload + end + + context 'procedure path does not exist' do + let(:procedure_path) { 'new_path' } + + it 'publish the given procedure' do + expect(procedure.published).to be_truthy + expect(procedure.path).to eq(procedure_path) + expect(response.status).to eq 200 + expect(flash[:notice]).to have_content 'Procédure publiée' + end + end + + context 'procedure path exists and is owned by current administrator' do + let(:procedure_path) { procedure2.path } + + it 'publish the given procedure' do + expect(procedure.published).to be_truthy + expect(procedure.path).to eq(procedure_path) + expect(response.status).to eq 200 + expect(flash[:notice]).to have_content 'Procédure publiée' + end + + it 'archive previous procedure' do + expect(procedure2.published).to be_truthy + expect(procedure2.archived).to be_truthy + expect(procedure2.path).to be_nil + end + end + + context 'procedure path exists and is not owned by current administrator' do + let(:procedure_path) { procedure3.path } + + it 'does not publish the given procedure' do + expect(procedure.published).to be_falsey + expect(procedure.path).to be_nil + expect(response.status).to eq 200 + end + + it 'previous procedure remains published' do + expect(procedure2.published).to be_truthy + expect(procedure2.archived).to be_falsey + expect(procedure2.path).to match(/fake_path/) + end + end + + context 'procedure path is invalid' do + let(:procedure_path) { 'Invalid Procedure Path' } + + it 'does not publish the given procedure' do + expect(procedure.published).to be_falsey + expect(procedure.path).to be_nil + expect(response).to redirect_to :admin_procedures + expect(flash[:alert]).to have_content 'Lien de la procédure invalide' + end + end + end + + context 'when admin is not the owner of the procedure' do + let(:admin_2) { create(:administrateur) } + + before do + sign_out admin + sign_in admin_2 + + put :publish, procedure_id: procedure.id, procedure_path: 'fake_path' + procedure.reload + end + + it 'fails' do + expect(response).to redirect_to :admin_procedures + expect(flash[:alert]).to have_content 'Procédure inéxistante' + end + end + end + describe 'PUT #archive' do let(:procedure) { create(:procedure, administrateur: admin) } context 'when admin is the owner of the procedure' do before do - put :archive, procedure_id: procedure.id, archive: archive + put :archive, procedure_id: procedure.id procedure.reload end context 'when owner want archive procedure' do - - let(:archive) { true } - it { expect(procedure.archived).to be_truthy } it { expect(response).to redirect_to :admin_procedures } - it { expect(flash[:notice]).to have_content 'Procédure éditée' } + it { expect(flash[:notice]).to have_content 'Procédure archivée' } end - context 'when owner want reactive procedure' do - - let(:archive) { false } + context 'when owner want to re-enable procedure' do + before do + put :publish, procedure_id: procedure.id, procedure_path: 'fake_path' + procedure.reload + end it { expect(procedure.archived).to be_falsey } - it { expect(response).to redirect_to :admin_procedures } - it { expect(flash[:notice]).to have_content 'Procédure éditée' } + it { expect(response.status).to eq 200 } + it { expect(flash[:notice]).to have_content 'Procédure publiée' } end end @@ -337,4 +422,60 @@ describe Admin::ProceduresController, type: :controller do it { expect(flash[:alert]).to have_content 'Procédure inéxistante' } end end + + describe 'GET #path_list' do + let!(:procedure) { create(:procedure, :published, administrateur: admin) } + let(:admin2) { create(:administrateur) } + let!(:procedure2) { create(:procedure, :published, administrateur: admin2) } + subject { get :path_list } + let(:body) { JSON.parse(response.body) } + + before do + subject + end + + it { expect(response.status).to eq(200) } + it { expect(body.size).to eq(2) } + it { expect(body.first['label']).to eq(procedure.path) } + it { expect(body.first['mine']).to be_truthy } + it { expect(body.second['label']).to eq(procedure2.path) } + it { expect(body.second['mine']).to be_falsy } + + context 'filtered' do + subject { get :path_list, request: procedure2.path } + + it { expect(response.status).to eq(200) } + it { expect(body.size).to eq(1) } + it { expect(body.first['label']).to eq(procedure2.path) } + it { expect(body.first['mine']).to be_falsy } + end + end + + describe 'POST transfer' do + let!(:procedure) { create :procedure, administrateur: admin } + + subject { post :transfer, email_admin: email_admin, procedure_id: procedure.id } + + context 'when admin is unknow' do + let(:email_admin) { 'plop' } + + it { expect(subject.status).to eq 404 } + end + + context 'when admin is know' do + let(:new_admin) { create :administrateur, email: 'new_admin@admin.com' } + let(:email_admin) { new_admin.email } + + it { expect(subject.status).to eq 200 } + it { expect {subject}.to change(Procedure, :count).by(1) } + + context { + before do + subject + end + + it { expect(Procedure.last.administrateur).to eq new_admin } + } + end + end end diff --git a/spec/controllers/api/v1/dossiers_controller_spec.rb b/spec/controllers/api/v1/dossiers_controller_spec.rb index 83518f772..6ff404ec4 100644 --- a/spec/controllers/api/v1/dossiers_controller_spec.rb +++ b/spec/controllers/api/v1/dossiers_controller_spec.rb @@ -52,9 +52,8 @@ describe API::V1::DossiersController do describe 'dossier' do subject { super().first } it { expect(subject[:id]).to eq(dossier.id) } - it { expect(subject[:nom_projet]).to eq(dossier.nom_projet) } it { expect(subject[:updated_at]).to eq("2008-09-01T08:05:00.000Z") } - it { expect(subject.keys.size).to eq(3) } + it { expect(subject.keys.size).to eq(2) } end end @@ -116,18 +115,19 @@ describe API::V1::DossiersController do let!(:dossier) { Timecop.freeze(date_creation) { create(:dossier, :with_entreprise, procedure: procedure) } } let(:dossier_id) { dossier.id } let(:body) { JSON.parse(retour.body, symbolize_names: true) } - let(:field_list) { [:id, :nom_projet, :created_at, :updated_at, :archived, :mandataire_social, :entreprise, :etablissement, :cerfa, :types_de_piece_justificative, :pieces_justificatives, :champs, :commentaires] } + let(:field_list) { [:id, :created_at, :updated_at, :archived, :mandataire_social, :total_commentaire, :entreprise, :etablissement, :cerfa, :types_de_piece_justificative, :pieces_justificatives, :champs, :commentaires, :state] } subject { body[:dossier] } it 'return REST code 200', :show_in_doc do expect(retour.code).to eq('200') end it { expect(subject[:id]).to eq(dossier.id) } - it { expect(subject[:nom_projet]).to eq(dossier.nom_projet) } + it { expect(subject[:state]).to eq(dossier.state) } it { expect(subject[:created_at]).to eq('2008-09-01T08:05:00.000Z') } it { expect(subject[:updated_at]).to eq('2008-09-01T08:05:00.000Z') } it { expect(subject[:archived]).to eq(dossier.archived) } it { expect(subject[:mandataire_social]).to eq(dossier.mandataire_social) } + it { expect(subject[:total_commentaire]).to eq(dossier.total_commentaire) } it { expect(subject.keys).to match_array(field_list) } @@ -230,7 +230,7 @@ describe API::V1::DossiersController do it { expect(subject[:libelle]).to eq('Description') } it { expect(subject[:description]).to eq('description de votre projet') } it { expect(subject.keys.include?(:order_place)).to be_truthy } - it { expect(subject[:type]).to eq('textarea') } + it { expect(subject[:type]).to eq('text') } end end end diff --git a/spec/controllers/api/v1/procedures_controller_spec.rb b/spec/controllers/api/v1/procedures_controller_spec.rb index a4134720a..f478cd5db 100644 --- a/spec/controllers/api/v1/procedures_controller_spec.rb +++ b/spec/controllers/api/v1/procedures_controller_spec.rb @@ -35,6 +35,7 @@ describe API::V1::ProceduresController do it { expect(subject[:direction]).to eq(procedure.direction) } it { expect(subject[:link]).to eq(procedure.lien_demarche) } it { expect(subject[:archived]).to eq(procedure.archived) } + it { expect(subject[:total_dossier]).to eq(procedure.total_dossier) } it { is_expected.to have_key(:types_de_champ) } it { expect(subject[:types_de_champ]).to be_an(Array) } describe 'type_de_champ' do diff --git a/spec/controllers/backoffice/commentaires_controller_spec.rb b/spec/controllers/backoffice/commentaires_controller_spec.rb index a85f0cc99..231f12c69 100644 --- a/spec/controllers/backoffice/commentaires_controller_spec.rb +++ b/spec/controllers/backoffice/commentaires_controller_spec.rb @@ -5,6 +5,7 @@ describe Backoffice::CommentairesController, type: :controller do let(:dossier_id) { dossier.id } let(:email_commentaire) { 'test@test.com' } let(:texte_commentaire) { 'Commentaire de test' } + let(:gestionnaire) { create(:gestionnaire) } before do allow(ClamavService).to receive(:safe_file?).and_return(true) @@ -12,16 +13,32 @@ describe Backoffice::CommentairesController, type: :controller do describe '#POST create' do before do - sign_in create(:gestionnaire) + sign_in gestionnaire end + context "création correct d'un commentaire" do + subject { post :create, dossier_id: dossier_id, email_commentaire: email_commentaire, texte_commentaire: texte_commentaire } + it 'depuis la page admin' do - post :create, dossier_id: dossier_id, email_commentaire: email_commentaire, texte_commentaire: texte_commentaire - expect(response).to redirect_to("/backoffice/dossiers/#{dossier_id}") + expect(subject).to redirect_to("/backoffice/dossiers/#{dossier_id}") + end + + it 'gestionnaire is automatically affect to follow the dossier' do + expect { subject }.to change(Follow, :count).by(1) + end + + context 'when gestionnaire already follow dossier' do + before do + create :follow, gestionnaire_id: gestionnaire.id, dossier_id: dossier_id + end + + it 'gestionnaire is automatically affect to follow the dossier' do + expect { subject }.to change(Follow, :count).by(0) + end end end - context 'when document is upload whith a commentaire', vcr: { cassette_name: 'controllers_backoffice_commentaires_controller_doc_upload_with_comment' } do + context 'when document is upload whith a commentaire', vcr: {cassette_name: 'controllers_backoffice_commentaires_controller_doc_upload_with_comment'} do let(:document_upload) { Rack::Test::UploadedFile.new("./spec/support/files/piece_justificative_0.pdf", 'application/pdf') } subject do @@ -80,7 +97,7 @@ describe Backoffice::CommentairesController, type: :controller do subject { dossier.state } - it {is_expected.to eq('replied')} + it { is_expected.to eq('replied') } it 'Notification email is send' do expect(NotificationMailer).to receive(:new_answer).and_return(NotificationMailer) diff --git a/spec/controllers/backoffice/dossiers_controller_spec.rb b/spec/controllers/backoffice/dossiers_controller_spec.rb index 895c0e4a5..19d5c280b 100644 --- a/spec/controllers/backoffice/dossiers_controller_spec.rb +++ b/spec/controllers/backoffice/dossiers_controller_spec.rb @@ -1,8 +1,12 @@ require 'rails_helper' describe Backoffice::DossiersController, type: :controller do + before do + @request.env['HTTP_REFERER'] = TPS::Application::URL + end + let(:dossier) { create(:dossier, :with_entreprise) } - let(:dossier_archived) { create(:dossier, :with_entreprise, archived: true) } + let(:dossier_archived) { create(:dossier, :with_entreprise, archived: true) } let(:dossier_id) { dossier.id } let(:bad_dossier_id) { Dossier.count + 10 } @@ -128,4 +132,31 @@ describe Backoffice::DossiersController, type: :controller do expect(dossier.state).to eq('closed') end end + + describe 'PUT #toggle_follow' do + before do + sign_in gestionnaire + end + + subject { put :follow, dossier_id: dossier_id } + + it { expect(subject.status).to eq 302 } + + describe 'flash alert' do + context 'when dossier is not follow by gestionnaire' do + before do + subject + end + it { expect(flash[:notice]).to have_content 'Dossier suivi' } + end + + context 'when dossier is follow by gestionnaire' do + before do + create :follow, gestionnaire_id: gestionnaire.id, dossier_id: dossier.id + subject + end + it { expect(flash[:notice]).to have_content 'Dossier relaché' } + end + end + end end diff --git a/spec/controllers/users/description_controller_spec.rb b/spec/controllers/users/description_controller_spec.rb index bcf08bcbb..58d0f9f2a 100644 --- a/spec/controllers/users/description_controller_spec.rb +++ b/spec/controllers/users/description_controller_spec.rb @@ -63,14 +63,13 @@ describe Users::DescriptionController, type: :controller, vcr: {cassette_name: ' describe 'POST #create' do let(:timestamp) { Time.now } - let(:nom_projet) { 'Projet de test' } let(:description) { 'Description de test Coucou, je suis un saut à la ligne Je suis un double saut la ligne.' } context 'Tous les attributs sont bons' do describe 'Premier enregistrement des données' do before do dossier.draft! - post :create, dossier_id: dossier_id, nom_projet: nom_projet + post :create, dossier_id: dossier_id dossier.reload end @@ -78,8 +77,6 @@ describe Users::DescriptionController, type: :controller, vcr: {cassette_name: ' expect(response).to redirect_to("/users/dossiers/#{dossier_id}/recapitulatif") end - it { expect(dossier.nom_projet).to eq nom_projet } - it 'etat du dossier est soumis' do expect(dossier.state).to eq('initiated') end @@ -88,7 +85,7 @@ describe Users::DescriptionController, type: :controller, vcr: {cassette_name: ' context 'En train de manipuler un dossier non brouillon' do before do dossier.initiated! - post :create, dossier_id: dossier_id, nom_projet: nom_projet, description: description + post :create, dossier_id: dossier_id dossier.reload end @@ -102,28 +99,10 @@ describe Users::DescriptionController, type: :controller, vcr: {cassette_name: ' end end - context 'Attribut(s) manquant(s)' do - subject { - post :create, - dossier_id: dossier_id, - nom_projet: nom_projet, - description: description - } - before { subject } - - context 'nom_projet empty' do - let(:nom_projet) { '' } - it { is_expected.to render_template(:show) } - it { expect(flash[:alert]).to be_present } - end - end - context 'Quand la procédure accepte les CERFA' do context 'Sauvegarde du CERFA PDF', vcr: {cassette_name: 'controllers_users_description_controller_save_cerfa'} do before do post :create, dossier_id: dossier_id, - nom_projet: nom_projet, - description: description, cerfa_pdf: cerfa_pdf dossier.reload end @@ -150,7 +129,7 @@ describe Users::DescriptionController, type: :controller, vcr: {cassette_name: ' let(:cerfas) { Cerfa.where(dossier_id: dossier_id) } before do - post :create, dossier_id: dossier_id, nom_projet: nom_projet, description: description, cerfa_pdf: cerfa_pdf + post :create, dossier_id: dossier_id, cerfa_pdf: cerfa_pdf end it "il y a deux CERFA PDF pour ce dossier" do @@ -165,8 +144,6 @@ describe Users::DescriptionController, type: :controller, vcr: {cassette_name: ' let!(:procedure) { create(:procedure) } before do post :create, dossier_id: dossier_id, - nom_projet: nom_projet, - description: description, cerfa_pdf: cerfa_pdf dossier.reload end @@ -186,8 +163,6 @@ describe Users::DescriptionController, type: :controller, vcr: {cassette_name: ' before do post :create, {dossier_id: dossier_id, - nom_projet: nom_projet, - description: description, champs: { "'#{dossier.champs.first.id}'" => dossier_champs_first, "'#{dossier.champs.second.id}'" => dossier_date_value @@ -228,8 +203,6 @@ describe Users::DescriptionController, type: :controller, vcr: {cassette_name: ' let(:all_pj_type) { dossier.procedure.type_de_piece_justificative_ids } before do post :create, {dossier_id: dossier_id, - nom_projet: nom_projet, - description: description, 'piece_justificative_'+all_pj_type[0].to_s => piece_justificative_0, 'piece_justificative_'+all_pj_type[1].to_s => piece_justificative_1} dossier.reload @@ -240,8 +213,6 @@ describe Users::DescriptionController, type: :controller, vcr: {cassette_name: ' expect(ClamavService).to receive(:safe_file?).twice post :create, {dossier_id: dossier_id, - nom_projet: nom_projet, - description: description, 'piece_justificative_'+all_pj_type[0].to_s => piece_justificative_0, 'piece_justificative_'+all_pj_type[1].to_s => piece_justificative_1} end diff --git a/spec/controllers/users/dossiers_controller_spec.rb b/spec/controllers/users/dossiers_controller_spec.rb index 4514cf689..8234c3668 100644 --- a/spec/controllers/users/dossiers_controller_spec.rb +++ b/spec/controllers/users/dossiers_controller_spec.rb @@ -152,16 +152,25 @@ describe Users::DossiersController, type: :controller do end end + describe 'GET #commencer' do + subject { get :commencer, procedure_path: procedure.path } + + it { expect(subject.status).to eq 302 } + it { expect(subject).to redirect_to new_users_dossier_path(procedure_id: procedure.id) } + end + describe 'POST #siret_informations' do + let(:user) { create(:user) } + before do stub_request(:get, "https://api-dev.apientreprise.fr/v2/etablissements/#{siret_not_found}?token=#{SIADETOKEN}") .to_return(status: 404, body: 'fake body') stub_request(:get, "https://api-dev.apientreprise.fr/v2/etablissements/#{siret}?token=#{SIADETOKEN}") - .to_return(status: 200, body: File.read('spec/support/files/etablissement.json')) + .to_return(status: status_entreprise_call, body: File.read('spec/support/files/etablissement.json')) stub_request(:get, "https://api-dev.apientreprise.fr/v2/entreprises/#{siren}?token=#{SIADETOKEN}") - .to_return(status: 200, body: File.read('spec/support/files/entreprise.json')) + .to_return(status: status_entreprise_call, body: File.read('spec/support/files/entreprise.json')) stub_request(:get, "https://api-dev.apientreprise.fr/v1/etablissements/exercices/#{siret}?token=#{SIADETOKEN}") .to_return(status: exercices_status, body: exercices_body) @@ -173,8 +182,7 @@ describe Users::DossiersController, type: :controller do end describe 'dossier attributs' do - let(:user) { create(:user) } - + let(:status_entreprise_call) { 200 } shared_examples 'with valid siret' do before do sign_in user @@ -315,6 +323,20 @@ describe Users::DossiersController, type: :controller do it { expect(response.to_a[2]).to be_an_instance_of ActionDispatch::Response::RackBody } end end + + context 'when REST error 400 is return' do + let(:status_entreprise_call) { 400 } + + subject { post :siret_informations, dossier_id: dossier.id, dossier: {siret: siret} } + + before do + sign_in user + subject + end + + it { expect(response.status).to eq 200 } + + end end describe 'PUT #update' do diff --git a/spec/decorators/procedure_decorator_spec.rb b/spec/decorators/procedure_decorator_spec.rb new file mode 100644 index 000000000..c730f7c84 --- /dev/null +++ b/spec/decorators/procedure_decorator_spec.rb @@ -0,0 +1,29 @@ +require 'spec_helper' + +describe ProcedureDecorator do + let(:procedure) { create(:procedure, :published, created_at: Time.new(2015, 12, 24, 14, 10)) } + subject { procedure.decorate } + + describe 'lien' do + subject { super().lien } + it { is_expected.to match(/fake_path/) } + end + + describe 'created_at_fr' do + subject { super().created_at_fr } + it { is_expected.to eq('24/12/2015 14:10') } + end + + describe 'logo_img' do + subject { super().logo_img } + it { is_expected.to eq('logo-tps.png') } + end + + describe 'geographic_information' do + subject { super().geographic_information } + it { expect(subject.use_api_carto).to be_falsey } + it { expect(subject.quartiers_prioritaires).to be_falsey } + it { expect(subject.cadastre).to be_falsey } + end + +end \ No newline at end of file diff --git a/spec/factories/dossier.rb b/spec/factories/dossier.rb index 3919ecccd..e3f1e3490 100644 --- a/spec/factories/dossier.rb +++ b/spec/factories/dossier.rb @@ -1,6 +1,5 @@ FactoryGirl.define do factory :dossier do - nom_projet "Demande de subvention dans le cadre d'accompagnement d'enfant à l'étranger" state 'draft' association :user, factory: [:user] diff --git a/spec/factories/follow.rb b/spec/factories/follow.rb new file mode 100644 index 000000000..9db1f67ce --- /dev/null +++ b/spec/factories/follow.rb @@ -0,0 +1,4 @@ +FactoryGirl.define do + factory :follow do + end +end diff --git a/spec/factories/procedure.rb b/spec/factories/procedure.rb index 376180656..d8d5dc8c1 100644 --- a/spec/factories/procedure.rb +++ b/spec/factories/procedure.rb @@ -1,4 +1,5 @@ FactoryGirl.define do + sequence(:published_path) { |n| "fake_path#{n}" } factory :procedure do lien_demarche 'http://localhost' libelle 'Demande de subvention' @@ -6,6 +7,7 @@ FactoryGirl.define do organisation "Orga SGMAP" direction "direction SGMAP" published false + administrateur { create(:administrateur) } after(:build) do |procedure, _evaluator| if procedure.module_api_carto.nil? @@ -55,8 +57,8 @@ FactoryGirl.define do end trait :published do - after(:build) do |procedure, _evaluator| - procedure.published = true + after(:create) do |procedure, _evaluator| + procedure.publish!(generate(:published_path)) end end end diff --git a/spec/factories/procedure_path.rb b/spec/factories/procedure_path.rb new file mode 100644 index 000000000..4620abe6e --- /dev/null +++ b/spec/factories/procedure_path.rb @@ -0,0 +1,5 @@ +FactoryGirl.define do + factory :procedure_path do + path 'fake_path' + end +end diff --git a/spec/factories/type_de_champ.rb b/spec/factories/type_de_champ.rb index 2b26e9ca0..8e348a254 100644 --- a/spec/factories/type_de_champ.rb +++ b/spec/factories/type_de_champ.rb @@ -2,7 +2,7 @@ FactoryGirl.define do factory :type_de_champ do libelle 'Description' description 'description de votre projet' - type_champ 'textarea' + type_champ 'text' order_place 1 mandatory false end diff --git a/spec/features/backoffice/navigate_to_dossier_spec.rb b/spec/features/backoffice/navigate_to_dossier_spec.rb index 41d0944f6..4a3664a99 100644 --- a/spec/features/backoffice/navigate_to_dossier_spec.rb +++ b/spec/features/backoffice/navigate_to_dossier_spec.rb @@ -5,7 +5,7 @@ feature 'on backoffice page' do let(:gestionnaire) { create(:gestionnaire, administrateurs: [administrateur]) } let(:procedure) { create(:procedure, administrateur: administrateur) } - let!(:dossier) { create(:dossier, :with_entreprise, procedure: procedure, state: 'initiated') } + let!(:dossier) { create(:dossier, :with_entreprise, procedure: procedure, state: 'initiated') } before do create :assign_to, gestionnaire: gestionnaire, procedure: procedure @@ -20,7 +20,7 @@ feature 'on backoffice page' do end context 'when he click on first dossier' do before do - page.click_on dossier.nom_projet + page.click_on dossier.entreprise.raison_sociale end scenario 'it redirect to dossier page' do expect(page).to have_css('#backoffice_dossier_show') diff --git a/spec/features/backoffice/search_file_spec.rb b/spec/features/backoffice/search_file_spec.rb index 8d0bb38df..d0db942d7 100644 --- a/spec/features/backoffice/search_file_spec.rb +++ b/spec/features/backoffice/search_file_spec.rb @@ -40,13 +40,11 @@ feature 'search file on gestionnaire backoffice' do context 'when terms input does return result' do let!(:dossier) { create(:dossier, :with_entreprise, procedure: procedure, state: 'initiated') } - let!(:dossier_2) { create(:dossier, procedure: procedure, state: 'initiated', nom_projet: 'Projet de test') } + let!(:dossier_2) { create(:dossier, procedure: procedure, state: 'initiated') } - let(:terms) { dossier.nom_projet } + let(:terms) { dossier.entreprise.raison_sociale } - it { expect(page).not_to have_content('Projet de test') } - - it { expect(page).to have_content(dossier.nom_projet) } + it { expect(page).to have_content(dossier.entreprise.raison_sociale) } context "when terms is a file's id" do let(:terms) { dossier.id } diff --git a/spec/features/description_page/upload_piece_justificative_spec.rb b/spec/features/description_page/upload_piece_justificative_spec.rb index 89b14e15a..09f97734d 100644 --- a/spec/features/description_page/upload_piece_justificative_spec.rb +++ b/spec/features/description_page/upload_piece_justificative_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' feature 'user is on description page' do - let!(:procedure) { create(:procedure, :with_two_type_de_piece_justificative, cerfa_flag: true) } + let!(:procedure) { create(:procedure, :with_two_type_de_piece_justificative, :with_type_de_champ, cerfa_flag: true) } let!(:dossier) { create(:dossier, :with_entreprise, procedure: procedure) } before do @@ -21,7 +21,7 @@ feature 'user is on description page' do context 'he fill description fields' do before do - find_by_id('nom_projet').set 'mon nom' + find_by_id("champs_#{dossier.champs.first.id}").set 'mon nom' end context 'before submit' do it 'dossier cerfa is empty' do diff --git a/spec/features/users/complete_demande_spec.rb b/spec/features/users/complete_demande_spec.rb index c028bd2a2..61f071c0c 100644 --- a/spec/features/users/complete_demande_spec.rb +++ b/spec/features/users/complete_demande_spec.rb @@ -2,17 +2,19 @@ require 'spec_helper' feature 'user path for dossier creation' do let(:user) { create(:user) } - let(:procedure) { create(:procedure, :published) } + let(:procedure) { create(:procedure, :published, :with_type_de_champ) } let(:siret) { '53272417600013' } let(:siren) { siret[0...9] } context 'user arrives on siret page', js: true do before do - visit new_users_dossiers_path(procedure_id: procedure.id) + visit commencer_path(procedure_path: procedure.path) end scenario 'he is redirected on login page' do expect(page).to have_css('#login_user') + expect(page).to have_css('#logo_procedure') + expect(page).to have_css('#titre_procedure') end context 'user sign_in' do @@ -66,7 +68,7 @@ feature 'user path for dossier creation' do end context 'user fill and validate description page' do before do - page.find_by_id('nom_projet').set 'Mon super projet' + page.find_by_id("champs_#{Dossier.last.champs.first.id}").set 'Mon super projet' page.click_on 'Soumettre mon dossier' end scenario 'user is on recap page' do diff --git a/spec/features/users/list_dossiers_spec.rb b/spec/features/users/list_dossiers_spec.rb index e9867fe61..22d0c1060 100644 --- a/spec/features/users/list_dossiers_spec.rb +++ b/spec/features/users/list_dossiers_spec.rb @@ -3,12 +3,15 @@ require 'spec_helper' feature 'user access to the list of his dossier' do let(:user) { create(:user) } - let!(:last_updated_dossier) { create(:dossier, user: user, state: 'replied')} - let!(:dossier1) { create(:dossier, user: user, nom_projet: 'mon permier dossier', state: 'replied') } - let!(:dossier2) { create(:dossier, nom_projet: 'mon deuxième dossier') } + let!(:last_updated_dossier) { create(:dossier, :with_entreprise, user: user, state: 'replied')} + let!(:dossier1) { create(:dossier, :with_entreprise, user: user, state: 'replied') } + let!(:dossier2) { create(:dossier, :with_entreprise) } before do - last_updated_dossier.update_attributes(nom_projet: 'salut la compagnie') + dossier1.update_column(:updated_at, "19/07/2016 15:35".to_time) + dossier1.entreprise.update_column(:raison_sociale, 'PLOP') + last_updated_dossier.entreprise.update_column(:raison_sociale, 'PLIP') + visit new_user_session_path within('#new_user') do page.find_by_id('user_email').set user.email @@ -17,12 +20,12 @@ feature 'user access to the list of his dossier' do end end scenario 'the list of dossier is displayed' do - expect(page).to have_content(dossier1.nom_projet) - expect(page).not_to have_content(dossier2.nom_projet) + expect(page).to have_content(dossier1.entreprise.raison_sociale) + expect(page).not_to have_content(dossier2.entreprise.raison_sociale) end scenario 'the list must be order by last updated' do - expect(page.body).to match(/#{last_updated_dossier.nom_projet}.*#{dossier1.nom_projet}/m) + expect(page.body).to match(/#{last_updated_dossier.entreprise.raison_sociale}.*#{dossier1.entreprise.raison_sociale}/m) end scenario 'the state of dossier is displayed' do @@ -31,7 +34,7 @@ feature 'user access to the list of his dossier' do context 'when user clicks on a projet in list' do before do - page.click_on dossier1.nom_projet + page.click_on dossier1.entreprise.raison_sociale end scenario 'user is redirected to dossier page' do expect(page).to have_css('#recap_dossier') diff --git a/spec/fixtures/cassettes/admin_procedure_edit.yml b/spec/fixtures/cassettes/admin_procedure_edit.yml new file mode 100644 index 000000000..a4f8431cf --- /dev/null +++ b/spec/fixtures/cassettes/admin_procedure_edit.yml @@ -0,0 +1,1344 @@ +--- +http_interactions: +- request: + method: post + uri: https://auth.cloud.ovh.net/v2.0/tokens + body: + encoding: UTF-8 + string: '{"auth":{"passwordCredentials":{"username":"ovh_fake_username","password":"ovh_fake_password"},"tenantName":"ovh_fake_tenant_name"}}' + headers: + User-Agent: + - fog/1.38.0 fog-core/1.38.0 + Content-Type: + - application/json + response: + status: + code: 200 + message: '' + headers: + Vary: + - X-Auth-Token + Content-Type: + - application/json + Content-Length: + - '7079' + Date: + - Thu, 12 May 2016 14:05:17 GMT + Connection: + - close + body: + encoding: UTF-8 + string: '{"access": {"token": {"issued_at": "2016-05-12T14:05:17.214079", "expires": + "2016-05-13T14:05:17Z", "id": "ff665d4700654b6d9c94964dfc1a262f", "tenant": + {"id": "a24c37ed11a84896914514384898c34b", "enabled": true, "name": "2627898119540674", + "description": "apientreprise"}, "audit_ids": ["Y2ixVCRJQe-D6IN-cPQQsw"]}, + "serviceCatalog": [{"endpoints": [{"adminURL": "https://compute.gra1.cloud.ovh.net/v2/a24c37ed11a84896914514384898c34b", + "region": "GRA1", "internalURL": "https://compute.gra1.cloud.ovh.net/v2/a24c37ed11a84896914514384898c34b", + "id": "17f6ef1cc63e492ab8d3f2bda8428cb0", "publicURL": "https://compute.gra1.cloud.ovh.net/v2/a24c37ed11a84896914514384898c34b"}, + {"adminURL": "https://compute.bhs1.cloud.ovh.net/v2/a24c37ed11a84896914514384898c34b", + "region": "BHS1", "internalURL": "https://compute.bhs1.cloud.ovh.net/v2/a24c37ed11a84896914514384898c34b", + "id": "21fdd202afd04470bbaf84f9396d0dcc", "publicURL": "https://compute.bhs1.cloud.ovh.net/v2/a24c37ed11a84896914514384898c34b"}, + {"adminURL": "https://compute.sbg1.cloud.ovh.net/v2/a24c37ed11a84896914514384898c34b", + "region": "SBG1", "internalURL": "https://compute.sbg1.cloud.ovh.net/v2/a24c37ed11a84896914514384898c34b", + "id": "a707bffedf1c4b80a124c585c67c1639", "publicURL": "https://compute.sbg1.cloud.ovh.net/v2/a24c37ed11a84896914514384898c34b"}], + "endpoints_links": [], "type": "compute", "name": "nova"}, {"endpoints": [{"adminURL": + "https://network.compute.gra1.cloud.ovh.net/", "region": "GRA1", "internalURL": + "https://network.compute.gra1.cloud.ovh.net/", "id": "26a339a8c7d5463f89ca937068ebbcd4", + "publicURL": "https://network.compute.gra1.cloud.ovh.net/"}, {"adminURL": + "https://network.compute.bhs1.cloud.ovh.net/", "region": "BHS1", "internalURL": + "https://network.compute.bhs1.cloud.ovh.net/", "id": "3fe2326789ec4e37af2e6b2c80a90876", + "publicURL": "https://network.compute.bhs1.cloud.ovh.net/"}, {"adminURL": + "https://network.compute.sbg1.cloud.ovh.net/", "region": "SBG1", "internalURL": + "https://network.compute.sbg1.cloud.ovh.net/", "id": "075839111e7a41f1bb458926e5f04cec", + "publicURL": "https://network.compute.sbg1.cloud.ovh.net/"}], "endpoints_links": + [], "type": "network", "name": "neutron"}, {"endpoints": [{"adminURL": "https://volume.compute.gra1.cloud.ovh.net/v2/a24c37ed11a84896914514384898c34b", + "region": "GRA1", "internalURL": "https://volume.compute.gra1.cloud.ovh.net/v2/a24c37ed11a84896914514384898c34b", + "id": "7231957fdf0346e5adebe860ac5e5e57", "publicURL": "https://volume.compute.gra1.cloud.ovh.net/v2/a24c37ed11a84896914514384898c34b"}, + {"adminURL": "https://volume.compute.bhs1.cloud.ovh.net/v2/a24c37ed11a84896914514384898c34b", + "region": "BHS1", "internalURL": "https://volume.compute.bhs1.cloud.ovh.net/v2/a24c37ed11a84896914514384898c34b", + "id": "2f5b68f95d7b4b1fad1a683dac8e8ca3", "publicURL": "https://volume.compute.bhs1.cloud.ovh.net/v2/a24c37ed11a84896914514384898c34b"}, + {"adminURL": "https://volume.compute.sbg1.cloud.ovh.net/v2/a24c37ed11a84896914514384898c34b", + "region": "SBG1", "internalURL": "https://volume.compute.sbg1.cloud.ovh.net/v2/a24c37ed11a84896914514384898c34b", + "id": "021b61bd7313479e8f8d77d21c7b434a", "publicURL": "https://volume.compute.sbg1.cloud.ovh.net/v2/a24c37ed11a84896914514384898c34b"}], + "endpoints_links": [], "type": "volumev2", "name": "cinderv2"}, {"endpoints": + [{"adminURL": "https://image.compute.gra1.cloud.ovh.net/", "region": "GRA1", + "internalURL": "https://image.compute.gra1.cloud.ovh.net/", "id": "56795c82f1744e47b7782f1fc2407212", + "publicURL": "https://image.compute.gra1.cloud.ovh.net/"}, {"adminURL": "https://image.compute.bhs1.cloud.ovh.net/", + "region": "BHS1", "internalURL": "https://image.compute.bhs1.cloud.ovh.net/", + "id": "5eaa4cbe80354ea482f2b0477c9c16f0", "publicURL": "https://image.compute.bhs1.cloud.ovh.net/"}, + {"adminURL": "https://image.compute.sbg1.cloud.ovh.net/", "region": "SBG1", + "internalURL": "https://image.compute.sbg1.cloud.ovh.net/", "id": "15758b246d1340e887a2170bd3399071", + "publicURL": "https://image.compute.sbg1.cloud.ovh.net/"}], "endpoints_links": + [], "type": "image", "name": "glance"}, {"endpoints": [{"adminURL": "https://volume.compute.gra1.cloud.ovh.net/v1/a24c37ed11a84896914514384898c34b", + "region": "GRA1", "internalURL": "https://volume.compute.gra1.cloud.ovh.net/v1/a24c37ed11a84896914514384898c34b", + "id": "a6936c8876c1490cbf91d0707e78d350", "publicURL": "https://volume.compute.gra1.cloud.ovh.net/v1/a24c37ed11a84896914514384898c34b"}, + {"adminURL": "https://volume.compute.bhs1.cloud.ovh.net/v1/a24c37ed11a84896914514384898c34b", + "region": "BHS1", "internalURL": "https://volume.compute.bhs1.cloud.ovh.net/v1/a24c37ed11a84896914514384898c34b", + "id": "43bc107cf78448faa9e5a6b3a5ca48dd", "publicURL": "https://volume.compute.bhs1.cloud.ovh.net/v1/a24c37ed11a84896914514384898c34b"}, + {"adminURL": "https://volume.compute.sbg1.cloud.ovh.net/v1/a24c37ed11a84896914514384898c34b", + "region": "SBG1", "internalURL": "https://volume.compute.sbg1.cloud.ovh.net/v1/a24c37ed11a84896914514384898c34b", + "id": "2be04ee1ddb148c19e91d3da5934fa55", "publicURL": "https://volume.compute.sbg1.cloud.ovh.net/v1/a24c37ed11a84896914514384898c34b"}], + "endpoints_links": [], "type": "volume", "name": "cinder"}, {"endpoints": + [{"adminURL": "https://storage.gra1.cloud.ovh.net", "region": "GRA1", "internalURL": + "http://127.0.0.1:8888/v1/AUTH_a24c37ed11a84896914514384898c34b", "id": "c96f61d071a74e36bd3c07e53d241ce3", + "publicURL": "https://storage.gra1.cloud.ovh.net/v1/AUTH_a24c37ed11a84896914514384898c34b"}, + {"adminURL": "https://storage.bhs1.cloud.ovh.net:8888/", "region": "BHS1", + "internalURL": "http://127.0.0.1:8888/v1/AUTH_a24c37ed11a84896914514384898c34b", + "id": "3327534a1a824389aae5d663b9821d67", "publicURL": "https://storage.bhs1.cloud.ovh.net/v1/AUTH_a24c37ed11a84896914514384898c34b"}, + {"adminURL": "https://storage.sbg1.cloud.ovh.net", "region": "SBG1", "internalURL": + "http://127.0.0.1:8888/v1/AUTH_a24c37ed11a84896914514384898c34b", "id": "2af96b87ad484cb7879a9ea554d5418c", + "publicURL": "https://storage.sbg1.cloud.ovh.net/v1/AUTH_a24c37ed11a84896914514384898c34b"}], + "endpoints_links": [], "type": "object-store", "name": "swift"}, {"endpoints": + [{"adminURL": "https://auth.cloud.ovh.net:35357/v2.0", "region": "GRA1", "internalURL": + "http://127.0.0.1:5000/v2.0", "id": "62101e498fc3404dbc18ec80888992cb", "publicURL": + "https://auth.cloud.ovh.net/v2.0"}, {"adminURL": "https://auth.cloud.ovh.net:35357/v2.0", + "region": "BHS1", "internalURL": "http://127.0.0.1:5000/v2.0", "id": "00e403276b3246c4a5c54dc7133f9f0a", + "publicURL": "https://auth.cloud.ovh.net/v2.0"}, {"adminURL": "https://auth.cloud.ovh.net:35357/v2.0", + "region": "SBG1", "internalURL": "http://127.0.0.1:5000/v2.0", "id": "6094ef2ed9f240ed9b648dfcc0d9f923", + "publicURL": "https://auth.cloud.ovh.net/v2.0"}], "endpoints_links": [], "type": + "identity", "name": "keystone"}], "user": {"username": "MhsuDbK4DsPr", "roles_links": + [], "id": "43914cf4645747ba90d075c62ebb5018", "roles": [{"name": "_member_"}], + "name": "MhsuDbK4DsPr"}, "metadata": {"is_admin": 0, "roles": ["9fe2ff9ee4384b1894a90878d3e92bab"]}}}' + http_version: + recorded_at: Thu, 12 May 2016 14:05:17 GMT +- request: + method: put + uri: https://storage.sbg1.cloud.ovh.net/v1/AUTH_a24c37ed11a84896914514384898c34b/tps_dev/procedure-3dbb3535-5388-4a37-bc2d-778327b9f998.png + body: + encoding: ASCII-8BIT + string: !binary |- + iVBORw0KGgoAAAANSUhEUgAAATwAAAFgCAYAAAAmScWKAAAKsWlDQ1BJQ0Mg + UHJvZmlsZQAASImVlgdUU1kax+976Y2W0FvovQsEkF4DKEgHUQkJJZQYAkFE + bMigAiOKiAioAzoIouCoFBkLIoiFQbGBdUAGAWUdLICKyj5gCTu7Z3fPfu98 + 7/7Ol/u++383757zB4D8jMXnJ8ESACTz0gQBni70sPAIOu53QAQ4QAAagMZi + p/Kd/f19ARKL419j6hGA5sb7RnO9/v33/xqSnJhUNgCQP8LRnFR2MsLnkWxn + 8wVpAKAykLrGhjT+HFcgTBMgAhE+M8dxC9wxx9EL3D8/JyjAFeFxAPBkFksQ + BwDpE1Knp7PjkD5kOYRNeRwuD2EvhB3Y8SwOwrsQNkxOXj/HiAagG/1PfeL+ + 0jNa1JPFihPxwrvMB96Nm8pPYm38P7fjf0dyknBxDXUkyfECrwBkVED2rCZx + vY+IedEr/RaZy5mfP8/xQq/gRWanukYsMofl5rPIwsRg50VmCZae5aYxgxZZ + sD5A1J+XtNJX1D+GKeKYVPfARY7lejAXOTM+KHSR07khKxc5NTHQZ2mOq6gu + EAaINMcKPETvmJy6pI3NWlorLT7Ia0lDmEgPJ8bNXVTnBYvm89NcRD35Sf5L + +pM8RfXU9EDRs2nIB7bICSxv/6U+/qL9ASHAE5gjlwVy0UEgcAcBgInckVXT + YjLS5oS7rudvFHDj4tPozsjJiaEzeWxjQ7q5qZklAHPncOFv/tA/f74gGfxS + LeEuALaITmjPUo2zCYDmbuRbHFmq6eQDQBkFoLOFLRSkL9TQczcMcsLFAQ3I + AxXklOsCI0SpFbADTohCb+AHgkA4WAvYIB4kAwHYALLAdpAL8sFecACUgaPg + GKgBp8FZ0AwugqvgOrgN7oKH4CkYAMPgDZgAU2AGgiAcRIGokDykCmlBBpA5 + xIAcIHfIFwqAwqEoKA7iQUIoC9oB5UNFUBlUCdVCv0AXoKvQTagXegwNQmPQ + e+gLjILJMA1WhrVhE5gBO8M+cBC8Bo6DU+BMOAfeA5fCVfApuAm+Ct+GH8ID + 8Bt4EgVQJJQMSg1lhGKgXFF+qAhULEqA2oLKQ5WgqlD1qFZUF+o+agA1jvqM + xqKpaDraCG2H9kIHo9noFPQWdAG6DF2DbkJ3oO+jB9ET6O8YCkYJY4CxxTAx + YZg4zAZMLqYEU41pxHRiHmKGMVNYLFYGq4O1xnphw7EJ2E3YAuxhbAO2DduL + HcJO4nA4eZwBzh7nh2Ph0nC5uEO4U7gruHu4YdwnPAmvijfHe+Aj8Dx8Nr4E + fxJ/GX8PP4KfIUgQtAi2BD8Ch7CRUEg4Tmgl3CEME2aIkkQdoj0xiJhA3E4s + JdYTO4nPiB9IJJI6yYa0isQlbSOVks6QbpAGSZ/JUmR9sis5kiwk7yGfILeR + H5M/UCgUbYoTJYKSRtlDqaVco7ygfBKjihmLMcU4YlvFysWaxO6JvRUniGuJ + O4uvFc8ULxE/J35HfFyCIKEt4SrBktgiUS5xQaJPYlKSKmkm6SeZLFkgeVLy + puSoFE5KW8pdiiOVI3VM6prUEBVF1aC6UtnUHdTj1E7qMA1L06ExaQm0fNpp + Wg9tQlpKepl0iHSGdLn0JekBGZSMtgxTJkmmUOaszCOZL7LKss6yMbK7Zetl + 78lOyynKOcnFyOXJNcg9lPsiT5d3l0+U3yffLP9cAa2gr7BKYYPCEYVOhXFF + mqKdIlsxT/Gs4hMlWElfKUBpk9IxpW6lSWUVZU9lvvIh5WvK4yoyKk4qCSrF + KpdVxlSpqg6qXNVi1Suqr+nSdGd6Er2U3kGfUFNS81ITqlWq9ajNqOuoB6tn + qzeoP9cgajA0YjWKNdo1JjRVNVdoZmnWaT7RImgxtOK1Dmp1aU1r62iHau/U + btYe1ZHTYepk6tTpPNOl6DrqpuhW6T7Qw+ox9BL1Duvd1Yf1LfXj9cv17xjA + BlYGXIPDBr2GGEMbQ55hlWGfEdnI2SjdqM5o0FjG2Nc427jZ+K2JpkmEyT6T + LpPvppamSabHTZ+aSZl5m2WbtZq9N9c3Z5uXmz+woFh4WGy1aLF4t8xgWcyy + I8v6LamWKyx3WrZbfrOythJY1VuNWWtaR1lXWPcxaAx/RgHjhg3GxsVmq81F + m8+2VrZptmdt/7Qzsku0O2k3ulxneczy48uH7NXtWfaV9gMOdIcoh58cBhzV + HFmOVY4vnTScOE7VTiPOes4Jzqec37qYughcGl2mXW1dN7u2uaHcPN3y3Hrc + pdyD3cvcX3ioe8R51HlMeFp6bvJs88J4+Xjt8+pjKjPZzFrmhLe192bvDh+y + T6BPmc9LX31fgW/rCniF94r9K56t1FrJW9nsB/yYfvv9nvvr+Kf4/7oKu8p/ + VfmqVwFmAVkBXYHUwHWBJwOnglyCCoOeBusGC4PbQ8RDIkNqQ6ZD3UKLQgfC + TMI2h90OVwjnhrdE4CJCIqojJle7rz6wejjSMjI38tEanTUZa26uVVibtPbS + OvF1rHXnojBRoVEno76y/FhVrMloZnRF9ATblX2Q/YbjxCnmjMXYxxTFjMTa + xxbFjsbZx+2PG4t3jC+JH+e6csu47xK8Eo4mTCf6JZ5InE0KTWpIxidHJV/g + SfESeR3rVdZnrO/lG/Bz+QMptikHUiYEPoLqVCh1TWpLGg0xPN1CXeEPwsF0 + h/Ty9E8bQjacy5DM4GV0b9TfuHvjSKZH5s+b0JvYm9qz1LK2Zw1udt5cuQXa + Er2lfavG1pytw9s8t9VsJ25P3P5btml2UfbHHaE7WnOUc7blDP3g+UNdrliu + ILdvp93Oo7vQu7i7enZb7D60+3seJ+9Wvml+Sf7XAnbBrR/Nfiz9cXZP7J6e + QqvCI3uxe3l7H+1z3FdTJFmUWTS0f8X+pmJ6cV7xxwPrDtwsWVZy9CDxoPDg + QKlvacshzUN7D30tiy97WO5S3lChVLG7Yvow5/C9I05H6o8qH80/+uUn7k/9 + lZ6VTVXaVSXHsMfSj706HnK862fGz7XVCtX51d9O8E4M1ATUdNRa19aeVDpZ + WAfXCevGTkWeunva7XRLvVF9ZYNMQ/4ZcEZ45vUvUb88Outztv0c41z9ea3z + FY3UxrwmqGlj00RzfPNAS3hL7wXvC+2tdq2Nvxr/euKi2sXyS9KXCi8TL+dc + nr2SeWWyjd82fjXu6lD7uvan18KuPehY1dHT6dN547rH9Wtdzl1XbtjfuHjT + 9uaFW4xbzbetbjd1W3Y3/mb5W2OPVU/THes7LXdt7rb2Lu+9fM/x3tX7bvev + P2A+uP1w5cPeR8GP+vsi+wb6Of2jj5Mev3uS/mTm6bZnmGd5zyWel7xQelH1 + u97vDQNWA5cG3Qa7Xwa+fDrEHnrzR+ofX4dzXlFelYyojtSOmo9eHPMYu/t6 + 9evhN/w3M+O5f5P8W8Vb3bfn/3T6s3sibGL4neDd7PuCD/IfTnxc9rF90n/y + xVTy1Mx03if5TzWfGZ+7voR+GZnZ8BX3tfSb3rfW7z7fn80mz87yWQLWvBVA + IQnHxgLw/gTiE8IBoCK+gii24JPnA1rw9vME/hMveOn5sAKgahsAc7YywAlh + ZNRuQ3oj7I9kkBOALSxE+Y9IjbUwX+hFakasScns7AfEH+L0APjWNzs70zw7 + +60aEfsEgLapBX8+FxGID9YznqPeNU8qwb/E3wGtxQeABsYopAAAAAlwSFlz + AAALEwAACxMBAJqcGAAAAgVpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADx4 + OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IlhN + UCBDb3JlIDUuNC4wIj4KICAgPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8v + d3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICAgICAg + PHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1s + bnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iCiAgICAg + ICAgICAgIHhtbG5zOnRpZmY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vdGlmZi8x + LjAvIj4KICAgICAgICAgPGV4aWY6UGl4ZWxZRGltZW5zaW9uPjc1MzwvZXhp + ZjpQaXhlbFlEaW1lbnNpb24+CiAgICAgICAgIDxleGlmOlBpeGVsWERpbWVu + c2lvbj4xNjQyPC9leGlmOlBpeGVsWERpbWVuc2lvbj4KICAgICAgICAgPHRp + ZmY6T3JpZW50YXRpb24+MTwvdGlmZjpPcmllbnRhdGlvbj4KICAgICAgPC9y + ZGY6RGVzY3JpcHRpb24+CiAgIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+CumR + lG4AAEAASURBVHgB7F0HYJRF2n42vfdKOhB6k64goKCCHcXe9fT0Ts/T099e + r3h3enbPdnqKnh1pKlZAAUV67wRIQnpvm7r5n3c2X9gkm82m7ybfwGa//cp8 + M+/MPPPO2wbQk04BnQI6BXQK6BTQKaBTQKeATgGdAjoFdAroFNApoFNAp4BO + AZ0CjkwBgyMXTi+bc1Kgvr7elSUPLMopiisqLU2CoT7BYDLEw2DwRz18YDBV + AIZi1JkyXVxdUkJCQ474Bvmm8plig8Fgcs5a66V2BgrogOcMreQEZSTIuRQU + lE7Jzcy7e8v63QsqyiqQuicbaTuyAFM9aktrULm3CjWohbeLG7yHusPN0xXu + 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 + iQ53FUu7ukMZBYqre3lzOkYFeTHOpkFpYNvdGQgSLuTuSuLj7FNUWJZeuAY7 + ObUBBKUMKjBe/+U7nLLwPlRTgWEQP9v2JAHIKPpQu9Ks5vh6+F96G2If/gP8 + Rg9TuVgO5vZka/e9ArztJrDduffcjWwLNUk02DhWpB7H8fcWoejh5zjRHeUy + dxJCXrqtKvyS8xb4RIZ96WjL3F5rAhLNUGCsPfnLralf3rk5I6iwmBMCAe32 + wSG49ZREjIy1tKfjDN1rJe25vtQdb5LOKUm4ukrGoFv+815cunQ7AyrUYpK/ + matrzdrDZnnYHs0VFTbvb36xHYCneWCMqCijB8ZzCNq7lx4YjL5S1YYCo/k7 + hRQEd0O8D2V7e8iV1CHyv68h6pLzdW6vOa3a+t0M+Iq27ETm8wtR/t4z6knP + MRch8smbd0acNfdcg7fj7K3RKzDCQRjx7Z7jn/zz1/SZP2RSTkfinRnli/to + ZjKDcjo3Ap+MUxGr636vbfW81q9byuoOpOfjOcrqXttyHKOolBAai7lJZzqA + BATIGzQIuaKoMDUE9my9OE2vCMpW2M+laQqMR/dtx4Lnf4sa/7H2KTCavtX8 + S94d4cljapZFtkduL+bB2+E/doS63u3cnrUyOek5S1qZqqqRSzOW7EdfR83W + zzih+MDn8psQ+YfrXgg6ecL9jrDM7Ux/b3cTEejct6cV3f/KL0effDOFcjoO + GH+6Kr3OqMNnj41BoLeHytNyoLb7JfoDaskhZNC4uqVr9+LyZRpX56ECc3aI + q9NoS25RPCoqGRjgCLdbtFtupz0v3+0EPIYDJTgbUMOZcOmy9zHwu/dQFTAU + LmXWjZctX2X1WLg9d3Z/anJNR/cy53xEvP6eku25Bvg1oaHV5/WTFhQga8L2 + 1MxYqnMLkPP5V8i/9XlOKZsJfL4IfOCRqoibLp/vMyjh695c5vYI4MnyNS23 + dPoHWzO+vH9Xjr+E9hYH8KfHROCKifGI0eR07MwySHukUBbN1ZcOLSeLfWl5 + eG7pJryxLQMjgjzpamz2lug0fdlO9e7uSKeiopzfPQF40kbhfG8uZXlXZx/H + Hx9/AK6u9MCguZLdCgxrDS12exGcaBkYpDZrA3zPvh6xT96NgAmj1d2WHIy1 + x/VzFhSQfiFjuEG+V37oKLLf/ghlT73J6SqFYagmI+j1O7bSTe08Q1jYcYsn + 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 + +urJek+brdgC8Ey1NXScbQqZfZU2zeslHJ3G1Rk56y/clonhiw/g/fRSJJGr + KyNdsmySs3mObf+uY56yXJYOvIVyvaqkgUquV9+V9nrMXykqgoORGxDgGNxd + 26TptjtExC+TmiwT/zdxJsrDToVbYRd5YDTY19WxPT0HRyE6ygc+RQxPVcUN + hTjR2L2Etaf20m9EoeHlYbbbo2yvZPbljbK9xiWzvctme97p6Pd42Z62WgBe + vUutOVKAo1esi8unyepEvrY7pww3fnEA161Jh+wyGkmu7gi5OjrbdUuSAajs + 9bjU2ka5XhbleiGU63maRK7XBUbfzFcUFXmREar8dgnJu6WmjpNpMds5isbI + y4PC8Msll9MDY2fnzVSUfR0jRrNB/UdSXudHeV1JPuV3bN/2yOvaQybhTYTb + E7u90TNQX1KqZHslD/0FdanpCtSV/E8K1R9SZaXNWrYAPBeTi01LZZu5OdJF + Ozkxmfw0rq6MQuG3Nmdg1JID+Oh4meLqjBwY2Xbm1ZnqyysE9NzYMY+bDEil + vZ4P7fW82EK1nZHrMU/h7kojIlDq1aCo4Dk9cesJIQPR6P6h45B5yvU0/qUH + BhVRHUqN8jra140wy+tcCgh2Iq/rKXLTrRF+DHRKpUbNPx5H8ZALUPnFN2ZD + Zg1w+zq35+VlE9lbAB6F6X2Dw7Ojk1nK6rZmluLK5Qfwm3XHEcKZWmR1wtV1 + hazO3gEkoFcroEeu8jhn7Q1+oTAkJSKE9pB1Yn3f3pFDGpgVFX7IZCgih1BU + 2EuMHriPW+0oBUalpze+OO0c5emgBP8doLOS10UFIiIpDEE1dA8rK+deGp1Q + TnSk/tLnRZNbQuUIjZXrww0oP28uSh54AnXH0sz9R+rWX7g9KzRsCXj18NUY + Gjsww0qWjn/KUlZXVFWLl39Nw3hydcsZ8HQgwwcxMFaXy+raQxWzMoMaPgrW + d1CuV51EP1za65nEQp8Z2d0uDQ0pHhWmfq6oaI3+OQSAcIaQeiFmIPZecg08 + KvZwEyM7uTwBD3JMJiqdvJIiEM1tNf0KKa+jcbFZOaGNpNbe3o3nBfS8zZrc + mn/9BcWJ58G4/GvOqJw4+7Imt91LWoPBvKSVUWX3yOrGhuvirC1ldRuOl+DC + pftxx69ZiGHIoAgPF6WB7S5ZXXuqwnlaLXFlybVV5Hr0ww2ODoeHkuuZJ2ub + +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 + dMDrlizWuUngi0jPKXpj/eZ982/+PEVtU7tyeiQuPGUSZk4apbapUcCn5Hs6 + t2cdmttKKoblK7l4gp2EvF/of/H5d5Dz0Jd0w1kD7/gYjL33bIxdvuCPbj4+ + j7L9R8QxiLZC/57KoQNeT5Sx0n12fKHxrB2/ZL7/6sc/Bz+6jbt/XB1xz8IE + nHXyZIyJC1U5qQHCKx34rET44UpGJjAThUR5Tj72bvgEKde9R36ulHI6H4x+ + YCUmrTttvV90+OVsb54ir4ehooAOeENEaQ4CtU3tm50pzz3/0R6s5zGS0yK8 + cO2p43HaggkICdB9ww1RUwxaNh3kdFU12L/pG+y9bwOqd6bRqsQTUTcswuSL + VhyMmJQscrqDg1YQPeEeKaADXo+kGZwHBD7vwrLqRz76Zt9vnt70C77LrMQ5 + k4Lx6+VTMH/GGHi4uaiMFZega3MHpxGsnKq0lQThzsWxafq2n/DTE2+h6I1d + 3H3TguC1UzDl6jW1o+fNWksNxSbGOybdq1uZ7P1KTge8fpFt4C9xkIw6fKTw + nf99tnvKXZsPo6a+GTfSjOXcZVMxZVwsjZUNLuYlJxlIerA9CiigI9a1y+lS + sfPl95B5/+csbBO8p8Zgwp/OwLhlC2519/W+n+3YYHu1OLZKpI+kYWxvDhjZ + prZ4x97Ud/794U8+T23PRpyXC65bmozVlO+NijK6oRIPt2wpHfiGsbFMsyZH + p1zWG8+RKM8txE/rP8Gh6z6knK6ERia+SLz/FEw997T3/KPCL2G7lZi+rl8P + HwV0wBs+2rflTODjNrXaazd/d/DBF97/GRsPFuPkUX64dMUkLJ0/Af7aNjXd + jKWNZsN10UFOV12LfZ98i133bEANz0Rx5L+Ya+biuEtXpUZPHreKQPfLcJVT + z7d7CuiA1z1dhuUugc8/u6D8ufc/37326Y9+wU+Ftbh8RgQuWDkNs6clwpln + uUowPRBnWAp6DGaqlq+st+KyuYxN3b4bO599F3kvij0dELQ8CdOuX1M/ZsH0 + c5xcXTcyni6nU5SxrT864NlWe6jScHCN++Vw9rtvfLhrzP1fpMKXAr1rl4zG + 2lOnIXl0hBZHfasBaIN1GClF6gB0rFR+SiZ2vPI+Mu7cwtPByuEzLgoT/0wz + kxUL7/D0972P7VE/Uuo+EuuhA56NtioHmmN9ffOKbbtT3nzl3R/dnv8+Bwui + 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 + RK5CYII= + headers: + User-Agent: + - fog/1.38.0 fog-core/1.38.0 + Content-Type: + - image/png + Accept: + - application/json + X-Auth-Token: + - fb685ff7f33040b490fd3c2d7592e383 + Content-Length: + - '53915' + response: + status: + code: 201 + message: '' + headers: + Last-Modified: + - Wed, 29 Jun 2016 14:09:52 GMT + Content-Length: + - '0' + Etag: + - 61cf45878a2aa26800cd7d7248e9e5fb + Content-Type: + - text/html; charset=UTF-8 + X-Trans-Id: + - tx26db11eba1d64a1bbf1c7-005773d6af + Date: + - Wed, 29 Jun 2016 14:09:51 GMT + Connection: + - close + body: + encoding: UTF-8 + string: '' + http_version: + recorded_at: Wed, 29 Jun 2016 14:09:51 GMT +recorded_with: VCR 3.0.1 diff --git a/spec/mailers/welcome_mailer_spec.rb b/spec/mailers/welcome_mailer_spec.rb index fa9ff057c..f60e24b10 100644 --- a/spec/mailers/welcome_mailer_spec.rb +++ b/spec/mailers/welcome_mailer_spec.rb @@ -4,8 +4,8 @@ describe WelcomeMailer, type: :mailer do describe ".welcome_email" do let(:user) { create(:user) } subject(:subject) { described_class.welcome_email(user) } - it { expect(subject.body).to match('https://tps.apientreprise.fr') } - it { expect(subject.body).to match('https://tps.apientreprise.fr/users/password/new') } + it { expect(subject.body).to match(root_url) } + it { expect(subject.body).to match(new_user_password_url) } it { expect(subject.body).to match(user.email) } it { expect(subject.body).to match('Bienvenue sur la plateforme TPS') } it { expect(subject.body).to match('Nous vous remercions de vous être inscrit sur TPS. Pour mémoire, voici quelques informations utiles :')} diff --git a/spec/models/dossier_spec.rb b/spec/models/dossier_spec.rb index c9ae004e5..b26b8243e 100644 --- a/spec/models/dossier_spec.rb +++ b/spec/models/dossier_spec.rb @@ -2,6 +2,7 @@ require 'spec_helper' describe Dossier do let(:user) { create(:user) } + describe 'database columns' do it { is_expected.to have_db_column(:autorisation_donnees) } it { is_expected.to have_db_column(:nom_projet) } @@ -24,6 +25,7 @@ describe Dossier do it { is_expected.to have_one(:entreprise) } it { is_expected.to belong_to(:user) } it { is_expected.to have_many(:invites) } + it { is_expected.to have_many(:follows) } end describe 'delegation' do @@ -33,14 +35,6 @@ describe Dossier do it { is_expected.to delegate_method(:types_de_champ).to(:procedure) } end - describe 'validation' do - context 'nom_projet' do - it { is_expected.to allow_value(nil).for(:nom_projet) } - it { is_expected.not_to allow_value('').for(:nom_projet) } - it { is_expected.to allow_value('mon super projet').for(:nom_projet) } - end - end - describe 'methods' do let(:dossier) { create(:dossier, :with_entreprise, user: user) } @@ -82,7 +76,7 @@ describe Dossier do end end - describe '#retrieve_last_piece_justificative_by_type', vcr: { cassette_name: 'models_dossier_retrieve_last_piece_justificative_by_type' } do + describe '#retrieve_last_piece_justificative_by_type', vcr: {cassette_name: 'models_dossier_retrieve_last_piece_justificative_by_type'} do let(:types_de_pj_dossier) { dossier.procedure.types_de_piece_justificative } subject { dossier.retrieve_last_piece_justificative_by_type types_de_pj_dossier.first } @@ -121,7 +115,7 @@ describe Dossier do it 'does not create default champs' do expect(subject).not_to receive(:build_default_champs) - subject.update_attributes(nom_projet: 'plop') + subject.update_attributes(state: 'initiated') end end end @@ -390,18 +384,18 @@ describe Dossier do create :assign_to, gestionnaire: gestionnaire, procedure: procedure_admin end - let!(:dossier1) { create(:dossier, procedure: procedure_admin, state: 'draft') } - let!(:dossier2) { create(:dossier, procedure: procedure_admin, state: 'initiated') } #a_traiter - let!(:dossier3) { create(:dossier, procedure: procedure_admin, state: 'initiated') } #a_traiter - let!(:dossier4) { create(:dossier, procedure: procedure_admin, state: 'replied') } #en_attente - let!(:dossier5) { create(:dossier, procedure: procedure_admin, state: 'updated') } #a_traiter - let!(:dossier6) { create(:dossier, procedure: procedure_admin_2, state: 'validated') } #en_attente - let!(:dossier7) { create(:dossier, procedure: procedure_admin_2, state: 'submitted') } #a_traiter - let!(:dossier8) { create(:dossier, procedure: procedure_admin_2, state: 'closed') } #termine - let!(:dossier9) { create(:dossier, procedure: procedure_admin, state: 'closed') } #termine - let!(:dossier10) { create(:dossier, procedure: procedure_admin, state: 'initiated', archived: true) } #a_traiter #archived - let!(:dossier11) { create(:dossier, procedure: procedure_admin, state: 'replied', archived: true) } #en_attente #archived - let!(:dossier12) { create(:dossier, procedure: procedure_admin, state: 'closed', archived: true) } #termine #archived + let!(:dossier1) { create(:dossier, procedure: procedure_admin, state: 'draft') } + let!(:dossier2) { create(:dossier, procedure: procedure_admin, state: 'initiated') } #a_traiter + let!(:dossier3) { create(:dossier, procedure: procedure_admin, state: 'initiated') } #a_traiter + let!(:dossier4) { create(:dossier, procedure: procedure_admin, state: 'replied') } #en_attente + let!(:dossier5) { create(:dossier, procedure: procedure_admin, state: 'updated') } #a_traiter + let!(:dossier6) { create(:dossier, procedure: procedure_admin_2, state: 'validated') } #en_attente + let!(:dossier7) { create(:dossier, procedure: procedure_admin_2, state: 'submitted') } #a_traiter + let!(:dossier8) { create(:dossier, procedure: procedure_admin_2, state: 'closed') } #termine + let!(:dossier9) { create(:dossier, procedure: procedure_admin, state: 'closed') } #termine + let!(:dossier10) { create(:dossier, procedure: procedure_admin, state: 'initiated', archived: true) } #a_traiter #archived + let!(:dossier11) { create(:dossier, procedure: procedure_admin, state: 'replied', archived: true) } #en_attente #archived + let!(:dossier12) { create(:dossier, procedure: procedure_admin, state: 'closed', archived: true) } #termine #archived describe '#waiting_for_gestionnaire' do subject { gestionnaire.dossiers.waiting_for_gestionnaire } @@ -442,11 +436,11 @@ describe Dossier do let(:procedure_1) { create(:procedure, administrateur: administrateur_1) } let(:procedure_2) { create(:procedure, administrateur: administrateur_2) } - let!(:dossier_0) { create(:dossier, nom_projet: 'je suis un brouillon', state: 'draft', procedure: procedure_1, user: create(:user, email: 'brouillon@clap.fr')) } - let!(:dossier_1) { create(:dossier, nom_projet: 'Projet de test', state: 'initiated', procedure: procedure_1, user: create(:user, email: 'contact@test.com')) } - let!(:dossier_2) { create(:dossier, nom_projet: 'Lili et Marcel', state: 'initiated', procedure: procedure_1, user: create(:user, email: 'plop@gmail.com')) } - let!(:dossier_3) { create(:dossier, nom_projet: 'Construction projet marcel', state: 'initiated', procedure: procedure_2, user: create(:user, email: 'peace@clap.fr')) } - let!(:dossier_archived) { create(:dossier, nom_projet: 'je suis un Marcel archivé', state: 'initiated', procedure: procedure_1, archived: true, user: create(:user, email: 'brouillonArchived@clap.fr')) } + let!(:dossier_0) { create(:dossier, state: 'draft', procedure: procedure_1, user: create(:user, email: 'brouillon@clap.fr')) } + let!(:dossier_1) { create(:dossier, state: 'initiated', procedure: procedure_1, user: create(:user, email: 'contact@test.com')) } + let!(:dossier_2) { create(:dossier, state: 'initiated', procedure: procedure_1, user: create(:user, email: 'plop@gmail.com')) } + let!(:dossier_3) { create(:dossier, state: 'initiated', procedure: procedure_2, user: create(:user, email: 'peace@clap.fr')) } + let!(:dossier_archived) { create(:dossier, state: 'initiated', procedure: procedure_1, archived: true, user: create(:user, email: 'brouillonArchived@clap.fr')) } let!(:etablissement_1) { create(:etablissement, entreprise: create(:entreprise, raison_sociale: 'OCTO Academy', dossier: dossier_1), dossier: dossier_1, siret: '41636169600051') } let!(:etablissement_2) { create(:etablissement, entreprise: create(:entreprise, raison_sociale: 'Plop octo', dossier: dossier_2), dossier: dossier_2, siret: '41816602300012') } @@ -464,12 +458,6 @@ describe Dossier do it { expect(subject.size).to eq(0) } end - describe 'search on file title' do - let(:terms) { 'Marcel' } - - it { expect(subject.size).to eq(1) } - end - describe 'search on contact email' do let(:terms) { 'clap' } @@ -511,8 +499,8 @@ describe Dossier do end describe '#cerfa_available?' do - let(:procedure) { create(:procedure, cerfa_flag: cerfa_flag) } - let(:dossier) { create(:dossier, procedure: procedure)} + let(:procedure) { create(:procedure, cerfa_flag: cerfa_flag) } + let(:dossier) { create(:dossier, procedure: procedure) } context 'Procedure accepts CERFA' do let(:cerfa_flag) { true } @@ -531,11 +519,10 @@ describe Dossier do end describe '#as_csv?' do - let(:procedure) { create(:procedure) } + let(:procedure) { create(:procedure) } let(:dossier) { create(:dossier, :with_entreprise, user: user, procedure: procedure) } subject { dossier.as_csv } - it { expect(subject[:nom_projet]).to eq("Demande de subvention dans le cadre d'accompagnement d'enfant à l'étranger") } it { expect(subject[:archived]).to be_falsey } it { expect(subject['etablissement.siret']).to eq('44011762001530') } it { expect(subject['etablissement.siege_social']).to be_truthy } @@ -577,11 +564,11 @@ describe Dossier do it { expect(dossier.entreprise.rna_information).not_to be_nil } it { expect(dossier.autorisation_donnees).to be_truthy } - it { expect{subject}.to change(RNAInformation, :count).by(-1) } - it { expect{subject}.to change(Exercice, :count).by(-1) } + it { expect { subject }.to change(RNAInformation, :count).by(-1) } + it { expect { subject }.to change(Exercice, :count).by(-1) } - it { expect{subject}.to change(Entreprise, :count).by(-1) } - it { expect{subject}.to change(Etablissement, :count).by(-1) } + it { expect { subject }.to change(Entreprise, :count).by(-1) } + it { expect { subject }.to change(Etablissement, :count).by(-1) } context 'when method reset! is call' do before do @@ -600,7 +587,7 @@ describe Dossier do let!(:procedure_2) { create :procedure } let(:dossier_1) { Dossier.new(id: 0, procedure: procedure_1) } - let(:dossier_2) { Dossier.new(id: 0, procedure: procedure_2) } + let(:dossier_2) { Dossier.new(id: 0, procedure: procedure_2) } before do create :type_de_champ, libelle: 'type_1_1', order_place: 1, procedure: dossier_1.procedure @@ -637,4 +624,27 @@ describe Dossier do end end + + describe '#total_follow' do + let(:dossier) { create(:dossier, :with_entreprise, user: user) } + let(:dossier2) { create(:dossier, :with_entreprise, user: user) } + + subject { dossier.total_follow } + + context 'when no body follow dossier' do + it { expect(subject).to eq 0 } + end + + context 'when 2 people follow dossier' do + before do + create :follow, dossier_id: dossier.id, gestionnaire_id: (create :gestionnaire).id + create :follow, dossier_id: dossier.id, gestionnaire_id: (create :gestionnaire).id + + create :follow, dossier_id: dossier2.id, gestionnaire_id: (create :gestionnaire).id + create :follow, dossier_id: dossier2.id, gestionnaire_id: (create :gestionnaire).id + end + + it { expect(subject).to eq 2 } + end + end end diff --git a/spec/models/gestionnaire_spec.rb b/spec/models/gestionnaire_spec.rb index a7aea4737..17a8b55a2 100644 --- a/spec/models/gestionnaire_spec.rb +++ b/spec/models/gestionnaire_spec.rb @@ -1,6 +1,17 @@ require 'rails_helper' describe Gestionnaire, type: :model do + let(:admin) { create :administrateur } + let!(:procedure) { create :procedure, administrateur: admin } + let!(:procedure_2) { create :procedure, administrateur: admin } + let(:gestionnaire) { create :gestionnaire, procedure_filter: procedure_filter, administrateurs: [admin] } + let(:procedure_filter) { [] } + + before do + create :assign_to, gestionnaire: gestionnaire, procedure: procedure + create :assign_to, gestionnaire: gestionnaire, procedure: procedure_2 + end + describe 'database column' do it { is_expected.to have_db_column(:email) } it { is_expected.to have_db_column(:encrypted_password) } @@ -20,20 +31,11 @@ describe Gestionnaire, type: :model do it { is_expected.to have_and_belong_to_many(:administrateurs) } it { is_expected.to have_many(:procedures) } it { is_expected.to have_many(:dossiers) } + it { is_expected.to have_many(:follows) } end describe '#dossiers_filter' do - let(:admin) { create :administrateur } - let(:procedure) { create :procedure, administrateur: admin } - let(:procedure_2) { create :procedure, administrateur: admin } - let(:gestionnaire) { create :gestionnaire, procedure_filter: procedure_filter, administrateurs: [admin] } let!(:dossier) { create :dossier, procedure: procedure } - let(:procedure_filter) { [] } - - before do - create :assign_to, gestionnaire: gestionnaire, procedure: procedure - create :assign_to, gestionnaire: gestionnaire, procedure: procedure_2 - end subject { gestionnaire.dossiers_filter } @@ -49,18 +51,6 @@ describe Gestionnaire, type: :model do end describe '#procedure_filter_list' do - let(:admin) { create :administrateur } - let!(:procedure) { create :procedure, administrateur: admin } - let!(:procedure_2) { create :procedure, administrateur: admin } - let(:gestionnaire) { create :gestionnaire, procedure_filter: procedure_filter, administrateurs: [admin] } - - before do - create :assign_to, gestionnaire: gestionnaire, procedure: procedure - create :assign_to, gestionnaire: gestionnaire, procedure: procedure_2 - end - - let(:procedure_filter) { [] } - subject { gestionnaire.procedure_filter_list } context 'when gestionnaire procedure_filter is empty' do @@ -73,4 +63,96 @@ describe Gestionnaire, type: :model do it { expect(subject).to eq [procedure.id] } end end + + describe '#toggle_follow_dossier' do + let!(:dossier) { create :dossier, procedure: procedure } + + subject { gestionnaire.toggle_follow_dossier dossier_id } + + context 'when dossier id not valid' do + let(:dossier_id) { 0 } + + it { expect(subject).to eq nil } + end + + context 'when dossier id is valid' do + let(:dossier_id) { dossier.id } + + context 'when dossier is not follow by gestionnaire' do + it 'value change in database' do + expect { subject }.to change(Follow, :count).by(1) + end + + it { expect(subject).to be_an_instance_of Follow } + end + + context 'when dossier is follow by gestionnaire' do + before do + create :follow, dossier_id: dossier.id, gestionnaire_id: gestionnaire.id + end + + it 'value change in database' do + expect { subject }.to change(Follow, :count).by(-1) + end + + it { expect(subject).to eq 1 } + end + end + + context 'when dossier instance is past' do + let(:dossier_id) { dossier } + + context 'when dossier is not follow by gestionnaire' do + it 'value change in database' do + expect { subject }.to change(Follow, :count).by(1) + end + + it { expect(subject).to be_an_instance_of Follow } + end + + context 'when dossier is follow by gestionnaire' do + before do + create :follow, dossier_id: dossier.id, gestionnaire_id: gestionnaire.id + end + + it 'value change in database' do + expect { subject }.to change(Follow, :count).by(-1) + end + + it { expect(subject).to eq 1 } + end + end + end + + describe '#follow?' do + let!(:dossier) { create :dossier, procedure: procedure } + + subject { gestionnaire.follow? dossier.id } + + context 'when gestionnaire follow a dossier' do + + before do + create :follow, dossier_id: dossier.id, gestionnaire_id: gestionnaire.id + end + + it { is_expected.to be_truthy } + end + + context 'when gestionnaire not follow a dossier' do + it { is_expected.to be_falsey } + end + end + + describe '#dossiers_follow' do + let!(:dossier) { create :dossier, procedure: procedure } + + before do + create :follow, dossier_id: dossier.id, gestionnaire_id: gestionnaire.id + end + + subject { gestionnaire.dossiers_follow } + + it { expect(Follow.all.size).to eq 1 } + it { expect(subject.first).to eq dossier } + end end diff --git a/spec/models/procedure_path_spec.rb b/spec/models/procedure_path_spec.rb new file mode 100644 index 000000000..eaf75f411 --- /dev/null +++ b/spec/models/procedure_path_spec.rb @@ -0,0 +1,38 @@ +require 'spec_helper' + +describe ProcedurePath do + describe 'assocations' do + it { is_expected.to belong_to(:administrateur) } + it { is_expected.to belong_to(:procedure) } + end + + describe 'attributes' do + it { is_expected.to have_db_column(:path) } + end + + describe 'validation' do + describe 'path' do + let(:admin) { create(:administrateur) } + let(:procedure) { create(:procedure) } + let(:procedure_path) { create(:procedure_path, administrateur: admin, procedure: procedure, path: path) } + + context 'when path is nil' do + let(:path) { nil } + it { expect{procedure_path}.to raise_error ActiveRecord::RecordInvalid } + end + context 'when path is empty' do + let(:path) { '' } + it { expect{procedure_path}.to raise_error ActiveRecord::RecordInvalid } + end + context 'when path is invalid' do + let(:path) { 'Demande de subvention' } + it { expect{procedure_path}.to raise_error ActiveRecord::RecordInvalid } + end + context 'when path is valid' do + let(:path) { 'ma_super_procedure' } + it { expect{procedure_path}.not_to raise_error } + end + end + end + +end \ No newline at end of file diff --git a/spec/models/procedure_spec.rb b/spec/models/procedure_spec.rb index f805f0292..1724ed8d2 100644 --- a/spec/models/procedure_spec.rb +++ b/spec/models/procedure_spec.rb @@ -19,6 +19,7 @@ describe Procedure do it { is_expected.to have_db_column(:logo) } it { is_expected.to have_db_column(:logo_secure_token) } it { is_expected.to have_db_column(:cerfa_flag) } + it { is_expected.to have_db_column(:published) } end describe 'validation' do @@ -156,10 +157,63 @@ describe Procedure do describe 'procedure status is reset' do let(:archived) { true } let(:published) { true } - it 'sets published and archived to false' do + it 'Not published nor archived' do expect(subject.archived).to be_falsey expect(subject.published).to be_falsey + expect(subject.path).to be_nil end end end + + describe 'publish' do + let(:procedure) { create(:procedure, :published) } + let(:procedure_path) { ProcedurePath.find(procedure.procedure_path.id) } + + it 'is available from a valid path' do + expect(procedure.path).to match(/fake_path/) + expect(procedure.published).to be_truthy + end + + it 'is correctly set in ProcedurePath table' do + expect(ProcedurePath.count(path: procedure.path)).to eq(1) + expect(procedure_path.procedure_id).to eq(procedure.id) + expect(procedure_path.administrateur_id).to eq(procedure.administrateur_id) + end + end + + describe 'archive' do + let(:procedure) { create(:procedure, :published) } + let(:procedure_path) { ProcedurePath.find(procedure.procedure_path.id) } + before do + procedure.archive + procedure.reload + end + + it 'is not available from a valid path anymore' do + expect(procedure.path).to be_nil + expect(procedure.published).to be_truthy + expect(procedure.archived).to be_truthy + end + + it 'is not in ProcedurePath table anymore' do + expect(ProcedurePath.count(path: procedure.path)).to eq(0) + expect(ProcedurePath.find_by_procedure_id(procedure.id)).to be_nil + end + end + + describe 'total_dossier' do + + let(:procedure) { create :procedure } + + before do + create :dossier, procedure: procedure, state: :initiated + create :dossier, procedure: procedure, state: :draft + create :dossier, procedure: procedure, state: :replied + end + + subject { procedure.total_dossier } + + it { is_expected.to eq 2 } + + end end diff --git a/spec/views/admin/previsualisations/show.html.haml_spec.rb b/spec/views/admin/previsualisations/show.html.haml_spec.rb index 4d97de3a8..82263c78b 100644 --- a/spec/views/admin/previsualisations/show.html.haml_spec.rb +++ b/spec/views/admin/previsualisations/show.html.haml_spec.rb @@ -21,10 +21,6 @@ describe 'admin/previsualisations/show.html.haml', type: :view do expect(rendered).to have_selector("form[action='/users/dossiers/#{dossier_id}/description'][method=post]") end - it 'Nom du projet' do - expect(rendered).to have_selector('input[id=nom_projet][name=nom_projet]') - end - it 'Charger votre CERFA (PDF)' do expect(rendered).to have_selector('input[type=file][name=cerfa_pdf][id=cerfa_pdf]') end @@ -66,17 +62,12 @@ describe 'admin/previsualisations/show.html.haml', type: :view do context 'les valeurs sont réaffichées si elles sont présentes dans la BDD' do let!(:dossier) do create(:dossier, - nom_projet: 'Projet de test', user: user) end before do render end - - it 'Nom du projet' do - expect(rendered).to have_selector("input[id=nom_projet][value='#{dossier.nom_projet}']") - end end context 'Champs' do diff --git a/spec/views/admin/procedures/edit.html.haml_spec.rb b/spec/views/admin/procedures/edit.html.haml_spec.rb index 44db3875c..ba4492a5c 100644 --- a/spec/views/admin/procedures/edit.html.haml_spec.rb +++ b/spec/views/admin/procedures/edit.html.haml_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'admin/procedures/edit.html.haml', type: :view do +describe 'admin/procedures/edit.html.haml', type: :view, vcr: { cassette_name: 'admin_procedure_edit' } do let(:logo) { Rack::Test::UploadedFile.new("./spec/support/files/logo_test_procedure.png", 'image/png') } let(:procedure) { create(:procedure, logo: logo) } diff --git a/spec/views/admin/procedures/show.html.haml_spec.rb b/spec/views/admin/procedures/show.html.haml_spec.rb index 7e5c272a1..06c716479 100644 --- a/spec/views/admin/procedures/show.html.haml_spec.rb +++ b/spec/views/admin/procedures/show.html.haml_spec.rb @@ -2,41 +2,64 @@ require 'spec_helper' describe 'admin/procedures/show.html.haml', type: :view do let(:archived) { false } - let(:published) { false } - let(:procedure) { create(:procedure, published: published, archived: archived) } + let(:procedure) { create(:procedure, archived: archived) } before do assign(:facade, AdminProceduresShowFacades.new(procedure.decorate)) assign(:procedure, procedure) - render end - describe 'publish button' do - it { expect(rendered).to have_content('Publier') } - end - - describe 'archive and unarchive button' do - let(:published) { true } - - context 'when procedure is published' do - it { expect(rendered).to have_content('Archiver') } + describe 'procedure is draft' do + before do + render end - context 'when procedure is archived' do - let(:archived) { true } - it { expect(rendered).to have_content('Réactiver') } + describe 'publish button is visible' do + it { expect(rendered).to have_css('a#publish') } + it { expect(rendered).not_to have_css('button#archive') } + it { expect(rendered).not_to have_css('a#reenable') } end - end - describe 'procedure link' do - - context 'is not present when not published' do + describe 'procedure link is not present' do it { expect(rendered).to have_content('Cette procédure n\'a pas encore été publiée et n\'est donc pas accessible par le public.') } end + end - context 'is present when already published' do - let(:published) { true } - it { expect(rendered).to have_content(new_users_dossiers_url(procedure_id: procedure.id)) } + describe 'procedure is published' do + before do + procedure.publish!('fake_path') + procedure.reload + render + end + + describe 'archive button is visible', js: true do + it { expect(rendered).not_to have_css('a#publish') } + it { expect(rendered).to have_css('button#archive') } + it { expect(rendered).not_to have_css('a#reenable') } + end + + describe 'procedure link is present' do + it { expect(rendered).to have_content(commencer_url(procedure_path: procedure.path)) } end end + + describe 'procedure is archived' do + before do + procedure.publish!('fake_path') + procedure.archive + procedure.reload + render + end + + describe 'Re-enable button is visible' do + it { expect(rendered).not_to have_css('a#publish') } + it { expect(rendered).not_to have_css('button#archive') } + it { expect(rendered).to have_css('a#reenable') } + end + + describe 'procedure link is not present' do + it { expect(rendered).to have_content('Cette procédure a été archivée et n\'est plus accessible par le public.') } + end + end + end \ No newline at end of file diff --git a/spec/views/backoffice/dossiers/index_html.haml_spec.rb b/spec/views/backoffice/dossiers/index_html.haml_spec.rb index 5cc52453a..a8d59f286 100644 --- a/spec/views/backoffice/dossiers/index_html.haml_spec.rb +++ b/spec/views/backoffice/dossiers/index_html.haml_spec.rb @@ -6,12 +6,17 @@ describe 'backoffice/dossiers/index.html.haml', type: :view do let!(:procedure) { create(:procedure, administrateur: administrateur) } - let!(:decorate_dossier_initiated) { create(:dossier, procedure: procedure, nom_projet: 'projet initiated', state: 'initiated').decorate } - let!(:decorate_dossier_replied) { create(:dossier, procedure: procedure, nom_projet: 'projet replied', state: 'replied').decorate } - let!(:decorate_dossier_closed) { create(:dossier, procedure: procedure, nom_projet: 'projet closed', state: 'closed').decorate } + let!(:decorate_dossier_initiated) { create(:dossier, :with_entreprise, procedure: procedure, state: 'initiated').decorate } + let!(:decorate_dossier_replied) { create(:dossier, :with_entreprise, procedure: procedure, state: 'replied').decorate } + let!(:decorate_dossier_closed) { create(:dossier, :with_entreprise, procedure: procedure, state: 'closed').decorate } before do + + decorate_dossier_closed.entreprise.update_column(:raison_sociale, 'plip') + decorate_dossier_replied.entreprise.update_column(:raison_sociale, 'plop') + create :assign_to, gestionnaire: gestionnaire, procedure: procedure + sign_in gestionnaire end describe 'on tab a_traiter' do @@ -29,12 +34,14 @@ describe 'backoffice/dossiers/index.html.haml', type: :view do subject { rendered } it { is_expected.to have_css('#backoffice_index') } it { is_expected.to have_content(procedure.libelle) } - it { is_expected.to have_content(decorate_dossier_initiated.nom_projet) } + it { is_expected.to have_content(decorate_dossier_initiated.entreprise.raison_sociale) } it { is_expected.to have_content(decorate_dossier_initiated.display_state) } it { is_expected.to have_content(decorate_dossier_initiated.last_update) } - it { is_expected.not_to have_content(decorate_dossier_replied.nom_projet) } - it { is_expected.not_to have_content(decorate_dossier_closed.nom_projet) } + it { is_expected.not_to have_content(decorate_dossier_replied.entreprise.raison_sociale) } + it { is_expected.not_to have_content(decorate_dossier_closed.entreprise.raison_sociale) } + + it { is_expected.to have_css("#suivre_dossier_#{gestionnaire.dossiers.waiting_for_gestionnaire.first.id}") } describe 'active tab' do it { is_expected.to have_selector('.active .text-danger') } @@ -56,18 +63,49 @@ describe 'backoffice/dossiers/index.html.haml', type: :view do subject { rendered } it { is_expected.to have_css('#backoffice_index') } it { is_expected.to have_content(procedure.libelle) } - it { is_expected.to have_content(decorate_dossier_replied.nom_projet) } + it { is_expected.to have_content(decorate_dossier_replied.entreprise.raison_sociale) } it { is_expected.to have_content(decorate_dossier_replied.display_state) } it { is_expected.to have_content(decorate_dossier_replied.last_update) } - it { is_expected.not_to have_content(decorate_dossier_initiated.nom_projet) } - it { is_expected.not_to have_content(decorate_dossier_closed.nom_projet) } + it { is_expected.not_to have_content(decorate_dossier_initiated.entreprise.raison_sociale) } + it { is_expected.not_to have_content(decorate_dossier_closed.entreprise.raison_sociale) } describe 'active tab' do it { is_expected.to have_selector('.active .text-info') } end end + describe 'on tab suivi' do + before do + create :follow, dossier_id: decorate_dossier_replied.id, gestionnaire_id: gestionnaire.id + + assign(:dossiers, (smart_listing_create :dossiers, + gestionnaire.dossiers_follow, + partial: "backoffice/dossiers/list", + array: true)) + assign(:suivi_class, 'active') + assign(:liste, 'suivi') + render + end + + subject { rendered } + + it { is_expected.to have_css('#backoffice_index') } + it { is_expected.to have_content(procedure.libelle) } + it { is_expected.to have_content(decorate_dossier_replied.entreprise.raison_sociale) } + it { is_expected.to have_content(decorate_dossier_replied.display_state) } + it { is_expected.to have_content(decorate_dossier_replied.last_update) } + + it { is_expected.not_to have_content(decorate_dossier_initiated.entreprise.raison_sociale) } + it { is_expected.not_to have_content(decorate_dossier_closed.entreprise.raison_sociale) } + + it { is_expected.to have_css("#suivre_dossier_#{gestionnaire.dossiers_follow.first.id}") } + + describe 'active tab' do + it { is_expected.to have_selector('.active .text-warning') } + end + end + describe 'on tab termine' do before do assign(:dossiers, (smart_listing_create :dossiers, @@ -83,12 +121,14 @@ describe 'backoffice/dossiers/index.html.haml', type: :view do it { is_expected.to have_css('#backoffice_index') } it { is_expected.to have_content(procedure.libelle) } - it { is_expected.to have_content(decorate_dossier_closed.nom_projet) } + it { is_expected.to have_content(decorate_dossier_closed.entreprise.raison_sociale) } it { is_expected.to have_content(decorate_dossier_closed.display_state) } it { is_expected.to have_content(decorate_dossier_closed.last_update) } - it { is_expected.not_to have_content(decorate_dossier_initiated.nom_projet) } - it { is_expected.not_to have_content(decorate_dossier_replied.nom_projet) } + it { is_expected.not_to have_content(decorate_dossier_initiated.entreprise.raison_sociale) } + it { is_expected.not_to have_content(decorate_dossier_replied.entreprise.raison_sociale) } + + it { is_expected.to have_css("#suivre_dossier_#{gestionnaire.dossiers.termine.first.id}") } describe 'active tab' do it { is_expected.to have_selector('.active .text-success') } diff --git a/spec/views/users/description/show.html.haml_spec.rb b/spec/views/users/description/show.html.haml_spec.rb index a6a283dc4..4a44776b7 100644 --- a/spec/views/users/description/show.html.haml_spec.rb +++ b/spec/views/users/description/show.html.haml_spec.rb @@ -22,10 +22,6 @@ describe 'users/description/show.html.haml', type: :view do expect(rendered).to have_selector("form[action='/users/dossiers/#{dossier_id}/description'][method=post]") end - it 'Nom du projet' do - expect(rendered).to have_selector('input[id=nom_projet][name=nom_projet]') - end - it 'Charger votre CERFA (PDF)' do expect(rendered).to have_selector('input[type=file][name=cerfa_pdf][id=cerfa_pdf]') end @@ -64,22 +60,6 @@ describe 'users/description/show.html.haml', type: :view do end end - context 'les valeurs sont réaffichées si elles sont présentes dans la BDD' do - let!(:dossier) do - create(:dossier, - nom_projet: 'Projet de test', - user: user) - end - - before do - render - end - - it 'Nom du projet' do - expect(rendered).to have_selector("input[id=nom_projet][value='#{dossier.nom_projet}']") - end - end - context 'Champs' do let(:champs) { dossier.champs } let(:types_de_champ) { procedure.types_de_champ.where(type_champ: 'datetime').first } diff --git a/spec/views/users/dossiers/index_html.haml_spec.rb b/spec/views/users/dossiers/index_html.haml_spec.rb index 6b4a48edd..381e607fe 100644 --- a/spec/views/users/dossiers/index_html.haml_spec.rb +++ b/spec/views/users/dossiers/index_html.haml_spec.rb @@ -3,10 +3,17 @@ require 'spec_helper' describe 'users/dossiers/index.html.haml', type: :view do let(:user) { create(:user) } - let!(:dossier) { create(:dossier, user: user, state: 'initiated', nom_projet: 'projet de test').decorate } - let!(:dossier_2) { create(:dossier, user: user, state: 'replied', nom_projet: 'projet répondu').decorate } - let!(:dossier_3) { create(:dossier, user: user, state: 'replied', nom_projet: 'projet répondu 2').decorate } - let!(:dossier_termine) { create(:dossier, user: user, state: 'closed').decorate } + let!(:dossier) { create(:dossier, :with_entreprise, user: user, state: 'initiated').decorate } + let!(:dossier_2) { create(:dossier, :with_entreprise, user: user, state: 'replied').decorate } + let!(:dossier_3) { create(:dossier, :with_entreprise, user: user, state: 'replied').decorate } + let!(:dossier_termine) { create(:dossier, :with_entreprise, user: user, state: 'closed').decorate } + + before do + dossier_2.entreprise.update_column(:raison_sociale, 'plip') + dossier_2.entreprise.update_column(:raison_sociale, 'plop') + dossier_3.entreprise.update_column(:raison_sociale, 'plup') + dossier_termine.entreprise.update_column(:raison_sociale, 'plap') + end describe 'params liste is a_traiter' do let(:dossiers_list) { user.dossiers.waiting_for_user('DESC') } @@ -32,14 +39,14 @@ describe 'users/dossiers/index.html.haml', type: :view do describe 'dossier replied is present' do it { is_expected.to have_content(dossier_2.procedure.libelle) } - it { is_expected.to have_content(dossier_2.nom_projet) } + it { is_expected.to have_content(dossier_2.entreprise.raison_sociale) } it { is_expected.to have_content(dossier_2.display_state) } it { is_expected.to have_content(dossier_2.last_update) } end describe 'dossier initiated and closed are not present' do - it { is_expected.not_to have_content(dossier.nom_projet) } - it { is_expected.not_to have_content(dossier_termine.nom_projet) } + it { is_expected.not_to have_content(dossier.entreprise.raison_sociale) } + it { is_expected.not_to have_content(dossier_termine.entreprise.raison_sociale) } end describe 'badges on tabs' do @@ -69,14 +76,14 @@ describe 'users/dossiers/index.html.haml', type: :view do describe 'dossier initiated is present' do it { is_expected.to have_content(dossier.procedure.libelle) } - it { is_expected.to have_content(dossier.nom_projet) } + it { is_expected.to have_content(dossier.entreprise.raison_sociale) } it { is_expected.to have_content(dossier.display_state) } it { is_expected.to have_content(dossier.last_update) } end describe 'dossier replied and closed are not present' do - it { is_expected.not_to have_content(dossier_2.nom_projet) } - it { is_expected.not_to have_content(dossier_termine.nom_projet) } + it { is_expected.not_to have_content(dossier_2.entreprise.raison_sociale) } + it { is_expected.not_to have_content(dossier_termine.entreprise.raison_sociale) } end end @@ -100,14 +107,14 @@ describe 'users/dossiers/index.html.haml', type: :view do describe 'dossier termine is present' do it { is_expected.to have_content(dossier_termine.procedure.libelle) } - it { is_expected.to have_content(dossier_termine.nom_projet) } + it { is_expected.to have_content(dossier_termine.entreprise.raison_sociale) } it { is_expected.to have_content(dossier_termine.display_state) } it { is_expected.to have_content(dossier_termine.last_update) } end describe 'dossier initiated and replied are not present' do - it { is_expected.not_to have_content(dossier.nom_projet) } - it { is_expected.not_to have_content(dossier_2.nom_projet) } + it { is_expected.not_to have_content(dossier.entreprise.raison_sociale) } + it { is_expected.not_to have_content(dossier_2.entreprise.raison_sociale) } end end end \ No newline at end of file diff --git a/vendor/assets/javascripts/handlebars.js b/vendor/assets/javascripts/handlebars.js new file mode 100644 index 000000000..289ae458a --- /dev/null +++ b/vendor/assets/javascripts/handlebars.js @@ -0,0 +1,4608 @@ +/*! + + handlebars v4.0.5 + +Copyright (C) 2011-2015 by Yehuda Katz + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +@license +*/ +(function webpackUniversalModuleDefinition(root, factory) { + if(typeof exports === 'object' && typeof module === 'object') + module.exports = factory(); + else if(typeof define === 'function' && define.amd) + define([], factory); + else if(typeof exports === 'object') + exports["Handlebars"] = factory(); + else + root["Handlebars"] = factory(); +})(this, function() { +return /******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; + +/******/ // The require function +/******/ function __webpack_require__(moduleId) { + +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) +/******/ return installedModules[moduleId].exports; + +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ exports: {}, +/******/ id: moduleId, +/******/ loaded: false +/******/ }; + +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); + +/******/ // Flag the module as loaded +/******/ module.loaded = true; + +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } + + +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; + +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; + +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; + +/******/ // Load entry module and return exports +/******/ return __webpack_require__(0); +/******/ }) +/************************************************************************/ +/******/ ([ +/* 0 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + var _interopRequireDefault = __webpack_require__(1)['default']; + + exports.__esModule = true; + + var _handlebarsRuntime = __webpack_require__(2); + + var _handlebarsRuntime2 = _interopRequireDefault(_handlebarsRuntime); + + // Compiler imports + + var _handlebarsCompilerAst = __webpack_require__(21); + + var _handlebarsCompilerAst2 = _interopRequireDefault(_handlebarsCompilerAst); + + var _handlebarsCompilerBase = __webpack_require__(22); + + var _handlebarsCompilerCompiler = __webpack_require__(27); + + var _handlebarsCompilerJavascriptCompiler = __webpack_require__(28); + + var _handlebarsCompilerJavascriptCompiler2 = _interopRequireDefault(_handlebarsCompilerJavascriptCompiler); + + var _handlebarsCompilerVisitor = __webpack_require__(25); + + var _handlebarsCompilerVisitor2 = _interopRequireDefault(_handlebarsCompilerVisitor); + + var _handlebarsNoConflict = __webpack_require__(20); + + var _handlebarsNoConflict2 = _interopRequireDefault(_handlebarsNoConflict); + + var _create = _handlebarsRuntime2['default'].create; + function create() { + var hb = _create(); + + hb.compile = function (input, options) { + return _handlebarsCompilerCompiler.compile(input, options, hb); + }; + hb.precompile = function (input, options) { + return _handlebarsCompilerCompiler.precompile(input, options, hb); + }; + + hb.AST = _handlebarsCompilerAst2['default']; + hb.Compiler = _handlebarsCompilerCompiler.Compiler; + hb.JavaScriptCompiler = _handlebarsCompilerJavascriptCompiler2['default']; + hb.Parser = _handlebarsCompilerBase.parser; + hb.parse = _handlebarsCompilerBase.parse; + + return hb; + } + + var inst = create(); + inst.create = create; + + _handlebarsNoConflict2['default'](inst); + + inst.Visitor = _handlebarsCompilerVisitor2['default']; + + inst['default'] = inst; + + exports['default'] = inst; + module.exports = exports['default']; + +/***/ }, +/* 1 */ +/***/ function(module, exports) { + + "use strict"; + + exports["default"] = function (obj) { + return obj && obj.__esModule ? obj : { + "default": obj + }; + }; + + exports.__esModule = true; + +/***/ }, +/* 2 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + var _interopRequireWildcard = __webpack_require__(3)['default']; + + var _interopRequireDefault = __webpack_require__(1)['default']; + + exports.__esModule = true; + + var _handlebarsBase = __webpack_require__(4); + + var base = _interopRequireWildcard(_handlebarsBase); + + // Each of these augment the Handlebars object. No need to setup here. + // (This is done to easily share code between commonjs and browse envs) + + var _handlebarsSafeString = __webpack_require__(18); + + var _handlebarsSafeString2 = _interopRequireDefault(_handlebarsSafeString); + + var _handlebarsException = __webpack_require__(6); + + var _handlebarsException2 = _interopRequireDefault(_handlebarsException); + + var _handlebarsUtils = __webpack_require__(5); + + var Utils = _interopRequireWildcard(_handlebarsUtils); + + var _handlebarsRuntime = __webpack_require__(19); + + var runtime = _interopRequireWildcard(_handlebarsRuntime); + + var _handlebarsNoConflict = __webpack_require__(20); + + var _handlebarsNoConflict2 = _interopRequireDefault(_handlebarsNoConflict); + + // For compatibility and usage outside of module systems, make the Handlebars object a namespace + function create() { + var hb = new base.HandlebarsEnvironment(); + + Utils.extend(hb, base); + hb.SafeString = _handlebarsSafeString2['default']; + hb.Exception = _handlebarsException2['default']; + hb.Utils = Utils; + hb.escapeExpression = Utils.escapeExpression; + + hb.VM = runtime; + hb.template = function (spec) { + return runtime.template(spec, hb); + }; + + return hb; + } + + var inst = create(); + inst.create = create; + + _handlebarsNoConflict2['default'](inst); + + inst['default'] = inst; + + exports['default'] = inst; + module.exports = exports['default']; + +/***/ }, +/* 3 */ +/***/ function(module, exports) { + + "use strict"; + + exports["default"] = function (obj) { + if (obj && obj.__esModule) { + return obj; + } else { + var newObj = {}; + + if (obj != null) { + for (var key in obj) { + if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; + } + } + + newObj["default"] = obj; + return newObj; + } + }; + + exports.__esModule = true; + +/***/ }, +/* 4 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + var _interopRequireDefault = __webpack_require__(1)['default']; + + exports.__esModule = true; + exports.HandlebarsEnvironment = HandlebarsEnvironment; + + var _utils = __webpack_require__(5); + + var _exception = __webpack_require__(6); + + var _exception2 = _interopRequireDefault(_exception); + + var _helpers = __webpack_require__(7); + + var _decorators = __webpack_require__(15); + + var _logger = __webpack_require__(17); + + var _logger2 = _interopRequireDefault(_logger); + + var VERSION = '4.0.5'; + exports.VERSION = VERSION; + var COMPILER_REVISION = 7; + + exports.COMPILER_REVISION = COMPILER_REVISION; + var REVISION_CHANGES = { + 1: '<= 1.0.rc.2', // 1.0.rc.2 is actually rev2 but doesn't report it + 2: '== 1.0.0-rc.3', + 3: '== 1.0.0-rc.4', + 4: '== 1.x.x', + 5: '== 2.0.0-alpha.x', + 6: '>= 2.0.0-beta.1', + 7: '>= 4.0.0' + }; + + exports.REVISION_CHANGES = REVISION_CHANGES; + var objectType = '[object Object]'; + + function HandlebarsEnvironment(helpers, partials, decorators) { + this.helpers = helpers || {}; + this.partials = partials || {}; + this.decorators = decorators || {}; + + _helpers.registerDefaultHelpers(this); + _decorators.registerDefaultDecorators(this); + } + + HandlebarsEnvironment.prototype = { + constructor: HandlebarsEnvironment, + + logger: _logger2['default'], + log: _logger2['default'].log, + + registerHelper: function registerHelper(name, fn) { + if (_utils.toString.call(name) === objectType) { + if (fn) { + throw new _exception2['default']('Arg not supported with multiple helpers'); + } + _utils.extend(this.helpers, name); + } else { + this.helpers[name] = fn; + } + }, + unregisterHelper: function unregisterHelper(name) { + delete this.helpers[name]; + }, + + registerPartial: function registerPartial(name, partial) { + if (_utils.toString.call(name) === objectType) { + _utils.extend(this.partials, name); + } else { + if (typeof partial === 'undefined') { + throw new _exception2['default']('Attempting to register a partial called "' + name + '" as undefined'); + } + this.partials[name] = partial; + } + }, + unregisterPartial: function unregisterPartial(name) { + delete this.partials[name]; + }, + + registerDecorator: function registerDecorator(name, fn) { + if (_utils.toString.call(name) === objectType) { + if (fn) { + throw new _exception2['default']('Arg not supported with multiple decorators'); + } + _utils.extend(this.decorators, name); + } else { + this.decorators[name] = fn; + } + }, + unregisterDecorator: function unregisterDecorator(name) { + delete this.decorators[name]; + } + }; + + var log = _logger2['default'].log; + + exports.log = log; + exports.createFrame = _utils.createFrame; + exports.logger = _logger2['default']; + +/***/ }, +/* 5 */ +/***/ function(module, exports) { + + 'use strict'; + + exports.__esModule = true; + exports.extend = extend; + exports.indexOf = indexOf; + exports.escapeExpression = escapeExpression; + exports.isEmpty = isEmpty; + exports.createFrame = createFrame; + exports.blockParams = blockParams; + exports.appendContextPath = appendContextPath; + var escape = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''', + '`': '`', + '=': '=' + }; + + var badChars = /[&<>"'`=]/g, + possible = /[&<>"'`=]/; + + function escapeChar(chr) { + return escape[chr]; + } + + function extend(obj /* , ...source */) { + for (var i = 1; i < arguments.length; i++) { + for (var key in arguments[i]) { + if (Object.prototype.hasOwnProperty.call(arguments[i], key)) { + obj[key] = arguments[i][key]; + } + } + } + + return obj; + } + + var toString = Object.prototype.toString; + + exports.toString = toString; + // Sourced from lodash + // https://github.com/bestiejs/lodash/blob/master/LICENSE.txt + /* eslint-disable func-style */ + var isFunction = function isFunction(value) { + return typeof value === 'function'; + }; + // fallback for older versions of Chrome and Safari + /* istanbul ignore next */ + if (isFunction(/x/)) { + exports.isFunction = isFunction = function (value) { + return typeof value === 'function' && toString.call(value) === '[object Function]'; + }; + } + exports.isFunction = isFunction; + + /* eslint-enable func-style */ + + /* istanbul ignore next */ + var isArray = Array.isArray || function (value) { + return value && typeof value === 'object' ? toString.call(value) === '[object Array]' : false; + }; + + exports.isArray = isArray; + // Older IE versions do not directly support indexOf so we must implement our own, sadly. + + function indexOf(array, value) { + for (var i = 0, len = array.length; i < len; i++) { + if (array[i] === value) { + return i; + } + } + return -1; + } + + function escapeExpression(string) { + if (typeof string !== 'string') { + // don't escape SafeStrings, since they're already safe + if (string && string.toHTML) { + return string.toHTML(); + } else if (string == null) { + return ''; + } else if (!string) { + return string + ''; + } + + // Force a string conversion as this will be done by the append regardless and + // the regex test will do this transparently behind the scenes, causing issues if + // an object's to string has escaped characters in it. + string = '' + string; + } + + if (!possible.test(string)) { + return string; + } + return string.replace(badChars, escapeChar); + } + + function isEmpty(value) { + if (!value && value !== 0) { + return true; + } else if (isArray(value) && value.length === 0) { + return true; + } else { + return false; + } + } + + function createFrame(object) { + var frame = extend({}, object); + frame._parent = object; + return frame; + } + + function blockParams(params, ids) { + params.path = ids; + return params; + } + + function appendContextPath(contextPath, id) { + return (contextPath ? contextPath + '.' : '') + id; + } + +/***/ }, +/* 6 */ +/***/ function(module, exports) { + + 'use strict'; + + exports.__esModule = true; + + var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack']; + + function Exception(message, node) { + var loc = node && node.loc, + line = undefined, + column = undefined; + if (loc) { + line = loc.start.line; + column = loc.start.column; + + message += ' - ' + line + ':' + column; + } + + var tmp = Error.prototype.constructor.call(this, message); + + // Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work. + for (var idx = 0; idx < errorProps.length; idx++) { + this[errorProps[idx]] = tmp[errorProps[idx]]; + } + + /* istanbul ignore else */ + if (Error.captureStackTrace) { + Error.captureStackTrace(this, Exception); + } + + if (loc) { + this.lineNumber = line; + this.column = column; + } + } + + Exception.prototype = new Error(); + + exports['default'] = Exception; + module.exports = exports['default']; + +/***/ }, +/* 7 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + var _interopRequireDefault = __webpack_require__(1)['default']; + + exports.__esModule = true; + exports.registerDefaultHelpers = registerDefaultHelpers; + + var _helpersBlockHelperMissing = __webpack_require__(8); + + var _helpersBlockHelperMissing2 = _interopRequireDefault(_helpersBlockHelperMissing); + + var _helpersEach = __webpack_require__(9); + + var _helpersEach2 = _interopRequireDefault(_helpersEach); + + var _helpersHelperMissing = __webpack_require__(10); + + var _helpersHelperMissing2 = _interopRequireDefault(_helpersHelperMissing); + + var _helpersIf = __webpack_require__(11); + + var _helpersIf2 = _interopRequireDefault(_helpersIf); + + var _helpersLog = __webpack_require__(12); + + var _helpersLog2 = _interopRequireDefault(_helpersLog); + + var _helpersLookup = __webpack_require__(13); + + var _helpersLookup2 = _interopRequireDefault(_helpersLookup); + + var _helpersWith = __webpack_require__(14); + + var _helpersWith2 = _interopRequireDefault(_helpersWith); + + function registerDefaultHelpers(instance) { + _helpersBlockHelperMissing2['default'](instance); + _helpersEach2['default'](instance); + _helpersHelperMissing2['default'](instance); + _helpersIf2['default'](instance); + _helpersLog2['default'](instance); + _helpersLookup2['default'](instance); + _helpersWith2['default'](instance); + } + +/***/ }, +/* 8 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + exports.__esModule = true; + + var _utils = __webpack_require__(5); + + exports['default'] = function (instance) { + instance.registerHelper('blockHelperMissing', function (context, options) { + var inverse = options.inverse, + fn = options.fn; + + if (context === true) { + return fn(this); + } else if (context === false || context == null) { + return inverse(this); + } else if (_utils.isArray(context)) { + if (context.length > 0) { + if (options.ids) { + options.ids = [options.name]; + } + + return instance.helpers.each(context, options); + } else { + return inverse(this); + } + } else { + if (options.data && options.ids) { + var data = _utils.createFrame(options.data); + data.contextPath = _utils.appendContextPath(options.data.contextPath, options.name); + options = { data: data }; + } + + return fn(context, options); + } + }); + }; + + module.exports = exports['default']; + +/***/ }, +/* 9 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + var _interopRequireDefault = __webpack_require__(1)['default']; + + exports.__esModule = true; + + var _utils = __webpack_require__(5); + + var _exception = __webpack_require__(6); + + var _exception2 = _interopRequireDefault(_exception); + + exports['default'] = function (instance) { + instance.registerHelper('each', function (context, options) { + if (!options) { + throw new _exception2['default']('Must pass iterator to #each'); + } + + var fn = options.fn, + inverse = options.inverse, + i = 0, + ret = '', + data = undefined, + contextPath = undefined; + + if (options.data && options.ids) { + contextPath = _utils.appendContextPath(options.data.contextPath, options.ids[0]) + '.'; + } + + if (_utils.isFunction(context)) { + context = context.call(this); + } + + if (options.data) { + data = _utils.createFrame(options.data); + } + + function execIteration(field, index, last) { + if (data) { + data.key = field; + data.index = index; + data.first = index === 0; + data.last = !!last; + + if (contextPath) { + data.contextPath = contextPath + field; + } + } + + ret = ret + fn(context[field], { + data: data, + blockParams: _utils.blockParams([context[field], field], [contextPath + field, null]) + }); + } + + if (context && typeof context === 'object') { + if (_utils.isArray(context)) { + for (var j = context.length; i < j; i++) { + if (i in context) { + execIteration(i, i, i === context.length - 1); + } + } + } else { + var priorKey = undefined; + + for (var key in context) { + if (context.hasOwnProperty(key)) { + // We're running the iterations one step out of sync so we can detect + // the last iteration without have to scan the object twice and create + // an itermediate keys array. + if (priorKey !== undefined) { + execIteration(priorKey, i - 1); + } + priorKey = key; + i++; + } + } + if (priorKey !== undefined) { + execIteration(priorKey, i - 1, true); + } + } + } + + if (i === 0) { + ret = inverse(this); + } + + return ret; + }); + }; + + module.exports = exports['default']; + +/***/ }, +/* 10 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + var _interopRequireDefault = __webpack_require__(1)['default']; + + exports.__esModule = true; + + var _exception = __webpack_require__(6); + + var _exception2 = _interopRequireDefault(_exception); + + exports['default'] = function (instance) { + instance.registerHelper('helperMissing', function () /* [args, ]options */{ + if (arguments.length === 1) { + // A missing field in a {{foo}} construct. + return undefined; + } else { + // Someone is actually trying to call something, blow up. + throw new _exception2['default']('Missing helper: "' + arguments[arguments.length - 1].name + '"'); + } + }); + }; + + module.exports = exports['default']; + +/***/ }, +/* 11 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + exports.__esModule = true; + + var _utils = __webpack_require__(5); + + exports['default'] = function (instance) { + instance.registerHelper('if', function (conditional, options) { + if (_utils.isFunction(conditional)) { + conditional = conditional.call(this); + } + + // Default behavior is to render the positive path if the value is truthy and not empty. + // The `includeZero` option may be set to treat the condtional as purely not empty based on the + // behavior of isEmpty. Effectively this determines if 0 is handled by the positive path or negative. + if (!options.hash.includeZero && !conditional || _utils.isEmpty(conditional)) { + return options.inverse(this); + } else { + return options.fn(this); + } + }); + + instance.registerHelper('unless', function (conditional, options) { + return instance.helpers['if'].call(this, conditional, { fn: options.inverse, inverse: options.fn, hash: options.hash }); + }); + }; + + module.exports = exports['default']; + +/***/ }, +/* 12 */ +/***/ function(module, exports) { + + 'use strict'; + + exports.__esModule = true; + + exports['default'] = function (instance) { + instance.registerHelper('log', function () /* message, options */{ + var args = [undefined], + options = arguments[arguments.length - 1]; + for (var i = 0; i < arguments.length - 1; i++) { + args.push(arguments[i]); + } + + var level = 1; + if (options.hash.level != null) { + level = options.hash.level; + } else if (options.data && options.data.level != null) { + level = options.data.level; + } + args[0] = level; + + instance.log.apply(instance, args); + }); + }; + + module.exports = exports['default']; + +/***/ }, +/* 13 */ +/***/ function(module, exports) { + + 'use strict'; + + exports.__esModule = true; + + exports['default'] = function (instance) { + instance.registerHelper('lookup', function (obj, field) { + return obj && obj[field]; + }); + }; + + module.exports = exports['default']; + +/***/ }, +/* 14 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + exports.__esModule = true; + + var _utils = __webpack_require__(5); + + exports['default'] = function (instance) { + instance.registerHelper('with', function (context, options) { + if (_utils.isFunction(context)) { + context = context.call(this); + } + + var fn = options.fn; + + if (!_utils.isEmpty(context)) { + var data = options.data; + if (options.data && options.ids) { + data = _utils.createFrame(options.data); + data.contextPath = _utils.appendContextPath(options.data.contextPath, options.ids[0]); + } + + return fn(context, { + data: data, + blockParams: _utils.blockParams([context], [data && data.contextPath]) + }); + } else { + return options.inverse(this); + } + }); + }; + + module.exports = exports['default']; + +/***/ }, +/* 15 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + var _interopRequireDefault = __webpack_require__(1)['default']; + + exports.__esModule = true; + exports.registerDefaultDecorators = registerDefaultDecorators; + + var _decoratorsInline = __webpack_require__(16); + + var _decoratorsInline2 = _interopRequireDefault(_decoratorsInline); + + function registerDefaultDecorators(instance) { + _decoratorsInline2['default'](instance); + } + +/***/ }, +/* 16 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + exports.__esModule = true; + + var _utils = __webpack_require__(5); + + exports['default'] = function (instance) { + instance.registerDecorator('inline', function (fn, props, container, options) { + var ret = fn; + if (!props.partials) { + props.partials = {}; + ret = function (context, options) { + // Create a new partials stack frame prior to exec. + var original = container.partials; + container.partials = _utils.extend({}, original, props.partials); + var ret = fn(context, options); + container.partials = original; + return ret; + }; + } + + props.partials[options.args[0]] = options.fn; + + return ret; + }); + }; + + module.exports = exports['default']; + +/***/ }, +/* 17 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + exports.__esModule = true; + + var _utils = __webpack_require__(5); + + var logger = { + methodMap: ['debug', 'info', 'warn', 'error'], + level: 'info', + + // Maps a given level value to the `methodMap` indexes above. + lookupLevel: function lookupLevel(level) { + if (typeof level === 'string') { + var levelMap = _utils.indexOf(logger.methodMap, level.toLowerCase()); + if (levelMap >= 0) { + level = levelMap; + } else { + level = parseInt(level, 10); + } + } + + return level; + }, + + // Can be overridden in the host environment + log: function log(level) { + level = logger.lookupLevel(level); + + if (typeof console !== 'undefined' && logger.lookupLevel(logger.level) <= level) { + var method = logger.methodMap[level]; + if (!console[method]) { + // eslint-disable-line no-console + method = 'log'; + } + + for (var _len = arguments.length, message = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { + message[_key - 1] = arguments[_key]; + } + + console[method].apply(console, message); // eslint-disable-line no-console + } + } + }; + + exports['default'] = logger; + module.exports = exports['default']; + +/***/ }, +/* 18 */ +/***/ function(module, exports) { + + // Build out our basic SafeString type + 'use strict'; + + exports.__esModule = true; + function SafeString(string) { + this.string = string; + } + + SafeString.prototype.toString = SafeString.prototype.toHTML = function () { + return '' + this.string; + }; + + exports['default'] = SafeString; + module.exports = exports['default']; + +/***/ }, +/* 19 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + var _interopRequireWildcard = __webpack_require__(3)['default']; + + var _interopRequireDefault = __webpack_require__(1)['default']; + + exports.__esModule = true; + exports.checkRevision = checkRevision; + exports.template = template; + exports.wrapProgram = wrapProgram; + exports.resolvePartial = resolvePartial; + exports.invokePartial = invokePartial; + exports.noop = noop; + + var _utils = __webpack_require__(5); + + var Utils = _interopRequireWildcard(_utils); + + var _exception = __webpack_require__(6); + + var _exception2 = _interopRequireDefault(_exception); + + var _base = __webpack_require__(4); + + function checkRevision(compilerInfo) { + var compilerRevision = compilerInfo && compilerInfo[0] || 1, + currentRevision = _base.COMPILER_REVISION; + + if (compilerRevision !== currentRevision) { + if (compilerRevision < currentRevision) { + var runtimeVersions = _base.REVISION_CHANGES[currentRevision], + compilerVersions = _base.REVISION_CHANGES[compilerRevision]; + throw new _exception2['default']('Template was precompiled with an older version of Handlebars than the current runtime. ' + 'Please update your precompiler to a newer version (' + runtimeVersions + ') or downgrade your runtime to an older version (' + compilerVersions + ').'); + } else { + // Use the embedded version info since the runtime doesn't know about this revision yet + throw new _exception2['default']('Template was precompiled with a newer version of Handlebars than the current runtime. ' + 'Please update your runtime to a newer version (' + compilerInfo[1] + ').'); + } + } + } + + function template(templateSpec, env) { + /* istanbul ignore next */ + if (!env) { + throw new _exception2['default']('No environment passed to template'); + } + if (!templateSpec || !templateSpec.main) { + throw new _exception2['default']('Unknown template object: ' + typeof templateSpec); + } + + templateSpec.main.decorator = templateSpec.main_d; + + // Note: Using env.VM references rather than local var references throughout this section to allow + // for external users to override these as psuedo-supported APIs. + env.VM.checkRevision(templateSpec.compiler); + + function invokePartialWrapper(partial, context, options) { + if (options.hash) { + context = Utils.extend({}, context, options.hash); + if (options.ids) { + options.ids[0] = true; + } + } + + partial = env.VM.resolvePartial.call(this, partial, context, options); + var result = env.VM.invokePartial.call(this, partial, context, options); + + if (result == null && env.compile) { + options.partials[options.name] = env.compile(partial, templateSpec.compilerOptions, env); + result = options.partials[options.name](context, options); + } + if (result != null) { + if (options.indent) { + var lines = result.split('\n'); + for (var i = 0, l = lines.length; i < l; i++) { + if (!lines[i] && i + 1 === l) { + break; + } + + lines[i] = options.indent + lines[i]; + } + result = lines.join('\n'); + } + return result; + } else { + throw new _exception2['default']('The partial ' + options.name + ' could not be compiled when running in runtime-only mode'); + } + } + + // Just add water + var container = { + strict: function strict(obj, name) { + if (!(name in obj)) { + throw new _exception2['default']('"' + name + '" not defined in ' + obj); + } + return obj[name]; + }, + lookup: function lookup(depths, name) { + var len = depths.length; + for (var i = 0; i < len; i++) { + if (depths[i] && depths[i][name] != null) { + return depths[i][name]; + } + } + }, + lambda: function lambda(current, context) { + return typeof current === 'function' ? current.call(context) : current; + }, + + escapeExpression: Utils.escapeExpression, + invokePartial: invokePartialWrapper, + + fn: function fn(i) { + var ret = templateSpec[i]; + ret.decorator = templateSpec[i + '_d']; + return ret; + }, + + programs: [], + program: function program(i, data, declaredBlockParams, blockParams, depths) { + var programWrapper = this.programs[i], + fn = this.fn(i); + if (data || depths || blockParams || declaredBlockParams) { + programWrapper = wrapProgram(this, i, fn, data, declaredBlockParams, blockParams, depths); + } else if (!programWrapper) { + programWrapper = this.programs[i] = wrapProgram(this, i, fn); + } + return programWrapper; + }, + + data: function data(value, depth) { + while (value && depth--) { + value = value._parent; + } + return value; + }, + merge: function merge(param, common) { + var obj = param || common; + + if (param && common && param !== common) { + obj = Utils.extend({}, common, param); + } + + return obj; + }, + + noop: env.VM.noop, + compilerInfo: templateSpec.compiler + }; + + function ret(context) { + var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; + + var data = options.data; + + ret._setup(options); + if (!options.partial && templateSpec.useData) { + data = initData(context, data); + } + var depths = undefined, + blockParams = templateSpec.useBlockParams ? [] : undefined; + if (templateSpec.useDepths) { + if (options.depths) { + depths = context !== options.depths[0] ? [context].concat(options.depths) : options.depths; + } else { + depths = [context]; + } + } + + function main(context /*, options*/) { + return '' + templateSpec.main(container, context, container.helpers, container.partials, data, blockParams, depths); + } + main = executeDecorators(templateSpec.main, main, container, options.depths || [], data, blockParams); + return main(context, options); + } + ret.isTop = true; + + ret._setup = function (options) { + if (!options.partial) { + container.helpers = container.merge(options.helpers, env.helpers); + + if (templateSpec.usePartial) { + container.partials = container.merge(options.partials, env.partials); + } + if (templateSpec.usePartial || templateSpec.useDecorators) { + container.decorators = container.merge(options.decorators, env.decorators); + } + } else { + container.helpers = options.helpers; + container.partials = options.partials; + container.decorators = options.decorators; + } + }; + + ret._child = function (i, data, blockParams, depths) { + if (templateSpec.useBlockParams && !blockParams) { + throw new _exception2['default']('must pass block params'); + } + if (templateSpec.useDepths && !depths) { + throw new _exception2['default']('must pass parent depths'); + } + + return wrapProgram(container, i, templateSpec[i], data, 0, blockParams, depths); + }; + return ret; + } + + function wrapProgram(container, i, fn, data, declaredBlockParams, blockParams, depths) { + function prog(context) { + var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; + + var currentDepths = depths; + if (depths && context !== depths[0]) { + currentDepths = [context].concat(depths); + } + + return fn(container, context, container.helpers, container.partials, options.data || data, blockParams && [options.blockParams].concat(blockParams), currentDepths); + } + + prog = executeDecorators(fn, prog, container, depths, data, blockParams); + + prog.program = i; + prog.depth = depths ? depths.length : 0; + prog.blockParams = declaredBlockParams || 0; + return prog; + } + + function resolvePartial(partial, context, options) { + if (!partial) { + if (options.name === '@partial-block') { + partial = options.data['partial-block']; + } else { + partial = options.partials[options.name]; + } + } else if (!partial.call && !options.name) { + // This is a dynamic partial that returned a string + options.name = partial; + partial = options.partials[partial]; + } + return partial; + } + + function invokePartial(partial, context, options) { + options.partial = true; + if (options.ids) { + options.data.contextPath = options.ids[0] || options.data.contextPath; + } + + var partialBlock = undefined; + if (options.fn && options.fn !== noop) { + options.data = _base.createFrame(options.data); + partialBlock = options.data['partial-block'] = options.fn; + + if (partialBlock.partials) { + options.partials = Utils.extend({}, options.partials, partialBlock.partials); + } + } + + if (partial === undefined && partialBlock) { + partial = partialBlock; + } + + if (partial === undefined) { + throw new _exception2['default']('The partial ' + options.name + ' could not be found'); + } else if (partial instanceof Function) { + return partial(context, options); + } + } + + function noop() { + return ''; + } + + function initData(context, data) { + if (!data || !('root' in data)) { + data = data ? _base.createFrame(data) : {}; + data.root = context; + } + return data; + } + + function executeDecorators(fn, prog, container, depths, data, blockParams) { + if (fn.decorator) { + var props = {}; + prog = fn.decorator(prog, props, container, depths && depths[0], data, blockParams, depths); + Utils.extend(prog, props); + } + return prog; + } + +/***/ }, +/* 20 */ +/***/ function(module, exports) { + + /* WEBPACK VAR INJECTION */(function(global) {/* global window */ + 'use strict'; + + exports.__esModule = true; + + exports['default'] = function (Handlebars) { + /* istanbul ignore next */ + var root = typeof global !== 'undefined' ? global : window, + $Handlebars = root.Handlebars; + /* istanbul ignore next */ + Handlebars.noConflict = function () { + if (root.Handlebars === Handlebars) { + root.Handlebars = $Handlebars; + } + return Handlebars; + }; + }; + + module.exports = exports['default']; + /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }()))) + +/***/ }, +/* 21 */ +/***/ function(module, exports) { + + 'use strict'; + + exports.__esModule = true; + var AST = { + // Public API used to evaluate derived attributes regarding AST nodes + helpers: { + // a mustache is definitely a helper if: + // * it is an eligible helper, and + // * it has at least one parameter or hash segment + helperExpression: function helperExpression(node) { + return node.type === 'SubExpression' || (node.type === 'MustacheStatement' || node.type === 'BlockStatement') && !!(node.params && node.params.length || node.hash); + }, + + scopedId: function scopedId(path) { + return (/^\.|this\b/.test(path.original) + ); + }, + + // an ID is simple if it only has one part, and that part is not + // `..` or `this`. + simpleId: function simpleId(path) { + return path.parts.length === 1 && !AST.helpers.scopedId(path) && !path.depth; + } + } + }; + + // Must be exported as an object rather than the root of the module as the jison lexer + // must modify the object to operate properly. + exports['default'] = AST; + module.exports = exports['default']; + +/***/ }, +/* 22 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + var _interopRequireDefault = __webpack_require__(1)['default']; + + var _interopRequireWildcard = __webpack_require__(3)['default']; + + exports.__esModule = true; + exports.parse = parse; + + var _parser = __webpack_require__(23); + + var _parser2 = _interopRequireDefault(_parser); + + var _whitespaceControl = __webpack_require__(24); + + var _whitespaceControl2 = _interopRequireDefault(_whitespaceControl); + + var _helpers = __webpack_require__(26); + + var Helpers = _interopRequireWildcard(_helpers); + + var _utils = __webpack_require__(5); + + exports.parser = _parser2['default']; + + var yy = {}; + _utils.extend(yy, Helpers); + + function parse(input, options) { + // Just return if an already-compiled AST was passed in. + if (input.type === 'Program') { + return input; + } + + _parser2['default'].yy = yy; + + // Altering the shared object here, but this is ok as parser is a sync operation + yy.locInfo = function (locInfo) { + return new yy.SourceLocation(options && options.srcName, locInfo); + }; + + var strip = new _whitespaceControl2['default'](options); + return strip.accept(_parser2['default'].parse(input)); + } + +/***/ }, +/* 23 */ +/***/ function(module, exports) { + + /* istanbul ignore next */ + /* Jison generated parser */ + "use strict"; + + var handlebars = (function () { + var parser = { trace: function trace() {}, + yy: {}, + symbols_: { "error": 2, "root": 3, "program": 4, "EOF": 5, "program_repetition0": 6, "statement": 7, "mustache": 8, "block": 9, "rawBlock": 10, "partial": 11, "partialBlock": 12, "content": 13, "COMMENT": 14, "CONTENT": 15, "openRawBlock": 16, "rawBlock_repetition_plus0": 17, "END_RAW_BLOCK": 18, "OPEN_RAW_BLOCK": 19, "helperName": 20, "openRawBlock_repetition0": 21, "openRawBlock_option0": 22, "CLOSE_RAW_BLOCK": 23, "openBlock": 24, "block_option0": 25, "closeBlock": 26, "openInverse": 27, "block_option1": 28, "OPEN_BLOCK": 29, "openBlock_repetition0": 30, "openBlock_option0": 31, "openBlock_option1": 32, "CLOSE": 33, "OPEN_INVERSE": 34, "openInverse_repetition0": 35, "openInverse_option0": 36, "openInverse_option1": 37, "openInverseChain": 38, "OPEN_INVERSE_CHAIN": 39, "openInverseChain_repetition0": 40, "openInverseChain_option0": 41, "openInverseChain_option1": 42, "inverseAndProgram": 43, "INVERSE": 44, "inverseChain": 45, "inverseChain_option0": 46, "OPEN_ENDBLOCK": 47, "OPEN": 48, "mustache_repetition0": 49, "mustache_option0": 50, "OPEN_UNESCAPED": 51, "mustache_repetition1": 52, "mustache_option1": 53, "CLOSE_UNESCAPED": 54, "OPEN_PARTIAL": 55, "partialName": 56, "partial_repetition0": 57, "partial_option0": 58, "openPartialBlock": 59, "OPEN_PARTIAL_BLOCK": 60, "openPartialBlock_repetition0": 61, "openPartialBlock_option0": 62, "param": 63, "sexpr": 64, "OPEN_SEXPR": 65, "sexpr_repetition0": 66, "sexpr_option0": 67, "CLOSE_SEXPR": 68, "hash": 69, "hash_repetition_plus0": 70, "hashSegment": 71, "ID": 72, "EQUALS": 73, "blockParams": 74, "OPEN_BLOCK_PARAMS": 75, "blockParams_repetition_plus0": 76, "CLOSE_BLOCK_PARAMS": 77, "path": 78, "dataName": 79, "STRING": 80, "NUMBER": 81, "BOOLEAN": 82, "UNDEFINED": 83, "NULL": 84, "DATA": 85, "pathSegments": 86, "SEP": 87, "$accept": 0, "$end": 1 }, + terminals_: { 2: "error", 5: "EOF", 14: "COMMENT", 15: "CONTENT", 18: "END_RAW_BLOCK", 19: "OPEN_RAW_BLOCK", 23: "CLOSE_RAW_BLOCK", 29: "OPEN_BLOCK", 33: "CLOSE", 34: "OPEN_INVERSE", 39: "OPEN_INVERSE_CHAIN", 44: "INVERSE", 47: "OPEN_ENDBLOCK", 48: "OPEN", 51: "OPEN_UNESCAPED", 54: "CLOSE_UNESCAPED", 55: "OPEN_PARTIAL", 60: "OPEN_PARTIAL_BLOCK", 65: "OPEN_SEXPR", 68: "CLOSE_SEXPR", 72: "ID", 73: "EQUALS", 75: "OPEN_BLOCK_PARAMS", 77: "CLOSE_BLOCK_PARAMS", 80: "STRING", 81: "NUMBER", 82: "BOOLEAN", 83: "UNDEFINED", 84: "NULL", 85: "DATA", 87: "SEP" }, + productions_: [0, [3, 2], [4, 1], [7, 1], [7, 1], [7, 1], [7, 1], [7, 1], [7, 1], [7, 1], [13, 1], [10, 3], [16, 5], [9, 4], [9, 4], [24, 6], [27, 6], [38, 6], [43, 2], [45, 3], [45, 1], [26, 3], [8, 5], [8, 5], [11, 5], [12, 3], [59, 5], [63, 1], [63, 1], [64, 5], [69, 1], [71, 3], [74, 3], [20, 1], [20, 1], [20, 1], [20, 1], [20, 1], [20, 1], [20, 1], [56, 1], [56, 1], [79, 2], [78, 1], [86, 3], [86, 1], [6, 0], [6, 2], [17, 1], [17, 2], [21, 0], [21, 2], [22, 0], [22, 1], [25, 0], [25, 1], [28, 0], [28, 1], [30, 0], [30, 2], [31, 0], [31, 1], [32, 0], [32, 1], [35, 0], [35, 2], [36, 0], [36, 1], [37, 0], [37, 1], [40, 0], [40, 2], [41, 0], [41, 1], [42, 0], [42, 1], [46, 0], [46, 1], [49, 0], [49, 2], [50, 0], [50, 1], [52, 0], [52, 2], [53, 0], [53, 1], [57, 0], [57, 2], [58, 0], [58, 1], [61, 0], [61, 2], [62, 0], [62, 1], [66, 0], [66, 2], [67, 0], [67, 1], [70, 1], [70, 2], [76, 1], [76, 2]], + performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate, $$, _$ + /**/) { + + var $0 = $$.length - 1; + switch (yystate) { + case 1: + return $$[$0 - 1]; + break; + case 2: + this.$ = yy.prepareProgram($$[$0]); + break; + case 3: + this.$ = $$[$0]; + break; + case 4: + this.$ = $$[$0]; + break; + case 5: + this.$ = $$[$0]; + break; + case 6: + this.$ = $$[$0]; + break; + case 7: + this.$ = $$[$0]; + break; + case 8: + this.$ = $$[$0]; + break; + case 9: + this.$ = { + type: 'CommentStatement', + value: yy.stripComment($$[$0]), + strip: yy.stripFlags($$[$0], $$[$0]), + loc: yy.locInfo(this._$) + }; + + break; + case 10: + this.$ = { + type: 'ContentStatement', + original: $$[$0], + value: $$[$0], + loc: yy.locInfo(this._$) + }; + + break; + case 11: + this.$ = yy.prepareRawBlock($$[$0 - 2], $$[$0 - 1], $$[$0], this._$); + break; + case 12: + this.$ = { path: $$[$0 - 3], params: $$[$0 - 2], hash: $$[$0 - 1] }; + break; + case 13: + this.$ = yy.prepareBlock($$[$0 - 3], $$[$0 - 2], $$[$0 - 1], $$[$0], false, this._$); + break; + case 14: + this.$ = yy.prepareBlock($$[$0 - 3], $$[$0 - 2], $$[$0 - 1], $$[$0], true, this._$); + break; + case 15: + this.$ = { open: $$[$0 - 5], path: $$[$0 - 4], params: $$[$0 - 3], hash: $$[$0 - 2], blockParams: $$[$0 - 1], strip: yy.stripFlags($$[$0 - 5], $$[$0]) }; + break; + case 16: + this.$ = { path: $$[$0 - 4], params: $$[$0 - 3], hash: $$[$0 - 2], blockParams: $$[$0 - 1], strip: yy.stripFlags($$[$0 - 5], $$[$0]) }; + break; + case 17: + this.$ = { path: $$[$0 - 4], params: $$[$0 - 3], hash: $$[$0 - 2], blockParams: $$[$0 - 1], strip: yy.stripFlags($$[$0 - 5], $$[$0]) }; + break; + case 18: + this.$ = { strip: yy.stripFlags($$[$0 - 1], $$[$0 - 1]), program: $$[$0] }; + break; + case 19: + var inverse = yy.prepareBlock($$[$0 - 2], $$[$0 - 1], $$[$0], $$[$0], false, this._$), + program = yy.prepareProgram([inverse], $$[$0 - 1].loc); + program.chained = true; + + this.$ = { strip: $$[$0 - 2].strip, program: program, chain: true }; + + break; + case 20: + this.$ = $$[$0]; + break; + case 21: + this.$ = { path: $$[$0 - 1], strip: yy.stripFlags($$[$0 - 2], $$[$0]) }; + break; + case 22: + this.$ = yy.prepareMustache($$[$0 - 3], $$[$0 - 2], $$[$0 - 1], $$[$0 - 4], yy.stripFlags($$[$0 - 4], $$[$0]), this._$); + break; + case 23: + this.$ = yy.prepareMustache($$[$0 - 3], $$[$0 - 2], $$[$0 - 1], $$[$0 - 4], yy.stripFlags($$[$0 - 4], $$[$0]), this._$); + break; + case 24: + this.$ = { + type: 'PartialStatement', + name: $$[$0 - 3], + params: $$[$0 - 2], + hash: $$[$0 - 1], + indent: '', + strip: yy.stripFlags($$[$0 - 4], $$[$0]), + loc: yy.locInfo(this._$) + }; + + break; + case 25: + this.$ = yy.preparePartialBlock($$[$0 - 2], $$[$0 - 1], $$[$0], this._$); + break; + case 26: + this.$ = { path: $$[$0 - 3], params: $$[$0 - 2], hash: $$[$0 - 1], strip: yy.stripFlags($$[$0 - 4], $$[$0]) }; + break; + case 27: + this.$ = $$[$0]; + break; + case 28: + this.$ = $$[$0]; + break; + case 29: + this.$ = { + type: 'SubExpression', + path: $$[$0 - 3], + params: $$[$0 - 2], + hash: $$[$0 - 1], + loc: yy.locInfo(this._$) + }; + + break; + case 30: + this.$ = { type: 'Hash', pairs: $$[$0], loc: yy.locInfo(this._$) }; + break; + case 31: + this.$ = { type: 'HashPair', key: yy.id($$[$0 - 2]), value: $$[$0], loc: yy.locInfo(this._$) }; + break; + case 32: + this.$ = yy.id($$[$0 - 1]); + break; + case 33: + this.$ = $$[$0]; + break; + case 34: + this.$ = $$[$0]; + break; + case 35: + this.$ = { type: 'StringLiteral', value: $$[$0], original: $$[$0], loc: yy.locInfo(this._$) }; + break; + case 36: + this.$ = { type: 'NumberLiteral', value: Number($$[$0]), original: Number($$[$0]), loc: yy.locInfo(this._$) }; + break; + case 37: + this.$ = { type: 'BooleanLiteral', value: $$[$0] === 'true', original: $$[$0] === 'true', loc: yy.locInfo(this._$) }; + break; + case 38: + this.$ = { type: 'UndefinedLiteral', original: undefined, value: undefined, loc: yy.locInfo(this._$) }; + break; + case 39: + this.$ = { type: 'NullLiteral', original: null, value: null, loc: yy.locInfo(this._$) }; + break; + case 40: + this.$ = $$[$0]; + break; + case 41: + this.$ = $$[$0]; + break; + case 42: + this.$ = yy.preparePath(true, $$[$0], this._$); + break; + case 43: + this.$ = yy.preparePath(false, $$[$0], this._$); + break; + case 44: + $$[$0 - 2].push({ part: yy.id($$[$0]), original: $$[$0], separator: $$[$0 - 1] });this.$ = $$[$0 - 2]; + break; + case 45: + this.$ = [{ part: yy.id($$[$0]), original: $$[$0] }]; + break; + case 46: + this.$ = []; + break; + case 47: + $$[$0 - 1].push($$[$0]); + break; + case 48: + this.$ = [$$[$0]]; + break; + case 49: + $$[$0 - 1].push($$[$0]); + break; + case 50: + this.$ = []; + break; + case 51: + $$[$0 - 1].push($$[$0]); + break; + case 58: + this.$ = []; + break; + case 59: + $$[$0 - 1].push($$[$0]); + break; + case 64: + this.$ = []; + break; + case 65: + $$[$0 - 1].push($$[$0]); + break; + case 70: + this.$ = []; + break; + case 71: + $$[$0 - 1].push($$[$0]); + break; + case 78: + this.$ = []; + break; + case 79: + $$[$0 - 1].push($$[$0]); + break; + case 82: + this.$ = []; + break; + case 83: + $$[$0 - 1].push($$[$0]); + break; + case 86: + this.$ = []; + break; + case 87: + $$[$0 - 1].push($$[$0]); + break; + case 90: + this.$ = []; + break; + case 91: + $$[$0 - 1].push($$[$0]); + break; + case 94: + this.$ = []; + break; + case 95: + $$[$0 - 1].push($$[$0]); + break; + case 98: + this.$ = [$$[$0]]; + break; + case 99: + $$[$0 - 1].push($$[$0]); + break; + case 100: + this.$ = [$$[$0]]; + break; + case 101: + $$[$0 - 1].push($$[$0]); + break; + } + }, + table: [{ 3: 1, 4: 2, 5: [2, 46], 6: 3, 14: [2, 46], 15: [2, 46], 19: [2, 46], 29: [2, 46], 34: [2, 46], 48: [2, 46], 51: [2, 46], 55: [2, 46], 60: [2, 46] }, { 1: [3] }, { 5: [1, 4] }, { 5: [2, 2], 7: 5, 8: 6, 9: 7, 10: 8, 11: 9, 12: 10, 13: 11, 14: [1, 12], 15: [1, 20], 16: 17, 19: [1, 23], 24: 15, 27: 16, 29: [1, 21], 34: [1, 22], 39: [2, 2], 44: [2, 2], 47: [2, 2], 48: [1, 13], 51: [1, 14], 55: [1, 18], 59: 19, 60: [1, 24] }, { 1: [2, 1] }, { 5: [2, 47], 14: [2, 47], 15: [2, 47], 19: [2, 47], 29: [2, 47], 34: [2, 47], 39: [2, 47], 44: [2, 47], 47: [2, 47], 48: [2, 47], 51: [2, 47], 55: [2, 47], 60: [2, 47] }, { 5: [2, 3], 14: [2, 3], 15: [2, 3], 19: [2, 3], 29: [2, 3], 34: [2, 3], 39: [2, 3], 44: [2, 3], 47: [2, 3], 48: [2, 3], 51: [2, 3], 55: [2, 3], 60: [2, 3] }, { 5: [2, 4], 14: [2, 4], 15: [2, 4], 19: [2, 4], 29: [2, 4], 34: [2, 4], 39: [2, 4], 44: [2, 4], 47: [2, 4], 48: [2, 4], 51: [2, 4], 55: [2, 4], 60: [2, 4] }, { 5: [2, 5], 14: [2, 5], 15: [2, 5], 19: [2, 5], 29: [2, 5], 34: [2, 5], 39: [2, 5], 44: [2, 5], 47: [2, 5], 48: [2, 5], 51: [2, 5], 55: [2, 5], 60: [2, 5] }, { 5: [2, 6], 14: [2, 6], 15: [2, 6], 19: [2, 6], 29: [2, 6], 34: [2, 6], 39: [2, 6], 44: [2, 6], 47: [2, 6], 48: [2, 6], 51: [2, 6], 55: [2, 6], 60: [2, 6] }, { 5: [2, 7], 14: [2, 7], 15: [2, 7], 19: [2, 7], 29: [2, 7], 34: [2, 7], 39: [2, 7], 44: [2, 7], 47: [2, 7], 48: [2, 7], 51: [2, 7], 55: [2, 7], 60: [2, 7] }, { 5: [2, 8], 14: [2, 8], 15: [2, 8], 19: [2, 8], 29: [2, 8], 34: [2, 8], 39: [2, 8], 44: [2, 8], 47: [2, 8], 48: [2, 8], 51: [2, 8], 55: [2, 8], 60: [2, 8] }, { 5: [2, 9], 14: [2, 9], 15: [2, 9], 19: [2, 9], 29: [2, 9], 34: [2, 9], 39: [2, 9], 44: [2, 9], 47: [2, 9], 48: [2, 9], 51: [2, 9], 55: [2, 9], 60: [2, 9] }, { 20: 25, 72: [1, 35], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 20: 36, 72: [1, 35], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 4: 37, 6: 3, 14: [2, 46], 15: [2, 46], 19: [2, 46], 29: [2, 46], 34: [2, 46], 39: [2, 46], 44: [2, 46], 47: [2, 46], 48: [2, 46], 51: [2, 46], 55: [2, 46], 60: [2, 46] }, { 4: 38, 6: 3, 14: [2, 46], 15: [2, 46], 19: [2, 46], 29: [2, 46], 34: [2, 46], 44: [2, 46], 47: [2, 46], 48: [2, 46], 51: [2, 46], 55: [2, 46], 60: [2, 46] }, { 13: 40, 15: [1, 20], 17: 39 }, { 20: 42, 56: 41, 64: 43, 65: [1, 44], 72: [1, 35], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 4: 45, 6: 3, 14: [2, 46], 15: [2, 46], 19: [2, 46], 29: [2, 46], 34: [2, 46], 47: [2, 46], 48: [2, 46], 51: [2, 46], 55: [2, 46], 60: [2, 46] }, { 5: [2, 10], 14: [2, 10], 15: [2, 10], 18: [2, 10], 19: [2, 10], 29: [2, 10], 34: [2, 10], 39: [2, 10], 44: [2, 10], 47: [2, 10], 48: [2, 10], 51: [2, 10], 55: [2, 10], 60: [2, 10] }, { 20: 46, 72: [1, 35], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 20: 47, 72: [1, 35], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 20: 48, 72: [1, 35], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 20: 42, 56: 49, 64: 43, 65: [1, 44], 72: [1, 35], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 33: [2, 78], 49: 50, 65: [2, 78], 72: [2, 78], 80: [2, 78], 81: [2, 78], 82: [2, 78], 83: [2, 78], 84: [2, 78], 85: [2, 78] }, { 23: [2, 33], 33: [2, 33], 54: [2, 33], 65: [2, 33], 68: [2, 33], 72: [2, 33], 75: [2, 33], 80: [2, 33], 81: [2, 33], 82: [2, 33], 83: [2, 33], 84: [2, 33], 85: [2, 33] }, { 23: [2, 34], 33: [2, 34], 54: [2, 34], 65: [2, 34], 68: [2, 34], 72: [2, 34], 75: [2, 34], 80: [2, 34], 81: [2, 34], 82: [2, 34], 83: [2, 34], 84: [2, 34], 85: [2, 34] }, { 23: [2, 35], 33: [2, 35], 54: [2, 35], 65: [2, 35], 68: [2, 35], 72: [2, 35], 75: [2, 35], 80: [2, 35], 81: [2, 35], 82: [2, 35], 83: [2, 35], 84: [2, 35], 85: [2, 35] }, { 23: [2, 36], 33: [2, 36], 54: [2, 36], 65: [2, 36], 68: [2, 36], 72: [2, 36], 75: [2, 36], 80: [2, 36], 81: [2, 36], 82: [2, 36], 83: [2, 36], 84: [2, 36], 85: [2, 36] }, { 23: [2, 37], 33: [2, 37], 54: [2, 37], 65: [2, 37], 68: [2, 37], 72: [2, 37], 75: [2, 37], 80: [2, 37], 81: [2, 37], 82: [2, 37], 83: [2, 37], 84: [2, 37], 85: [2, 37] }, { 23: [2, 38], 33: [2, 38], 54: [2, 38], 65: [2, 38], 68: [2, 38], 72: [2, 38], 75: [2, 38], 80: [2, 38], 81: [2, 38], 82: [2, 38], 83: [2, 38], 84: [2, 38], 85: [2, 38] }, { 23: [2, 39], 33: [2, 39], 54: [2, 39], 65: [2, 39], 68: [2, 39], 72: [2, 39], 75: [2, 39], 80: [2, 39], 81: [2, 39], 82: [2, 39], 83: [2, 39], 84: [2, 39], 85: [2, 39] }, { 23: [2, 43], 33: [2, 43], 54: [2, 43], 65: [2, 43], 68: [2, 43], 72: [2, 43], 75: [2, 43], 80: [2, 43], 81: [2, 43], 82: [2, 43], 83: [2, 43], 84: [2, 43], 85: [2, 43], 87: [1, 51] }, { 72: [1, 35], 86: 52 }, { 23: [2, 45], 33: [2, 45], 54: [2, 45], 65: [2, 45], 68: [2, 45], 72: [2, 45], 75: [2, 45], 80: [2, 45], 81: [2, 45], 82: [2, 45], 83: [2, 45], 84: [2, 45], 85: [2, 45], 87: [2, 45] }, { 52: 53, 54: [2, 82], 65: [2, 82], 72: [2, 82], 80: [2, 82], 81: [2, 82], 82: [2, 82], 83: [2, 82], 84: [2, 82], 85: [2, 82] }, { 25: 54, 38: 56, 39: [1, 58], 43: 57, 44: [1, 59], 45: 55, 47: [2, 54] }, { 28: 60, 43: 61, 44: [1, 59], 47: [2, 56] }, { 13: 63, 15: [1, 20], 18: [1, 62] }, { 15: [2, 48], 18: [2, 48] }, { 33: [2, 86], 57: 64, 65: [2, 86], 72: [2, 86], 80: [2, 86], 81: [2, 86], 82: [2, 86], 83: [2, 86], 84: [2, 86], 85: [2, 86] }, { 33: [2, 40], 65: [2, 40], 72: [2, 40], 80: [2, 40], 81: [2, 40], 82: [2, 40], 83: [2, 40], 84: [2, 40], 85: [2, 40] }, { 33: [2, 41], 65: [2, 41], 72: [2, 41], 80: [2, 41], 81: [2, 41], 82: [2, 41], 83: [2, 41], 84: [2, 41], 85: [2, 41] }, { 20: 65, 72: [1, 35], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 26: 66, 47: [1, 67] }, { 30: 68, 33: [2, 58], 65: [2, 58], 72: [2, 58], 75: [2, 58], 80: [2, 58], 81: [2, 58], 82: [2, 58], 83: [2, 58], 84: [2, 58], 85: [2, 58] }, { 33: [2, 64], 35: 69, 65: [2, 64], 72: [2, 64], 75: [2, 64], 80: [2, 64], 81: [2, 64], 82: [2, 64], 83: [2, 64], 84: [2, 64], 85: [2, 64] }, { 21: 70, 23: [2, 50], 65: [2, 50], 72: [2, 50], 80: [2, 50], 81: [2, 50], 82: [2, 50], 83: [2, 50], 84: [2, 50], 85: [2, 50] }, { 33: [2, 90], 61: 71, 65: [2, 90], 72: [2, 90], 80: [2, 90], 81: [2, 90], 82: [2, 90], 83: [2, 90], 84: [2, 90], 85: [2, 90] }, { 20: 75, 33: [2, 80], 50: 72, 63: 73, 64: 76, 65: [1, 44], 69: 74, 70: 77, 71: 78, 72: [1, 79], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 72: [1, 80] }, { 23: [2, 42], 33: [2, 42], 54: [2, 42], 65: [2, 42], 68: [2, 42], 72: [2, 42], 75: [2, 42], 80: [2, 42], 81: [2, 42], 82: [2, 42], 83: [2, 42], 84: [2, 42], 85: [2, 42], 87: [1, 51] }, { 20: 75, 53: 81, 54: [2, 84], 63: 82, 64: 76, 65: [1, 44], 69: 83, 70: 77, 71: 78, 72: [1, 79], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 26: 84, 47: [1, 67] }, { 47: [2, 55] }, { 4: 85, 6: 3, 14: [2, 46], 15: [2, 46], 19: [2, 46], 29: [2, 46], 34: [2, 46], 39: [2, 46], 44: [2, 46], 47: [2, 46], 48: [2, 46], 51: [2, 46], 55: [2, 46], 60: [2, 46] }, { 47: [2, 20] }, { 20: 86, 72: [1, 35], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 4: 87, 6: 3, 14: [2, 46], 15: [2, 46], 19: [2, 46], 29: [2, 46], 34: [2, 46], 47: [2, 46], 48: [2, 46], 51: [2, 46], 55: [2, 46], 60: [2, 46] }, { 26: 88, 47: [1, 67] }, { 47: [2, 57] }, { 5: [2, 11], 14: [2, 11], 15: [2, 11], 19: [2, 11], 29: [2, 11], 34: [2, 11], 39: [2, 11], 44: [2, 11], 47: [2, 11], 48: [2, 11], 51: [2, 11], 55: [2, 11], 60: [2, 11] }, { 15: [2, 49], 18: [2, 49] }, { 20: 75, 33: [2, 88], 58: 89, 63: 90, 64: 76, 65: [1, 44], 69: 91, 70: 77, 71: 78, 72: [1, 79], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 65: [2, 94], 66: 92, 68: [2, 94], 72: [2, 94], 80: [2, 94], 81: [2, 94], 82: [2, 94], 83: [2, 94], 84: [2, 94], 85: [2, 94] }, { 5: [2, 25], 14: [2, 25], 15: [2, 25], 19: [2, 25], 29: [2, 25], 34: [2, 25], 39: [2, 25], 44: [2, 25], 47: [2, 25], 48: [2, 25], 51: [2, 25], 55: [2, 25], 60: [2, 25] }, { 20: 93, 72: [1, 35], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 20: 75, 31: 94, 33: [2, 60], 63: 95, 64: 76, 65: [1, 44], 69: 96, 70: 77, 71: 78, 72: [1, 79], 75: [2, 60], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 20: 75, 33: [2, 66], 36: 97, 63: 98, 64: 76, 65: [1, 44], 69: 99, 70: 77, 71: 78, 72: [1, 79], 75: [2, 66], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 20: 75, 22: 100, 23: [2, 52], 63: 101, 64: 76, 65: [1, 44], 69: 102, 70: 77, 71: 78, 72: [1, 79], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 20: 75, 33: [2, 92], 62: 103, 63: 104, 64: 76, 65: [1, 44], 69: 105, 70: 77, 71: 78, 72: [1, 79], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 33: [1, 106] }, { 33: [2, 79], 65: [2, 79], 72: [2, 79], 80: [2, 79], 81: [2, 79], 82: [2, 79], 83: [2, 79], 84: [2, 79], 85: [2, 79] }, { 33: [2, 81] }, { 23: [2, 27], 33: [2, 27], 54: [2, 27], 65: [2, 27], 68: [2, 27], 72: [2, 27], 75: [2, 27], 80: [2, 27], 81: [2, 27], 82: [2, 27], 83: [2, 27], 84: [2, 27], 85: [2, 27] }, { 23: [2, 28], 33: [2, 28], 54: [2, 28], 65: [2, 28], 68: [2, 28], 72: [2, 28], 75: [2, 28], 80: [2, 28], 81: [2, 28], 82: [2, 28], 83: [2, 28], 84: [2, 28], 85: [2, 28] }, { 23: [2, 30], 33: [2, 30], 54: [2, 30], 68: [2, 30], 71: 107, 72: [1, 108], 75: [2, 30] }, { 23: [2, 98], 33: [2, 98], 54: [2, 98], 68: [2, 98], 72: [2, 98], 75: [2, 98] }, { 23: [2, 45], 33: [2, 45], 54: [2, 45], 65: [2, 45], 68: [2, 45], 72: [2, 45], 73: [1, 109], 75: [2, 45], 80: [2, 45], 81: [2, 45], 82: [2, 45], 83: [2, 45], 84: [2, 45], 85: [2, 45], 87: [2, 45] }, { 23: [2, 44], 33: [2, 44], 54: [2, 44], 65: [2, 44], 68: [2, 44], 72: [2, 44], 75: [2, 44], 80: [2, 44], 81: [2, 44], 82: [2, 44], 83: [2, 44], 84: [2, 44], 85: [2, 44], 87: [2, 44] }, { 54: [1, 110] }, { 54: [2, 83], 65: [2, 83], 72: [2, 83], 80: [2, 83], 81: [2, 83], 82: [2, 83], 83: [2, 83], 84: [2, 83], 85: [2, 83] }, { 54: [2, 85] }, { 5: [2, 13], 14: [2, 13], 15: [2, 13], 19: [2, 13], 29: [2, 13], 34: [2, 13], 39: [2, 13], 44: [2, 13], 47: [2, 13], 48: [2, 13], 51: [2, 13], 55: [2, 13], 60: [2, 13] }, { 38: 56, 39: [1, 58], 43: 57, 44: [1, 59], 45: 112, 46: 111, 47: [2, 76] }, { 33: [2, 70], 40: 113, 65: [2, 70], 72: [2, 70], 75: [2, 70], 80: [2, 70], 81: [2, 70], 82: [2, 70], 83: [2, 70], 84: [2, 70], 85: [2, 70] }, { 47: [2, 18] }, { 5: [2, 14], 14: [2, 14], 15: [2, 14], 19: [2, 14], 29: [2, 14], 34: [2, 14], 39: [2, 14], 44: [2, 14], 47: [2, 14], 48: [2, 14], 51: [2, 14], 55: [2, 14], 60: [2, 14] }, { 33: [1, 114] }, { 33: [2, 87], 65: [2, 87], 72: [2, 87], 80: [2, 87], 81: [2, 87], 82: [2, 87], 83: [2, 87], 84: [2, 87], 85: [2, 87] }, { 33: [2, 89] }, { 20: 75, 63: 116, 64: 76, 65: [1, 44], 67: 115, 68: [2, 96], 69: 117, 70: 77, 71: 78, 72: [1, 79], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 33: [1, 118] }, { 32: 119, 33: [2, 62], 74: 120, 75: [1, 121] }, { 33: [2, 59], 65: [2, 59], 72: [2, 59], 75: [2, 59], 80: [2, 59], 81: [2, 59], 82: [2, 59], 83: [2, 59], 84: [2, 59], 85: [2, 59] }, { 33: [2, 61], 75: [2, 61] }, { 33: [2, 68], 37: 122, 74: 123, 75: [1, 121] }, { 33: [2, 65], 65: [2, 65], 72: [2, 65], 75: [2, 65], 80: [2, 65], 81: [2, 65], 82: [2, 65], 83: [2, 65], 84: [2, 65], 85: [2, 65] }, { 33: [2, 67], 75: [2, 67] }, { 23: [1, 124] }, { 23: [2, 51], 65: [2, 51], 72: [2, 51], 80: [2, 51], 81: [2, 51], 82: [2, 51], 83: [2, 51], 84: [2, 51], 85: [2, 51] }, { 23: [2, 53] }, { 33: [1, 125] }, { 33: [2, 91], 65: [2, 91], 72: [2, 91], 80: [2, 91], 81: [2, 91], 82: [2, 91], 83: [2, 91], 84: [2, 91], 85: [2, 91] }, { 33: [2, 93] }, { 5: [2, 22], 14: [2, 22], 15: [2, 22], 19: [2, 22], 29: [2, 22], 34: [2, 22], 39: [2, 22], 44: [2, 22], 47: [2, 22], 48: [2, 22], 51: [2, 22], 55: [2, 22], 60: [2, 22] }, { 23: [2, 99], 33: [2, 99], 54: [2, 99], 68: [2, 99], 72: [2, 99], 75: [2, 99] }, { 73: [1, 109] }, { 20: 75, 63: 126, 64: 76, 65: [1, 44], 72: [1, 35], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 5: [2, 23], 14: [2, 23], 15: [2, 23], 19: [2, 23], 29: [2, 23], 34: [2, 23], 39: [2, 23], 44: [2, 23], 47: [2, 23], 48: [2, 23], 51: [2, 23], 55: [2, 23], 60: [2, 23] }, { 47: [2, 19] }, { 47: [2, 77] }, { 20: 75, 33: [2, 72], 41: 127, 63: 128, 64: 76, 65: [1, 44], 69: 129, 70: 77, 71: 78, 72: [1, 79], 75: [2, 72], 78: 26, 79: 27, 80: [1, 28], 81: [1, 29], 82: [1, 30], 83: [1, 31], 84: [1, 32], 85: [1, 34], 86: 33 }, { 5: [2, 24], 14: [2, 24], 15: [2, 24], 19: [2, 24], 29: [2, 24], 34: [2, 24], 39: [2, 24], 44: [2, 24], 47: [2, 24], 48: [2, 24], 51: [2, 24], 55: [2, 24], 60: [2, 24] }, { 68: [1, 130] }, { 65: [2, 95], 68: [2, 95], 72: [2, 95], 80: [2, 95], 81: [2, 95], 82: [2, 95], 83: [2, 95], 84: [2, 95], 85: [2, 95] }, { 68: [2, 97] }, { 5: [2, 21], 14: [2, 21], 15: [2, 21], 19: [2, 21], 29: [2, 21], 34: [2, 21], 39: [2, 21], 44: [2, 21], 47: [2, 21], 48: [2, 21], 51: [2, 21], 55: [2, 21], 60: [2, 21] }, { 33: [1, 131] }, { 33: [2, 63] }, { 72: [1, 133], 76: 132 }, { 33: [1, 134] }, { 33: [2, 69] }, { 15: [2, 12] }, { 14: [2, 26], 15: [2, 26], 19: [2, 26], 29: [2, 26], 34: [2, 26], 47: [2, 26], 48: [2, 26], 51: [2, 26], 55: [2, 26], 60: [2, 26] }, { 23: [2, 31], 33: [2, 31], 54: [2, 31], 68: [2, 31], 72: [2, 31], 75: [2, 31] }, { 33: [2, 74], 42: 135, 74: 136, 75: [1, 121] }, { 33: [2, 71], 65: [2, 71], 72: [2, 71], 75: [2, 71], 80: [2, 71], 81: [2, 71], 82: [2, 71], 83: [2, 71], 84: [2, 71], 85: [2, 71] }, { 33: [2, 73], 75: [2, 73] }, { 23: [2, 29], 33: [2, 29], 54: [2, 29], 65: [2, 29], 68: [2, 29], 72: [2, 29], 75: [2, 29], 80: [2, 29], 81: [2, 29], 82: [2, 29], 83: [2, 29], 84: [2, 29], 85: [2, 29] }, { 14: [2, 15], 15: [2, 15], 19: [2, 15], 29: [2, 15], 34: [2, 15], 39: [2, 15], 44: [2, 15], 47: [2, 15], 48: [2, 15], 51: [2, 15], 55: [2, 15], 60: [2, 15] }, { 72: [1, 138], 77: [1, 137] }, { 72: [2, 100], 77: [2, 100] }, { 14: [2, 16], 15: [2, 16], 19: [2, 16], 29: [2, 16], 34: [2, 16], 44: [2, 16], 47: [2, 16], 48: [2, 16], 51: [2, 16], 55: [2, 16], 60: [2, 16] }, { 33: [1, 139] }, { 33: [2, 75] }, { 33: [2, 32] }, { 72: [2, 101], 77: [2, 101] }, { 14: [2, 17], 15: [2, 17], 19: [2, 17], 29: [2, 17], 34: [2, 17], 39: [2, 17], 44: [2, 17], 47: [2, 17], 48: [2, 17], 51: [2, 17], 55: [2, 17], 60: [2, 17] }], + defaultActions: { 4: [2, 1], 55: [2, 55], 57: [2, 20], 61: [2, 57], 74: [2, 81], 83: [2, 85], 87: [2, 18], 91: [2, 89], 102: [2, 53], 105: [2, 93], 111: [2, 19], 112: [2, 77], 117: [2, 97], 120: [2, 63], 123: [2, 69], 124: [2, 12], 136: [2, 75], 137: [2, 32] }, + parseError: function parseError(str, hash) { + throw new Error(str); + }, + parse: function parse(input) { + var self = this, + stack = [0], + vstack = [null], + lstack = [], + table = this.table, + yytext = "", + yylineno = 0, + yyleng = 0, + recovering = 0, + TERROR = 2, + EOF = 1; + this.lexer.setInput(input); + this.lexer.yy = this.yy; + this.yy.lexer = this.lexer; + this.yy.parser = this; + if (typeof this.lexer.yylloc == "undefined") this.lexer.yylloc = {}; + var yyloc = this.lexer.yylloc; + lstack.push(yyloc); + var ranges = this.lexer.options && this.lexer.options.ranges; + if (typeof this.yy.parseError === "function") this.parseError = this.yy.parseError; + function popStack(n) { + stack.length = stack.length - 2 * n; + vstack.length = vstack.length - n; + lstack.length = lstack.length - n; + } + function lex() { + var token; + token = self.lexer.lex() || 1; + if (typeof token !== "number") { + token = self.symbols_[token] || token; + } + return token; + } + var symbol, + preErrorSymbol, + state, + action, + a, + r, + yyval = {}, + p, + len, + newState, + expected; + while (true) { + state = stack[stack.length - 1]; + if (this.defaultActions[state]) { + action = this.defaultActions[state]; + } else { + if (symbol === null || typeof symbol == "undefined") { + symbol = lex(); + } + action = table[state] && table[state][symbol]; + } + if (typeof action === "undefined" || !action.length || !action[0]) { + var errStr = ""; + if (!recovering) { + expected = []; + for (p in table[state]) if (this.terminals_[p] && p > 2) { + expected.push("'" + this.terminals_[p] + "'"); + } + if (this.lexer.showPosition) { + errStr = "Parse error on line " + (yylineno + 1) + ":\n" + this.lexer.showPosition() + "\nExpecting " + expected.join(", ") + ", got '" + (this.terminals_[symbol] || symbol) + "'"; + } else { + errStr = "Parse error on line " + (yylineno + 1) + ": Unexpected " + (symbol == 1 ? "end of input" : "'" + (this.terminals_[symbol] || symbol) + "'"); + } + this.parseError(errStr, { text: this.lexer.match, token: this.terminals_[symbol] || symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected }); + } + } + if (action[0] instanceof Array && action.length > 1) { + throw new Error("Parse Error: multiple actions possible at state: " + state + ", token: " + symbol); + } + switch (action[0]) { + case 1: + stack.push(symbol); + vstack.push(this.lexer.yytext); + lstack.push(this.lexer.yylloc); + stack.push(action[1]); + symbol = null; + if (!preErrorSymbol) { + yyleng = this.lexer.yyleng; + yytext = this.lexer.yytext; + yylineno = this.lexer.yylineno; + yyloc = this.lexer.yylloc; + if (recovering > 0) recovering--; + } else { + symbol = preErrorSymbol; + preErrorSymbol = null; + } + break; + case 2: + len = this.productions_[action[1]][1]; + yyval.$ = vstack[vstack.length - len]; + yyval._$ = { first_line: lstack[lstack.length - (len || 1)].first_line, last_line: lstack[lstack.length - 1].last_line, first_column: lstack[lstack.length - (len || 1)].first_column, last_column: lstack[lstack.length - 1].last_column }; + if (ranges) { + yyval._$.range = [lstack[lstack.length - (len || 1)].range[0], lstack[lstack.length - 1].range[1]]; + } + r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack); + if (typeof r !== "undefined") { + return r; + } + if (len) { + stack = stack.slice(0, -1 * len * 2); + vstack = vstack.slice(0, -1 * len); + lstack = lstack.slice(0, -1 * len); + } + stack.push(this.productions_[action[1]][0]); + vstack.push(yyval.$); + lstack.push(yyval._$); + newState = table[stack[stack.length - 2]][stack[stack.length - 1]]; + stack.push(newState); + break; + case 3: + return true; + } + } + return true; + } + }; + /* Jison generated lexer */ + var lexer = (function () { + var lexer = { EOF: 1, + parseError: function parseError(str, hash) { + if (this.yy.parser) { + this.yy.parser.parseError(str, hash); + } else { + throw new Error(str); + } + }, + setInput: function setInput(input) { + this._input = input; + this._more = this._less = this.done = false; + this.yylineno = this.yyleng = 0; + this.yytext = this.matched = this.match = ''; + this.conditionStack = ['INITIAL']; + this.yylloc = { first_line: 1, first_column: 0, last_line: 1, last_column: 0 }; + if (this.options.ranges) this.yylloc.range = [0, 0]; + this.offset = 0; + return this; + }, + input: function input() { + var ch = this._input[0]; + this.yytext += ch; + this.yyleng++; + this.offset++; + this.match += ch; + this.matched += ch; + var lines = ch.match(/(?:\r\n?|\n).*/g); + if (lines) { + this.yylineno++; + this.yylloc.last_line++; + } else { + this.yylloc.last_column++; + } + if (this.options.ranges) this.yylloc.range[1]++; + + this._input = this._input.slice(1); + return ch; + }, + unput: function unput(ch) { + var len = ch.length; + var lines = ch.split(/(?:\r\n?|\n)/g); + + this._input = ch + this._input; + this.yytext = this.yytext.substr(0, this.yytext.length - len - 1); + //this.yyleng -= len; + this.offset -= len; + var oldLines = this.match.split(/(?:\r\n?|\n)/g); + this.match = this.match.substr(0, this.match.length - 1); + this.matched = this.matched.substr(0, this.matched.length - 1); + + if (lines.length - 1) this.yylineno -= lines.length - 1; + var r = this.yylloc.range; + + this.yylloc = { first_line: this.yylloc.first_line, + last_line: this.yylineno + 1, + first_column: this.yylloc.first_column, + last_column: lines ? (lines.length === oldLines.length ? this.yylloc.first_column : 0) + oldLines[oldLines.length - lines.length].length - lines[0].length : this.yylloc.first_column - len + }; + + if (this.options.ranges) { + this.yylloc.range = [r[0], r[0] + this.yyleng - len]; + } + return this; + }, + more: function more() { + this._more = true; + return this; + }, + less: function less(n) { + this.unput(this.match.slice(n)); + }, + pastInput: function pastInput() { + var past = this.matched.substr(0, this.matched.length - this.match.length); + return (past.length > 20 ? '...' : '') + past.substr(-20).replace(/\n/g, ""); + }, + upcomingInput: function upcomingInput() { + var next = this.match; + if (next.length < 20) { + next += this._input.substr(0, 20 - next.length); + } + return (next.substr(0, 20) + (next.length > 20 ? '...' : '')).replace(/\n/g, ""); + }, + showPosition: function showPosition() { + var pre = this.pastInput(); + var c = new Array(pre.length + 1).join("-"); + return pre + this.upcomingInput() + "\n" + c + "^"; + }, + next: function next() { + if (this.done) { + return this.EOF; + } + if (!this._input) this.done = true; + + var token, match, tempMatch, index, col, lines; + if (!this._more) { + this.yytext = ''; + this.match = ''; + } + var rules = this._currentRules(); + for (var i = 0; i < rules.length; i++) { + tempMatch = this._input.match(this.rules[rules[i]]); + if (tempMatch && (!match || tempMatch[0].length > match[0].length)) { + match = tempMatch; + index = i; + if (!this.options.flex) break; + } + } + if (match) { + lines = match[0].match(/(?:\r\n?|\n).*/g); + if (lines) this.yylineno += lines.length; + this.yylloc = { first_line: this.yylloc.last_line, + last_line: this.yylineno + 1, + first_column: this.yylloc.last_column, + last_column: lines ? lines[lines.length - 1].length - lines[lines.length - 1].match(/\r?\n?/)[0].length : this.yylloc.last_column + match[0].length }; + this.yytext += match[0]; + this.match += match[0]; + this.matches = match; + this.yyleng = this.yytext.length; + if (this.options.ranges) { + this.yylloc.range = [this.offset, this.offset += this.yyleng]; + } + this._more = false; + this._input = this._input.slice(match[0].length); + this.matched += match[0]; + token = this.performAction.call(this, this.yy, this, rules[index], this.conditionStack[this.conditionStack.length - 1]); + if (this.done && this._input) this.done = false; + if (token) return token;else return; + } + if (this._input === "") { + return this.EOF; + } else { + return this.parseError('Lexical error on line ' + (this.yylineno + 1) + '. Unrecognized text.\n' + this.showPosition(), { text: "", token: null, line: this.yylineno }); + } + }, + lex: function lex() { + var r = this.next(); + if (typeof r !== 'undefined') { + return r; + } else { + return this.lex(); + } + }, + begin: function begin(condition) { + this.conditionStack.push(condition); + }, + popState: function popState() { + return this.conditionStack.pop(); + }, + _currentRules: function _currentRules() { + return this.conditions[this.conditionStack[this.conditionStack.length - 1]].rules; + }, + topState: function topState() { + return this.conditionStack[this.conditionStack.length - 2]; + }, + pushState: function begin(condition) { + this.begin(condition); + } }; + lexer.options = {}; + lexer.performAction = function anonymous(yy, yy_, $avoiding_name_collisions, YY_START + /**/) { + + function strip(start, end) { + return yy_.yytext = yy_.yytext.substr(start, yy_.yyleng - end); + } + + var YYSTATE = YY_START; + switch ($avoiding_name_collisions) { + case 0: + if (yy_.yytext.slice(-2) === "\\\\") { + strip(0, 1); + this.begin("mu"); + } else if (yy_.yytext.slice(-1) === "\\") { + strip(0, 1); + this.begin("emu"); + } else { + this.begin("mu"); + } + if (yy_.yytext) return 15; + + break; + case 1: + return 15; + break; + case 2: + this.popState(); + return 15; + + break; + case 3: + this.begin('raw');return 15; + break; + case 4: + this.popState(); + // Should be using `this.topState()` below, but it currently + // returns the second top instead of the first top. Opened an + // issue about it at https://github.com/zaach/jison/issues/291 + if (this.conditionStack[this.conditionStack.length - 1] === 'raw') { + return 15; + } else { + yy_.yytext = yy_.yytext.substr(5, yy_.yyleng - 9); + return 'END_RAW_BLOCK'; + } + + break; + case 5: + return 15; + break; + case 6: + this.popState(); + return 14; + + break; + case 7: + return 65; + break; + case 8: + return 68; + break; + case 9: + return 19; + break; + case 10: + this.popState(); + this.begin('raw'); + return 23; + + break; + case 11: + return 55; + break; + case 12: + return 60; + break; + case 13: + return 29; + break; + case 14: + return 47; + break; + case 15: + this.popState();return 44; + break; + case 16: + this.popState();return 44; + break; + case 17: + return 34; + break; + case 18: + return 39; + break; + case 19: + return 51; + break; + case 20: + return 48; + break; + case 21: + this.unput(yy_.yytext); + this.popState(); + this.begin('com'); + + break; + case 22: + this.popState(); + return 14; + + break; + case 23: + return 48; + break; + case 24: + return 73; + break; + case 25: + return 72; + break; + case 26: + return 72; + break; + case 27: + return 87; + break; + case 28: + // ignore whitespace + break; + case 29: + this.popState();return 54; + break; + case 30: + this.popState();return 33; + break; + case 31: + yy_.yytext = strip(1, 2).replace(/\\"/g, '"');return 80; + break; + case 32: + yy_.yytext = strip(1, 2).replace(/\\'/g, "'");return 80; + break; + case 33: + return 85; + break; + case 34: + return 82; + break; + case 35: + return 82; + break; + case 36: + return 83; + break; + case 37: + return 84; + break; + case 38: + return 81; + break; + case 39: + return 75; + break; + case 40: + return 77; + break; + case 41: + return 72; + break; + case 42: + yy_.yytext = yy_.yytext.replace(/\\([\\\]])/g, '$1');return 72; + break; + case 43: + return 'INVALID'; + break; + case 44: + return 5; + break; + } + }; + lexer.rules = [/^(?:[^\x00]*?(?=(\{\{)))/, /^(?:[^\x00]+)/, /^(?:[^\x00]{2,}?(?=(\{\{|\\\{\{|\\\\\{\{|$)))/, /^(?:\{\{\{\{(?=[^/]))/, /^(?:\{\{\{\{\/[^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=[=}\s\/.])\}\}\}\})/, /^(?:[^\x00]*?(?=(\{\{\{\{)))/, /^(?:[\s\S]*?--(~)?\}\})/, /^(?:\()/, /^(?:\))/, /^(?:\{\{\{\{)/, /^(?:\}\}\}\})/, /^(?:\{\{(~)?>)/, /^(?:\{\{(~)?#>)/, /^(?:\{\{(~)?#\*?)/, /^(?:\{\{(~)?\/)/, /^(?:\{\{(~)?\^\s*(~)?\}\})/, /^(?:\{\{(~)?\s*else\s*(~)?\}\})/, /^(?:\{\{(~)?\^)/, /^(?:\{\{(~)?\s*else\b)/, /^(?:\{\{(~)?\{)/, /^(?:\{\{(~)?&)/, /^(?:\{\{(~)?!--)/, /^(?:\{\{(~)?![\s\S]*?\}\})/, /^(?:\{\{(~)?\*?)/, /^(?:=)/, /^(?:\.\.)/, /^(?:\.(?=([=~}\s\/.)|])))/, /^(?:[\/.])/, /^(?:\s+)/, /^(?:\}(~)?\}\})/, /^(?:(~)?\}\})/, /^(?:"(\\["]|[^"])*")/, /^(?:'(\\[']|[^'])*')/, /^(?:@)/, /^(?:true(?=([~}\s)])))/, /^(?:false(?=([~}\s)])))/, /^(?:undefined(?=([~}\s)])))/, /^(?:null(?=([~}\s)])))/, /^(?:-?[0-9]+(?:\.[0-9]+)?(?=([~}\s)])))/, /^(?:as\s+\|)/, /^(?:\|)/, /^(?:([^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=([=~}\s\/.)|]))))/, /^(?:\[(\\\]|[^\]])*\])/, /^(?:.)/, /^(?:$)/]; + lexer.conditions = { "mu": { "rules": [7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44], "inclusive": false }, "emu": { "rules": [2], "inclusive": false }, "com": { "rules": [6], "inclusive": false }, "raw": { "rules": [3, 4, 5], "inclusive": false }, "INITIAL": { "rules": [0, 1, 44], "inclusive": true } }; + return lexer; + })(); + parser.lexer = lexer; + function Parser() { + this.yy = {}; + }Parser.prototype = parser;parser.Parser = Parser; + return new Parser(); + })();exports.__esModule = true; + exports['default'] = handlebars; + +/***/ }, +/* 24 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + var _interopRequireDefault = __webpack_require__(1)['default']; + + exports.__esModule = true; + + var _visitor = __webpack_require__(25); + + var _visitor2 = _interopRequireDefault(_visitor); + + function WhitespaceControl() { + var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; + + this.options = options; + } + WhitespaceControl.prototype = new _visitor2['default'](); + + WhitespaceControl.prototype.Program = function (program) { + var doStandalone = !this.options.ignoreStandalone; + + var isRoot = !this.isRootSeen; + this.isRootSeen = true; + + var body = program.body; + for (var i = 0, l = body.length; i < l; i++) { + var current = body[i], + strip = this.accept(current); + + if (!strip) { + continue; + } + + var _isPrevWhitespace = isPrevWhitespace(body, i, isRoot), + _isNextWhitespace = isNextWhitespace(body, i, isRoot), + openStandalone = strip.openStandalone && _isPrevWhitespace, + closeStandalone = strip.closeStandalone && _isNextWhitespace, + inlineStandalone = strip.inlineStandalone && _isPrevWhitespace && _isNextWhitespace; + + if (strip.close) { + omitRight(body, i, true); + } + if (strip.open) { + omitLeft(body, i, true); + } + + if (doStandalone && inlineStandalone) { + omitRight(body, i); + + if (omitLeft(body, i)) { + // If we are on a standalone node, save the indent info for partials + if (current.type === 'PartialStatement') { + // Pull out the whitespace from the final line + current.indent = /([ \t]+$)/.exec(body[i - 1].original)[1]; + } + } + } + if (doStandalone && openStandalone) { + omitRight((current.program || current.inverse).body); + + // Strip out the previous content node if it's whitespace only + omitLeft(body, i); + } + if (doStandalone && closeStandalone) { + // Always strip the next node + omitRight(body, i); + + omitLeft((current.inverse || current.program).body); + } + } + + return program; + }; + + WhitespaceControl.prototype.BlockStatement = WhitespaceControl.prototype.DecoratorBlock = WhitespaceControl.prototype.PartialBlockStatement = function (block) { + this.accept(block.program); + this.accept(block.inverse); + + // Find the inverse program that is involed with whitespace stripping. + var program = block.program || block.inverse, + inverse = block.program && block.inverse, + firstInverse = inverse, + lastInverse = inverse; + + if (inverse && inverse.chained) { + firstInverse = inverse.body[0].program; + + // Walk the inverse chain to find the last inverse that is actually in the chain. + while (lastInverse.chained) { + lastInverse = lastInverse.body[lastInverse.body.length - 1].program; + } + } + + var strip = { + open: block.openStrip.open, + close: block.closeStrip.close, + + // Determine the standalone candiacy. Basically flag our content as being possibly standalone + // so our parent can determine if we actually are standalone + openStandalone: isNextWhitespace(program.body), + closeStandalone: isPrevWhitespace((firstInverse || program).body) + }; + + if (block.openStrip.close) { + omitRight(program.body, null, true); + } + + if (inverse) { + var inverseStrip = block.inverseStrip; + + if (inverseStrip.open) { + omitLeft(program.body, null, true); + } + + if (inverseStrip.close) { + omitRight(firstInverse.body, null, true); + } + if (block.closeStrip.open) { + omitLeft(lastInverse.body, null, true); + } + + // Find standalone else statments + if (!this.options.ignoreStandalone && isPrevWhitespace(program.body) && isNextWhitespace(firstInverse.body)) { + omitLeft(program.body); + omitRight(firstInverse.body); + } + } else if (block.closeStrip.open) { + omitLeft(program.body, null, true); + } + + return strip; + }; + + WhitespaceControl.prototype.Decorator = WhitespaceControl.prototype.MustacheStatement = function (mustache) { + return mustache.strip; + }; + + WhitespaceControl.prototype.PartialStatement = WhitespaceControl.prototype.CommentStatement = function (node) { + /* istanbul ignore next */ + var strip = node.strip || {}; + return { + inlineStandalone: true, + open: strip.open, + close: strip.close + }; + }; + + function isPrevWhitespace(body, i, isRoot) { + if (i === undefined) { + i = body.length; + } + + // Nodes that end with newlines are considered whitespace (but are special + // cased for strip operations) + var prev = body[i - 1], + sibling = body[i - 2]; + if (!prev) { + return isRoot; + } + + if (prev.type === 'ContentStatement') { + return (sibling || !isRoot ? /\r?\n\s*?$/ : /(^|\r?\n)\s*?$/).test(prev.original); + } + } + function isNextWhitespace(body, i, isRoot) { + if (i === undefined) { + i = -1; + } + + var next = body[i + 1], + sibling = body[i + 2]; + if (!next) { + return isRoot; + } + + if (next.type === 'ContentStatement') { + return (sibling || !isRoot ? /^\s*?\r?\n/ : /^\s*?(\r?\n|$)/).test(next.original); + } + } + + // Marks the node to the right of the position as omitted. + // I.e. {{foo}}' ' will mark the ' ' node as omitted. + // + // If i is undefined, then the first child will be marked as such. + // + // If mulitple is truthy then all whitespace will be stripped out until non-whitespace + // content is met. + function omitRight(body, i, multiple) { + var current = body[i == null ? 0 : i + 1]; + if (!current || current.type !== 'ContentStatement' || !multiple && current.rightStripped) { + return; + } + + var original = current.value; + current.value = current.value.replace(multiple ? /^\s+/ : /^[ \t]*\r?\n?/, ''); + current.rightStripped = current.value !== original; + } + + // Marks the node to the left of the position as omitted. + // I.e. ' '{{foo}} will mark the ' ' node as omitted. + // + // If i is undefined then the last child will be marked as such. + // + // If mulitple is truthy then all whitespace will be stripped out until non-whitespace + // content is met. + function omitLeft(body, i, multiple) { + var current = body[i == null ? body.length - 1 : i - 1]; + if (!current || current.type !== 'ContentStatement' || !multiple && current.leftStripped) { + return; + } + + // We omit the last node if it's whitespace only and not preceeded by a non-content node. + var original = current.value; + current.value = current.value.replace(multiple ? /\s+$/ : /[ \t]+$/, ''); + current.leftStripped = current.value !== original; + return current.leftStripped; + } + + exports['default'] = WhitespaceControl; + module.exports = exports['default']; + +/***/ }, +/* 25 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + var _interopRequireDefault = __webpack_require__(1)['default']; + + exports.__esModule = true; + + var _exception = __webpack_require__(6); + + var _exception2 = _interopRequireDefault(_exception); + + function Visitor() { + this.parents = []; + } + + Visitor.prototype = { + constructor: Visitor, + mutating: false, + + // Visits a given value. If mutating, will replace the value if necessary. + acceptKey: function acceptKey(node, name) { + var value = this.accept(node[name]); + if (this.mutating) { + // Hacky sanity check: This may have a few false positives for type for the helper + // methods but will generally do the right thing without a lot of overhead. + if (value && !Visitor.prototype[value.type]) { + throw new _exception2['default']('Unexpected node type "' + value.type + '" found when accepting ' + name + ' on ' + node.type); + } + node[name] = value; + } + }, + + // Performs an accept operation with added sanity check to ensure + // required keys are not removed. + acceptRequired: function acceptRequired(node, name) { + this.acceptKey(node, name); + + if (!node[name]) { + throw new _exception2['default'](node.type + ' requires ' + name); + } + }, + + // Traverses a given array. If mutating, empty respnses will be removed + // for child elements. + acceptArray: function acceptArray(array) { + for (var i = 0, l = array.length; i < l; i++) { + this.acceptKey(array, i); + + if (!array[i]) { + array.splice(i, 1); + i--; + l--; + } + } + }, + + accept: function accept(object) { + if (!object) { + return; + } + + /* istanbul ignore next: Sanity code */ + if (!this[object.type]) { + throw new _exception2['default']('Unknown type: ' + object.type, object); + } + + if (this.current) { + this.parents.unshift(this.current); + } + this.current = object; + + var ret = this[object.type](object); + + this.current = this.parents.shift(); + + if (!this.mutating || ret) { + return ret; + } else if (ret !== false) { + return object; + } + }, + + Program: function Program(program) { + this.acceptArray(program.body); + }, + + MustacheStatement: visitSubExpression, + Decorator: visitSubExpression, + + BlockStatement: visitBlock, + DecoratorBlock: visitBlock, + + PartialStatement: visitPartial, + PartialBlockStatement: function PartialBlockStatement(partial) { + visitPartial.call(this, partial); + + this.acceptKey(partial, 'program'); + }, + + ContentStatement: function ContentStatement() /* content */{}, + CommentStatement: function CommentStatement() /* comment */{}, + + SubExpression: visitSubExpression, + + PathExpression: function PathExpression() /* path */{}, + + StringLiteral: function StringLiteral() /* string */{}, + NumberLiteral: function NumberLiteral() /* number */{}, + BooleanLiteral: function BooleanLiteral() /* bool */{}, + UndefinedLiteral: function UndefinedLiteral() /* literal */{}, + NullLiteral: function NullLiteral() /* literal */{}, + + Hash: function Hash(hash) { + this.acceptArray(hash.pairs); + }, + HashPair: function HashPair(pair) { + this.acceptRequired(pair, 'value'); + } + }; + + function visitSubExpression(mustache) { + this.acceptRequired(mustache, 'path'); + this.acceptArray(mustache.params); + this.acceptKey(mustache, 'hash'); + } + function visitBlock(block) { + visitSubExpression.call(this, block); + + this.acceptKey(block, 'program'); + this.acceptKey(block, 'inverse'); + } + function visitPartial(partial) { + this.acceptRequired(partial, 'name'); + this.acceptArray(partial.params); + this.acceptKey(partial, 'hash'); + } + + exports['default'] = Visitor; + module.exports = exports['default']; + +/***/ }, +/* 26 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + var _interopRequireDefault = __webpack_require__(1)['default']; + + exports.__esModule = true; + exports.SourceLocation = SourceLocation; + exports.id = id; + exports.stripFlags = stripFlags; + exports.stripComment = stripComment; + exports.preparePath = preparePath; + exports.prepareMustache = prepareMustache; + exports.prepareRawBlock = prepareRawBlock; + exports.prepareBlock = prepareBlock; + exports.prepareProgram = prepareProgram; + exports.preparePartialBlock = preparePartialBlock; + + var _exception = __webpack_require__(6); + + var _exception2 = _interopRequireDefault(_exception); + + function validateClose(open, close) { + close = close.path ? close.path.original : close; + + if (open.path.original !== close) { + var errorNode = { loc: open.path.loc }; + + throw new _exception2['default'](open.path.original + " doesn't match " + close, errorNode); + } + } + + function SourceLocation(source, locInfo) { + this.source = source; + this.start = { + line: locInfo.first_line, + column: locInfo.first_column + }; + this.end = { + line: locInfo.last_line, + column: locInfo.last_column + }; + } + + function id(token) { + if (/^\[.*\]$/.test(token)) { + return token.substr(1, token.length - 2); + } else { + return token; + } + } + + function stripFlags(open, close) { + return { + open: open.charAt(2) === '~', + close: close.charAt(close.length - 3) === '~' + }; + } + + function stripComment(comment) { + return comment.replace(/^\{\{~?\!-?-?/, '').replace(/-?-?~?\}\}$/, ''); + } + + function preparePath(data, parts, loc) { + loc = this.locInfo(loc); + + var original = data ? '@' : '', + dig = [], + depth = 0, + depthString = ''; + + for (var i = 0, l = parts.length; i < l; i++) { + var part = parts[i].part, + + // If we have [] syntax then we do not treat path references as operators, + // i.e. foo.[this] resolves to approximately context.foo['this'] + isLiteral = parts[i].original !== part; + original += (parts[i].separator || '') + part; + + if (!isLiteral && (part === '..' || part === '.' || part === 'this')) { + if (dig.length > 0) { + throw new _exception2['default']('Invalid path: ' + original, { loc: loc }); + } else if (part === '..') { + depth++; + depthString += '../'; + } + } else { + dig.push(part); + } + } + + return { + type: 'PathExpression', + data: data, + depth: depth, + parts: dig, + original: original, + loc: loc + }; + } + + function prepareMustache(path, params, hash, open, strip, locInfo) { + // Must use charAt to support IE pre-10 + var escapeFlag = open.charAt(3) || open.charAt(2), + escaped = escapeFlag !== '{' && escapeFlag !== '&'; + + var decorator = /\*/.test(open); + return { + type: decorator ? 'Decorator' : 'MustacheStatement', + path: path, + params: params, + hash: hash, + escaped: escaped, + strip: strip, + loc: this.locInfo(locInfo) + }; + } + + function prepareRawBlock(openRawBlock, contents, close, locInfo) { + validateClose(openRawBlock, close); + + locInfo = this.locInfo(locInfo); + var program = { + type: 'Program', + body: contents, + strip: {}, + loc: locInfo + }; + + return { + type: 'BlockStatement', + path: openRawBlock.path, + params: openRawBlock.params, + hash: openRawBlock.hash, + program: program, + openStrip: {}, + inverseStrip: {}, + closeStrip: {}, + loc: locInfo + }; + } + + function prepareBlock(openBlock, program, inverseAndProgram, close, inverted, locInfo) { + if (close && close.path) { + validateClose(openBlock, close); + } + + var decorator = /\*/.test(openBlock.open); + + program.blockParams = openBlock.blockParams; + + var inverse = undefined, + inverseStrip = undefined; + + if (inverseAndProgram) { + if (decorator) { + throw new _exception2['default']('Unexpected inverse block on decorator', inverseAndProgram); + } + + if (inverseAndProgram.chain) { + inverseAndProgram.program.body[0].closeStrip = close.strip; + } + + inverseStrip = inverseAndProgram.strip; + inverse = inverseAndProgram.program; + } + + if (inverted) { + inverted = inverse; + inverse = program; + program = inverted; + } + + return { + type: decorator ? 'DecoratorBlock' : 'BlockStatement', + path: openBlock.path, + params: openBlock.params, + hash: openBlock.hash, + program: program, + inverse: inverse, + openStrip: openBlock.strip, + inverseStrip: inverseStrip, + closeStrip: close && close.strip, + loc: this.locInfo(locInfo) + }; + } + + function prepareProgram(statements, loc) { + if (!loc && statements.length) { + var firstLoc = statements[0].loc, + lastLoc = statements[statements.length - 1].loc; + + /* istanbul ignore else */ + if (firstLoc && lastLoc) { + loc = { + source: firstLoc.source, + start: { + line: firstLoc.start.line, + column: firstLoc.start.column + }, + end: { + line: lastLoc.end.line, + column: lastLoc.end.column + } + }; + } + } + + return { + type: 'Program', + body: statements, + strip: {}, + loc: loc + }; + } + + function preparePartialBlock(open, program, close, locInfo) { + validateClose(open, close); + + return { + type: 'PartialBlockStatement', + name: open.path, + params: open.params, + hash: open.hash, + program: program, + openStrip: open.strip, + closeStrip: close && close.strip, + loc: this.locInfo(locInfo) + }; + } + +/***/ }, +/* 27 */ +/***/ function(module, exports, __webpack_require__) { + + /* eslint-disable new-cap */ + + 'use strict'; + + var _interopRequireDefault = __webpack_require__(1)['default']; + + exports.__esModule = true; + exports.Compiler = Compiler; + exports.precompile = precompile; + exports.compile = compile; + + var _exception = __webpack_require__(6); + + var _exception2 = _interopRequireDefault(_exception); + + var _utils = __webpack_require__(5); + + var _ast = __webpack_require__(21); + + var _ast2 = _interopRequireDefault(_ast); + + var slice = [].slice; + + function Compiler() {} + + // the foundHelper register will disambiguate helper lookup from finding a + // function in a context. This is necessary for mustache compatibility, which + // requires that context functions in blocks are evaluated by blockHelperMissing, + // and then proceed as if the resulting value was provided to blockHelperMissing. + + Compiler.prototype = { + compiler: Compiler, + + equals: function equals(other) { + var len = this.opcodes.length; + if (other.opcodes.length !== len) { + return false; + } + + for (var i = 0; i < len; i++) { + var opcode = this.opcodes[i], + otherOpcode = other.opcodes[i]; + if (opcode.opcode !== otherOpcode.opcode || !argEquals(opcode.args, otherOpcode.args)) { + return false; + } + } + + // We know that length is the same between the two arrays because they are directly tied + // to the opcode behavior above. + len = this.children.length; + for (var i = 0; i < len; i++) { + if (!this.children[i].equals(other.children[i])) { + return false; + } + } + + return true; + }, + + guid: 0, + + compile: function compile(program, options) { + this.sourceNode = []; + this.opcodes = []; + this.children = []; + this.options = options; + this.stringParams = options.stringParams; + this.trackIds = options.trackIds; + + options.blockParams = options.blockParams || []; + + // These changes will propagate to the other compiler components + var knownHelpers = options.knownHelpers; + options.knownHelpers = { + 'helperMissing': true, + 'blockHelperMissing': true, + 'each': true, + 'if': true, + 'unless': true, + 'with': true, + 'log': true, + 'lookup': true + }; + if (knownHelpers) { + for (var _name in knownHelpers) { + /* istanbul ignore else */ + if (_name in knownHelpers) { + options.knownHelpers[_name] = knownHelpers[_name]; + } + } + } + + return this.accept(program); + }, + + compileProgram: function compileProgram(program) { + var childCompiler = new this.compiler(), + // eslint-disable-line new-cap + result = childCompiler.compile(program, this.options), + guid = this.guid++; + + this.usePartial = this.usePartial || result.usePartial; + + this.children[guid] = result; + this.useDepths = this.useDepths || result.useDepths; + + return guid; + }, + + accept: function accept(node) { + /* istanbul ignore next: Sanity code */ + if (!this[node.type]) { + throw new _exception2['default']('Unknown type: ' + node.type, node); + } + + this.sourceNode.unshift(node); + var ret = this[node.type](node); + this.sourceNode.shift(); + return ret; + }, + + Program: function Program(program) { + this.options.blockParams.unshift(program.blockParams); + + var body = program.body, + bodyLength = body.length; + for (var i = 0; i < bodyLength; i++) { + this.accept(body[i]); + } + + this.options.blockParams.shift(); + + this.isSimple = bodyLength === 1; + this.blockParams = program.blockParams ? program.blockParams.length : 0; + + return this; + }, + + BlockStatement: function BlockStatement(block) { + transformLiteralToPath(block); + + var program = block.program, + inverse = block.inverse; + + program = program && this.compileProgram(program); + inverse = inverse && this.compileProgram(inverse); + + var type = this.classifySexpr(block); + + if (type === 'helper') { + this.helperSexpr(block, program, inverse); + } else if (type === 'simple') { + this.simpleSexpr(block); + + // now that the simple mustache is resolved, we need to + // evaluate it by executing `blockHelperMissing` + this.opcode('pushProgram', program); + this.opcode('pushProgram', inverse); + this.opcode('emptyHash'); + this.opcode('blockValue', block.path.original); + } else { + this.ambiguousSexpr(block, program, inverse); + + // now that the simple mustache is resolved, we need to + // evaluate it by executing `blockHelperMissing` + this.opcode('pushProgram', program); + this.opcode('pushProgram', inverse); + this.opcode('emptyHash'); + this.opcode('ambiguousBlockValue'); + } + + this.opcode('append'); + }, + + DecoratorBlock: function DecoratorBlock(decorator) { + var program = decorator.program && this.compileProgram(decorator.program); + var params = this.setupFullMustacheParams(decorator, program, undefined), + path = decorator.path; + + this.useDecorators = true; + this.opcode('registerDecorator', params.length, path.original); + }, + + PartialStatement: function PartialStatement(partial) { + this.usePartial = true; + + var program = partial.program; + if (program) { + program = this.compileProgram(partial.program); + } + + var params = partial.params; + if (params.length > 1) { + throw new _exception2['default']('Unsupported number of partial arguments: ' + params.length, partial); + } else if (!params.length) { + if (this.options.explicitPartialContext) { + this.opcode('pushLiteral', 'undefined'); + } else { + params.push({ type: 'PathExpression', parts: [], depth: 0 }); + } + } + + var partialName = partial.name.original, + isDynamic = partial.name.type === 'SubExpression'; + if (isDynamic) { + this.accept(partial.name); + } + + this.setupFullMustacheParams(partial, program, undefined, true); + + var indent = partial.indent || ''; + if (this.options.preventIndent && indent) { + this.opcode('appendContent', indent); + indent = ''; + } + + this.opcode('invokePartial', isDynamic, partialName, indent); + this.opcode('append'); + }, + PartialBlockStatement: function PartialBlockStatement(partialBlock) { + this.PartialStatement(partialBlock); + }, + + MustacheStatement: function MustacheStatement(mustache) { + this.SubExpression(mustache); + + if (mustache.escaped && !this.options.noEscape) { + this.opcode('appendEscaped'); + } else { + this.opcode('append'); + } + }, + Decorator: function Decorator(decorator) { + this.DecoratorBlock(decorator); + }, + + ContentStatement: function ContentStatement(content) { + if (content.value) { + this.opcode('appendContent', content.value); + } + }, + + CommentStatement: function CommentStatement() {}, + + SubExpression: function SubExpression(sexpr) { + transformLiteralToPath(sexpr); + var type = this.classifySexpr(sexpr); + + if (type === 'simple') { + this.simpleSexpr(sexpr); + } else if (type === 'helper') { + this.helperSexpr(sexpr); + } else { + this.ambiguousSexpr(sexpr); + } + }, + ambiguousSexpr: function ambiguousSexpr(sexpr, program, inverse) { + var path = sexpr.path, + name = path.parts[0], + isBlock = program != null || inverse != null; + + this.opcode('getContext', path.depth); + + this.opcode('pushProgram', program); + this.opcode('pushProgram', inverse); + + path.strict = true; + this.accept(path); + + this.opcode('invokeAmbiguous', name, isBlock); + }, + + simpleSexpr: function simpleSexpr(sexpr) { + var path = sexpr.path; + path.strict = true; + this.accept(path); + this.opcode('resolvePossibleLambda'); + }, + + helperSexpr: function helperSexpr(sexpr, program, inverse) { + var params = this.setupFullMustacheParams(sexpr, program, inverse), + path = sexpr.path, + name = path.parts[0]; + + if (this.options.knownHelpers[name]) { + this.opcode('invokeKnownHelper', params.length, name); + } else if (this.options.knownHelpersOnly) { + throw new _exception2['default']('You specified knownHelpersOnly, but used the unknown helper ' + name, sexpr); + } else { + path.strict = true; + path.falsy = true; + + this.accept(path); + this.opcode('invokeHelper', params.length, path.original, _ast2['default'].helpers.simpleId(path)); + } + }, + + PathExpression: function PathExpression(path) { + this.addDepth(path.depth); + this.opcode('getContext', path.depth); + + var name = path.parts[0], + scoped = _ast2['default'].helpers.scopedId(path), + blockParamId = !path.depth && !scoped && this.blockParamIndex(name); + + if (blockParamId) { + this.opcode('lookupBlockParam', blockParamId, path.parts); + } else if (!name) { + // Context reference, i.e. `{{foo .}}` or `{{foo ..}}` + this.opcode('pushContext'); + } else if (path.data) { + this.options.data = true; + this.opcode('lookupData', path.depth, path.parts, path.strict); + } else { + this.opcode('lookupOnContext', path.parts, path.falsy, path.strict, scoped); + } + }, + + StringLiteral: function StringLiteral(string) { + this.opcode('pushString', string.value); + }, + + NumberLiteral: function NumberLiteral(number) { + this.opcode('pushLiteral', number.value); + }, + + BooleanLiteral: function BooleanLiteral(bool) { + this.opcode('pushLiteral', bool.value); + }, + + UndefinedLiteral: function UndefinedLiteral() { + this.opcode('pushLiteral', 'undefined'); + }, + + NullLiteral: function NullLiteral() { + this.opcode('pushLiteral', 'null'); + }, + + Hash: function Hash(hash) { + var pairs = hash.pairs, + i = 0, + l = pairs.length; + + this.opcode('pushHash'); + + for (; i < l; i++) { + this.pushParam(pairs[i].value); + } + while (i--) { + this.opcode('assignToHash', pairs[i].key); + } + this.opcode('popHash'); + }, + + // HELPERS + opcode: function opcode(name) { + this.opcodes.push({ opcode: name, args: slice.call(arguments, 1), loc: this.sourceNode[0].loc }); + }, + + addDepth: function addDepth(depth) { + if (!depth) { + return; + } + + this.useDepths = true; + }, + + classifySexpr: function classifySexpr(sexpr) { + var isSimple = _ast2['default'].helpers.simpleId(sexpr.path); + + var isBlockParam = isSimple && !!this.blockParamIndex(sexpr.path.parts[0]); + + // a mustache is an eligible helper if: + // * its id is simple (a single part, not `this` or `..`) + var isHelper = !isBlockParam && _ast2['default'].helpers.helperExpression(sexpr); + + // if a mustache is an eligible helper but not a definite + // helper, it is ambiguous, and will be resolved in a later + // pass or at runtime. + var isEligible = !isBlockParam && (isHelper || isSimple); + + // if ambiguous, we can possibly resolve the ambiguity now + // An eligible helper is one that does not have a complex path, i.e. `this.foo`, `../foo` etc. + if (isEligible && !isHelper) { + var _name2 = sexpr.path.parts[0], + options = this.options; + + if (options.knownHelpers[_name2]) { + isHelper = true; + } else if (options.knownHelpersOnly) { + isEligible = false; + } + } + + if (isHelper) { + return 'helper'; + } else if (isEligible) { + return 'ambiguous'; + } else { + return 'simple'; + } + }, + + pushParams: function pushParams(params) { + for (var i = 0, l = params.length; i < l; i++) { + this.pushParam(params[i]); + } + }, + + pushParam: function pushParam(val) { + var value = val.value != null ? val.value : val.original || ''; + + if (this.stringParams) { + if (value.replace) { + value = value.replace(/^(\.?\.\/)*/g, '').replace(/\//g, '.'); + } + + if (val.depth) { + this.addDepth(val.depth); + } + this.opcode('getContext', val.depth || 0); + this.opcode('pushStringParam', value, val.type); + + if (val.type === 'SubExpression') { + // SubExpressions get evaluated and passed in + // in string params mode. + this.accept(val); + } + } else { + if (this.trackIds) { + var blockParamIndex = undefined; + if (val.parts && !_ast2['default'].helpers.scopedId(val) && !val.depth) { + blockParamIndex = this.blockParamIndex(val.parts[0]); + } + if (blockParamIndex) { + var blockParamChild = val.parts.slice(1).join('.'); + this.opcode('pushId', 'BlockParam', blockParamIndex, blockParamChild); + } else { + value = val.original || value; + if (value.replace) { + value = value.replace(/^this(?:\.|$)/, '').replace(/^\.\//, '').replace(/^\.$/, ''); + } + + this.opcode('pushId', val.type, value); + } + } + this.accept(val); + } + }, + + setupFullMustacheParams: function setupFullMustacheParams(sexpr, program, inverse, omitEmpty) { + var params = sexpr.params; + this.pushParams(params); + + this.opcode('pushProgram', program); + this.opcode('pushProgram', inverse); + + if (sexpr.hash) { + this.accept(sexpr.hash); + } else { + this.opcode('emptyHash', omitEmpty); + } + + return params; + }, + + blockParamIndex: function blockParamIndex(name) { + for (var depth = 0, len = this.options.blockParams.length; depth < len; depth++) { + var blockParams = this.options.blockParams[depth], + param = blockParams && _utils.indexOf(blockParams, name); + if (blockParams && param >= 0) { + return [depth, param]; + } + } + } + }; + + function precompile(input, options, env) { + if (input == null || typeof input !== 'string' && input.type !== 'Program') { + throw new _exception2['default']('You must pass a string or Handlebars AST to Handlebars.precompile. You passed ' + input); + } + + options = options || {}; + if (!('data' in options)) { + options.data = true; + } + if (options.compat) { + options.useDepths = true; + } + + var ast = env.parse(input, options), + environment = new env.Compiler().compile(ast, options); + return new env.JavaScriptCompiler().compile(environment, options); + } + + function compile(input, options, env) { + if (options === undefined) options = {}; + + if (input == null || typeof input !== 'string' && input.type !== 'Program') { + throw new _exception2['default']('You must pass a string or Handlebars AST to Handlebars.compile. You passed ' + input); + } + + if (!('data' in options)) { + options.data = true; + } + if (options.compat) { + options.useDepths = true; + } + + var compiled = undefined; + + function compileInput() { + var ast = env.parse(input, options), + environment = new env.Compiler().compile(ast, options), + templateSpec = new env.JavaScriptCompiler().compile(environment, options, undefined, true); + return env.template(templateSpec); + } + + // Template is only compiled on first use and cached after that point. + function ret(context, execOptions) { + if (!compiled) { + compiled = compileInput(); + } + return compiled.call(this, context, execOptions); + } + ret._setup = function (setupOptions) { + if (!compiled) { + compiled = compileInput(); + } + return compiled._setup(setupOptions); + }; + ret._child = function (i, data, blockParams, depths) { + if (!compiled) { + compiled = compileInput(); + } + return compiled._child(i, data, blockParams, depths); + }; + return ret; + } + + function argEquals(a, b) { + if (a === b) { + return true; + } + + if (_utils.isArray(a) && _utils.isArray(b) && a.length === b.length) { + for (var i = 0; i < a.length; i++) { + if (!argEquals(a[i], b[i])) { + return false; + } + } + return true; + } + } + + function transformLiteralToPath(sexpr) { + if (!sexpr.path.parts) { + var literal = sexpr.path; + // Casting to string here to make false and 0 literal values play nicely with the rest + // of the system. + sexpr.path = { + type: 'PathExpression', + data: false, + depth: 0, + parts: [literal.original + ''], + original: literal.original + '', + loc: literal.loc + }; + } + } + +/***/ }, +/* 28 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + var _interopRequireDefault = __webpack_require__(1)['default']; + + exports.__esModule = true; + + var _base = __webpack_require__(4); + + var _exception = __webpack_require__(6); + + var _exception2 = _interopRequireDefault(_exception); + + var _utils = __webpack_require__(5); + + var _codeGen = __webpack_require__(29); + + var _codeGen2 = _interopRequireDefault(_codeGen); + + function Literal(value) { + this.value = value; + } + + function JavaScriptCompiler() {} + + JavaScriptCompiler.prototype = { + // PUBLIC API: You can override these methods in a subclass to provide + // alternative compiled forms for name lookup and buffering semantics + nameLookup: function nameLookup(parent, name /* , type*/) { + if (JavaScriptCompiler.isValidJavaScriptVariableName(name)) { + return [parent, '.', name]; + } else { + return [parent, '[', JSON.stringify(name), ']']; + } + }, + depthedLookup: function depthedLookup(name) { + return [this.aliasable('container.lookup'), '(depths, "', name, '")']; + }, + + compilerInfo: function compilerInfo() { + var revision = _base.COMPILER_REVISION, + versions = _base.REVISION_CHANGES[revision]; + return [revision, versions]; + }, + + appendToBuffer: function appendToBuffer(source, location, explicit) { + // Force a source as this simplifies the merge logic. + if (!_utils.isArray(source)) { + source = [source]; + } + source = this.source.wrap(source, location); + + if (this.environment.isSimple) { + return ['return ', source, ';']; + } else if (explicit) { + // This is a case where the buffer operation occurs as a child of another + // construct, generally braces. We have to explicitly output these buffer + // operations to ensure that the emitted code goes in the correct location. + return ['buffer += ', source, ';']; + } else { + source.appendToBuffer = true; + return source; + } + }, + + initializeBuffer: function initializeBuffer() { + return this.quotedString(''); + }, + // END PUBLIC API + + compile: function compile(environment, options, context, asObject) { + this.environment = environment; + this.options = options; + this.stringParams = this.options.stringParams; + this.trackIds = this.options.trackIds; + this.precompile = !asObject; + + this.name = this.environment.name; + this.isChild = !!context; + this.context = context || { + decorators: [], + programs: [], + environments: [] + }; + + this.preamble(); + + this.stackSlot = 0; + this.stackVars = []; + this.aliases = {}; + this.registers = { list: [] }; + this.hashes = []; + this.compileStack = []; + this.inlineStack = []; + this.blockParams = []; + + this.compileChildren(environment, options); + + this.useDepths = this.useDepths || environment.useDepths || environment.useDecorators || this.options.compat; + this.useBlockParams = this.useBlockParams || environment.useBlockParams; + + var opcodes = environment.opcodes, + opcode = undefined, + firstLoc = undefined, + i = undefined, + l = undefined; + + for (i = 0, l = opcodes.length; i < l; i++) { + opcode = opcodes[i]; + + this.source.currentLocation = opcode.loc; + firstLoc = firstLoc || opcode.loc; + this[opcode.opcode].apply(this, opcode.args); + } + + // Flush any trailing content that might be pending. + this.source.currentLocation = firstLoc; + this.pushSource(''); + + /* istanbul ignore next */ + if (this.stackSlot || this.inlineStack.length || this.compileStack.length) { + throw new _exception2['default']('Compile completed with content left on stack'); + } + + if (!this.decorators.isEmpty()) { + this.useDecorators = true; + + this.decorators.prepend('var decorators = container.decorators;\n'); + this.decorators.push('return fn;'); + + if (asObject) { + this.decorators = Function.apply(this, ['fn', 'props', 'container', 'depth0', 'data', 'blockParams', 'depths', this.decorators.merge()]); + } else { + this.decorators.prepend('function(fn, props, container, depth0, data, blockParams, depths) {\n'); + this.decorators.push('}\n'); + this.decorators = this.decorators.merge(); + } + } else { + this.decorators = undefined; + } + + var fn = this.createFunctionContext(asObject); + if (!this.isChild) { + var ret = { + compiler: this.compilerInfo(), + main: fn + }; + + if (this.decorators) { + ret.main_d = this.decorators; // eslint-disable-line camelcase + ret.useDecorators = true; + } + + var _context = this.context; + var programs = _context.programs; + var decorators = _context.decorators; + + for (i = 0, l = programs.length; i < l; i++) { + if (programs[i]) { + ret[i] = programs[i]; + if (decorators[i]) { + ret[i + '_d'] = decorators[i]; + ret.useDecorators = true; + } + } + } + + if (this.environment.usePartial) { + ret.usePartial = true; + } + if (this.options.data) { + ret.useData = true; + } + if (this.useDepths) { + ret.useDepths = true; + } + if (this.useBlockParams) { + ret.useBlockParams = true; + } + if (this.options.compat) { + ret.compat = true; + } + + if (!asObject) { + ret.compiler = JSON.stringify(ret.compiler); + + this.source.currentLocation = { start: { line: 1, column: 0 } }; + ret = this.objectLiteral(ret); + + if (options.srcName) { + ret = ret.toStringWithSourceMap({ file: options.destName }); + ret.map = ret.map && ret.map.toString(); + } else { + ret = ret.toString(); + } + } else { + ret.compilerOptions = this.options; + } + + return ret; + } else { + return fn; + } + }, + + preamble: function preamble() { + // track the last context pushed into place to allow skipping the + // getContext opcode when it would be a noop + this.lastContext = 0; + this.source = new _codeGen2['default'](this.options.srcName); + this.decorators = new _codeGen2['default'](this.options.srcName); + }, + + createFunctionContext: function createFunctionContext(asObject) { + var varDeclarations = ''; + + var locals = this.stackVars.concat(this.registers.list); + if (locals.length > 0) { + varDeclarations += ', ' + locals.join(', '); + } + + // Generate minimizer alias mappings + // + // When using true SourceNodes, this will update all references to the given alias + // as the source nodes are reused in situ. For the non-source node compilation mode, + // aliases will not be used, but this case is already being run on the client and + // we aren't concern about minimizing the template size. + var aliasCount = 0; + for (var alias in this.aliases) { + // eslint-disable-line guard-for-in + var node = this.aliases[alias]; + + if (this.aliases.hasOwnProperty(alias) && node.children && node.referenceCount > 1) { + varDeclarations += ', alias' + ++aliasCount + '=' + alias; + node.children[0] = 'alias' + aliasCount; + } + } + + var params = ['container', 'depth0', 'helpers', 'partials', 'data']; + + if (this.useBlockParams || this.useDepths) { + params.push('blockParams'); + } + if (this.useDepths) { + params.push('depths'); + } + + // Perform a second pass over the output to merge content when possible + var source = this.mergeSource(varDeclarations); + + if (asObject) { + params.push(source); + + return Function.apply(this, params); + } else { + return this.source.wrap(['function(', params.join(','), ') {\n ', source, '}']); + } + }, + mergeSource: function mergeSource(varDeclarations) { + var isSimple = this.environment.isSimple, + appendOnly = !this.forceBuffer, + appendFirst = undefined, + sourceSeen = undefined, + bufferStart = undefined, + bufferEnd = undefined; + this.source.each(function (line) { + if (line.appendToBuffer) { + if (bufferStart) { + line.prepend(' + '); + } else { + bufferStart = line; + } + bufferEnd = line; + } else { + if (bufferStart) { + if (!sourceSeen) { + appendFirst = true; + } else { + bufferStart.prepend('buffer += '); + } + bufferEnd.add(';'); + bufferStart = bufferEnd = undefined; + } + + sourceSeen = true; + if (!isSimple) { + appendOnly = false; + } + } + }); + + if (appendOnly) { + if (bufferStart) { + bufferStart.prepend('return '); + bufferEnd.add(';'); + } else if (!sourceSeen) { + this.source.push('return "";'); + } + } else { + varDeclarations += ', buffer = ' + (appendFirst ? '' : this.initializeBuffer()); + + if (bufferStart) { + bufferStart.prepend('return buffer + '); + bufferEnd.add(';'); + } else { + this.source.push('return buffer;'); + } + } + + if (varDeclarations) { + this.source.prepend('var ' + varDeclarations.substring(2) + (appendFirst ? '' : ';\n')); + } + + return this.source.merge(); + }, + + // [blockValue] + // + // On stack, before: hash, inverse, program, value + // On stack, after: return value of blockHelperMissing + // + // The purpose of this opcode is to take a block of the form + // `{{#this.foo}}...{{/this.foo}}`, resolve the value of `foo`, and + // replace it on the stack with the result of properly + // invoking blockHelperMissing. + blockValue: function blockValue(name) { + var blockHelperMissing = this.aliasable('helpers.blockHelperMissing'), + params = [this.contextName(0)]; + this.setupHelperArgs(name, 0, params); + + var blockName = this.popStack(); + params.splice(1, 0, blockName); + + this.push(this.source.functionCall(blockHelperMissing, 'call', params)); + }, + + // [ambiguousBlockValue] + // + // On stack, before: hash, inverse, program, value + // Compiler value, before: lastHelper=value of last found helper, if any + // On stack, after, if no lastHelper: same as [blockValue] + // On stack, after, if lastHelper: value + ambiguousBlockValue: function ambiguousBlockValue() { + // We're being a bit cheeky and reusing the options value from the prior exec + var blockHelperMissing = this.aliasable('helpers.blockHelperMissing'), + params = [this.contextName(0)]; + this.setupHelperArgs('', 0, params, true); + + this.flushInline(); + + var current = this.topStack(); + params.splice(1, 0, current); + + this.pushSource(['if (!', this.lastHelper, ') { ', current, ' = ', this.source.functionCall(blockHelperMissing, 'call', params), '}']); + }, + + // [appendContent] + // + // On stack, before: ... + // On stack, after: ... + // + // Appends the string value of `content` to the current buffer + appendContent: function appendContent(content) { + if (this.pendingContent) { + content = this.pendingContent + content; + } else { + this.pendingLocation = this.source.currentLocation; + } + + this.pendingContent = content; + }, + + // [append] + // + // On stack, before: value, ... + // On stack, after: ... + // + // Coerces `value` to a String and appends it to the current buffer. + // + // If `value` is truthy, or 0, it is coerced into a string and appended + // Otherwise, the empty string is appended + append: function append() { + if (this.isInline()) { + this.replaceStack(function (current) { + return [' != null ? ', current, ' : ""']; + }); + + this.pushSource(this.appendToBuffer(this.popStack())); + } else { + var local = this.popStack(); + this.pushSource(['if (', local, ' != null) { ', this.appendToBuffer(local, undefined, true), ' }']); + if (this.environment.isSimple) { + this.pushSource(['else { ', this.appendToBuffer("''", undefined, true), ' }']); + } + } + }, + + // [appendEscaped] + // + // On stack, before: value, ... + // On stack, after: ... + // + // Escape `value` and append it to the buffer + appendEscaped: function appendEscaped() { + this.pushSource(this.appendToBuffer([this.aliasable('container.escapeExpression'), '(', this.popStack(), ')'])); + }, + + // [getContext] + // + // On stack, before: ... + // On stack, after: ... + // Compiler value, after: lastContext=depth + // + // Set the value of the `lastContext` compiler value to the depth + getContext: function getContext(depth) { + this.lastContext = depth; + }, + + // [pushContext] + // + // On stack, before: ... + // On stack, after: currentContext, ... + // + // Pushes the value of the current context onto the stack. + pushContext: function pushContext() { + this.pushStackLiteral(this.contextName(this.lastContext)); + }, + + // [lookupOnContext] + // + // On stack, before: ... + // On stack, after: currentContext[name], ... + // + // Looks up the value of `name` on the current context and pushes + // it onto the stack. + lookupOnContext: function lookupOnContext(parts, falsy, strict, scoped) { + var i = 0; + + if (!scoped && this.options.compat && !this.lastContext) { + // The depthed query is expected to handle the undefined logic for the root level that + // is implemented below, so we evaluate that directly in compat mode + this.push(this.depthedLookup(parts[i++])); + } else { + this.pushContext(); + } + + this.resolvePath('context', parts, i, falsy, strict); + }, + + // [lookupBlockParam] + // + // On stack, before: ... + // On stack, after: blockParam[name], ... + // + // Looks up the value of `parts` on the given block param and pushes + // it onto the stack. + lookupBlockParam: function lookupBlockParam(blockParamId, parts) { + this.useBlockParams = true; + + this.push(['blockParams[', blockParamId[0], '][', blockParamId[1], ']']); + this.resolvePath('context', parts, 1); + }, + + // [lookupData] + // + // On stack, before: ... + // On stack, after: data, ... + // + // Push the data lookup operator + lookupData: function lookupData(depth, parts, strict) { + if (!depth) { + this.pushStackLiteral('data'); + } else { + this.pushStackLiteral('container.data(data, ' + depth + ')'); + } + + this.resolvePath('data', parts, 0, true, strict); + }, + + resolvePath: function resolvePath(type, parts, i, falsy, strict) { + // istanbul ignore next + + var _this = this; + + if (this.options.strict || this.options.assumeObjects) { + this.push(strictLookup(this.options.strict && strict, this, parts, type)); + return; + } + + var len = parts.length; + for (; i < len; i++) { + /* eslint-disable no-loop-func */ + this.replaceStack(function (current) { + var lookup = _this.nameLookup(current, parts[i], type); + // We want to ensure that zero and false are handled properly if the context (falsy flag) + // needs to have the special handling for these values. + if (!falsy) { + return [' != null ? ', lookup, ' : ', current]; + } else { + // Otherwise we can use generic falsy handling + return [' && ', lookup]; + } + }); + /* eslint-enable no-loop-func */ + } + }, + + // [resolvePossibleLambda] + // + // On stack, before: value, ... + // On stack, after: resolved value, ... + // + // If the `value` is a lambda, replace it on the stack by + // the return value of the lambda + resolvePossibleLambda: function resolvePossibleLambda() { + this.push([this.aliasable('container.lambda'), '(', this.popStack(), ', ', this.contextName(0), ')']); + }, + + // [pushStringParam] + // + // On stack, before: ... + // On stack, after: string, currentContext, ... + // + // This opcode is designed for use in string mode, which + // provides the string value of a parameter along with its + // depth rather than resolving it immediately. + pushStringParam: function pushStringParam(string, type) { + this.pushContext(); + this.pushString(type); + + // If it's a subexpression, the string result + // will be pushed after this opcode. + if (type !== 'SubExpression') { + if (typeof string === 'string') { + this.pushString(string); + } else { + this.pushStackLiteral(string); + } + } + }, + + emptyHash: function emptyHash(omitEmpty) { + if (this.trackIds) { + this.push('{}'); // hashIds + } + if (this.stringParams) { + this.push('{}'); // hashContexts + this.push('{}'); // hashTypes + } + this.pushStackLiteral(omitEmpty ? 'undefined' : '{}'); + }, + pushHash: function pushHash() { + if (this.hash) { + this.hashes.push(this.hash); + } + this.hash = { values: [], types: [], contexts: [], ids: [] }; + }, + popHash: function popHash() { + var hash = this.hash; + this.hash = this.hashes.pop(); + + if (this.trackIds) { + this.push(this.objectLiteral(hash.ids)); + } + if (this.stringParams) { + this.push(this.objectLiteral(hash.contexts)); + this.push(this.objectLiteral(hash.types)); + } + + this.push(this.objectLiteral(hash.values)); + }, + + // [pushString] + // + // On stack, before: ... + // On stack, after: quotedString(string), ... + // + // Push a quoted version of `string` onto the stack + pushString: function pushString(string) { + this.pushStackLiteral(this.quotedString(string)); + }, + + // [pushLiteral] + // + // On stack, before: ... + // On stack, after: value, ... + // + // Pushes a value onto the stack. This operation prevents + // the compiler from creating a temporary variable to hold + // it. + pushLiteral: function pushLiteral(value) { + this.pushStackLiteral(value); + }, + + // [pushProgram] + // + // On stack, before: ... + // On stack, after: program(guid), ... + // + // Push a program expression onto the stack. This takes + // a compile-time guid and converts it into a runtime-accessible + // expression. + pushProgram: function pushProgram(guid) { + if (guid != null) { + this.pushStackLiteral(this.programExpression(guid)); + } else { + this.pushStackLiteral(null); + } + }, + + // [registerDecorator] + // + // On stack, before: hash, program, params..., ... + // On stack, after: ... + // + // Pops off the decorator's parameters, invokes the decorator, + // and inserts the decorator into the decorators list. + registerDecorator: function registerDecorator(paramSize, name) { + var foundDecorator = this.nameLookup('decorators', name, 'decorator'), + options = this.setupHelperArgs(name, paramSize); + + this.decorators.push(['fn = ', this.decorators.functionCall(foundDecorator, '', ['fn', 'props', 'container', options]), ' || fn;']); + }, + + // [invokeHelper] + // + // On stack, before: hash, inverse, program, params..., ... + // On stack, after: result of helper invocation + // + // Pops off the helper's parameters, invokes the helper, + // and pushes the helper's return value onto the stack. + // + // If the helper is not found, `helperMissing` is called. + invokeHelper: function invokeHelper(paramSize, name, isSimple) { + var nonHelper = this.popStack(), + helper = this.setupHelper(paramSize, name), + simple = isSimple ? [helper.name, ' || '] : ''; + + var lookup = ['('].concat(simple, nonHelper); + if (!this.options.strict) { + lookup.push(' || ', this.aliasable('helpers.helperMissing')); + } + lookup.push(')'); + + this.push(this.source.functionCall(lookup, 'call', helper.callParams)); + }, + + // [invokeKnownHelper] + // + // On stack, before: hash, inverse, program, params..., ... + // On stack, after: result of helper invocation + // + // This operation is used when the helper is known to exist, + // so a `helperMissing` fallback is not required. + invokeKnownHelper: function invokeKnownHelper(paramSize, name) { + var helper = this.setupHelper(paramSize, name); + this.push(this.source.functionCall(helper.name, 'call', helper.callParams)); + }, + + // [invokeAmbiguous] + // + // On stack, before: hash, inverse, program, params..., ... + // On stack, after: result of disambiguation + // + // This operation is used when an expression like `{{foo}}` + // is provided, but we don't know at compile-time whether it + // is a helper or a path. + // + // This operation emits more code than the other options, + // and can be avoided by passing the `knownHelpers` and + // `knownHelpersOnly` flags at compile-time. + invokeAmbiguous: function invokeAmbiguous(name, helperCall) { + this.useRegister('helper'); + + var nonHelper = this.popStack(); + + this.emptyHash(); + var helper = this.setupHelper(0, name, helperCall); + + var helperName = this.lastHelper = this.nameLookup('helpers', name, 'helper'); + + var lookup = ['(', '(helper = ', helperName, ' || ', nonHelper, ')']; + if (!this.options.strict) { + lookup[0] = '(helper = '; + lookup.push(' != null ? helper : ', this.aliasable('helpers.helperMissing')); + } + + this.push(['(', lookup, helper.paramsInit ? ['),(', helper.paramsInit] : [], '),', '(typeof helper === ', this.aliasable('"function"'), ' ? ', this.source.functionCall('helper', 'call', helper.callParams), ' : helper))']); + }, + + // [invokePartial] + // + // On stack, before: context, ... + // On stack after: result of partial invocation + // + // This operation pops off a context, invokes a partial with that context, + // and pushes the result of the invocation back. + invokePartial: function invokePartial(isDynamic, name, indent) { + var params = [], + options = this.setupParams(name, 1, params); + + if (isDynamic) { + name = this.popStack(); + delete options.name; + } + + if (indent) { + options.indent = JSON.stringify(indent); + } + options.helpers = 'helpers'; + options.partials = 'partials'; + options.decorators = 'container.decorators'; + + if (!isDynamic) { + params.unshift(this.nameLookup('partials', name, 'partial')); + } else { + params.unshift(name); + } + + if (this.options.compat) { + options.depths = 'depths'; + } + options = this.objectLiteral(options); + params.push(options); + + this.push(this.source.functionCall('container.invokePartial', '', params)); + }, + + // [assignToHash] + // + // On stack, before: value, ..., hash, ... + // On stack, after: ..., hash, ... + // + // Pops a value off the stack and assigns it to the current hash + assignToHash: function assignToHash(key) { + var value = this.popStack(), + context = undefined, + type = undefined, + id = undefined; + + if (this.trackIds) { + id = this.popStack(); + } + if (this.stringParams) { + type = this.popStack(); + context = this.popStack(); + } + + var hash = this.hash; + if (context) { + hash.contexts[key] = context; + } + if (type) { + hash.types[key] = type; + } + if (id) { + hash.ids[key] = id; + } + hash.values[key] = value; + }, + + pushId: function pushId(type, name, child) { + if (type === 'BlockParam') { + this.pushStackLiteral('blockParams[' + name[0] + '].path[' + name[1] + ']' + (child ? ' + ' + JSON.stringify('.' + child) : '')); + } else if (type === 'PathExpression') { + this.pushString(name); + } else if (type === 'SubExpression') { + this.pushStackLiteral('true'); + } else { + this.pushStackLiteral('null'); + } + }, + + // HELPERS + + compiler: JavaScriptCompiler, + + compileChildren: function compileChildren(environment, options) { + var children = environment.children, + child = undefined, + compiler = undefined; + + for (var i = 0, l = children.length; i < l; i++) { + child = children[i]; + compiler = new this.compiler(); // eslint-disable-line new-cap + + var index = this.matchExistingProgram(child); + + if (index == null) { + this.context.programs.push(''); // Placeholder to prevent name conflicts for nested children + index = this.context.programs.length; + child.index = index; + child.name = 'program' + index; + this.context.programs[index] = compiler.compile(child, options, this.context, !this.precompile); + this.context.decorators[index] = compiler.decorators; + this.context.environments[index] = child; + + this.useDepths = this.useDepths || compiler.useDepths; + this.useBlockParams = this.useBlockParams || compiler.useBlockParams; + } else { + child.index = index; + child.name = 'program' + index; + + this.useDepths = this.useDepths || child.useDepths; + this.useBlockParams = this.useBlockParams || child.useBlockParams; + } + } + }, + matchExistingProgram: function matchExistingProgram(child) { + for (var i = 0, len = this.context.environments.length; i < len; i++) { + var environment = this.context.environments[i]; + if (environment && environment.equals(child)) { + return i; + } + } + }, + + programExpression: function programExpression(guid) { + var child = this.environment.children[guid], + programParams = [child.index, 'data', child.blockParams]; + + if (this.useBlockParams || this.useDepths) { + programParams.push('blockParams'); + } + if (this.useDepths) { + programParams.push('depths'); + } + + return 'container.program(' + programParams.join(', ') + ')'; + }, + + useRegister: function useRegister(name) { + if (!this.registers[name]) { + this.registers[name] = true; + this.registers.list.push(name); + } + }, + + push: function push(expr) { + if (!(expr instanceof Literal)) { + expr = this.source.wrap(expr); + } + + this.inlineStack.push(expr); + return expr; + }, + + pushStackLiteral: function pushStackLiteral(item) { + this.push(new Literal(item)); + }, + + pushSource: function pushSource(source) { + if (this.pendingContent) { + this.source.push(this.appendToBuffer(this.source.quotedString(this.pendingContent), this.pendingLocation)); + this.pendingContent = undefined; + } + + if (source) { + this.source.push(source); + } + }, + + replaceStack: function replaceStack(callback) { + var prefix = ['('], + stack = undefined, + createdStack = undefined, + usedLiteral = undefined; + + /* istanbul ignore next */ + if (!this.isInline()) { + throw new _exception2['default']('replaceStack on non-inline'); + } + + // We want to merge the inline statement into the replacement statement via ',' + var top = this.popStack(true); + + if (top instanceof Literal) { + // Literals do not need to be inlined + stack = [top.value]; + prefix = ['(', stack]; + usedLiteral = true; + } else { + // Get or create the current stack name for use by the inline + createdStack = true; + var _name = this.incrStack(); + + prefix = ['((', this.push(_name), ' = ', top, ')']; + stack = this.topStack(); + } + + var item = callback.call(this, stack); + + if (!usedLiteral) { + this.popStack(); + } + if (createdStack) { + this.stackSlot--; + } + this.push(prefix.concat(item, ')')); + }, + + incrStack: function incrStack() { + this.stackSlot++; + if (this.stackSlot > this.stackVars.length) { + this.stackVars.push('stack' + this.stackSlot); + } + return this.topStackName(); + }, + topStackName: function topStackName() { + return 'stack' + this.stackSlot; + }, + flushInline: function flushInline() { + var inlineStack = this.inlineStack; + this.inlineStack = []; + for (var i = 0, len = inlineStack.length; i < len; i++) { + var entry = inlineStack[i]; + /* istanbul ignore if */ + if (entry instanceof Literal) { + this.compileStack.push(entry); + } else { + var stack = this.incrStack(); + this.pushSource([stack, ' = ', entry, ';']); + this.compileStack.push(stack); + } + } + }, + isInline: function isInline() { + return this.inlineStack.length; + }, + + popStack: function popStack(wrapped) { + var inline = this.isInline(), + item = (inline ? this.inlineStack : this.compileStack).pop(); + + if (!wrapped && item instanceof Literal) { + return item.value; + } else { + if (!inline) { + /* istanbul ignore next */ + if (!this.stackSlot) { + throw new _exception2['default']('Invalid stack pop'); + } + this.stackSlot--; + } + return item; + } + }, + + topStack: function topStack() { + var stack = this.isInline() ? this.inlineStack : this.compileStack, + item = stack[stack.length - 1]; + + /* istanbul ignore if */ + if (item instanceof Literal) { + return item.value; + } else { + return item; + } + }, + + contextName: function contextName(context) { + if (this.useDepths && context) { + return 'depths[' + context + ']'; + } else { + return 'depth' + context; + } + }, + + quotedString: function quotedString(str) { + return this.source.quotedString(str); + }, + + objectLiteral: function objectLiteral(obj) { + return this.source.objectLiteral(obj); + }, + + aliasable: function aliasable(name) { + var ret = this.aliases[name]; + if (ret) { + ret.referenceCount++; + return ret; + } + + ret = this.aliases[name] = this.source.wrap(name); + ret.aliasable = true; + ret.referenceCount = 1; + + return ret; + }, + + setupHelper: function setupHelper(paramSize, name, blockHelper) { + var params = [], + paramsInit = this.setupHelperArgs(name, paramSize, params, blockHelper); + var foundHelper = this.nameLookup('helpers', name, 'helper'), + callContext = this.aliasable(this.contextName(0) + ' != null ? ' + this.contextName(0) + ' : {}'); + + return { + params: params, + paramsInit: paramsInit, + name: foundHelper, + callParams: [callContext].concat(params) + }; + }, + + setupParams: function setupParams(helper, paramSize, params) { + var options = {}, + contexts = [], + types = [], + ids = [], + objectArgs = !params, + param = undefined; + + if (objectArgs) { + params = []; + } + + options.name = this.quotedString(helper); + options.hash = this.popStack(); + + if (this.trackIds) { + options.hashIds = this.popStack(); + } + if (this.stringParams) { + options.hashTypes = this.popStack(); + options.hashContexts = this.popStack(); + } + + var inverse = this.popStack(), + program = this.popStack(); + + // Avoid setting fn and inverse if neither are set. This allows + // helpers to do a check for `if (options.fn)` + if (program || inverse) { + options.fn = program || 'container.noop'; + options.inverse = inverse || 'container.noop'; + } + + // The parameters go on to the stack in order (making sure that they are evaluated in order) + // so we need to pop them off the stack in reverse order + var i = paramSize; + while (i--) { + param = this.popStack(); + params[i] = param; + + if (this.trackIds) { + ids[i] = this.popStack(); + } + if (this.stringParams) { + types[i] = this.popStack(); + contexts[i] = this.popStack(); + } + } + + if (objectArgs) { + options.args = this.source.generateArray(params); + } + + if (this.trackIds) { + options.ids = this.source.generateArray(ids); + } + if (this.stringParams) { + options.types = this.source.generateArray(types); + options.contexts = this.source.generateArray(contexts); + } + + if (this.options.data) { + options.data = 'data'; + } + if (this.useBlockParams) { + options.blockParams = 'blockParams'; + } + return options; + }, + + setupHelperArgs: function setupHelperArgs(helper, paramSize, params, useRegister) { + var options = this.setupParams(helper, paramSize, params); + options = this.objectLiteral(options); + if (useRegister) { + this.useRegister('options'); + params.push('options'); + return ['options=', options]; + } else if (params) { + params.push(options); + return ''; + } else { + return options; + } + } + }; + + (function () { + var reservedWords = ('break else new var' + ' case finally return void' + ' catch for switch while' + ' continue function this with' + ' default if throw' + ' delete in try' + ' do instanceof typeof' + ' abstract enum int short' + ' boolean export interface static' + ' byte extends long super' + ' char final native synchronized' + ' class float package throws' + ' const goto private transient' + ' debugger implements protected volatile' + ' double import public let yield await' + ' null true false').split(' '); + + var compilerWords = JavaScriptCompiler.RESERVED_WORDS = {}; + + for (var i = 0, l = reservedWords.length; i < l; i++) { + compilerWords[reservedWords[i]] = true; + } + })(); + + JavaScriptCompiler.isValidJavaScriptVariableName = function (name) { + return !JavaScriptCompiler.RESERVED_WORDS[name] && /^[a-zA-Z_$][0-9a-zA-Z_$]*$/.test(name); + }; + + function strictLookup(requireTerminal, compiler, parts, type) { + var stack = compiler.popStack(), + i = 0, + len = parts.length; + if (requireTerminal) { + len--; + } + + for (; i < len; i++) { + stack = compiler.nameLookup(stack, parts[i], type); + } + + if (requireTerminal) { + return [compiler.aliasable('container.strict'), '(', stack, ', ', compiler.quotedString(parts[i]), ')']; + } else { + return stack; + } + } + + exports['default'] = JavaScriptCompiler; + module.exports = exports['default']; + +/***/ }, +/* 29 */ +/***/ function(module, exports, __webpack_require__) { + + /* global define */ + 'use strict'; + + exports.__esModule = true; + + var _utils = __webpack_require__(5); + + var SourceNode = undefined; + + try { + /* istanbul ignore next */ + if (false) { + // We don't support this in AMD environments. For these environments, we asusme that + // they are running on the browser and thus have no need for the source-map library. + var SourceMap = require('source-map'); + SourceNode = SourceMap.SourceNode; + } + } catch (err) {} + /* NOP */ + + /* istanbul ignore if: tested but not covered in istanbul due to dist build */ + if (!SourceNode) { + SourceNode = function (line, column, srcFile, chunks) { + this.src = ''; + if (chunks) { + this.add(chunks); + } + }; + /* istanbul ignore next */ + SourceNode.prototype = { + add: function add(chunks) { + if (_utils.isArray(chunks)) { + chunks = chunks.join(''); + } + this.src += chunks; + }, + prepend: function prepend(chunks) { + if (_utils.isArray(chunks)) { + chunks = chunks.join(''); + } + this.src = chunks + this.src; + }, + toStringWithSourceMap: function toStringWithSourceMap() { + return { code: this.toString() }; + }, + toString: function toString() { + return this.src; + } + }; + } + + function castChunk(chunk, codeGen, loc) { + if (_utils.isArray(chunk)) { + var ret = []; + + for (var i = 0, len = chunk.length; i < len; i++) { + ret.push(codeGen.wrap(chunk[i], loc)); + } + return ret; + } else if (typeof chunk === 'boolean' || typeof chunk === 'number') { + // Handle primitives that the SourceNode will throw up on + return chunk + ''; + } + return chunk; + } + + function CodeGen(srcFile) { + this.srcFile = srcFile; + this.source = []; + } + + CodeGen.prototype = { + isEmpty: function isEmpty() { + return !this.source.length; + }, + prepend: function prepend(source, loc) { + this.source.unshift(this.wrap(source, loc)); + }, + push: function push(source, loc) { + this.source.push(this.wrap(source, loc)); + }, + + merge: function merge() { + var source = this.empty(); + this.each(function (line) { + source.add([' ', line, '\n']); + }); + return source; + }, + + each: function each(iter) { + for (var i = 0, len = this.source.length; i < len; i++) { + iter(this.source[i]); + } + }, + + empty: function empty() { + var loc = this.currentLocation || { start: {} }; + return new SourceNode(loc.start.line, loc.start.column, this.srcFile); + }, + wrap: function wrap(chunk) { + var loc = arguments.length <= 1 || arguments[1] === undefined ? this.currentLocation || { start: {} } : arguments[1]; + + if (chunk instanceof SourceNode) { + return chunk; + } + + chunk = castChunk(chunk, this, loc); + + return new SourceNode(loc.start.line, loc.start.column, this.srcFile, chunk); + }, + + functionCall: function functionCall(fn, type, params) { + params = this.generateList(params); + return this.wrap([fn, type ? '.' + type + '(' : '(', params, ')']); + }, + + quotedString: function quotedString(str) { + return '"' + (str + '').replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n').replace(/\r/g, '\\r').replace(/\u2028/g, '\\u2028') // Per Ecma-262 7.3 + 7.8.4 + .replace(/\u2029/g, '\\u2029') + '"'; + }, + + objectLiteral: function objectLiteral(obj) { + var pairs = []; + + for (var key in obj) { + if (obj.hasOwnProperty(key)) { + var value = castChunk(obj[key], this); + if (value !== 'undefined') { + pairs.push([this.quotedString(key), ':', value]); + } + } + } + + var ret = this.generateList(pairs); + ret.prepend('{'); + ret.add('}'); + return ret; + }, + + generateList: function generateList(entries) { + var ret = this.empty(); + + for (var i = 0, len = entries.length; i < len; i++) { + if (i) { + ret.add(','); + } + + ret.add(castChunk(entries[i], this)); + } + + return ret; + }, + + generateArray: function generateArray(entries) { + var ret = this.generateList(entries); + ret.prepend('['); + ret.add(']'); + + return ret; + } + }; + + exports['default'] = CodeGen; + module.exports = exports['default']; + +/***/ } +/******/ ]) +}); +; \ No newline at end of file