From 4c979320d2724cb9dac5a2f55356cf882b5f4399 Mon Sep 17 00:00:00 2001 From: Sebastian Fiedlschuster Date: Wed, 8 Oct 2014 13:58:26 +0200 Subject: [PATCH 1/8] continuing work on the events feature. editing an event is now possible. Date and time can be selected using a picker with best_in_place. Users can join events. trello: https://trello.com/c/cOa9EpLf/677-veranstaltungen --- Gemfile.lock | 5 +- .../wingolf_layout/events.css.sass | 12 + app/models/ability.rb | 15 +- config/routes.rb | 1 - ...13_add_location_to_events.your_platform.rb | 6 + db/schema.rb | 3 +- spec/models/ability_spec.rb | 28 +- .../app/assets/javascripts/your_platform.js | 3 + .../your_platform/events.js.coffee | 55 + .../stylesheets/your_platform/center.css.sass | 6 + .../stylesheets/your_platform/hidden.css.sass | 2 + .../app/controllers/events_controller.rb | 47 +- .../app/helpers/avatar_helper.rb | 6 +- .../app/helpers/events_helper.rb | 11 - .../engines/your_platform/app/models/event.rb | 55 +- .../engines/your_platform/app/models/group.rb | 4 +- .../app/models/structureable_mixins/roles.rb | 12 +- .../app/views/events/_form.html.erb | 12 - .../app/views/events/_upcoming_list.html.haml | 7 - .../app/views/events/edit.html.erb | 6 - .../app/views/events/index.html.erb | 29 - .../app/views/events/new.html.erb | 5 - .../app/views/events/show.html.erb | 25 - .../app/views/events/show.html.haml | 45 + .../views/groups/_member_avatars.html.haml | 3 + .../app/views/groups/show.html.haml | 7 +- .../app/views/pages/show.html.haml | 3 +- .../views/shared/_upcoming_events.html.haml | 25 + .../your_platform/config/locales/de.yml | 1 + .../config/locales/events/de.yml | 10 + .../config/locales/events/en.yml | 9 + vendor/engines/your_platform/config/routes.rb | 5 + .../20141008101744_add_location_to_events.rb | 5 + .../your_platform/lib/your_platform/engine.rb | 1 + .../best_in_place_datetime.js.coffee | 45 + .../javascripts/jquery-ui-timepicker-addon.js | 2223 +++++++++++++++++ .../javascripts/jquery-ui-timepicker-de.js | 26 + .../your_platform/your_platform.gemspec | 1 + 38 files changed, 2643 insertions(+), 121 deletions(-) create mode 100644 app/assets/stylesheets/wingolf_layout/events.css.sass create mode 100644 db/migrate/20141008101813_add_location_to_events.your_platform.rb create mode 100644 vendor/engines/your_platform/app/assets/javascripts/your_platform/events.js.coffee create mode 100644 vendor/engines/your_platform/app/assets/stylesheets/your_platform/center.css.sass create mode 100644 vendor/engines/your_platform/app/assets/stylesheets/your_platform/hidden.css.sass delete mode 100644 vendor/engines/your_platform/app/helpers/events_helper.rb delete mode 100644 vendor/engines/your_platform/app/views/events/_form.html.erb delete mode 100644 vendor/engines/your_platform/app/views/events/_upcoming_list.html.haml delete mode 100644 vendor/engines/your_platform/app/views/events/edit.html.erb delete mode 100644 vendor/engines/your_platform/app/views/events/index.html.erb delete mode 100644 vendor/engines/your_platform/app/views/events/new.html.erb delete mode 100644 vendor/engines/your_platform/app/views/events/show.html.erb create mode 100644 vendor/engines/your_platform/app/views/events/show.html.haml create mode 100644 vendor/engines/your_platform/app/views/groups/_member_avatars.html.haml create mode 100644 vendor/engines/your_platform/app/views/shared/_upcoming_events.html.haml create mode 100644 vendor/engines/your_platform/config/locales/events/de.yml create mode 100644 vendor/engines/your_platform/config/locales/events/en.yml create mode 100644 vendor/engines/your_platform/db/migrate/20141008101744_add_location_to_events.rb create mode 100644 vendor/engines/your_platform/vendor/assets/javascripts/best_in_place_datetime.js.coffee create mode 100644 vendor/engines/your_platform/vendor/assets/javascripts/jquery-ui-timepicker-addon.js create mode 100644 vendor/engines/your_platform/vendor/assets/javascripts/jquery-ui-timepicker-de.js diff --git a/Gemfile.lock b/Gemfile.lock index 7ab47706e..c61a31bda 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -53,6 +53,7 @@ PATH best_in_place (~> 2.1.0) cancan carrierwave + delocalize devise (= 2.2.3) edit_mode (>= 1.0.0) fnordmetric @@ -180,6 +181,8 @@ GEM daemons (1.1.9) database_cleaner (0.9.1) debug_inspector (0.0.2) + delocalize (0.4.0) + rails (>= 3.0) devise (2.2.3) bcrypt-ruby (~> 3.0) orm_adapter (~> 0.1) @@ -307,7 +310,7 @@ GEM orm_adapter (0.5.0) passgen (1.0.2) pdf-core (0.2.5) - phony (2.4.1) + phony (2.5.3) poltergeist (1.5.0) capybara (~> 2.1) cliver (~> 0.3.1) diff --git a/app/assets/stylesheets/wingolf_layout/events.css.sass b/app/assets/stylesheets/wingolf_layout/events.css.sass new file mode 100644 index 000000000..eb2cd3b8f --- /dev/null +++ b/app/assets/stylesheets/wingolf_layout/events.css.sass @@ -0,0 +1,12 @@ +table.event_details + th + text-align: left + td + width: 75% + tr + vertical-align: top + tr.description + textarea + height: 100px + input, textarea + width: 100% diff --git a/app/models/ability.rb b/app/models/ability.rb index cfaaa5367..e5e252171 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -118,6 +118,10 @@ def initialize(user, options = {}) user_group_membership.user == user end + # All users can join events. + # + can :join, Event + # LOCAL ADMINS # Local admins can manage their groups, this groups' subgroups # and all users within their groups. They can also execute workflows. @@ -154,7 +158,16 @@ def initialize(user, options = {}) # can :export_member_list, Group do |group| user.in? group.officers_of_self_and_parent_groups - end + end + + # Local officers can create events in their groups. + # + can :create_event, Group do |group| + user.in? group.officers_of_self_and_parent_groups + end + can :update, Event do |event| + user.in? event.group.officers_of_self_and_parent_groups + end # DEVELOPERS can :use, Rack::MiniProfiler do diff --git a/config/routes.rb b/config/routes.rb index 6fa0d96a4..f008f7651 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -12,7 +12,6 @@ # get "angular_test", controller: "angular_test", action: "index" resources :posts - resources :events match "users/new/:alias" => "users#new" diff --git a/db/migrate/20141008101813_add_location_to_events.your_platform.rb b/db/migrate/20141008101813_add_location_to_events.your_platform.rb new file mode 100644 index 000000000..2edbf38a1 --- /dev/null +++ b/db/migrate/20141008101813_add_location_to_events.your_platform.rb @@ -0,0 +1,6 @@ +# This migration comes from your_platform (originally 20141008101744) +class AddLocationToEvents < ActiveRecord::Migration + def change + add_column :events, :location, :string + end +end diff --git a/db/schema.rb b/db/schema.rb index 36a432157..ac4ecb72d 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20140808223512) do +ActiveRecord::Schema.define(:version => 20141008101813) do create_table "activities", :force => true do |t| t.integer "trackable_id" @@ -77,6 +77,7 @@ t.datetime "end_at" t.datetime "created_at", :null => false t.datetime "updated_at", :null => false + t.string "location" end create_table "flags", :force => true do |t| diff --git a/spec/models/ability_spec.rb b/spec/models/ability_spec.rb index 879f3d1cc..28ce43070 100644 --- a/spec/models/ability_spec.rb +++ b/spec/models/ability_spec.rb @@ -10,7 +10,7 @@ c.alias_example_to :he end -describe "User: abilities" do +describe Ability do # I'm sorry. I do have problems with cancan's terminology, here. # For me, the User can do something, i.e. I would ask @@ -30,7 +30,7 @@ subject { ability } let(:the_user) { subject } - context "when the user is a global admin", :focus do + context "when the user is a global admin" do before { user.global_admin = true } he "should be able to manage everything" do @page = create(:page) @@ -162,6 +162,15 @@ end end + context "(joining events)" do + before do + @group = create :group + @event = @group.child_events.create + end + he { should be_able_to :join, @event } + he { should_not be_able_to :create_event, @group } + end + describe "if other users are hidden" do before do @hidden_user = create(:user) @@ -284,6 +293,21 @@ he "should not be able to export the member list of parent groups" do the_user.should_not be_able_to :export_member_list, @parent_group end + + he "should be able to create an event in his group" do + the_user.should be_able_to :create_event, @group + end + he "should be able to update events in his group" do + @event = @group.child_events.create + the_user.should be_able_to :update, @event + end + he "should be able to create events in subgroups of his group" do + the_user.should be_able_to :create_event, @sub_group + end + he "should be able to update events in subgroups of his group" do + @event = @sub_group.child_events.create + the_user.should be_able_to :update, @event + end end end diff --git a/vendor/engines/your_platform/app/assets/javascripts/your_platform.js b/vendor/engines/your_platform/app/assets/javascripts/your_platform.js index 5b1252e0e..f43c81810 100644 --- a/vendor/engines/your_platform/app/assets/javascripts/your_platform.js +++ b/vendor/engines/your_platform/app/assets/javascripts/your_platform.js @@ -2,7 +2,10 @@ //= require jquery.appear-1.1.1 //= require edit_mode //= require slim_breadcrumb +//= require jquery-ui-timepicker-addon +//= require jquery-ui-timepicker-de //= require best_in_place +//= require best_in_place_datetime //= require twitter/bootstrap //= require jquery-fileupload/basic //= require jquery-fileupload/vendor/tmpl diff --git a/vendor/engines/your_platform/app/assets/javascripts/your_platform/events.js.coffee b/vendor/engines/your_platform/app/assets/javascripts/your_platform/events.js.coffee new file mode 100644 index 000000000..b5f2013c9 --- /dev/null +++ b/vendor/engines/your_platform/app/assets/javascripts/your_platform/events.js.coffee @@ -0,0 +1,55 @@ +ready = -> + + # Hide edit buttons on the events#show page, since + # the edit mode does not work properly with the datepicker, yet. + # + # The fields can be edited separately by clicking on them + # (best_in_place). + # + $('body.events * .edit_button').hide() + + #$('body.events * .start_at * input').lock() + + $.timepicker.setDefaults + dateFormat: "DD, dd. MM yy,", + timeFormat: "HH:mm 'Uhr'", + parse: 'loose' + showSecond: false, + stepMinute: 5, + hour: 20, + minute: 15 + + $('#join_event').click (event)-> + btn = $(this) + $.ajax({ + type: 'POST', + url: btn.attr('href'), + success: (r) -> + btn.button('reset') + btn.hide() + $('#leave_event').show() + $('#attendees_avatars').html(r.attendees_avatars) + } + ) + btn.data('loading-text', btn.text() + " ...") + btn.button('loading') + event.preventDefault() + + $('#leave_event').click (event)-> + btn = $(this) + $.ajax({ + type: 'DELETE', + url: btn.attr('href'), + success: (r)-> + btn.button('reset') + btn.hide() + $('#join_event').show() + $('#attendees_avatars').html(r.attendees_avatars) + } + ) + btn.data('loading-text', btn.text() + " ...") + btn.button('loading') + event.preventDefault() + +$(document).ready(ready) +$(document).on('page:load', ready) diff --git a/vendor/engines/your_platform/app/assets/stylesheets/your_platform/center.css.sass b/vendor/engines/your_platform/app/assets/stylesheets/your_platform/center.css.sass new file mode 100644 index 000000000..5135a0a04 --- /dev/null +++ b/vendor/engines/your_platform/app/assets/stylesheets/your_platform/center.css.sass @@ -0,0 +1,6 @@ +.left + text-align: left +.center + text-align: center +.right + text-align: right \ No newline at end of file diff --git a/vendor/engines/your_platform/app/assets/stylesheets/your_platform/hidden.css.sass b/vendor/engines/your_platform/app/assets/stylesheets/your_platform/hidden.css.sass new file mode 100644 index 000000000..a9596b9ad --- /dev/null +++ b/vendor/engines/your_platform/app/assets/stylesheets/your_platform/hidden.css.sass @@ -0,0 +1,2 @@ +.hidden + display: none \ No newline at end of file diff --git a/vendor/engines/your_platform/app/controllers/events_controller.rb b/vendor/engines/your_platform/app/controllers/events_controller.rb index 9dbf30cc5..28bce9ba8 100644 --- a/vendor/engines/your_platform/app/controllers/events_controller.rb +++ b/vendor/engines/your_platform/app/controllers/events_controller.rb @@ -16,6 +16,7 @@ def index # GET /events/1 # GET /events/1.json def show + @navable = @event respond_to do |format| format.html # show.html.erb format.json { render json: @event } @@ -41,6 +42,9 @@ def edit # POST /events.json def create @event = Event.new(params[:event]) + + # TODO + # @event.contact_people << current_user respond_to do |format| if @event.save @@ -57,12 +61,12 @@ def create # PUT /events/1.json def update respond_to do |format| - if @event.update_attributes(params[:event]) + if @event.update_attributes!(params[:event]) format.html { redirect_to @event, notice: 'Event was successfully updated.' } - format.json { head :no_content } + format.json { respond_with_bip(@event) } else format.html { render action: "edit" } - format.json { render json: @event.errors, status: :unprocessable_entity } + format.json { respond_with_bip(@event) } end end end @@ -77,4 +81,41 @@ def destroy format.json { head :no_content } end end + + + # POST /events/1/join + def join + change_attendance(true) + end + def leave + change_attendance(false) + end + + private + + def change_attendance(join = true) + @event = Event.find params[:event_id] + authorize! :join, @event + + if join + @event.attendees_group.assign_user current_user, at: Time.zone.now + else + @event.attendees_group.child_users.destroy(current_user) + end + + respond_to do |format| + format.html { redirect_to event_url(@event) } + format.json do + render json: { + attendees_avatars: render_to_string( + partial: 'groups/member_avatars.html.haml', + layout: false, + formats: [:json, :html], + locals: {group: @event.attendees_group} + ) + } + end + end + end + end diff --git a/vendor/engines/your_platform/app/helpers/avatar_helper.rb b/vendor/engines/your_platform/app/helpers/avatar_helper.rb index 3db80f307..5766933bc 100644 --- a/vendor/engines/your_platform/app/helpers/avatar_helper.rb +++ b/vendor/engines/your_platform/app/helpers/avatar_helper.rb @@ -9,8 +9,8 @@ def user_avatar( user, options = {} ) options[:size] ||= 36 options[:gravatar] ||= {} options[:gravatar][:size] ||= options[:size] - options[:alt] ||= "Gravatar: #{email}" - options[:title] ||= options[:alt] + #options[:alt] ||= "Gravatar: #{email}" + #options[:title] ||= options[:alt] options['data-toggle'] ||= 'tooltip' options[:gravatar][:secure] = true @@ -23,7 +23,7 @@ def user_avatar( user, options = {} ) # options[:gravatar][:default] ||= user_avatar_default_url - render partial: 'shared/avatar', locals: { email: email, options: options } + render partial: 'shared/avatar', formats: [:html], locals: { email: email, options: options } end diff --git a/vendor/engines/your_platform/app/helpers/events_helper.rb b/vendor/engines/your_platform/app/helpers/events_helper.rb deleted file mode 100644 index b991ca07b..000000000 --- a/vendor/engines/your_platform/app/helpers/events_helper.rb +++ /dev/null @@ -1,11 +0,0 @@ -module EventsHelper - - def upcoming_events_list_for_group( group ) - render partial: "events/upcoming_list", locals: { events: group.upcoming_events } - end - - def upcoming_events_list_for_user( user ) - render partial: "events/upcoming_list", locals: { events: user.upcoming_events } - end - -end diff --git a/vendor/engines/your_platform/app/models/event.rb b/vendor/engines/your_platform/app/models/event.rb index 2692155dd..97af34123 100644 --- a/vendor/engines/your_platform/app/models/event.rb +++ b/vendor/engines/your_platform/app/models/event.rb @@ -1,5 +1,5 @@ class Event < ActiveRecord::Base - attr_accessible :description, :end_at, :name, :start_at + attr_accessible :description, :location, :end_at, :name, :start_at, :localized_start_at, :localized_end_at is_structureable ancestor_class_names: %w(Group), descendant_class_names: %w(Group) is_navable @@ -32,6 +32,59 @@ def group=( group ) def groups self.parent_groups end + + # Times + # ========================================================================================== + + def localized_start_at + I18n.localize start_at.to_time if start_at.present? + end + def localized_start_at=(string) + attribute_will_change! :start_at + #self.start_at = string.present? ? Time.parse(string) : nil + self.start_at = string # conversion handled by the delocalize gem + end + + def localized_end_at + I18n.localize end_at.to_time if end_at.present? + end + def localized_end_at=(string) + attribute_will_change! :end_at + #self.end_at = string.present? ? Time.parse(string) : nil + self.end_at = string # conversion handled by the delocalize gem + end + + + + # Contact People and Attendees + # ========================================================================================== + + def find_contact_people_group + find_special_group :contact_people + end + def create_contact_people_group + create_special_group :contact_people + end + def contact_people_group + find_contact_people_group || create_contact_people_group + end + def contact_people + contact_people_group.child_users + end + + def find_attendees_group + find_special_group :attendees + end + def create_attendees_group + create_special_group :attendees + end + def attendees_group + find_attendees_group || create_attendees_group + end + def attendees + attendees_group.child_users + end + # Scopes # ========================================================================================== diff --git a/vendor/engines/your_platform/app/models/group.rb b/vendor/engines/your_platform/app/models/group.rb index 5f76edf2a..fb04eb006 100644 --- a/vendor/engines/your_platform/app/models/group.rb +++ b/vendor/engines/your_platform/app/models/group.rb @@ -19,7 +19,7 @@ class Group < ActiveRecord::Base include ActiveModel::ForbiddenAttributesProtection # TODO: Move into initializer - is_structureable( ancestor_class_names: %w(Group Page), + is_structureable( ancestor_class_names: %w(Group Page Event), descendant_class_names: %w(Group User Page Workflow Event) ) is_navable has_profile_fields @@ -124,7 +124,7 @@ def child_workflows # ------------------------------------------------------------------------------------------ def upcoming_events - self.events.upcoming + self.descendant_events.upcoming end diff --git a/vendor/engines/your_platform/app/models/structureable_mixins/roles.rb b/vendor/engines/your_platform/app/models/structureable_mixins/roles.rb index b97434b21..3b22d8bf5 100644 --- a/vendor/engines/your_platform/app/models/structureable_mixins/roles.rb +++ b/vendor/engines/your_platform/app/models/structureable_mixins/roles.rb @@ -41,13 +41,15 @@ def delete_caches_concerning_roles # |------------ admins_parent # if has_flag?(:officers_parent) || has_flag?(:admins_parent) - parent_groups.each { |group| group.delete_cache } + parent_groups.each do |group| + group.delete_cache + group.descendants.each do |descendant| + descendant.delete_cached :admins_of_ancestors + descendant.delete_cached :admins_of_self_and_ancestors + end + end end end - descendants.each do |descendant| - descendant.delete_cached :admins_of_ancestors - descendant.delete_cached :admins_of_self_and_ancestors - end end diff --git a/vendor/engines/your_platform/app/views/events/_form.html.erb b/vendor/engines/your_platform/app/views/events/_form.html.erb deleted file mode 100644 index afe450845..000000000 --- a/vendor/engines/your_platform/app/views/events/_form.html.erb +++ /dev/null @@ -1,12 +0,0 @@ -<%= semantic_form_for @event do |f| %> - <%= f.inputs do %> - <%= f.input :name %> - <%= f.input :description %> - <%= f.input :start_at %> - <%= f.input :end_at %> - <% end %> - - <%= f.actions do %> - <%= f.action :submit, :as => :input %> - <% end %> -<% end %> diff --git a/vendor/engines/your_platform/app/views/events/_upcoming_list.html.haml b/vendor/engines/your_platform/app/views/events/_upcoming_list.html.haml deleted file mode 100644 index b20e331b5..000000000 --- a/vendor/engines/your_platform/app/views/events/_upcoming_list.html.haml +++ /dev/null @@ -1,7 +0,0 @@ -%ul - - for event in events do - %li - %span{ :class => "event start_at_date" } - = link_to localize( event.start_at.to_date, :format => :long ), events_path( :start_at_date =>event.start_at.to_date ) - %span{ :class => "event start_at_time" }= localize( event.start_at, :format =>:time ) - %span{ :class => "event name" }= link_to( event.name, event ) diff --git a/vendor/engines/your_platform/app/views/events/edit.html.erb b/vendor/engines/your_platform/app/views/events/edit.html.erb deleted file mode 100644 index d7d45259b..000000000 --- a/vendor/engines/your_platform/app/views/events/edit.html.erb +++ /dev/null @@ -1,6 +0,0 @@ -

Editing event

- -<%= render 'form' %> - -<%= link_to 'Show', @event %> | -<%= link_to 'Back', events_path %> diff --git a/vendor/engines/your_platform/app/views/events/index.html.erb b/vendor/engines/your_platform/app/views/events/index.html.erb deleted file mode 100644 index 9ab23ac9a..000000000 --- a/vendor/engines/your_platform/app/views/events/index.html.erb +++ /dev/null @@ -1,29 +0,0 @@ -

Listing events

- - - - - - - - - - - - -<% @events.each do |event| %> - - - - - - - - - -<% end %> -
NameDescriptionStart atEnd at
<%= event.name %><%= event.description %><%= event.start_at %><%= event.end_at %><%= link_to 'Show', event %><%= link_to 'Edit', edit_event_path(event) %><%= link_to 'Destroy', event, method: :delete, data: { confirm: 'Are you sure?' } %>
- -
- -<%= link_to 'New Event', new_event_path %> diff --git a/vendor/engines/your_platform/app/views/events/new.html.erb b/vendor/engines/your_platform/app/views/events/new.html.erb deleted file mode 100644 index 6119978e9..000000000 --- a/vendor/engines/your_platform/app/views/events/new.html.erb +++ /dev/null @@ -1,5 +0,0 @@ -

New event

- -<%= render 'form' %> - -<%= link_to 'Back', events_path %> diff --git a/vendor/engines/your_platform/app/views/events/show.html.erb b/vendor/engines/your_platform/app/views/events/show.html.erb deleted file mode 100644 index 897d9a22c..000000000 --- a/vendor/engines/your_platform/app/views/events/show.html.erb +++ /dev/null @@ -1,25 +0,0 @@ -

<%= notice %>

- -

- Name: - <%= @event.name %> -

- -

- Description: - <%= @event.description %> -

- -

- Start at: - <%= @event.start_at %> -

- -

- End at: - <%= @event.end_at %> -

- - -<%= link_to 'Edit', edit_event_path(@event) %> | -<%= link_to 'Back', events_path %> diff --git a/vendor/engines/your_platform/app/views/events/show.html.haml b/vendor/engines/your_platform/app/views/events/show.html.haml new file mode 100644 index 000000000..7ffba83bc --- /dev/null +++ b/vendor/engines/your_platform/app/views/events/show.html.haml @@ -0,0 +1,45 @@ +%h1= best_in_place_if can?(:update, @event), @event, :name +%div + %table.event_details + %tr.description + %th=t :description + %td= best_in_place_if can?(:update, @event), @event, :description, placeholder: I18n.t(:description), type: 'textarea' + %tr.start_at + %th=t :start_at + %td= best_in_place_if can?(:update, @event), @event, :localized_start_at, type: 'datetime' + %tr.end_at + %th + =t :end_at + = surround '(', ')' do + =t :optional + %td= best_in_place_if can?(:update, @event), @event, :localized_end_at, type: 'datetime' + %tr.location + %th=t :location + %td= best_in_place_if can?(:update, @event), @event, :location + %tr.contact_person + %th=t :contact_person + %td= multiple_users_best_in_place @event.contact_people_group, :direct_members_titles_string + +%h1=t :attendees +%div + %div#attendees_avatars + = render partial: 'groups/member_avatars', locals: {group: @event.attendees_group} + + - if can? :join, @event + %p.center + - if @event.attendees.include? current_user + - join_button_hidden_class = 'hidden' + - else + - leave_button_hidden_class = 'hidden' + %a.btn.btn-success#join_event{href: event_join_path(@event, format: 'json'), class: join_button_hidden_class} + %i.icon-user + An dieser Veranstaltung teilnehmen + %a.btn.btn-danger#leave_event{href: event_leave_path(@event, format: 'json'), class: leave_button_hidden_class} + %i.icon-user + Doch nicht teilnehmen + + - if can? :update, @event + %p.center + %a.btn.has_tooltip{title: 'Auf diesen Knopf drücken, dann den Nachrichtentext überprüfen und bestätigen.', data: {position: 'bottom'}} + %i.icon-envelope + = "Alle Mitglieder der Gruppe '#{@event.group.name}' per E-Mail einladen …" \ No newline at end of file diff --git a/vendor/engines/your_platform/app/views/groups/_member_avatars.html.haml b/vendor/engines/your_platform/app/views/groups/_member_avatars.html.haml new file mode 100644 index 000000000..de4a7bd96 --- /dev/null +++ b/vendor/engines/your_platform/app/views/groups/_member_avatars.html.haml @@ -0,0 +1,3 @@ +%div.group_member_avatars + - for membership in group.memberships + = user_avatar(membership.user, class: 'has_tooltip', title: "#{membership.user.title} ist seit dem #{I18n.l(membership.valid_from.to_date)} als Teilnehmer eingetragen.", data: {placement: 'bottom'}) \ No newline at end of file diff --git a/vendor/engines/your_platform/app/views/groups/show.html.haml b/vendor/engines/your_platform/app/views/groups/show.html.haml index 310a0f050..8a43fb766 100644 --- a/vendor/engines/your_platform/app/views/groups/show.html.haml +++ b/vendor/engines/your_platform/app/views/groups/show.html.haml @@ -12,11 +12,8 @@ -# - if @posts && @posts.count > 0 -# = render partial: 'posts/posts', locals: { posts: @posts } --# / umcoming events --# - if @group.events.upcoming && ( @group.events.upcoming.count > 0 || can?(:manage, @group) ) --# %h1.section.upcoming_events Kommende Veranstaltungen --# %div --# = upcoming_events_list_for_group( @group ) +-# umcoming events += render partial: 'shared/upcoming_events', locals: {events: @group.upcoming_events, force_show: can?(:manage, @group)} - unless @child_groups -# contact info diff --git a/vendor/engines/your_platform/app/views/pages/show.html.haml b/vendor/engines/your_platform/app/views/pages/show.html.haml index 577cc6dc7..2f9361ffb 100644 --- a/vendor/engines/your_platform/app/views/pages/show.html.haml +++ b/vendor/engines/your_platform/app/views/pages/show.html.haml @@ -1,6 +1,7 @@ - if @page.has_flag? :intranet_root %div.col2 - = render 'root/what_is_new' + = render partial: 'shared/upcoming_events', locals: {events: current_user.upcoming_events} + = render partial: 'root/what_is_new' = render @page %div.col1 = render partial: 'root/start_page_elements' diff --git a/vendor/engines/your_platform/app/views/shared/_upcoming_events.html.haml b/vendor/engines/your_platform/app/views/shared/_upcoming_events.html.haml new file mode 100644 index 000000000..af8036079 --- /dev/null +++ b/vendor/engines/your_platform/app/views/shared/_upcoming_events.html.haml @@ -0,0 +1,25 @@ +-# locals: events, force_show + +- if events.count > 0 || force_show + %h1.upcoming_events=t :upcoming_events + %div + %ul + - for event in events do + - group = event.group + - corporation = group.try(:corporation) + %li + = succeed ',' do + %span{class: "event start_at_date"}= localize event.start_at.to_date, :format => :long + -# = link_to localize(event.start_at.to_date, :format => :long), events_path(start_at_date: event.start_at.to_date) + = succeed ',' do + %span{class: "event start_at_time"}= localize event.start_at, :format => :time + - if group + %span{class: "event group"} + = link_to group.name, group + - if corporation && (corporation.id != group.id) + = surround '(', ')' do + = link_to corporation.token, corporation + = ":" + %span{class: "event name"} + %strong + = link_to event.name, event diff --git a/vendor/engines/your_platform/config/locales/de.yml b/vendor/engines/your_platform/config/locales/de.yml index d2558fd23..f92db3d80 100644 --- a/vendor/engines/your_platform/config/locales/de.yml +++ b/vendor/engines/your_platform/config/locales/de.yml @@ -25,5 +25,6 @@ de: feedback: Verbesserungsvorschläge confirm: Bestätigen + optional: optional show_all: Alle anzeigen diff --git a/vendor/engines/your_platform/config/locales/events/de.yml b/vendor/engines/your_platform/config/locales/events/de.yml new file mode 100644 index 000000000..27b8c29fe --- /dev/null +++ b/vendor/engines/your_platform/config/locales/events/de.yml @@ -0,0 +1,10 @@ +de: + events: Veranstaltungen + upcoming_events: Anstehende Veranstaltungen + start_at: Beginn + end_at: Ende + contact_person: Ansprechpartner + contact_people: Ansprechpartner + attendees: Teilnehmer + location: Ort + \ No newline at end of file diff --git a/vendor/engines/your_platform/config/locales/events/en.yml b/vendor/engines/your_platform/config/locales/events/en.yml new file mode 100644 index 000000000..ca92b1ff0 --- /dev/null +++ b/vendor/engines/your_platform/config/locales/events/en.yml @@ -0,0 +1,9 @@ +en: + events: Events + upcoming_events: Upcoming Events + start_at: Start at + end_at: End at + contact_person: Contact Person + contact_people: Contact People + attendees: Attendees + location: Location \ No newline at end of file diff --git a/vendor/engines/your_platform/config/routes.rb b/vendor/engines/your_platform/config/routes.rb index c247574d6..761afcb18 100644 --- a/vendor/engines/your_platform/config/routes.rb +++ b/vendor/engines/your_platform/config/routes.rb @@ -31,6 +31,11 @@ resources :status_group_memberships resources :relationships + resources :events do + post :join, to: 'events#join' + delete :leave, to: 'events#leave' + end + resources :bookmarks get :my_bookmarks, controller: "bookmarks", action: "index" diff --git a/vendor/engines/your_platform/db/migrate/20141008101744_add_location_to_events.rb b/vendor/engines/your_platform/db/migrate/20141008101744_add_location_to_events.rb new file mode 100644 index 000000000..d9cee524c --- /dev/null +++ b/vendor/engines/your_platform/db/migrate/20141008101744_add_location_to_events.rb @@ -0,0 +1,5 @@ +class AddLocationToEvents < ActiveRecord::Migration + def change + add_column :events, :location, :string + end +end diff --git a/vendor/engines/your_platform/lib/your_platform/engine.rb b/vendor/engines/your_platform/lib/your_platform/engine.rb index fd79d5604..44dd1aa8e 100644 --- a/vendor/engines/your_platform/lib/your_platform/engine.rb +++ b/vendor/engines/your_platform/lib/your_platform/engine.rb @@ -36,6 +36,7 @@ # In Place Editing require 'best_in_place' +require 'delocalize' # Geo Coding require 'geocoder' diff --git a/vendor/engines/your_platform/vendor/assets/javascripts/best_in_place_datetime.js.coffee b/vendor/engines/your_platform/vendor/assets/javascripts/best_in_place_datetime.js.coffee new file mode 100644 index 000000000..65c4fae73 --- /dev/null +++ b/vendor/engines/your_platform/vendor/assets/javascripts/best_in_place_datetime.js.coffee @@ -0,0 +1,45 @@ +datetime = + "datetime" : + activateForm: -> + that = this + output = jQuery(document.createElement('form')) + .addClass('form_in_place') + .attr('action', 'javascript:void(0);') + .attr('style', 'display:inline') + input_elt = jQuery(document.createElement('input')) + .attr('type', 'text') + .attr('name', this.attributeName) + .attr('value', this.sanitizeValue(this.display_value)) + + if (this.inner_class != null) + input_elt.addClass(this.inner_class) + + output.append(input_elt) + + this.element.html(output) + this.setHtmlAttributes() + this.element.find('input')[0].select() + this.element.find("form").bind('submit', {editor: this}, BestInPlaceEditor.forms.input.submitHandler) + this.element.find("input").bind('keyup', {editor: this}, BestInPlaceEditor.forms.input.keyupHandler) + + this.element.find('input') + .datetimepicker + onClose: -> + that.update() + + .datepicker('show') + , + + getValue: -> + this.sanitizeValue(this.element.find("input").val()) + , + + submitHandler : (event) -> + event.data.editor.update() + , + + keyupHandler : (event) -> + if (event.keyCode == 27) + event.data.editor.abort() + +$.extend BestInPlaceEditor.forms, datetime diff --git a/vendor/engines/your_platform/vendor/assets/javascripts/jquery-ui-timepicker-addon.js b/vendor/engines/your_platform/vendor/assets/javascripts/jquery-ui-timepicker-addon.js new file mode 100644 index 000000000..0baae3e7d --- /dev/null +++ b/vendor/engines/your_platform/vendor/assets/javascripts/jquery-ui-timepicker-addon.js @@ -0,0 +1,2223 @@ +/*! jQuery Timepicker Addon - v1.5.0 - 2014-09-01 +* http://trentrichardson.com/examples/timepicker +* Copyright (c) 2014 Trent Richardson; Licensed MIT */ +(function ($) { + + /* + * Lets not redefine timepicker, Prevent "Uncaught RangeError: Maximum call stack size exceeded" + */ + $.ui.timepicker = $.ui.timepicker || {}; + if ($.ui.timepicker.version) { + return; + } + + /* + * Extend jQueryUI, get it started with our version number + */ + $.extend($.ui, { + timepicker: { + version: "1.5.0" + } + }); + + /* + * Timepicker manager. + * Use the singleton instance of this class, $.timepicker, to interact with the time picker. + * Settings for (groups of) time pickers are maintained in an instance object, + * allowing multiple different settings on the same page. + */ + var Timepicker = function () { + this.regional = []; // Available regional settings, indexed by language code + this.regional[''] = { // Default regional settings + currentText: 'Now', + closeText: 'Done', + amNames: ['AM', 'A'], + pmNames: ['PM', 'P'], + timeFormat: 'HH:mm', + timeSuffix: '', + timeOnlyTitle: 'Choose Time', + timeText: 'Time', + hourText: 'Hour', + minuteText: 'Minute', + secondText: 'Second', + millisecText: 'Millisecond', + microsecText: 'Microsecond', + timezoneText: 'Time Zone', + isRTL: false + }; + this._defaults = { // Global defaults for all the datetime picker instances + showButtonPanel: true, + timeOnly: false, + timeOnlyShowDate: false, + showHour: null, + showMinute: null, + showSecond: null, + showMillisec: null, + showMicrosec: null, + showTimezone: null, + showTime: true, + stepHour: 1, + stepMinute: 1, + stepSecond: 1, + stepMillisec: 1, + stepMicrosec: 1, + hour: 0, + minute: 0, + second: 0, + millisec: 0, + microsec: 0, + timezone: null, + hourMin: 0, + minuteMin: 0, + secondMin: 0, + millisecMin: 0, + microsecMin: 0, + hourMax: 23, + minuteMax: 59, + secondMax: 59, + millisecMax: 999, + microsecMax: 999, + minDateTime: null, + maxDateTime: null, + maxTime: null, + minTime: null, + onSelect: null, + hourGrid: 0, + minuteGrid: 0, + secondGrid: 0, + millisecGrid: 0, + microsecGrid: 0, + alwaysSetTime: true, + separator: ' ', + altFieldTimeOnly: true, + altTimeFormat: null, + altSeparator: null, + altTimeSuffix: null, + altRedirectFocus: true, + pickerTimeFormat: null, + pickerTimeSuffix: null, + showTimepicker: true, + timezoneList: null, + addSliderAccess: false, + sliderAccessArgs: null, + controlType: 'slider', + defaultValue: null, + parse: 'strict' + }; + $.extend(this._defaults, this.regional['']); + }; + + $.extend(Timepicker.prototype, { + $input: null, + $altInput: null, + $timeObj: null, + inst: null, + hour_slider: null, + minute_slider: null, + second_slider: null, + millisec_slider: null, + microsec_slider: null, + timezone_select: null, + maxTime: null, + minTime: null, + hour: 0, + minute: 0, + second: 0, + millisec: 0, + microsec: 0, + timezone: null, + hourMinOriginal: null, + minuteMinOriginal: null, + secondMinOriginal: null, + millisecMinOriginal: null, + microsecMinOriginal: null, + hourMaxOriginal: null, + minuteMaxOriginal: null, + secondMaxOriginal: null, + millisecMaxOriginal: null, + microsecMaxOriginal: null, + ampm: '', + formattedDate: '', + formattedTime: '', + formattedDateTime: '', + timezoneList: null, + units: ['hour', 'minute', 'second', 'millisec', 'microsec'], + support: {}, + control: null, + + /* + * Override the default settings for all instances of the time picker. + * @param {Object} settings object - the new settings to use as defaults (anonymous object) + * @return {Object} the manager object + */ + setDefaults: function (settings) { + extendRemove(this._defaults, settings || {}); + return this; + }, + + /* + * Create a new Timepicker instance + */ + _newInst: function ($input, opts) { + var tp_inst = new Timepicker(), + inlineSettings = {}, + fns = {}, + overrides, i; + + for (var attrName in this._defaults) { + if (this._defaults.hasOwnProperty(attrName)) { + var attrValue = $input.attr('time:' + attrName); + if (attrValue) { + try { + inlineSettings[attrName] = eval(attrValue); + } catch (err) { + inlineSettings[attrName] = attrValue; + } + } + } + } + + overrides = { + beforeShow: function (input, dp_inst) { + if ($.isFunction(tp_inst._defaults.evnts.beforeShow)) { + return tp_inst._defaults.evnts.beforeShow.call($input[0], input, dp_inst, tp_inst); + } + }, + onChangeMonthYear: function (year, month, dp_inst) { + // Update the time as well : this prevents the time from disappearing from the $input field. + tp_inst._updateDateTime(dp_inst); + if ($.isFunction(tp_inst._defaults.evnts.onChangeMonthYear)) { + tp_inst._defaults.evnts.onChangeMonthYear.call($input[0], year, month, dp_inst, tp_inst); + } + }, + onClose: function (dateText, dp_inst) { + if (tp_inst.timeDefined === true && $input.val() !== '') { + tp_inst._updateDateTime(dp_inst); + } + if ($.isFunction(tp_inst._defaults.evnts.onClose)) { + tp_inst._defaults.evnts.onClose.call($input[0], dateText, dp_inst, tp_inst); + } + } + }; + for (i in overrides) { + if (overrides.hasOwnProperty(i)) { + fns[i] = opts[i] || null; + } + } + + tp_inst._defaults = $.extend({}, this._defaults, inlineSettings, opts, overrides, { + evnts: fns, + timepicker: tp_inst // add timepicker as a property of datepicker: $.datepicker._get(dp_inst, 'timepicker'); + }); + tp_inst.amNames = $.map(tp_inst._defaults.amNames, function (val) { + return val.toUpperCase(); + }); + tp_inst.pmNames = $.map(tp_inst._defaults.pmNames, function (val) { + return val.toUpperCase(); + }); + + // detect which units are supported + tp_inst.support = detectSupport( + tp_inst._defaults.timeFormat + + (tp_inst._defaults.pickerTimeFormat ? tp_inst._defaults.pickerTimeFormat : '') + + (tp_inst._defaults.altTimeFormat ? tp_inst._defaults.altTimeFormat : '')); + + // controlType is string - key to our this._controls + if (typeof(tp_inst._defaults.controlType) === 'string') { + if (tp_inst._defaults.controlType === 'slider' && typeof($.ui.slider) === 'undefined') { + tp_inst._defaults.controlType = 'select'; + } + tp_inst.control = tp_inst._controls[tp_inst._defaults.controlType]; + } + // controlType is an object and must implement create, options, value methods + else { + tp_inst.control = tp_inst._defaults.controlType; + } + + // prep the timezone options + var timezoneList = [-720, -660, -600, -570, -540, -480, -420, -360, -300, -270, -240, -210, -180, -120, -60, + 0, 60, 120, 180, 210, 240, 270, 300, 330, 345, 360, 390, 420, 480, 525, 540, 570, 600, 630, 660, 690, 720, 765, 780, 840]; + if (tp_inst._defaults.timezoneList !== null) { + timezoneList = tp_inst._defaults.timezoneList; + } + var tzl = timezoneList.length, tzi = 0, tzv = null; + if (tzl > 0 && typeof timezoneList[0] !== 'object') { + for (; tzi < tzl; tzi++) { + tzv = timezoneList[tzi]; + timezoneList[tzi] = { value: tzv, label: $.timepicker.timezoneOffsetString(tzv, tp_inst.support.iso8601) }; + } + } + tp_inst._defaults.timezoneList = timezoneList; + + // set the default units + tp_inst.timezone = tp_inst._defaults.timezone !== null ? $.timepicker.timezoneOffsetNumber(tp_inst._defaults.timezone) : + ((new Date()).getTimezoneOffset() * -1); + tp_inst.hour = tp_inst._defaults.hour < tp_inst._defaults.hourMin ? tp_inst._defaults.hourMin : + tp_inst._defaults.hour > tp_inst._defaults.hourMax ? tp_inst._defaults.hourMax : tp_inst._defaults.hour; + tp_inst.minute = tp_inst._defaults.minute < tp_inst._defaults.minuteMin ? tp_inst._defaults.minuteMin : + tp_inst._defaults.minute > tp_inst._defaults.minuteMax ? tp_inst._defaults.minuteMax : tp_inst._defaults.minute; + tp_inst.second = tp_inst._defaults.second < tp_inst._defaults.secondMin ? tp_inst._defaults.secondMin : + tp_inst._defaults.second > tp_inst._defaults.secondMax ? tp_inst._defaults.secondMax : tp_inst._defaults.second; + tp_inst.millisec = tp_inst._defaults.millisec < tp_inst._defaults.millisecMin ? tp_inst._defaults.millisecMin : + tp_inst._defaults.millisec > tp_inst._defaults.millisecMax ? tp_inst._defaults.millisecMax : tp_inst._defaults.millisec; + tp_inst.microsec = tp_inst._defaults.microsec < tp_inst._defaults.microsecMin ? tp_inst._defaults.microsecMin : + tp_inst._defaults.microsec > tp_inst._defaults.microsecMax ? tp_inst._defaults.microsecMax : tp_inst._defaults.microsec; + tp_inst.ampm = ''; + tp_inst.$input = $input; + + if (tp_inst._defaults.altField) { + tp_inst.$altInput = $(tp_inst._defaults.altField); + if (tp_inst._defaults.altRedirectFocus === true) { + tp_inst.$altInput.css({ + cursor: 'pointer' + }).focus(function () { + $input.trigger("focus"); + }); + } + } + + if (tp_inst._defaults.minDate === 0 || tp_inst._defaults.minDateTime === 0) { + tp_inst._defaults.minDate = new Date(); + } + if (tp_inst._defaults.maxDate === 0 || tp_inst._defaults.maxDateTime === 0) { + tp_inst._defaults.maxDate = new Date(); + } + + // datepicker needs minDate/maxDate, timepicker needs minDateTime/maxDateTime.. + if (tp_inst._defaults.minDate !== undefined && tp_inst._defaults.minDate instanceof Date) { + tp_inst._defaults.minDateTime = new Date(tp_inst._defaults.minDate.getTime()); + } + if (tp_inst._defaults.minDateTime !== undefined && tp_inst._defaults.minDateTime instanceof Date) { + tp_inst._defaults.minDate = new Date(tp_inst._defaults.minDateTime.getTime()); + } + if (tp_inst._defaults.maxDate !== undefined && tp_inst._defaults.maxDate instanceof Date) { + tp_inst._defaults.maxDateTime = new Date(tp_inst._defaults.maxDate.getTime()); + } + if (tp_inst._defaults.maxDateTime !== undefined && tp_inst._defaults.maxDateTime instanceof Date) { + tp_inst._defaults.maxDate = new Date(tp_inst._defaults.maxDateTime.getTime()); + } + tp_inst.$input.bind('focus', function () { + tp_inst._onFocus(); + }); + + return tp_inst; + }, + + /* + * add our sliders to the calendar + */ + _addTimePicker: function (dp_inst) { + var currDT = (this.$altInput && this._defaults.altFieldTimeOnly) ? this.$input.val() + ' ' + this.$altInput.val() : this.$input.val(); + + this.timeDefined = this._parseTime(currDT); + this._limitMinMaxDateTime(dp_inst, false); + this._injectTimePicker(); + }, + + /* + * parse the time string from input value or _setTime + */ + _parseTime: function (timeString, withDate) { + if (!this.inst) { + this.inst = $.datepicker._getInst(this.$input[0]); + } + + if (withDate || !this._defaults.timeOnly) { + var dp_dateFormat = $.datepicker._get(this.inst, 'dateFormat'); + try { + var parseRes = parseDateTimeInternal(dp_dateFormat, this._defaults.timeFormat, timeString, $.datepicker._getFormatConfig(this.inst), this._defaults); + if (!parseRes.timeObj) { + return false; + } + $.extend(this, parseRes.timeObj); + } catch (err) { + $.timepicker.log("Error parsing the date/time string: " + err + + "\ndate/time string = " + timeString + + "\ntimeFormat = " + this._defaults.timeFormat + + "\ndateFormat = " + dp_dateFormat); + return false; + } + return true; + } else { + var timeObj = $.datepicker.parseTime(this._defaults.timeFormat, timeString, this._defaults); + if (!timeObj) { + return false; + } + $.extend(this, timeObj); + return true; + } + }, + + /* + * generate and inject html for timepicker into ui datepicker + */ + _injectTimePicker: function () { + var $dp = this.inst.dpDiv, + o = this.inst.settings, + tp_inst = this, + litem = '', + uitem = '', + show = null, + max = {}, + gridSize = {}, + size = null, + i = 0, + l = 0; + + // Prevent displaying twice + if ($dp.find("div.ui-timepicker-div").length === 0 && o.showTimepicker) { + var noDisplay = ' style="display:none;"', + html = '
' + '
' + o.timeText + '
' + + '
'; + + // Create the markup + for (i = 0, l = this.units.length; i < l; i++) { + litem = this.units[i]; + uitem = litem.substr(0, 1).toUpperCase() + litem.substr(1); + show = o['show' + uitem] !== null ? o['show' + uitem] : this.support[litem]; + + // Added by Peter Medeiros: + // - Figure out what the hour/minute/second max should be based on the step values. + // - Example: if stepMinute is 15, then minMax is 45. + max[litem] = parseInt((o[litem + 'Max'] - ((o[litem + 'Max'] - o[litem + 'Min']) % o['step' + uitem])), 10); + gridSize[litem] = 0; + + html += '
' + o[litem + 'Text'] + '
' + + '
'; + + if (show && o[litem + 'Grid'] > 0) { + html += '
'; + + if (litem === 'hour') { + for (var h = o[litem + 'Min']; h <= max[litem]; h += parseInt(o[litem + 'Grid'], 10)) { + gridSize[litem]++; + var tmph = $.datepicker.formatTime(this.support.ampm ? 'hht' : 'HH', {hour: h}, o); + html += ''; + } + } + else { + for (var m = o[litem + 'Min']; m <= max[litem]; m += parseInt(o[litem + 'Grid'], 10)) { + gridSize[litem]++; + html += ''; + } + } + + html += '
' + tmph + '' + ((m < 10) ? '0' : '') + m + '
'; + } + html += '
'; + } + + // Timezone + var showTz = o.showTimezone !== null ? o.showTimezone : this.support.timezone; + html += '
' + o.timezoneText + '
'; + html += '
'; + + // Create the elements from string + html += '
'; + var $tp = $(html); + + // if we only want time picker... + if (o.timeOnly === true) { + $tp.prepend('
' + '
' + o.timeOnlyTitle + '
' + '
'); + $dp.find('.ui-datepicker-header, .ui-datepicker-calendar').hide(); + } + + // add sliders, adjust grids, add events + for (i = 0, l = tp_inst.units.length; i < l; i++) { + litem = tp_inst.units[i]; + uitem = litem.substr(0, 1).toUpperCase() + litem.substr(1); + show = o['show' + uitem] !== null ? o['show' + uitem] : this.support[litem]; + + // add the slider + tp_inst[litem + '_slider'] = tp_inst.control.create(tp_inst, $tp.find('.ui_tpicker_' + litem + '_slider'), litem, tp_inst[litem], o[litem + 'Min'], max[litem], o['step' + uitem]); + + // adjust the grid and add click event + if (show && o[litem + 'Grid'] > 0) { + size = 100 * gridSize[litem] * o[litem + 'Grid'] / (max[litem] - o[litem + 'Min']); + $tp.find('.ui_tpicker_' + litem + ' table').css({ + width: size + "%", + marginLeft: o.isRTL ? '0' : ((size / (-2 * gridSize[litem])) + "%"), + marginRight: o.isRTL ? ((size / (-2 * gridSize[litem])) + "%") : '0', + borderCollapse: 'collapse' + }).find("td").click(function (e) { + var $t = $(this), + h = $t.html(), + n = parseInt(h.replace(/[^0-9]/g), 10), + ap = h.replace(/[^apm]/ig), + f = $t.data('for'); // loses scope, so we use data-for + + if (f === 'hour') { + if (ap.indexOf('p') !== -1 && n < 12) { + n += 12; + } + else { + if (ap.indexOf('a') !== -1 && n === 12) { + n = 0; + } + } + } + + tp_inst.control.value(tp_inst, tp_inst[f + '_slider'], litem, n); + + tp_inst._onTimeChange(); + tp_inst._onSelectHandler(); + }).css({ + cursor: 'pointer', + width: (100 / gridSize[litem]) + '%', + textAlign: 'center', + overflow: 'hidden' + }); + } // end if grid > 0 + } // end for loop + + // Add timezone options + this.timezone_select = $tp.find('.ui_tpicker_timezone').append('').find("select"); + $.fn.append.apply(this.timezone_select, + $.map(o.timezoneList, function (val, idx) { + return $("