serum-rails 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.gitignore +6 -0
- data/.rspec +3 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +42 -0
- data/LICENSE +22 -0
- data/README.md +40 -0
- data/bin/serum-rails +13 -0
- data/lib/serum/rails/app.rb +50 -0
- data/lib/serum/rails/code_scanner.rb +49 -0
- data/lib/serum/rails/metrics.rb +118 -0
- data/lib/serum/rails/type_selection.rb +31 -0
- data/lib/serum/rails/version.rb +7 -0
- data/lib/serum-rails.rb +5 -0
- data/serum-rails.gemspec +23 -0
- data/spec/serum/rails/metrics_spec.rb +87 -0
- data/spec/spec_helper.rb +80 -0
- data/spec/test_app/Capfile +4 -0
- data/spec/test_app/Gemfile +54 -0
- data/spec/test_app/Gemfile.lock +163 -0
- data/spec/test_app/Rakefile +11 -0
- data/spec/test_app/app/controllers/admin/categories_controller.rb +17 -0
- data/spec/test_app/app/controllers/admin/users_controller.rb +30 -0
- data/spec/test_app/app/controllers/application_controller/i18n_trait.rb +24 -0
- data/spec/test_app/app/controllers/application_controller/navigation_trait.rb +19 -0
- data/spec/test_app/app/controllers/application_controller/security_trait/disabled_clearance_accounts_trait.rb +41 -0
- data/spec/test_app/app/controllers/application_controller/security_trait.rb +65 -0
- data/spec/test_app/app/controllers/application_controller.rb +13 -0
- data/spec/test_app/app/controllers/conferences_controller.rb +67 -0
- data/spec/test_app/app/controllers/friendship_requests_controller.rb +31 -0
- data/spec/test_app/app/controllers/invitations_controller.rb +39 -0
- data/spec/test_app/app/controllers/members_controller.rb +21 -0
- data/spec/test_app/app/controllers/passwords_controller.rb +33 -0
- data/spec/test_app/app/controllers/sessions_controller.rb +17 -0
- data/spec/test_app/app/controllers/shared/boring_controller_trait/deletable_trait.rb +23 -0
- data/spec/test_app/app/controllers/shared/boring_controller_trait/flash_trait.rb +23 -0
- data/spec/test_app/app/controllers/shared/boring_controller_trait/helpers_trait.rb +35 -0
- data/spec/test_app/app/controllers/shared/boring_controller_trait/index_trait.rb +39 -0
- data/spec/test_app/app/controllers/shared/boring_controller_trait/log_changes_trait.rb +18 -0
- data/spec/test_app/app/controllers/shared/boring_controller_trait.rb +16 -0
- data/spec/test_app/app/controllers/users_controller.rb +49 -0
- data/spec/test_app/app/controllers/ws/api_controller/formatters_trait.rb +91 -0
- data/spec/test_app/app/controllers/ws/api_controller/parsers_trait.rb +59 -0
- data/spec/test_app/app/controllers/ws/api_controller.rb +70 -0
- data/spec/test_app/app/controllers/ws/attendees_controller.rb +43 -0
- data/spec/test_app/app/controllers/ws/categories_controller.rb +30 -0
- data/spec/test_app/app/controllers/ws/conferences_controller.rb +62 -0
- data/spec/test_app/app/controllers/ws/contacts_controller.rb +41 -0
- data/spec/test_app/app/controllers/ws/factory_controller.rb +45 -0
- data/spec/test_app/app/controllers/ws/members_controller.rb +34 -0
- data/spec/test_app/app/controllers/ws/series_controller.rb +19 -0
- data/spec/test_app/app/controllers/ws/tests_controller.rb +23 -0
- data/spec/test_app/app/helpers/application_helper.rb +180 -0
- data/spec/test_app/app/helpers/mail_helper.rb +9 -0
- data/spec/test_app/app/helpers/tags_helper.rb +16 -0
- data/spec/test_app/app/helpers/user_helper.rb +23 -0
- data/spec/test_app/app/models/attendance.rb +11 -0
- data/spec/test_app/app/models/category/ancestry_trait.rb +23 -0
- data/spec/test_app/app/models/category.rb +25 -0
- data/spec/test_app/app/models/conference/attendance_trait.rb +23 -0
- data/spec/test_app/app/models/conference/categories_trait.rb +26 -0
- data/spec/test_app/app/models/conference/icalendar_trait.rb +36 -0
- data/spec/test_app/app/models/conference/search_trait.rb +44 -0
- data/spec/test_app/app/models/conference.rb +24 -0
- data/spec/test_app/app/models/conference_category.rb +6 -0
- data/spec/test_app/app/models/conference_search.rb +51 -0
- data/spec/test_app/app/models/error.rb +25 -0
- data/spec/test_app/app/models/friendship.rb +14 -0
- data/spec/test_app/app/models/friendship_request.rb +19 -0
- data/spec/test_app/app/models/invitation.rb +38 -0
- data/spec/test_app/app/models/member_search.rb +18 -0
- data/spec/test_app/app/models/navigation.rb +38 -0
- data/spec/test_app/app/models/patterns.rb +14 -0
- data/spec/test_app/app/models/permissions.rb +106 -0
- data/spec/test_app/app/models/shared/afterlife_trait.rb +18 -0
- data/spec/test_app/app/models/shared/choice_trait.rb +37 -0
- data/spec/test_app/app/models/shared/deletable_trait.rb +8 -0
- data/spec/test_app/app/models/shared/flag_trait.rb +30 -0
- data/spec/test_app/app/models/shared/indestructible_trait.rb +19 -0
- data/spec/test_app/app/models/shared/list_field_trait.rb +70 -0
- data/spec/test_app/app/models/shared/person_name_trait.rb +25 -0
- data/spec/test_app/app/models/shared/searchable_trait.rb +71 -0
- data/spec/test_app/app/models/shared/sortable_trait.rb +24 -0
- data/spec/test_app/app/models/user/authentication_trait.rb +33 -0
- data/spec/test_app/app/models/user/authorization_trait.rb +17 -0
- data/spec/test_app/app/models/user/friends_trait.rb +42 -0
- data/spec/test_app/app/models/user/search_trait.rb +43 -0
- data/spec/test_app/app/models/user.rb +61 -0
- data/spec/test_app/app/models/util.rb +19 -0
- data/spec/test_app/app/views/admin/categories/_form.html.haml +18 -0
- data/spec/test_app/app/views/admin/categories/_list.html.haml +17 -0
- data/spec/test_app/app/views/admin/categories/edit.html.haml +6 -0
- data/spec/test_app/app/views/admin/categories/index.html.haml +6 -0
- data/spec/test_app/app/views/admin/categories/new.html.haml +6 -0
- data/spec/test_app/app/views/admin/categories/show.html.haml +27 -0
- data/spec/test_app/app/views/admin/users/_form.html.haml +49 -0
- data/spec/test_app/app/views/admin/users/_head.html.haml +5 -0
- data/spec/test_app/app/views/admin/users/_list.html.haml +20 -0
- data/spec/test_app/app/views/admin/users/_search.html.haml +9 -0
- data/spec/test_app/app/views/admin/users/deleted.html.haml +5 -0
- data/spec/test_app/app/views/admin/users/edit.html.haml +5 -0
- data/spec/test_app/app/views/admin/users/index.html.haml +7 -0
- data/spec/test_app/app/views/admin/users/new.html.haml +6 -0
- data/spec/test_app/app/views/clearance_mailer/change_password.erb +9 -0
- data/spec/test_app/app/views/conferences/_categories_list.html.haml +8 -0
- data/spec/test_app/app/views/conferences/_form.html.haml +32 -0
- data/spec/test_app/app/views/conferences/_head.html.haml +4 -0
- data/spec/test_app/app/views/conferences/_list.html.haml +22 -0
- data/spec/test_app/app/views/conferences/_search.html.haml +24 -0
- data/spec/test_app/app/views/conferences/edit.html.haml +8 -0
- data/spec/test_app/app/views/conferences/index.html.haml +22 -0
- data/spec/test_app/app/views/conferences/new.html.haml +9 -0
- data/spec/test_app/app/views/conferences/search.html.haml +7 -0
- data/spec/test_app/app/views/conferences/show.html.haml +78 -0
- data/spec/test_app/app/views/invitations/index.html.haml +26 -0
- data/spec/test_app/app/views/invitations/new.html.haml +22 -0
- data/spec/test_app/app/views/layouts/_flashes.html.haml +13 -0
- data/spec/test_app/app/views/layouts/_javascript_requirement.html.haml +8 -0
- data/spec/test_app/app/views/layouts/_javascripts.html.haml +4 -0
- data/spec/test_app/app/views/layouts/_notifications.html.haml +11 -0
- data/spec/test_app/app/views/layouts/_session_navigation.html.haml +12 -0
- data/spec/test_app/app/views/layouts/_stylesheets.html.haml +6 -0
- data/spec/test_app/app/views/layouts/screen.html.haml +30 -0
- data/spec/test_app/app/views/members/index.html.haml +40 -0
- data/spec/test_app/app/views/passwords/edit.html.haml +21 -0
- data/spec/test_app/app/views/passwords/new.html.haml +14 -0
- data/spec/test_app/app/views/sessions/new.html.haml +19 -0
- data/spec/test_app/app/views/users/_form.html.haml +45 -0
- data/spec/test_app/app/views/users/_head.html.haml +3 -0
- data/spec/test_app/app/views/users/edit.html.haml +5 -0
- data/spec/test_app/app/views/users/new.html.haml +6 -0
- data/spec/test_app/app/views/users/show.html.haml +49 -0
- data/spec/test_app/assets/data.json +981 -0
- data/spec/test_app/config/boot.rb +125 -0
- data/spec/test_app/config/cucumber.yml +8 -0
- data/spec/test_app/config/database.sample.yml +24 -0
- data/spec/test_app/config/deploy/production.rb +9 -0
- data/spec/test_app/config/deploy.rb +100 -0
- data/spec/test_app/config/environment.rb +27 -0
- data/spec/test_app/config/environments/cucumber.rb +28 -0
- data/spec/test_app/config/environments/development.rb +22 -0
- data/spec/test_app/config/environments/production.rb +35 -0
- data/spec/test_app/config/environments/test.rb +30 -0
- data/spec/test_app/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/test_app/config/initializers/clearance.rb +5 -0
- data/spec/test_app/config/initializers/collect_hash.rb +23 -0
- data/spec/test_app/config/initializers/collection_path_with_params.rb +18 -0
- data/spec/test_app/config/initializers/cookie_verification_secret.rb +10 -0
- data/spec/test_app/config/initializers/form_builder.rb +166 -0
- data/spec/test_app/config/initializers/inflections.rb +10 -0
- data/spec/test_app/config/initializers/invert_ordered_hash.rb +12 -0
- data/spec/test_app/config/initializers/localize_textfield_input_for_numbers.rb +30 -0
- data/spec/test_app/config/initializers/mime_types.rb +7 -0
- data/spec/test_app/config/initializers/new_rails_defaults.rb +21 -0
- data/spec/test_app/config/initializers/preload_associations.rb +7 -0
- data/spec/test_app/config/initializers/query_diet.rb +6 -0
- data/spec/test_app/config/initializers/saner_field_with_errors.rb +5 -0
- data/spec/test_app/config/initializers/session_store.rb +18 -0
- data/spec/test_app/config/locales/en.yml +231 -0
- data/spec/test_app/config/preinitializer.rb +22 -0
- data/spec/test_app/config/routes.rb +47 -0
- data/spec/test_app/db/migrate/20110114164517_initial_tables.rb +52 -0
- data/spec/test_app/db/migrate/20110118090858_add_profile_fields_to_user.rb +15 -0
- data/spec/test_app/db/migrate/20110118092741_create_conference.rb +20 -0
- data/spec/test_app/db/migrate/20110118093503_create_category.rb +17 -0
- data/spec/test_app/db/migrate/20110118104438_create_conference_category.rb +16 -0
- data/spec/test_app/db/migrate/20110118105959_create_attendance.rb +17 -0
- data/spec/test_app/db/migrate/20110118122324_create_friendship_requests.rb +15 -0
- data/spec/test_app/db/migrate/20110118162436_create_invitation.rb +16 -0
- data/spec/test_app/db/migrate/20110118170347_create_friendships.rb +15 -0
- data/spec/test_app/db/migrate/20110118193528_add_timestamps_to_conference.rb +12 -0
- data/spec/test_app/db/migrate/20110119075012_set_unique_index_on_username_for_user.rb +11 -0
- data/spec/test_app/db/migrate/20110119093458_remove_first_and_last_name_from_user.rb +12 -0
- data/spec/test_app/db/migrate/20110119110857_add_missing_indexes.rb +23 -0
- data/spec/test_app/db/migrate/20110119115751_add_uniqueness_of_name_to_category.rb +9 -0
- data/spec/test_app/db/seeds.rb +5 -0
- data/spec/test_app/lib/scripts/create_many_users_sql.rb +14 -0
- data/spec/test_app/lib/tasks/cucumber.rake +53 -0
- data/spec/test_app/lib/tasks/pending_migrations.rake +24 -0
- data/spec/test_app/lib/tasks/rcov.rake +42 -0
- data/spec/test_app/lib/tasks/rspec.rake +144 -0
- data/spec/test_app/public/404.html +30 -0
- data/spec/test_app/public/422.html +30 -0
- data/spec/test_app/public/500.html +30 -0
- data/spec/test_app/public/favicon.ico +0 -0
- data/spec/test_app/public/images/ajax-loader.gif +0 -0
- data/spec/test_app/public/images/icons/balloon-quotation.png +0 -0
- data/spec/test_app/public/images/icons/balloon-sound.png +0 -0
- data/spec/test_app/public/images/icons/balloon.png +0 -0
- data/spec/test_app/public/images/icons/bell.png +0 -0
- data/spec/test_app/public/images/icons/bin.png +0 -0
- data/spec/test_app/public/images/icons/bookmark.png +0 -0
- data/spec/test_app/public/images/icons/cake.png +0 -0
- data/spec/test_app/public/images/icons/calendar-blue.png +0 -0
- data/spec/test_app/public/images/icons/calendar-month.png +0 -0
- data/spec/test_app/public/images/icons/calendar-month_light.png +0 -0
- data/spec/test_app/public/images/icons/calendar-select.png +0 -0
- data/spec/test_app/public/images/icons/card-address.png +0 -0
- data/spec/test_app/public/images/icons/cards-address.png +0 -0
- data/spec/test_app/public/images/icons/cross-script.png +0 -0
- data/spec/test_app/public/images/icons/cross-small.png +0 -0
- data/spec/test_app/public/images/icons/cross.png +0 -0
- data/spec/test_app/public/images/icons/crown-silver.png +0 -0
- data/spec/test_app/public/images/icons/crown.png +0 -0
- data/spec/test_app/public/images/icons/dashboard.png +0 -0
- data/spec/test_app/public/images/icons/document-list.png +0 -0
- data/spec/test_app/public/images/icons/document-node.png +0 -0
- data/spec/test_app/public/images/icons/document-number.png +0 -0
- data/spec/test_app/public/images/icons/document-stamp.png +0 -0
- data/spec/test_app/public/images/icons/document-task.png +0 -0
- data/spec/test_app/public/images/icons/document-text.png +0 -0
- data/spec/test_app/public/images/icons/fire.png +0 -0
- data/spec/test_app/public/images/icons/folder-medium.png +0 -0
- data/spec/test_app/public/images/icons/folder.png +0 -0
- data/spec/test_app/public/images/icons/gear.png +0 -0
- data/spec/test_app/public/images/icons/globe-green.png +0 -0
- data/spec/test_app/public/images/icons/globe-green_light.png +0 -0
- data/spec/test_app/public/images/icons/globe-medium-green.png +0 -0
- data/spec/test_app/public/images/icons/heart-break.png +0 -0
- data/spec/test_app/public/images/icons/heart.png +0 -0
- data/spec/test_app/public/images/icons/images-flickr.png +0 -0
- data/spec/test_app/public/images/icons/information-balloon.png +0 -0
- data/spec/test_app/public/images/icons/key.png +0 -0
- data/spec/test_app/public/images/icons/light-bulb.png +0 -0
- data/spec/test_app/public/images/icons/lock--exclamation.png +0 -0
- data/spec/test_app/public/images/icons/lock.png +0 -0
- data/spec/test_app/public/images/icons/magnifier-medium.png +0 -0
- data/spec/test_app/public/images/icons/magnifier.png +0 -0
- data/spec/test_app/public/images/icons/mail.png +0 -0
- data/spec/test_app/public/images/icons/paper-clip-small.png +0 -0
- data/spec/test_app/public/images/icons/paper-clip.png +0 -0
- data/spec/test_app/public/images/icons/pencil.png +0 -0
- data/spec/test_app/public/images/icons/plus-circle.png +0 -0
- data/spec/test_app/public/images/icons/plus-small.png +0 -0
- data/spec/test_app/public/images/icons/plus.png +0 -0
- data/spec/test_app/public/images/icons/printer.png +0 -0
- data/spec/test_app/public/images/icons/question-balloon.png +0 -0
- data/spec/test_app/public/images/icons/question-white.png +0 -0
- data/spec/test_app/public/images/icons/question.png +0 -0
- data/spec/test_app/public/images/icons/quill.png +0 -0
- data/spec/test_app/public/images/icons/robot.png +0 -0
- data/spec/test_app/public/images/icons/ruby_16.png +0 -0
- data/spec/test_app/public/images/icons/ruby_2_16.png +0 -0
- data/spec/test_app/public/images/icons/ruby_32.png +0 -0
- data/spec/test_app/public/images/icons/show.png +0 -0
- data/spec/test_app/public/images/icons/star.png +0 -0
- data/spec/test_app/public/images/icons/sticky-note-text.png +0 -0
- data/spec/test_app/public/images/icons/telephone-fax.png +0 -0
- data/spec/test_app/public/images/icons/telephone-fax_light.png +0 -0
- data/spec/test_app/public/images/icons/telephone.png +0 -0
- data/spec/test_app/public/images/icons/telephone_light.png +0 -0
- data/spec/test_app/public/images/icons/thumb-up.png +0 -0
- data/spec/test_app/public/images/icons/thumb.png +0 -0
- data/spec/test_app/public/images/icons/tick.png +0 -0
- data/spec/test_app/public/images/icons/trophy-silver.png +0 -0
- data/spec/test_app/public/images/icons/trophy.png +0 -0
- data/spec/test_app/public/images/icons/user-gray.png +0 -0
- data/spec/test_app/public/images/icons/user-gray_gray.png +0 -0
- data/spec/test_app/public/images/icons/user-medium.png +0 -0
- data/spec/test_app/public/images/icons/user-red.png +0 -0
- data/spec/test_app/public/images/icons/users.png +0 -0
- data/spec/test_app/public/images/icons/users_gray.png +0 -0
- data/spec/test_app/public/images/icons/xfn.png +0 -0
- data/spec/test_app/public/images/legend_bg.png +0 -0
- data/spec/test_app/public/images/poshytip/tip-deepgray.png +0 -0
- data/spec/test_app/public/images/poshytip/tip-deepgray_arrows.png +0 -0
- data/spec/test_app/public/images/stripes_bottom_dark.png +0 -0
- data/spec/test_app/public/images/stripes_top_dark.png +0 -0
- data/spec/test_app/public/images/ui/ui-bg_diagonals-thick_18_b81900_40x40.png +0 -0
- data/spec/test_app/public/images/ui/ui-bg_diagonals-thick_20_666666_40x40.png +0 -0
- data/spec/test_app/public/images/ui/ui-bg_flat_10_000000_40x100.png +0 -0
- data/spec/test_app/public/images/ui/ui-bg_glass_100_f6f6f6_1x400.png +0 -0
- data/spec/test_app/public/images/ui/ui-bg_glass_100_fdf5ce_1x400.png +0 -0
- data/spec/test_app/public/images/ui/ui-bg_glass_65_ffffff_1x400.png +0 -0
- data/spec/test_app/public/images/ui/ui-bg_gloss-wave_35_f6a828_500x100.png +0 -0
- data/spec/test_app/public/images/ui/ui-bg_highlight-soft_100_eeeeee_1x100.png +0 -0
- data/spec/test_app/public/images/ui/ui-bg_highlight-soft_75_ffe45c_1x100.png +0 -0
- data/spec/test_app/public/images/ui/ui-icons_222222_256x240.png +0 -0
- data/spec/test_app/public/images/ui/ui-icons_228ef1_256x240.png +0 -0
- data/spec/test_app/public/images/ui/ui-icons_ef8c08_256x240.png +0 -0
- data/spec/test_app/public/images/ui/ui-icons_ffd27a_256x240.png +0 -0
- data/spec/test_app/public/images/ui/ui-icons_ffffff_256x240.png +0 -0
- data/spec/test_app/public/javascripts/application.js +175 -0
- data/spec/test_app/public/javascripts/lib/jquery-1.4.2.min.js +154 -0
- data/spec/test_app/public/javascripts/lib/jquery-ui-1.8.5.custom.min.js +249 -0
- data/spec/test_app/public/javascripts/lib/jquery-ui-1.8.5.custom.min.txt +9 -0
- data/spec/test_app/public/javascripts/lib/jquery-ui-timepicker-addon.js +682 -0
- data/spec/test_app/public/javascripts/lib/jquery-ui-timepicker-addon.min.js +22 -0
- data/spec/test_app/public/javascripts/lib/jquery.elastic.js +6 -0
- data/spec/test_app/public/javascripts/lib/jquery.elastic.performance.js +128 -0
- data/spec/test_app/public/javascripts/lib/jquery.elastic.source.js +117 -0
- data/spec/test_app/public/javascripts/lib/jquery.poshytip.js +414 -0
- data/spec/test_app/public/javascripts/lib/jquery.poshytip.min.js +7 -0
- data/spec/test_app/public/javascripts/lib/jquery.ui.datepicker-de.js +23 -0
- data/spec/test_app/public/javascripts/lib/jrails.js +1 -0
- data/spec/test_app/public/javascripts/lib/multiple-autocomplete.js +39 -0
- data/spec/test_app/public/javascripts/lib/underscore-min.js +17 -0
- data/spec/test_app/public/javascripts/lib/underscore.js +704 -0
- data/spec/test_app/public/robots.txt +5 -0
- data/spec/test_app/public/stylesheets/lib/PIE.htc +77 -0
- data/spec/test_app/public/stylesheets/lib/PIE_uncompressed.htc +3064 -0
- data/spec/test_app/public/stylesheets/lib/jquery-ui-timepicker-addon.css +11 -0
- data/spec/test_app/public/stylesheets/lib/poshytip/tip-darkgray/tip-darkgray.css +62 -0
- data/spec/test_app/public/stylesheets/lib/poshytip/tip-darkgray/tip-darkgray.png +0 -0
- data/spec/test_app/public/stylesheets/lib/poshytip/tip-darkgray/tip-darkgray_arrows.png +0 -0
- data/spec/test_app/public/stylesheets/lib/poshytip/tip-deepgray/tip-deepgray.css +64 -0
- data/spec/test_app/public/stylesheets/lib/poshytip/tip-green/tip-green.css +57 -0
- data/spec/test_app/public/stylesheets/lib/poshytip/tip-green/tip-green_arrows.gif +0 -0
- data/spec/test_app/public/stylesheets/lib/poshytip/tip-skyblue/tip-skyblue.css +58 -0
- data/spec/test_app/public/stylesheets/lib/poshytip/tip-skyblue/tip-skyblue.png +0 -0
- data/spec/test_app/public/stylesheets/lib/poshytip/tip-skyblue/tip-skyblue_arrows.png +0 -0
- data/spec/test_app/public/stylesheets/lib/poshytip/tip-twitter/tip-twitter.css +59 -0
- data/spec/test_app/public/stylesheets/lib/poshytip/tip-twitter/tip-twitter_arrows.gif +0 -0
- data/spec/test_app/public/stylesheets/lib/poshytip/tip-violet/tip-violet.css +60 -0
- data/spec/test_app/public/stylesheets/lib/poshytip/tip-violet/tip-violet.png +0 -0
- data/spec/test_app/public/stylesheets/lib/poshytip/tip-violet/tip-violet_arrows.png +0 -0
- data/spec/test_app/public/stylesheets/lib/poshytip/tip-yellow/tip-yellow.css +60 -0
- data/spec/test_app/public/stylesheets/lib/poshytip/tip-yellow/tip-yellow.png +0 -0
- data/spec/test_app/public/stylesheets/lib/poshytip/tip-yellow/tip-yellow_arrows.png +0 -0
- data/spec/test_app/public/stylesheets/lib/poshytip/tip-yellowsimple/tip-yellowsimple.css +60 -0
- data/spec/test_app/public/stylesheets/lib/poshytip/tip-yellowsimple/tip-yellowsimple_arrows.gif +0 -0
- data/spec/test_app/public/stylesheets/lib/ui-lightness/jquery-ui-1.8.5.custom.css +459 -0
- data/spec/test_app/public/stylesheets/sass/_mixins.sass +79 -0
- data/spec/test_app/public/stylesheets/sass/_reset.sass +40 -0
- data/spec/test_app/public/stylesheets/sass/print.sass +22 -0
- data/spec/test_app/public/stylesheets/sass/screen.sass +866 -0
- data/spec/test_app/script/about +4 -0
- data/spec/test_app/script/autospec +6 -0
- data/spec/test_app/script/console +3 -0
- data/spec/test_app/script/cucumber +10 -0
- data/spec/test_app/script/dbconsole +3 -0
- data/spec/test_app/script/destroy +3 -0
- data/spec/test_app/script/generate +3 -0
- data/spec/test_app/script/performance/benchmarker +3 -0
- data/spec/test_app/script/performance/profiler +3 -0
- data/spec/test_app/script/plugin +3 -0
- data/spec/test_app/script/runner +3 -0
- data/spec/test_app/script/server +3 -0
- data/spec/test_app/script/spec +10 -0
- metadata +748 -0
@@ -0,0 +1,3064 @@
|
|
1
|
+
<!--
|
2
|
+
PIE: CSS3 rendering for IE
|
3
|
+
Version 1.0beta2
|
4
|
+
http://css3pie.com
|
5
|
+
Dual-licensed for use under the Apache License Version 2.0 or the General Public License (GPL) Version 2.
|
6
|
+
-->
|
7
|
+
<PUBLIC:COMPONENT lightWeight="true">
|
8
|
+
<PUBLIC:ATTACH EVENT="onresize" FOR="element" ONEVENT="update()" />
|
9
|
+
<PUBLIC:ATTACH EVENT="onresize" FOR="window" ONEVENT="update()" />
|
10
|
+
<PUBLIC:ATTACH EVENT="onmove" FOR="element" ONEVENT="update()" />
|
11
|
+
<PUBLIC:ATTACH EVENT="onpropertychange" FOR="element" ONEVENT="propChanged()" />
|
12
|
+
<PUBLIC:ATTACH EVENT="onmouseenter" FOR="element" ONEVENT="mouseEntered()" />
|
13
|
+
<PUBLIC:ATTACH EVENT="onmouseleave" FOR="element" ONEVENT="mouseLeft()" />
|
14
|
+
<PUBLIC:ATTACH EVENT="oncontentready" FOR="element" ONEVENT="update()" />
|
15
|
+
<PUBLIC:ATTACH EVENT="ondocumentready" FOR="element" ONEVENT="update()" />
|
16
|
+
<PUBLIC:ATTACH EVENT="ondetach" FOR="element" ONEVENT="cleanup()" />
|
17
|
+
|
18
|
+
<script type="text/javascript">
|
19
|
+
var PIE = window['PIE'];
|
20
|
+
|
21
|
+
if( !PIE ) {
|
22
|
+
PIE = window['PIE'] = {
|
23
|
+
CSS_PREFIX: '-pie-',
|
24
|
+
STYLE_PREFIX: 'Pie',
|
25
|
+
CLASS_PREFIX: 'pie_'
|
26
|
+
};
|
27
|
+
|
28
|
+
// Detect IE6
|
29
|
+
if( !window.XMLHttpRequest ) {
|
30
|
+
PIE.isIE6 = true;
|
31
|
+
|
32
|
+
// IE6 can't access properties with leading dash, but can without it.
|
33
|
+
PIE.CSS_PREFIX = PIE.CSS_PREFIX.replace( /^-/, '' );
|
34
|
+
}
|
35
|
+
|
36
|
+
// Detect IE8
|
37
|
+
PIE.ie8DocMode = element.document.documentMode;
|
38
|
+
PIE.isIE8 = !!PIE.ie8DocMode;
|
39
|
+
|
40
|
+
// Set up polling - this is a brute-force workaround for issues in IE8 caused by it not
|
41
|
+
// always firing the onmove and onresize events when elements are moved or resized.
|
42
|
+
if( PIE.ie8DocMode === 8 ) {
|
43
|
+
PIE.ie8Poller = {
|
44
|
+
fns: {},
|
45
|
+
|
46
|
+
add: function( fn ) {
|
47
|
+
var id = fn.id || ( fn.id = '' + new Date().getTime() + Math.random() );
|
48
|
+
this.fns[ id ] = fn;
|
49
|
+
},
|
50
|
+
|
51
|
+
remove: function( fn ) {
|
52
|
+
delete this.fns[ fn.id ];
|
53
|
+
},
|
54
|
+
|
55
|
+
fire: function() {
|
56
|
+
var fns = this.fns, id;
|
57
|
+
for( id in fns ) {
|
58
|
+
if( fns.hasOwnProperty( id ) ) {
|
59
|
+
fns[ id ]();
|
60
|
+
}
|
61
|
+
}
|
62
|
+
}
|
63
|
+
};
|
64
|
+
setInterval( function() { PIE.ie8Poller.fire() }, 250 )
|
65
|
+
}
|
66
|
+
|
67
|
+
|
68
|
+
/**
|
69
|
+
* Utility functions
|
70
|
+
*/
|
71
|
+
PIE.Util = {
|
72
|
+
|
73
|
+
/**
|
74
|
+
* To create a VML element, it must be created by a Document which has the VML
|
75
|
+
* namespace set. Unfortunately, if you try to add the namespace programatically
|
76
|
+
* into the main document, you will get an "Unspecified error" when trying to
|
77
|
+
* access document.namespaces before the document is finished loading. To get
|
78
|
+
* around this, we create a DocumentFragment, which in IE land is apparently a
|
79
|
+
* full-fledged Document. It allows adding namespaces immediately, so we add the
|
80
|
+
* namespace there and then have it create the VML element.
|
81
|
+
* @param {string} tag The tag name for the VML element
|
82
|
+
* @return {Element} The new VML element
|
83
|
+
*/
|
84
|
+
createVmlElement: function( tag ) {
|
85
|
+
var vmlPrefix = 'css3vml',
|
86
|
+
vmlDoc = PIE._vmlCreatorDoc;
|
87
|
+
if( !vmlDoc ) {
|
88
|
+
vmlDoc = PIE._vmlCreatorDoc = element.document.createDocumentFragment();
|
89
|
+
vmlDoc.namespaces.add( vmlPrefix, 'urn:schemas-microsoft-com:vml' );
|
90
|
+
}
|
91
|
+
return vmlDoc.createElement( vmlPrefix + ':' + tag );
|
92
|
+
},
|
93
|
+
|
94
|
+
|
95
|
+
/**
|
96
|
+
* Simple utility for merging objects
|
97
|
+
* @param {Object} obj1 The main object into which all others will be merged
|
98
|
+
* @param {...Object} var_args Other objects which will be merged into the first, in order
|
99
|
+
*/
|
100
|
+
merge: function( obj1 ) {
|
101
|
+
var i, len, p, objN, args = arguments;
|
102
|
+
for( i = 1, len = args.length; i < len; i++ ) {
|
103
|
+
objN = args[i];
|
104
|
+
for( p in objN ) {
|
105
|
+
if( objN.hasOwnProperty( p ) ) {
|
106
|
+
obj1[ p ] = objN[ p ];
|
107
|
+
}
|
108
|
+
}
|
109
|
+
}
|
110
|
+
return obj1;
|
111
|
+
},
|
112
|
+
|
113
|
+
|
114
|
+
/**
|
115
|
+
* Execute a callback function, passing it the dimensions of a given image once
|
116
|
+
* they are known.
|
117
|
+
* @param {string} src The source URL of the image
|
118
|
+
* @param {function({w:number, h:number})} func The callback function to be called once the image dimensions are known
|
119
|
+
* @param {Object} ctx A context object which will be used as the 'this' value within the executed callback function
|
120
|
+
*/
|
121
|
+
withImageSize: function( src, func, ctx ) {
|
122
|
+
var sizes = PIE._imgSizes || ( PIE._imgSizes = {} ),
|
123
|
+
size = sizes[ src ], img;
|
124
|
+
if( size ) {
|
125
|
+
func.call( ctx, size );
|
126
|
+
} else {
|
127
|
+
img = new Image();
|
128
|
+
img.onload = function() {
|
129
|
+
size = sizes[ src ] = { w: img.width, h: img.height };
|
130
|
+
func.call( ctx, size );
|
131
|
+
img.onload = null;
|
132
|
+
};
|
133
|
+
img.src = src;
|
134
|
+
}
|
135
|
+
}
|
136
|
+
};/**
|
137
|
+
* Wrapper for length and percentage style values
|
138
|
+
* @constructor
|
139
|
+
* @param {string} val The CSS string representing the length. It is assumed that this will already have
|
140
|
+
* been validated as a valid length or percentage syntax.
|
141
|
+
*/
|
142
|
+
PIE.Length = (function() {
|
143
|
+
function Length( val ) {
|
144
|
+
this.val = val;
|
145
|
+
}
|
146
|
+
|
147
|
+
Length.prototype = {
|
148
|
+
/**
|
149
|
+
* Regular expression for matching the length unit
|
150
|
+
* @private
|
151
|
+
*/
|
152
|
+
unitRE: /(px|em|ex|mm|cm|in|pt|pc|%)$/,
|
153
|
+
|
154
|
+
/**
|
155
|
+
* Get the numeric value of the length
|
156
|
+
* @return {number} The value
|
157
|
+
*/
|
158
|
+
getNumber: function() {
|
159
|
+
var num = this._number;
|
160
|
+
if( num === undefined ) {
|
161
|
+
num = this._number = parseFloat( this.val );
|
162
|
+
}
|
163
|
+
return num;
|
164
|
+
},
|
165
|
+
|
166
|
+
/**
|
167
|
+
* Get the unit of the length
|
168
|
+
* @return {string} The unit
|
169
|
+
*/
|
170
|
+
getUnit: function() {
|
171
|
+
var unit = this._unit, m;
|
172
|
+
if( !unit ) {
|
173
|
+
m = this.val.match( this.unitRE );
|
174
|
+
unit = this._unit = ( m && m[0] ) || 'px';
|
175
|
+
}
|
176
|
+
return unit;
|
177
|
+
},
|
178
|
+
|
179
|
+
/**
|
180
|
+
* Determine whether this is a percentage length value
|
181
|
+
* @return {boolean}
|
182
|
+
*/
|
183
|
+
isPercentage: function() {
|
184
|
+
return this.getUnit() === '%';
|
185
|
+
},
|
186
|
+
|
187
|
+
/**
|
188
|
+
* Resolve this length into a number of pixels.
|
189
|
+
* @param {Element} el - the context element, used to resolve font-relative values
|
190
|
+
* @param {(function():number|number)=} pct100 - the number of pixels that equal a 100% percentage. This can be either a number or a
|
191
|
+
* function which will be called to return the number.
|
192
|
+
*/
|
193
|
+
pixels: function( el, pct100 ) {
|
194
|
+
var num = this.getNumber(),
|
195
|
+
unit = this.getUnit();
|
196
|
+
switch( unit ) {
|
197
|
+
case "px":
|
198
|
+
return num;
|
199
|
+
case "%":
|
200
|
+
return num * ( typeof pct100 === 'function' ? pct100() : pct100 ) / 100;
|
201
|
+
case "em":
|
202
|
+
return num * this.getEmPixels( el );
|
203
|
+
case "ex":
|
204
|
+
return num * this.getEmPixels( el ) / 2;
|
205
|
+
default:
|
206
|
+
return num * Length.conversions[ unit ];
|
207
|
+
}
|
208
|
+
},
|
209
|
+
|
210
|
+
/**
|
211
|
+
* The em and ex units are relative to the font-size of the current element,
|
212
|
+
* however if the font-size is set using non-pixel units then we get that value
|
213
|
+
* rather than a pixel conversion. To get around this, we keep a floating element
|
214
|
+
* with width:1em which we insert into the target element and then read its offsetWidth.
|
215
|
+
* But if the font-size *is* specified in pixels, then we use that directly to avoid
|
216
|
+
* the expensive DOM manipulation.
|
217
|
+
* @param el
|
218
|
+
*/
|
219
|
+
getEmPixels: function( el ) {
|
220
|
+
var fs = el.currentStyle.fontSize,
|
221
|
+
tester, s, px;
|
222
|
+
|
223
|
+
if( fs.indexOf( 'px' ) > 0 ) {
|
224
|
+
return parseFloat( fs );
|
225
|
+
} else {
|
226
|
+
tester = this._tester;
|
227
|
+
if( !tester ) {
|
228
|
+
tester = this._tester = el.document.createElement( 'length-calc' );
|
229
|
+
s = tester.style;
|
230
|
+
s.width = '1em';
|
231
|
+
s.position = 'absolute';
|
232
|
+
s.top = s.left = -9999;
|
233
|
+
}
|
234
|
+
el.appendChild( tester );
|
235
|
+
px = tester.offsetWidth;
|
236
|
+
el.removeChild( tester );
|
237
|
+
return px;
|
238
|
+
}
|
239
|
+
}
|
240
|
+
};
|
241
|
+
|
242
|
+
Length.conversions = (function() {
|
243
|
+
var units = [ 'mm', 'cm', 'in', 'pt', 'pc' ],
|
244
|
+
vals = {},
|
245
|
+
parent = element.parentNode,
|
246
|
+
i = 0, len = units.length, unit, el, s;
|
247
|
+
for( ; i < len; i++ ) {
|
248
|
+
unit = units[i];
|
249
|
+
el = element.document.createElement( 'length-calc' );
|
250
|
+
s = el.style;
|
251
|
+
s.position = 'absolute';
|
252
|
+
s.top = s.left = -9999;
|
253
|
+
s.width = '100' + unit;
|
254
|
+
parent.appendChild( el );
|
255
|
+
vals[ unit ] = el.offsetWidth / 100;
|
256
|
+
parent.removeChild( el );
|
257
|
+
}
|
258
|
+
return vals;
|
259
|
+
})();
|
260
|
+
|
261
|
+
Length.ZERO = new Length( '0' );
|
262
|
+
|
263
|
+
return Length;
|
264
|
+
})();
|
265
|
+
/**
|
266
|
+
* Wrapper for a CSS3 bg-position value. Takes up to 2 position keywords and 2 lengths/percentages.
|
267
|
+
* @constructor
|
268
|
+
* @param {Array.<PIE.Tokenizer.Token>} tokens The tokens making up the background position value.
|
269
|
+
*/
|
270
|
+
PIE.BgPosition = (function() {
|
271
|
+
function BgPosition( tokens ) {
|
272
|
+
this.tokens = tokens;
|
273
|
+
}
|
274
|
+
BgPosition.prototype = {
|
275
|
+
/**
|
276
|
+
* Normalize the values into the form:
|
277
|
+
* [ xOffsetSide, xOffsetLength, yOffsetSide, yOffsetLength ]
|
278
|
+
* where: xOffsetSide is either 'left' or 'right',
|
279
|
+
* yOffsetSide is either 'top' or 'bottom',
|
280
|
+
* and x/yOffsetLength are both PIE.Length objects.
|
281
|
+
* @return {Array}
|
282
|
+
*/
|
283
|
+
getValues: function() {
|
284
|
+
if( !this._values ) {
|
285
|
+
var tokens = this.tokens,
|
286
|
+
len = tokens.length,
|
287
|
+
length_zero = PIE.Length.ZERO,
|
288
|
+
length_fifty = new PIE.Length( '50%' ),
|
289
|
+
type_ident = PIE.Tokenizer.Type.IDENT,
|
290
|
+
type_length = PIE.Tokenizer.Type.LENGTH,
|
291
|
+
type_percent = PIE.Tokenizer.Type.PERCENT,
|
292
|
+
type, value,
|
293
|
+
vert_idents = { 'top': 1, 'center': 1, 'bottom': 1 },
|
294
|
+
horiz_idents = { 'left': 1, 'center': 1, 'right': 1 },
|
295
|
+
vals = [ 'left', length_zero, 'top', length_zero ];
|
296
|
+
|
297
|
+
// If only one value, the second is assumed to be 'center'
|
298
|
+
if( len === 1 ) {
|
299
|
+
tokens.push( { type: type_ident, value: 'center' } );
|
300
|
+
len++;
|
301
|
+
}
|
302
|
+
|
303
|
+
// Two values - CSS2
|
304
|
+
if( len === 2 ) {
|
305
|
+
// If both idents, they can appear in either order, so switch them if needed
|
306
|
+
if( type_ident & ( tokens[0].type | tokens[1].type ) &&
|
307
|
+
tokens[0].value in vert_idents && tokens[1].value in horiz_idents ) {
|
308
|
+
tokens.push( tokens.shift() );
|
309
|
+
}
|
310
|
+
if( tokens[0].type & type_ident ) {
|
311
|
+
if( tokens[0].value === 'center' ) {
|
312
|
+
vals[1] = length_fifty;
|
313
|
+
} else {
|
314
|
+
vals[0] = tokens[0].value;
|
315
|
+
}
|
316
|
+
}
|
317
|
+
else if( tokens[0].isLengthOrPercent() ) {
|
318
|
+
vals[1] = new PIE.Length( tokens[0].value );
|
319
|
+
}
|
320
|
+
if( tokens[1].type & type_ident ) {
|
321
|
+
if( tokens[1].value === 'center' ) {
|
322
|
+
vals[3] = length_fifty;
|
323
|
+
} else {
|
324
|
+
vals[2] = tokens[1].value;
|
325
|
+
}
|
326
|
+
}
|
327
|
+
else if( tokens[1].isLengthOrPercent() ) {
|
328
|
+
vals[3] = new PIE.Length( tokens[1].value );
|
329
|
+
}
|
330
|
+
}
|
331
|
+
|
332
|
+
// Three or four values - CSS3
|
333
|
+
else {
|
334
|
+
// TODO
|
335
|
+
}
|
336
|
+
|
337
|
+
this._values = vals;
|
338
|
+
}
|
339
|
+
return this._values;
|
340
|
+
},
|
341
|
+
|
342
|
+
/**
|
343
|
+
* Find the coordinates of the background image from the upper-left corner of the background area
|
344
|
+
* @param {Element} el
|
345
|
+
* @param {number} width - the width for percentages (background area width minus image width)
|
346
|
+
* @param {number} height - the height for percentages (background area height minus image height)
|
347
|
+
* @return {Object} { x: Number, y: Number }
|
348
|
+
*/
|
349
|
+
coords: function( el, width, height ) {
|
350
|
+
var vals = this.getValues(),
|
351
|
+
pxX = vals[1].pixels( el, width ),
|
352
|
+
pxY = vals[3].pixels( el, height );
|
353
|
+
|
354
|
+
return {
|
355
|
+
x: Math.round( vals[0] === 'right' ? width - pxX : pxX ),
|
356
|
+
y: Math.round( vals[2] === 'bottom' ? height - pxY : pxY )
|
357
|
+
};
|
358
|
+
}
|
359
|
+
};
|
360
|
+
|
361
|
+
return BgPosition;
|
362
|
+
})();/**
|
363
|
+
* Wrapper for angle values; handles conversion to degrees from all allowed angle units
|
364
|
+
* @constructor
|
365
|
+
* @param {string} val The raw CSS value for the angle. It is assumed it has been pre-validated.
|
366
|
+
*/
|
367
|
+
PIE.Angle = (function() {
|
368
|
+
function Angle( val ) {
|
369
|
+
this.val = val;
|
370
|
+
}
|
371
|
+
Angle.prototype = {
|
372
|
+
unitRE: /[a-z]+$/i,
|
373
|
+
|
374
|
+
/**
|
375
|
+
* @return {string} The unit of the angle value
|
376
|
+
*/
|
377
|
+
getUnit: function() {
|
378
|
+
return this._unit || ( this._unit = this.val.match( this.unitRE )[0].toLowerCase() );
|
379
|
+
},
|
380
|
+
|
381
|
+
/**
|
382
|
+
* Get the numeric value of the angle in degrees.
|
383
|
+
* @return {number} The degrees value
|
384
|
+
*/
|
385
|
+
degrees: function() {
|
386
|
+
var deg = this._deg, u, n;
|
387
|
+
if( deg === undefined ) {
|
388
|
+
u = this.getUnit();
|
389
|
+
n = parseFloat( this.val, 10 );
|
390
|
+
deg = this._deg = ( u === 'deg' ? n : u === 'rad' ? n / Math.PI * 180 : u === 'grad' ? n / 400 * 360 : u === 'turn' ? n * 360 : 0 );
|
391
|
+
}
|
392
|
+
return deg;
|
393
|
+
}
|
394
|
+
};
|
395
|
+
|
396
|
+
return Angle;
|
397
|
+
})();/**
|
398
|
+
* Abstraction for colors values. Allows detection of rgba values.
|
399
|
+
* @constructor
|
400
|
+
* @param {string} val The raw CSS string value for the color
|
401
|
+
*/
|
402
|
+
PIE.Color = (function() {
|
403
|
+
function Color( val ) {
|
404
|
+
this.val = val;
|
405
|
+
}
|
406
|
+
|
407
|
+
/**
|
408
|
+
* Regular expression for matching rgba colors and extracting their components
|
409
|
+
* @type {RegExp}
|
410
|
+
*/
|
411
|
+
Color.rgbaRE = /\s*rgba\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d+|\d*\.\d+)\s*\)\s*/;
|
412
|
+
|
413
|
+
Color.prototype = {
|
414
|
+
/**
|
415
|
+
* @private
|
416
|
+
*/
|
417
|
+
parse: function() {
|
418
|
+
if( !this._color ) {
|
419
|
+
var v = this.val,
|
420
|
+
m = v.match( Color.rgbaRE );
|
421
|
+
if( m ) {
|
422
|
+
this._color = 'rgb(' + m[1] + ',' + m[2] + ',' + m[3] + ')';
|
423
|
+
this._alpha = parseFloat( m[4] );
|
424
|
+
} else {
|
425
|
+
this._color = v;
|
426
|
+
this._alpha = 1;
|
427
|
+
}
|
428
|
+
}
|
429
|
+
},
|
430
|
+
|
431
|
+
/**
|
432
|
+
* Retrieve the value of the color in a format usable by IE natively. This will be the same as
|
433
|
+
* the raw input value, except for rgba values which will be converted to an rgb value.
|
434
|
+
* @param {Element} el The context element, used to get 'currentColor' keyword value.
|
435
|
+
* @return {string} Color value
|
436
|
+
*/
|
437
|
+
value: function( el ) {
|
438
|
+
this.parse();
|
439
|
+
return this._color === 'currentColor' ? el.currentStyle.color : this._color;
|
440
|
+
},
|
441
|
+
|
442
|
+
/**
|
443
|
+
* Retrieve the alpha value of the color. Will be 1 for all values except for rgba values
|
444
|
+
* with an alpha component.
|
445
|
+
* @return {number} The alpha value, from 0 to 1.
|
446
|
+
*/
|
447
|
+
alpha: function() {
|
448
|
+
this.parse();
|
449
|
+
return this._alpha;
|
450
|
+
}
|
451
|
+
};
|
452
|
+
|
453
|
+
return Color;
|
454
|
+
})();/**
|
455
|
+
* A tokenizer for CSS value strings.
|
456
|
+
* @constructor
|
457
|
+
* @param {string} css The CSS value string
|
458
|
+
*/
|
459
|
+
PIE.Tokenizer = (function() {
|
460
|
+
function Tokenizer( css ) {
|
461
|
+
this.css = css;
|
462
|
+
this.ch = 0;
|
463
|
+
this.tokens = [];
|
464
|
+
this.tokenIndex = 0;
|
465
|
+
}
|
466
|
+
|
467
|
+
/**
|
468
|
+
* Enumeration of token type constants.
|
469
|
+
* @enum {number}
|
470
|
+
*/
|
471
|
+
var Type = Tokenizer.Type = {
|
472
|
+
ANGLE: 1,
|
473
|
+
CHARACTER: 2,
|
474
|
+
COLOR: 4,
|
475
|
+
DIMEN: 8,
|
476
|
+
FUNCTION: 16,
|
477
|
+
IDENT: 32,
|
478
|
+
LENGTH: 64,
|
479
|
+
NUMBER: 128,
|
480
|
+
OPERATOR: 256,
|
481
|
+
PERCENT: 512,
|
482
|
+
STRING: 1024,
|
483
|
+
URL: 2048
|
484
|
+
};
|
485
|
+
|
486
|
+
/**
|
487
|
+
* A single token
|
488
|
+
* @constructor
|
489
|
+
* @param {number} type The type of the token - see PIE.Tokenizer.Type
|
490
|
+
* @param {string} value The value of the token
|
491
|
+
*/
|
492
|
+
Tokenizer.Token = function( type, value ) {
|
493
|
+
this.type = type;
|
494
|
+
this.value = value;
|
495
|
+
};
|
496
|
+
Tokenizer.Token.prototype = {
|
497
|
+
isLength: function() {
|
498
|
+
return this.type & Type.LENGTH || ( this.type & Type.NUMBER && this.value === '0' );
|
499
|
+
},
|
500
|
+
isLengthOrPercent: function() {
|
501
|
+
return this.isLength() || this.type & Type.PERCENT;
|
502
|
+
}
|
503
|
+
};
|
504
|
+
|
505
|
+
Tokenizer.prototype = {
|
506
|
+
whitespace: /\s/,
|
507
|
+
number: /^[\+\-]?(\d*\.)?\d+/,
|
508
|
+
url: /^url\(\s*("([^"]*)"|'([^']*)'|([!#$%&*-~]*))\s*\)/i,
|
509
|
+
ident: /^\-?[_a-z][\w-]*/i,
|
510
|
+
string: /^("([^"]*)"|'([^']*)')/,
|
511
|
+
operator: /^[\/,]/,
|
512
|
+
hash: /^#[\w]+/,
|
513
|
+
hashColor: /^#([\da-f]{6}|[\da-f]{3})/i,
|
514
|
+
|
515
|
+
unitTypes: {
|
516
|
+
'px': Type.LENGTH, 'em': Type.LENGTH, 'ex': Type.LENGTH,
|
517
|
+
'mm': Type.LENGTH, 'cm': Type.LENGTH, 'in': Type.LENGTH,
|
518
|
+
'pt': Type.LENGTH, 'pc': Type.LENGTH,
|
519
|
+
'deg': Type.ANGLE, 'rad': Type.ANGLE, 'grad': Type.ANGLE
|
520
|
+
},
|
521
|
+
|
522
|
+
colorNames: {
|
523
|
+
'aqua':1, 'black':1, 'blue':1, 'fuchsia':1, 'gray':1, 'green':1, 'lime':1, 'maroon':1,
|
524
|
+
'navy':1, 'olive':1, 'purple':1, 'red':1, 'silver':1, 'teal':1, 'white':1, 'yellow': 1,
|
525
|
+
'currentColor': 1
|
526
|
+
},
|
527
|
+
|
528
|
+
colorFunctions: {
|
529
|
+
'rgb': 1, 'rgba': 1, 'hsl': 1, 'hsla': 1
|
530
|
+
},
|
531
|
+
|
532
|
+
|
533
|
+
/**
|
534
|
+
* Advance to and return the next token in the CSS string. If the end of the CSS string has
|
535
|
+
* been reached, null will be returned.
|
536
|
+
* @param {boolean} forget - if true, the token will not be stored for the purposes of backtracking with prev().
|
537
|
+
* @return {PIE.Tokenizer.Token}
|
538
|
+
*/
|
539
|
+
next: function( forget ) {
|
540
|
+
var css, ch, firstChar, match, type, val,
|
541
|
+
me = this;
|
542
|
+
|
543
|
+
function newToken( type, value ) {
|
544
|
+
var tok = new Tokenizer.Token( type, value );
|
545
|
+
if( !forget ) {
|
546
|
+
me.tokens.push( tok );
|
547
|
+
me.tokenIndex++;
|
548
|
+
}
|
549
|
+
return tok;
|
550
|
+
}
|
551
|
+
function failure() {
|
552
|
+
me.tokenIndex++;
|
553
|
+
return null;
|
554
|
+
}
|
555
|
+
|
556
|
+
// In case we previously backed up, return the stored token in the next slot
|
557
|
+
if( this.tokenIndex < this.tokens.length ) {
|
558
|
+
return this.tokens[ this.tokenIndex++ ];
|
559
|
+
}
|
560
|
+
|
561
|
+
// Move past leading whitespace characters
|
562
|
+
while( this.whitespace.test( this.css.charAt( this.ch ) ) ) {
|
563
|
+
this.ch++;
|
564
|
+
}
|
565
|
+
if( this.ch >= this.css.length ) {
|
566
|
+
return failure();
|
567
|
+
}
|
568
|
+
|
569
|
+
ch = this.ch;
|
570
|
+
css = this.css.substring( this.ch );
|
571
|
+
firstChar = css.charAt( 0 );
|
572
|
+
switch( firstChar ) {
|
573
|
+
case '#':
|
574
|
+
if( match = css.match( this.hashColor ) ) {
|
575
|
+
this.ch += match[0].length;
|
576
|
+
return newToken( Type.COLOR, match[0] );
|
577
|
+
}
|
578
|
+
break;
|
579
|
+
|
580
|
+
case '"':
|
581
|
+
case "'":
|
582
|
+
if( match = css.match( this.string ) ) {
|
583
|
+
this.ch += match[0].length;
|
584
|
+
return newToken( Type.STRING, match[2] || match[3] || '' );
|
585
|
+
}
|
586
|
+
break;
|
587
|
+
|
588
|
+
case "/":
|
589
|
+
case ",":
|
590
|
+
this.ch++;
|
591
|
+
return newToken( Type.OPERATOR, firstChar );
|
592
|
+
|
593
|
+
case 'u':
|
594
|
+
if( match = css.match( this.url ) ) {
|
595
|
+
this.ch += match[0].length;
|
596
|
+
return newToken( Type.URL, match[2] || match[3] || match[4] || '' );
|
597
|
+
}
|
598
|
+
}
|
599
|
+
|
600
|
+
// Numbers and values starting with numbers
|
601
|
+
if( match = css.match( this.number ) ) {
|
602
|
+
val = match[0];
|
603
|
+
this.ch += val.length;
|
604
|
+
|
605
|
+
// Check if it is followed by a unit
|
606
|
+
if( css.charAt( val.length ) === '%' ) {
|
607
|
+
this.ch++;
|
608
|
+
return newToken( Type.PERCENT, val + '%' );
|
609
|
+
}
|
610
|
+
if( match = css.substring( val.length ).match( this.ident ) ) {
|
611
|
+
val += match[0];
|
612
|
+
this.ch += match[0].length;
|
613
|
+
return newToken( this.unitTypes[ match[0].toLowerCase() ] || Type.DIMEN, val );
|
614
|
+
}
|
615
|
+
|
616
|
+
// Plain ol' number
|
617
|
+
return newToken( Type.NUMBER, val );
|
618
|
+
}
|
619
|
+
|
620
|
+
// Identifiers
|
621
|
+
if( match = css.match( this.ident ) ) {
|
622
|
+
val = match[0];
|
623
|
+
this.ch += val.length;
|
624
|
+
|
625
|
+
// Named colors
|
626
|
+
if( val.toLowerCase() in this.colorNames ) {
|
627
|
+
return newToken( Type.COLOR, val );
|
628
|
+
}
|
629
|
+
|
630
|
+
// Functions
|
631
|
+
if( css.charAt( val.length ) === '(' ) {
|
632
|
+
this.ch++;
|
633
|
+
|
634
|
+
// Color values in function format: rgb, rgba, hsl, hsla
|
635
|
+
if( val.toLowerCase() in this.colorFunctions ) {
|
636
|
+
function isNum( tok ) {
|
637
|
+
return tok && tok.type & Type.NUMBER;
|
638
|
+
}
|
639
|
+
function isNumOrPct( tok ) {
|
640
|
+
return tok && ( tok.type & ( Type.NUMBER | Type.PERCENT ) );
|
641
|
+
}
|
642
|
+
function isValue( tok, val ) {
|
643
|
+
return tok && tok.value === val;
|
644
|
+
}
|
645
|
+
function next() {
|
646
|
+
return me.next( 1 );
|
647
|
+
}
|
648
|
+
|
649
|
+
if( ( val.charAt(0) === 'r' ? isNumOrPct( next() ) : isNum( next() ) ) &&
|
650
|
+
isValue( next(), ',' ) &&
|
651
|
+
isNumOrPct( next() ) &&
|
652
|
+
isValue( next(), ',' ) &&
|
653
|
+
isNumOrPct( next() ) &&
|
654
|
+
( val === 'rgb' || val === 'hsa' || (
|
655
|
+
isValue( next(), ',' ) &&
|
656
|
+
isNum( next() )
|
657
|
+
) ) &&
|
658
|
+
isValue( next(), ')' ) ) {
|
659
|
+
return newToken( Type.COLOR, this.css.substring( ch, this.ch ) );
|
660
|
+
}
|
661
|
+
return failure();
|
662
|
+
}
|
663
|
+
|
664
|
+
return newToken( Type.FUNCTION, val + '(' );
|
665
|
+
}
|
666
|
+
|
667
|
+
// Other identifier
|
668
|
+
return newToken( Type.IDENT, val );
|
669
|
+
}
|
670
|
+
|
671
|
+
// Standalone character
|
672
|
+
this.ch++;
|
673
|
+
return newToken( Type.CHARACTER, firstChar );
|
674
|
+
},
|
675
|
+
|
676
|
+
/**
|
677
|
+
* Determine whether there is another token
|
678
|
+
* @return {boolean}
|
679
|
+
*/
|
680
|
+
hasNext: function() {
|
681
|
+
var next = this.next();
|
682
|
+
this.prev();
|
683
|
+
return !!next;
|
684
|
+
},
|
685
|
+
|
686
|
+
/**
|
687
|
+
* Back up and return the previous token
|
688
|
+
* @return {PIE.Tokenizer.Token}
|
689
|
+
*/
|
690
|
+
prev: function() {
|
691
|
+
return this.tokens[ this.tokenIndex-- - 2 ];
|
692
|
+
},
|
693
|
+
|
694
|
+
/**
|
695
|
+
* Retrieve all the tokens in the CSS string
|
696
|
+
* @return {Array.<PIE.Tokenizer.Token>}
|
697
|
+
*/
|
698
|
+
all: function() {
|
699
|
+
while( this.next() ) {}
|
700
|
+
return this.tokens;
|
701
|
+
},
|
702
|
+
|
703
|
+
/**
|
704
|
+
* Return a list of tokens from the current position until the given function returns
|
705
|
+
* true. The final token will not be included in the list.
|
706
|
+
* @param {function():boolean} func - test function
|
707
|
+
* @param {boolean} require - if true, then if the end of the CSS string is reached
|
708
|
+
* before the test function returns true, null will be returned instead of the
|
709
|
+
* tokens that have been found so far.
|
710
|
+
* @return {Array.<PIE.Tokenizer.Token>}
|
711
|
+
*/
|
712
|
+
until: function( func, require ) {
|
713
|
+
var list = [], t, hit;
|
714
|
+
while( t = this.next() ) {
|
715
|
+
if( func( t ) ) {
|
716
|
+
hit = true;
|
717
|
+
this.prev();
|
718
|
+
break;
|
719
|
+
}
|
720
|
+
list.push( t );
|
721
|
+
}
|
722
|
+
return require && !hit ? null : list;
|
723
|
+
}
|
724
|
+
};
|
725
|
+
|
726
|
+
return Tokenizer;
|
727
|
+
})();PIE.StyleInfoBase = {
|
728
|
+
|
729
|
+
/**
|
730
|
+
* Create a new StyleInfo class, with the standard constructor, and augmented by
|
731
|
+
* the StyleInfoBase's members.
|
732
|
+
* @param proto
|
733
|
+
*/
|
734
|
+
newStyleInfo: function( proto ) {
|
735
|
+
function StyleInfo( el ) {
|
736
|
+
this.element = el;
|
737
|
+
}
|
738
|
+
PIE.Util.merge( StyleInfo.prototype, PIE.StyleInfoBase, proto );
|
739
|
+
return StyleInfo;
|
740
|
+
},
|
741
|
+
|
742
|
+
/**
|
743
|
+
* Get an object representation of the target CSS style, caching it as long as the
|
744
|
+
* underlying CSS value hasn't changed.
|
745
|
+
* @return {Object}
|
746
|
+
*/
|
747
|
+
getProps: function() {
|
748
|
+
if( this.changed() ) {
|
749
|
+
this._props = this.parseCss( this._css = this.getCss() );
|
750
|
+
}
|
751
|
+
return this._props;
|
752
|
+
},
|
753
|
+
|
754
|
+
/**
|
755
|
+
* Get the raw CSS value for the target style
|
756
|
+
* @return {string}
|
757
|
+
*/
|
758
|
+
getCss: function() {
|
759
|
+
var el = this.element,
|
760
|
+
s = el.style,
|
761
|
+
cs = el.currentStyle,
|
762
|
+
cssProp = this.cssProperty,
|
763
|
+
styleProp = this.styleProperty,
|
764
|
+
prefixedCssProp = this._prefixedCssProp || ( this._prefixedCssProp = PIE.CSS_PREFIX + cssProp ),
|
765
|
+
prefixedStyleProp = this._prefixedStyleProp || ( this._prefixedStyleProp = PIE.STYLE_PREFIX + styleProp.charAt(0).toUpperCase() + styleProp.substring(1) );
|
766
|
+
return s[ prefixedStyleProp ] || cs.getAttribute( prefixedCssProp ) || s[ styleProp ] || cs.getAttribute( cssProp );
|
767
|
+
},
|
768
|
+
|
769
|
+
/**
|
770
|
+
* Determine whether the target CSS style is active.
|
771
|
+
* @return {boolean}
|
772
|
+
*/
|
773
|
+
isActive: function() {
|
774
|
+
return !!this.getProps();
|
775
|
+
},
|
776
|
+
|
777
|
+
/**
|
778
|
+
* Determine whether the target CSS style has changed since the last time it was parsed.
|
779
|
+
* @return {boolean}
|
780
|
+
*/
|
781
|
+
changed: function() {
|
782
|
+
return this._css !== this.getCss();
|
783
|
+
}
|
784
|
+
};
|
785
|
+
/**
|
786
|
+
* Handles parsing, caching, and detecting changes to background (and -pie-background) CSS
|
787
|
+
* @constructor
|
788
|
+
* @param {Element} el the target element
|
789
|
+
*/
|
790
|
+
PIE.BackgroundStyleInfo = PIE.StyleInfoBase.newStyleInfo( {
|
791
|
+
|
792
|
+
cssProperty: PIE.CSS_PREFIX + 'background',
|
793
|
+
styleProperty: PIE.STYLE_PREFIX + 'Background',
|
794
|
+
|
795
|
+
attachIdents: { 'scroll':1, 'fixed':1, 'local':1 },
|
796
|
+
repeatIdents: { 'repeat-x':1, 'repeat-y':1, 'repeat':1, 'no-repeat':1 },
|
797
|
+
originIdents: { 'padding-box':1, 'border-box':1, 'content-box':1 },
|
798
|
+
clipIdents: { 'padding-box':1, 'border-box':1 },
|
799
|
+
positionIdents: { 'top':1, 'right':1, 'bottom':1, 'left':1, 'center':1 },
|
800
|
+
sizeIdents: { 'contain':1, 'cover':1 },
|
801
|
+
|
802
|
+
/**
|
803
|
+
* For background styles, we support the -pie-background property but fall back to the standard
|
804
|
+
* backround* properties. The reason we have to use the prefixed version is that IE natively
|
805
|
+
* parses the standard properties and if it sees something it doesn't know how to parse, for example
|
806
|
+
* multiple values or gradient definitions, it will throw that away and not make it available through
|
807
|
+
* currentStyle.
|
808
|
+
*
|
809
|
+
* Format of return object:
|
810
|
+
* {
|
811
|
+
* color: <PIE.Color>,
|
812
|
+
* images: [
|
813
|
+
* {
|
814
|
+
* type: 'image',
|
815
|
+
* url: 'image.png',
|
816
|
+
* repeat: <'no-repeat' | 'repeat-x' | 'repeat-y' | 'repeat'>,
|
817
|
+
* position: <PIE.BgPosition>,
|
818
|
+
* attachment: <'scroll' | 'fixed' | 'local'>,
|
819
|
+
* origin: <'border-box' | 'padding-box' | 'content-box'>,
|
820
|
+
* clip: <'border-box' | 'padding-box'>,
|
821
|
+
* size: <'contain' | 'cover' | { w: <'auto' | PIE.Length>, h: <'auto' | PIE.Length> }>
|
822
|
+
* },
|
823
|
+
* {
|
824
|
+
* type: 'linear-gradient',
|
825
|
+
* gradientStart: <PIE.BgPosition>,
|
826
|
+
* angle: <PIE.Angle>,
|
827
|
+
* stops: [
|
828
|
+
* { color: <PIE.Color>, offset: <PIE.Length> },
|
829
|
+
* { color: <PIE.Color>, offset: <PIE.Length> }, ...
|
830
|
+
* ]
|
831
|
+
* }
|
832
|
+
* ]
|
833
|
+
* }
|
834
|
+
* @param {String} css
|
835
|
+
* @override
|
836
|
+
*/
|
837
|
+
parseCss: function( css ) {
|
838
|
+
var el = this.element,
|
839
|
+
cs = el.currentStyle,
|
840
|
+
rs = el.runtimeStyle,
|
841
|
+
tokenizer, token, image,
|
842
|
+
tok_type = PIE.Tokenizer.Type,
|
843
|
+
type_operator = tok_type.OPERATOR,
|
844
|
+
type_ident = tok_type.IDENT,
|
845
|
+
type_color = tok_type.COLOR,
|
846
|
+
tokType, tokVal,
|
847
|
+
positionIdents = this.positionIdents,
|
848
|
+
gradient, stop,
|
849
|
+
props = null;
|
850
|
+
|
851
|
+
function isBgPosToken( token ) {
|
852
|
+
return token.isLengthOrPercent() || ( token.type & type_ident && token.value in positionIdents );
|
853
|
+
}
|
854
|
+
|
855
|
+
function sizeToken( token ) {
|
856
|
+
return ( token.isLengthOrPercent() && new PIE.Length( token.value ) ) || ( token.value === 'auto' && 'auto' );
|
857
|
+
}
|
858
|
+
|
859
|
+
// If the CSS3-specific -pie-background property is present, parse it
|
860
|
+
if( this.getCss3() ) {
|
861
|
+
tokenizer = new PIE.Tokenizer( css );
|
862
|
+
props = { images: [] };
|
863
|
+
image = {};
|
864
|
+
|
865
|
+
while( token = tokenizer.next() ) {
|
866
|
+
tokType = token.type;
|
867
|
+
tokVal = token.value;
|
868
|
+
|
869
|
+
if( !image.type && tokType & tok_type.FUNCTION && tokVal === 'linear-gradient(' ) {
|
870
|
+
gradient = { stops: [], type: 'linear-gradient' };
|
871
|
+
stop = {};
|
872
|
+
while( token = tokenizer.next() ) {
|
873
|
+
tokType = token.type;
|
874
|
+
tokVal = token.value;
|
875
|
+
|
876
|
+
// If we reached the end of the function and had at least 2 stops, flush the info
|
877
|
+
if( tokType & tok_type.CHARACTER && tokVal === ')' ) {
|
878
|
+
if( stop.color ) {
|
879
|
+
gradient.stops.push( stop );
|
880
|
+
}
|
881
|
+
if( gradient.stops.length > 1 ) {
|
882
|
+
PIE.Util.merge( image, gradient );
|
883
|
+
}
|
884
|
+
break;
|
885
|
+
}
|
886
|
+
|
887
|
+
// Color stop - must start with color
|
888
|
+
if( tokType & type_color ) {
|
889
|
+
// if we already have an angle/position, make sure that the previous token was a comma
|
890
|
+
if( gradient.angle || gradient.gradientStart ) {
|
891
|
+
token = tokenizer.prev();
|
892
|
+
if( token.type !== type_operator ) {
|
893
|
+
break; //fail
|
894
|
+
}
|
895
|
+
tokenizer.next();
|
896
|
+
}
|
897
|
+
|
898
|
+
stop = {
|
899
|
+
color: new PIE.Color( tokVal )
|
900
|
+
};
|
901
|
+
// check for offset following color
|
902
|
+
token = tokenizer.next();
|
903
|
+
if( token.isLengthOrPercent() ) {
|
904
|
+
stop.offset = new PIE.Length( token.value );
|
905
|
+
} else {
|
906
|
+
tokenizer.prev();
|
907
|
+
}
|
908
|
+
}
|
909
|
+
// Angle - can only appear in first spot
|
910
|
+
else if( tokType & tok_type.ANGLE && !gradient.angle && !stop.color && !gradient.stops.length ) {
|
911
|
+
gradient.angle = new PIE.Angle( token.value );
|
912
|
+
}
|
913
|
+
else if( isBgPosToken( token ) && !gradient.gradientStart && !stop.color && !gradient.stops.length ) {
|
914
|
+
tokenizer.prev();
|
915
|
+
gradient.gradientStart = new PIE.BgPosition(
|
916
|
+
tokenizer.until( function( t ) {
|
917
|
+
return !isBgPosToken( t );
|
918
|
+
}, false )
|
919
|
+
);
|
920
|
+
}
|
921
|
+
else if( tokType & type_operator && tokVal === ',' ) {
|
922
|
+
if( stop.color ) {
|
923
|
+
gradient.stops.push( stop );
|
924
|
+
stop = {};
|
925
|
+
}
|
926
|
+
}
|
927
|
+
else {
|
928
|
+
// Found something we didn't recognize; fail without adding image
|
929
|
+
break;
|
930
|
+
}
|
931
|
+
}
|
932
|
+
}
|
933
|
+
else if( !image.type && tokType & tok_type.URL ) {
|
934
|
+
image.url = tokVal;
|
935
|
+
image.type = 'image';
|
936
|
+
}
|
937
|
+
else if( isBgPosToken( token ) && !image.size ) {
|
938
|
+
tokenizer.prev();
|
939
|
+
image.position = new PIE.BgPosition(
|
940
|
+
tokenizer.until( function( t ) {
|
941
|
+
return !isBgPosToken( t );
|
942
|
+
}, false )
|
943
|
+
);
|
944
|
+
}
|
945
|
+
else if( tokType & type_ident ) {
|
946
|
+
if( tokVal in this.repeatIdents ) {
|
947
|
+
image.repeat = tokVal;
|
948
|
+
}
|
949
|
+
else if( tokVal in this.originIdents ) {
|
950
|
+
image.origin = tokVal;
|
951
|
+
if( tokVal in this.clipIdents ) {
|
952
|
+
image.clip = tokVal;
|
953
|
+
}
|
954
|
+
}
|
955
|
+
else if( tokVal in this.attachIdents ) {
|
956
|
+
image.attachment = tokVal;
|
957
|
+
}
|
958
|
+
}
|
959
|
+
else if( tokType & type_color && !props.color ) {
|
960
|
+
props.color = new PIE.Color( tokVal );
|
961
|
+
}
|
962
|
+
else if( tokType & type_operator ) {
|
963
|
+
// background size
|
964
|
+
if( tokVal === '/' ) {
|
965
|
+
token = tokenizer.next();
|
966
|
+
tokType = token.type;
|
967
|
+
tokVal = token.value;
|
968
|
+
if( tokType & type_ident && tokVal in this.sizeIdents ) {
|
969
|
+
image.size = tokVal;
|
970
|
+
}
|
971
|
+
else if( tokVal = sizeToken( token ) ) {
|
972
|
+
image.size = {
|
973
|
+
w: tokVal,
|
974
|
+
h: sizeToken( tokenizer.next() ) || ( tokenizer.prev() && tokVal )
|
975
|
+
};
|
976
|
+
}
|
977
|
+
}
|
978
|
+
// new layer
|
979
|
+
else if( tokVal === ',' && image.type ) {
|
980
|
+
props.images.push( image );
|
981
|
+
image = {};
|
982
|
+
}
|
983
|
+
}
|
984
|
+
else {
|
985
|
+
// Found something unrecognized; chuck everything
|
986
|
+
return null;
|
987
|
+
}
|
988
|
+
}
|
989
|
+
|
990
|
+
// leftovers
|
991
|
+
if( image.type ) {
|
992
|
+
props.images.push( image );
|
993
|
+
}
|
994
|
+
}
|
995
|
+
|
996
|
+
// Otherwise, use the standard background properties; let IE give us the values rather than parsing them
|
997
|
+
else {
|
998
|
+
this.withActualBg( function() {
|
999
|
+
var posX = cs.backgroundPositionX,
|
1000
|
+
posY = cs.backgroundPositionY,
|
1001
|
+
img = cs.backgroundImage,
|
1002
|
+
color = cs.backgroundColor;
|
1003
|
+
|
1004
|
+
props = {};
|
1005
|
+
if( color !== 'transparent' ) {
|
1006
|
+
props.color = new PIE.Color( color )
|
1007
|
+
}
|
1008
|
+
if( img !== 'none' ) {
|
1009
|
+
props.images = [ {
|
1010
|
+
type: 'image',
|
1011
|
+
url: new PIE.Tokenizer( img ).next().value,
|
1012
|
+
repeat: cs.backgroundRepeat,
|
1013
|
+
position: new PIE.BgPosition( new PIE.Tokenizer( posX + ' ' + posY ).all() )
|
1014
|
+
} ];
|
1015
|
+
}
|
1016
|
+
} );
|
1017
|
+
}
|
1018
|
+
|
1019
|
+
return props;
|
1020
|
+
},
|
1021
|
+
|
1022
|
+
/**
|
1023
|
+
* Execute a function with the actual background styles (not overridden with runtimeStyle
|
1024
|
+
* properties set by the renderers) available via currentStyle.
|
1025
|
+
* @param fn
|
1026
|
+
*/
|
1027
|
+
withActualBg: function( fn ) {
|
1028
|
+
var rs = this.element.runtimeStyle,
|
1029
|
+
rsImage = rs.backgroundImage,
|
1030
|
+
rsColor = rs.backgroundColor,
|
1031
|
+
ret;
|
1032
|
+
|
1033
|
+
rs.backgroundImage = rs.backgroundColor = '';
|
1034
|
+
|
1035
|
+
ret = fn.call( this );
|
1036
|
+
|
1037
|
+
rs.backgroundImage = rsImage;
|
1038
|
+
rs.backgroundColor = rsColor;
|
1039
|
+
|
1040
|
+
return ret;
|
1041
|
+
},
|
1042
|
+
|
1043
|
+
getCss: function() {
|
1044
|
+
var cs = this.element.currentStyle;
|
1045
|
+
return this.getCss3() ||
|
1046
|
+
this.withActualBg( function() {
|
1047
|
+
return cs.backgroundColor + ' ' + cs.backgroundImage + ' ' + cs.backgroundRepeat + ' ' +
|
1048
|
+
cs.backgroundPositionX + ' ' + cs.backgroundPositionY;
|
1049
|
+
} );
|
1050
|
+
},
|
1051
|
+
|
1052
|
+
getCss3: function() {
|
1053
|
+
var el = this.element;
|
1054
|
+
return el.style[ this.styleProperty ] || el.currentStyle.getAttribute( this.cssProperty );
|
1055
|
+
},
|
1056
|
+
|
1057
|
+
/**
|
1058
|
+
* The isActive logic is slightly different, because getProps() always returns an object
|
1059
|
+
* even if it is just falling back to the native background properties. But we only want
|
1060
|
+
* to report is as being "active" if the -pie-background override property is present and
|
1061
|
+
* parses successfully.
|
1062
|
+
*/
|
1063
|
+
isActive: function() {
|
1064
|
+
return this.getCss3() && !!this.getProps();
|
1065
|
+
}
|
1066
|
+
|
1067
|
+
} );/**
|
1068
|
+
* Handles parsing, caching, and detecting changes to border CSS
|
1069
|
+
* @constructor
|
1070
|
+
* @param {Element} el the target element
|
1071
|
+
*/
|
1072
|
+
PIE.BorderStyleInfo = PIE.StyleInfoBase.newStyleInfo( {
|
1073
|
+
|
1074
|
+
sides: [ 'Top', 'Right', 'Bottom', 'Left' ],
|
1075
|
+
namedWidths: {
|
1076
|
+
thin: '1px',
|
1077
|
+
medium: '3px',
|
1078
|
+
thick: '5px'
|
1079
|
+
},
|
1080
|
+
|
1081
|
+
parseCss: function( css ) {
|
1082
|
+
var w = {},
|
1083
|
+
s = {},
|
1084
|
+
c = {},
|
1085
|
+
active = false,
|
1086
|
+
colorsSame = true,
|
1087
|
+
stylesSame = true,
|
1088
|
+
widthsSame = true;
|
1089
|
+
|
1090
|
+
this.withActualBorder( function() {
|
1091
|
+
var el = this.element,
|
1092
|
+
cs = el.currentStyle,
|
1093
|
+
i = 0,
|
1094
|
+
style, color, width, lastStyle, lastColor, lastWidth, side, ltr;
|
1095
|
+
for( ; i < 4; i++ ) {
|
1096
|
+
side = this.sides[ i ];
|
1097
|
+
|
1098
|
+
ltr = side.charAt(0).toLowerCase();
|
1099
|
+
style = s[ ltr ] = cs[ 'border' + side + 'Style' ];
|
1100
|
+
color = cs[ 'border' + side + 'Color' ];
|
1101
|
+
width = cs[ 'border' + side + 'Width' ];
|
1102
|
+
|
1103
|
+
if( i > 0 ) {
|
1104
|
+
if( style !== lastStyle ) { stylesSame = false; }
|
1105
|
+
if( color !== lastColor ) { colorsSame = false; }
|
1106
|
+
if( width !== lastWidth ) { widthsSame = false; }
|
1107
|
+
}
|
1108
|
+
lastStyle = style;
|
1109
|
+
lastColor = color;
|
1110
|
+
lastWidth = width;
|
1111
|
+
|
1112
|
+
c[ ltr ] = new PIE.Color( color );
|
1113
|
+
|
1114
|
+
width = w[ ltr ] = new PIE.Length( s[ ltr ] === 'none' ? '0' : ( this.namedWidths[ width ] || width ) );
|
1115
|
+
if( width.pixels( this.element ) > 0 ) {
|
1116
|
+
active = true;
|
1117
|
+
}
|
1118
|
+
}
|
1119
|
+
} );
|
1120
|
+
|
1121
|
+
return active ? {
|
1122
|
+
widths: w,
|
1123
|
+
styles: s,
|
1124
|
+
colors: c,
|
1125
|
+
widthsSame: widthsSame,
|
1126
|
+
colorsSame: colorsSame,
|
1127
|
+
stylesSame: stylesSame
|
1128
|
+
} : null;
|
1129
|
+
},
|
1130
|
+
|
1131
|
+
getCss: function() {
|
1132
|
+
var el = this.element,
|
1133
|
+
cs = el.currentStyle,
|
1134
|
+
css;
|
1135
|
+
|
1136
|
+
this.withActualBorder( function() {
|
1137
|
+
css = cs.borderWidth + '|' + cs.borderStyle + '|' + cs.borderColor;
|
1138
|
+
} );
|
1139
|
+
return css;
|
1140
|
+
},
|
1141
|
+
|
1142
|
+
/**
|
1143
|
+
* Execute a function with the actual border styles (not overridden with runtimeStyle
|
1144
|
+
* properties set by the renderers) available via currentStyle.
|
1145
|
+
* @param fn
|
1146
|
+
*/
|
1147
|
+
withActualBorder: function( fn ) {
|
1148
|
+
var rs = this.element.runtimeStyle,
|
1149
|
+
rsWidth = rs.borderWidth,
|
1150
|
+
rsStyle = rs.borderStyle,
|
1151
|
+
rsColor = rs.borderColor,
|
1152
|
+
ret;
|
1153
|
+
|
1154
|
+
rs.borderWidth = rs.borderStyle = rs.borderColor = '';
|
1155
|
+
|
1156
|
+
ret = fn.call( this );
|
1157
|
+
|
1158
|
+
rs.borderWidth = rsWidth;
|
1159
|
+
rs.borderStyle = rsStyle;
|
1160
|
+
rs.borderColor = rsColor;
|
1161
|
+
|
1162
|
+
return ret;
|
1163
|
+
}
|
1164
|
+
|
1165
|
+
} );
|
1166
|
+
/**
|
1167
|
+
* Handles parsing, caching, and detecting changes to border-radius CSS
|
1168
|
+
* @constructor
|
1169
|
+
* @param {Element} el the target element
|
1170
|
+
*/
|
1171
|
+
(function() {
|
1172
|
+
|
1173
|
+
PIE.BorderRadiusStyleInfo = PIE.StyleInfoBase.newStyleInfo( {
|
1174
|
+
|
1175
|
+
cssProperty: 'border-radius',
|
1176
|
+
styleProperty: 'borderRadius',
|
1177
|
+
|
1178
|
+
parseCss: function( css ) {
|
1179
|
+
var p = null, x, y,
|
1180
|
+
tokenizer, token, length,
|
1181
|
+
hasNonZero = false;
|
1182
|
+
|
1183
|
+
function newLength( v ) {
|
1184
|
+
return new PIE.Length( v );
|
1185
|
+
}
|
1186
|
+
|
1187
|
+
if( css ) {
|
1188
|
+
tokenizer = new PIE.Tokenizer( css );
|
1189
|
+
|
1190
|
+
function collectLengths() {
|
1191
|
+
var arr = [], num;
|
1192
|
+
while( ( token = tokenizer.next() ) && token.isLengthOrPercent() ) {
|
1193
|
+
length = newLength( token.value );
|
1194
|
+
num = length.getNumber();
|
1195
|
+
if( num < 0 ) {
|
1196
|
+
return null;
|
1197
|
+
}
|
1198
|
+
if( num > 0 ) {
|
1199
|
+
hasNonZero = true;
|
1200
|
+
}
|
1201
|
+
arr.push( length );
|
1202
|
+
}
|
1203
|
+
return arr.length > 0 && arr.length < 5 ? {
|
1204
|
+
'tl': arr[0],
|
1205
|
+
'tr': arr[1] || arr[0],
|
1206
|
+
'br': arr[2] || arr[0],
|
1207
|
+
'bl': arr[3] || arr[1] || arr[0]
|
1208
|
+
} : null;
|
1209
|
+
}
|
1210
|
+
|
1211
|
+
// Grab the initial sequence of lengths
|
1212
|
+
if( x = collectLengths() ) {
|
1213
|
+
// See if there is a slash followed by more lengths, for the y-axis radii
|
1214
|
+
if( token ) {
|
1215
|
+
if( token.type & PIE.Tokenizer.Type.OPERATOR && token.value === '/' ) {
|
1216
|
+
y = collectLengths();
|
1217
|
+
}
|
1218
|
+
} else {
|
1219
|
+
y = x;
|
1220
|
+
}
|
1221
|
+
|
1222
|
+
// Treat all-zero values the same as no value
|
1223
|
+
if( hasNonZero && x && y ) {
|
1224
|
+
p = { x: x, y : y };
|
1225
|
+
}
|
1226
|
+
}
|
1227
|
+
}
|
1228
|
+
|
1229
|
+
return p;
|
1230
|
+
}
|
1231
|
+
} );
|
1232
|
+
|
1233
|
+
var ZERO = PIE.Length.ZERO,
|
1234
|
+
zeros = { 'tl': ZERO, 'tr': ZERO, 'br': ZERO, 'bl': ZERO };
|
1235
|
+
PIE.BorderRadiusStyleInfo.ALL_ZERO = { x: zeros, y: zeros };
|
1236
|
+
|
1237
|
+
})();/**
|
1238
|
+
* Handles parsing, caching, and detecting changes to border-image CSS
|
1239
|
+
* @constructor
|
1240
|
+
* @param {Element} el the target element
|
1241
|
+
*/
|
1242
|
+
PIE.BorderImageStyleInfo = PIE.StyleInfoBase.newStyleInfo( {
|
1243
|
+
|
1244
|
+
cssProperty: 'border-image',
|
1245
|
+
styleProperty: 'borderImage',
|
1246
|
+
|
1247
|
+
repeatIdents: { 'stretch':1, 'round':1, 'repeat':1, 'space':1 },
|
1248
|
+
|
1249
|
+
parseCss: function( css ) {
|
1250
|
+
var p = null, tokenizer, token, type, value,
|
1251
|
+
slices, widths, outsets,
|
1252
|
+
slashCount = 0, cs,
|
1253
|
+
Type = PIE.Tokenizer.Type,
|
1254
|
+
IDENT = Type.IDENT,
|
1255
|
+
NUMBER = Type.NUMBER,
|
1256
|
+
LENGTH = Type.LENGTH,
|
1257
|
+
PERCENT = Type.PERCENT;
|
1258
|
+
|
1259
|
+
if( css ) {
|
1260
|
+
tokenizer = new PIE.Tokenizer( css );
|
1261
|
+
p = {};
|
1262
|
+
|
1263
|
+
function isSlash( token ) {
|
1264
|
+
return token && ( token.type & Type.OPERATOR ) && ( token.value === '/' );
|
1265
|
+
}
|
1266
|
+
|
1267
|
+
function isFillIdent( token ) {
|
1268
|
+
return token && ( token.type & IDENT ) && ( token.value === 'fill' );
|
1269
|
+
}
|
1270
|
+
|
1271
|
+
function collectSlicesEtc() {
|
1272
|
+
slices = tokenizer.until( function( tok ) {
|
1273
|
+
return !( tok.type & ( NUMBER | PERCENT ) );
|
1274
|
+
} );
|
1275
|
+
|
1276
|
+
if( isFillIdent( tokenizer.next() ) && !p.fill ) {
|
1277
|
+
p.fill = true;
|
1278
|
+
} else {
|
1279
|
+
tokenizer.prev();
|
1280
|
+
}
|
1281
|
+
|
1282
|
+
if( isSlash( tokenizer.next() ) ) {
|
1283
|
+
slashCount++;
|
1284
|
+
widths = tokenizer.until( function( tok ) {
|
1285
|
+
return !( token.type & ( NUMBER | PERCENT | LENGTH ) ) && !( ( token.type & IDENT ) && token.value === 'auto' );
|
1286
|
+
} );
|
1287
|
+
|
1288
|
+
if( isSlash( tokenizer.next() ) ) {
|
1289
|
+
slashCount++;
|
1290
|
+
outsets = tokenizer.until( function( tok ) {
|
1291
|
+
return !( token.type & ( NUMBER | LENGTH ) );
|
1292
|
+
} );
|
1293
|
+
}
|
1294
|
+
} else {
|
1295
|
+
tokenizer.prev();
|
1296
|
+
}
|
1297
|
+
}
|
1298
|
+
|
1299
|
+
while( token = tokenizer.next() ) {
|
1300
|
+
type = token.type;
|
1301
|
+
value = token.value;
|
1302
|
+
|
1303
|
+
// Numbers and/or 'fill' keyword: slice values. May be followed optionally by width values, followed optionally by outset values
|
1304
|
+
if( type & ( NUMBER | PERCENT ) && !slices ) {
|
1305
|
+
tokenizer.prev();
|
1306
|
+
collectSlicesEtc();
|
1307
|
+
}
|
1308
|
+
else if( isFillIdent( token ) && !p.fill ) {
|
1309
|
+
p.fill = true;
|
1310
|
+
collectSlicesEtc();
|
1311
|
+
}
|
1312
|
+
|
1313
|
+
// Idents: one or values for 'repeat'
|
1314
|
+
else if( ( type & IDENT ) && this.repeatIdents[value] && !p.repeat ) {
|
1315
|
+
p.repeat = { h: value };
|
1316
|
+
if( token = tokenizer.next() ) {
|
1317
|
+
if( ( token.type & IDENT ) && this.repeatIdents[token.value] ) {
|
1318
|
+
p.repeat.v = token.value;
|
1319
|
+
} else {
|
1320
|
+
tokenizer.prev();
|
1321
|
+
}
|
1322
|
+
}
|
1323
|
+
}
|
1324
|
+
|
1325
|
+
// URL of the image
|
1326
|
+
else if( ( type & Type.URL ) && !p.src ) {
|
1327
|
+
p.src = value;
|
1328
|
+
}
|
1329
|
+
|
1330
|
+
// Found something unrecognized; exit.
|
1331
|
+
else {
|
1332
|
+
return null;
|
1333
|
+
}
|
1334
|
+
}
|
1335
|
+
|
1336
|
+
// Validate what we collected
|
1337
|
+
if( !p.src || !slices || slices.length < 1 || slices.length > 4 ||
|
1338
|
+
( widths && widths.length > 4 ) || ( slashCount === 1 && widths.length < 1 ) ||
|
1339
|
+
( outsets && outsets.length > 4 ) || ( slashCount === 2 && outsets.length < 1 ) ) {
|
1340
|
+
return null;
|
1341
|
+
}
|
1342
|
+
|
1343
|
+
// Fill in missing values
|
1344
|
+
if( !p.repeat ) {
|
1345
|
+
p.repeat = { h: 'stretch' };
|
1346
|
+
}
|
1347
|
+
if( !p.repeat.v ) {
|
1348
|
+
p.repeat.v = p.repeat.h;
|
1349
|
+
}
|
1350
|
+
|
1351
|
+
function distributeSides( tokens, convertFn ) {
|
1352
|
+
return {
|
1353
|
+
t: convertFn( tokens[0] ),
|
1354
|
+
r: convertFn( tokens[1] || tokens[0] ),
|
1355
|
+
b: convertFn( tokens[2] || tokens[0] ),
|
1356
|
+
l: convertFn( tokens[3] || tokens[1] || tokens[0] )
|
1357
|
+
};
|
1358
|
+
}
|
1359
|
+
|
1360
|
+
p.slice = distributeSides( slices, function( tok ) {
|
1361
|
+
return new PIE.Length( ( tok.type & NUMBER ) ? tok.value + 'px' : tok.value );
|
1362
|
+
} );
|
1363
|
+
|
1364
|
+
p.width = widths && widths.length > 0 ?
|
1365
|
+
distributeSides( widths, function( tok ) {
|
1366
|
+
return tok.type & ( LENGTH | PERCENT ) ? new PIE.Length( tok.value ) : tok.value;
|
1367
|
+
} ) :
|
1368
|
+
( cs = this.element.currentStyle ) && {
|
1369
|
+
t: new PIE.Length( cs.borderTopWidth ),
|
1370
|
+
r: new PIE.Length( cs.borderRightWidth ),
|
1371
|
+
b: new PIE.Length( cs.borderBottomWidth ),
|
1372
|
+
l: new PIE.Length( cs.borderLeftWidth )
|
1373
|
+
};
|
1374
|
+
|
1375
|
+
p.outset = distributeSides( outsets || [ 0 ], function( tok ) {
|
1376
|
+
return tok.type & LENGTH ? new PIE.Length( tok.value ) : tok.value;
|
1377
|
+
} );
|
1378
|
+
}
|
1379
|
+
|
1380
|
+
return p;
|
1381
|
+
}
|
1382
|
+
} );/**
|
1383
|
+
* Handles parsing, caching, and detecting changes to box-shadow CSS
|
1384
|
+
* @constructor
|
1385
|
+
* @param {Element} el the target element
|
1386
|
+
*/
|
1387
|
+
PIE.BoxShadowStyleInfo = PIE.StyleInfoBase.newStyleInfo( {
|
1388
|
+
|
1389
|
+
cssProperty: 'box-shadow',
|
1390
|
+
styleProperty: 'boxShadow',
|
1391
|
+
|
1392
|
+
parseCss: function( css ) {
|
1393
|
+
var props,
|
1394
|
+
Length = PIE.Length,
|
1395
|
+
Type = PIE.Tokenizer.Type,
|
1396
|
+
tokenizer;
|
1397
|
+
|
1398
|
+
if( css ) {
|
1399
|
+
tokenizer = new PIE.Tokenizer( css );
|
1400
|
+
props = { outset: [], inset: [] };
|
1401
|
+
|
1402
|
+
function parseItem() {
|
1403
|
+
var token, type, value, color, lengths, inset, len;
|
1404
|
+
|
1405
|
+
while( token = tokenizer.next() ) {
|
1406
|
+
value = token.value;
|
1407
|
+
type = token.type;
|
1408
|
+
|
1409
|
+
if( type & Type.OPERATOR && value === ',' ) {
|
1410
|
+
break;
|
1411
|
+
}
|
1412
|
+
else if( token.isLength() && !lengths ) {
|
1413
|
+
tokenizer.prev();
|
1414
|
+
lengths = tokenizer.until( function( token ) {
|
1415
|
+
return !token.isLength();
|
1416
|
+
} );
|
1417
|
+
}
|
1418
|
+
else if( type & Type.COLOR && !color ) {
|
1419
|
+
color = value;
|
1420
|
+
}
|
1421
|
+
else if( type & Type.IDENT && value === 'inset' && !inset ) {
|
1422
|
+
inset = true;
|
1423
|
+
}
|
1424
|
+
else { //encountered an unrecognized token; fail.
|
1425
|
+
return false;
|
1426
|
+
}
|
1427
|
+
}
|
1428
|
+
|
1429
|
+
len = lengths && lengths.length;
|
1430
|
+
if( len > 1 && len < 5 ) {
|
1431
|
+
( inset ? props.inset : props.outset ).push( {
|
1432
|
+
xOffset: new Length( lengths[0].value ),
|
1433
|
+
yOffset: new Length( lengths[1].value ),
|
1434
|
+
blur: new Length( lengths[2] ? lengths[2].value : '0' ),
|
1435
|
+
spread: new Length( lengths[3] ? lengths[3].value : '0' ),
|
1436
|
+
color: new PIE.Color( color || 'currentColor' )
|
1437
|
+
} );
|
1438
|
+
return true;
|
1439
|
+
}
|
1440
|
+
return false;
|
1441
|
+
}
|
1442
|
+
|
1443
|
+
while( parseItem() ) {}
|
1444
|
+
}
|
1445
|
+
|
1446
|
+
return props && ( props.inset.length || props.outset.length ) ? props : null;
|
1447
|
+
}
|
1448
|
+
} );
|
1449
|
+
/**
|
1450
|
+
* Retrieves the state of the element's visibility and display
|
1451
|
+
* @constructor
|
1452
|
+
* @param {Element} el the target element
|
1453
|
+
*/
|
1454
|
+
PIE.VisibilityStyleInfo = PIE.StyleInfoBase.newStyleInfo( {
|
1455
|
+
|
1456
|
+
getCss: function() {
|
1457
|
+
var cs = this.element.currentStyle;
|
1458
|
+
return cs.visibility + '|' + cs.display;
|
1459
|
+
},
|
1460
|
+
|
1461
|
+
parseCss: function() {
|
1462
|
+
var el = this.element,
|
1463
|
+
rs = el.runtimeStyle,
|
1464
|
+
cs = el.currentStyle,
|
1465
|
+
rsVis = rs.visibility,
|
1466
|
+
csVis;
|
1467
|
+
|
1468
|
+
rs.visibility = '';
|
1469
|
+
csVis = cs.visibility;
|
1470
|
+
rs.visibility = rsVis;
|
1471
|
+
|
1472
|
+
return {
|
1473
|
+
visible: csVis !== 'hidden',
|
1474
|
+
displayed: cs.display !== 'none'
|
1475
|
+
}
|
1476
|
+
},
|
1477
|
+
|
1478
|
+
/**
|
1479
|
+
* Always return false for isActive, since this property alone will not trigger
|
1480
|
+
* a renderer to do anything.
|
1481
|
+
*/
|
1482
|
+
isActive: function() {
|
1483
|
+
return false;
|
1484
|
+
}
|
1485
|
+
|
1486
|
+
} );
|
1487
|
+
PIE.RendererBase = {
|
1488
|
+
|
1489
|
+
/**
|
1490
|
+
* Create a new Renderer class, with the standard constructor, and augmented by
|
1491
|
+
* the RendererBase's members.
|
1492
|
+
* @param proto
|
1493
|
+
*/
|
1494
|
+
newRenderer: function( proto ) {
|
1495
|
+
function Renderer( el, styleInfos, parent ) {
|
1496
|
+
this.element = el;
|
1497
|
+
this.styleInfos = styleInfos;
|
1498
|
+
this.parent = parent;
|
1499
|
+
}
|
1500
|
+
PIE.Util.merge( Renderer.prototype, PIE.RendererBase, proto );
|
1501
|
+
return Renderer;
|
1502
|
+
},
|
1503
|
+
|
1504
|
+
/**
|
1505
|
+
* Determine if the renderer needs to be updated
|
1506
|
+
* @return {boolean}
|
1507
|
+
*/
|
1508
|
+
needsUpdate: function() {
|
1509
|
+
return false;
|
1510
|
+
},
|
1511
|
+
|
1512
|
+
/**
|
1513
|
+
* Tell the renderer to update based on modified properties
|
1514
|
+
*/
|
1515
|
+
updateProps: function() {
|
1516
|
+
},
|
1517
|
+
|
1518
|
+
/**
|
1519
|
+
* Tell the renderer to update based on modified element position
|
1520
|
+
*/
|
1521
|
+
updatePos: function() {
|
1522
|
+
},
|
1523
|
+
|
1524
|
+
/**
|
1525
|
+
* Tell the renderer to update based on modified element dimensions
|
1526
|
+
*/
|
1527
|
+
updateSize: function() {
|
1528
|
+
},
|
1529
|
+
|
1530
|
+
|
1531
|
+
/**
|
1532
|
+
* Add a layer element, with the given z-order index, to the renderer's main box element. We can't use
|
1533
|
+
* z-index because that breaks when the root rendering box's z-index is 'auto' in IE8+ standards mode.
|
1534
|
+
* So instead we make sure they are inserted into the DOM in the correct order.
|
1535
|
+
* @param {number} index
|
1536
|
+
* @param {Element} el
|
1537
|
+
*/
|
1538
|
+
addLayer: function( index, el ) {
|
1539
|
+
this.removeLayer( index );
|
1540
|
+
for( var layers = this._layers || ( this._layers = [] ), i = index + 1, len = layers.length, layer; i < len; i++ ) {
|
1541
|
+
layer = layers[i];
|
1542
|
+
if( layer ) {
|
1543
|
+
break;
|
1544
|
+
}
|
1545
|
+
}
|
1546
|
+
layers[index] = el;
|
1547
|
+
this.getBox().insertBefore( el, layer || null );
|
1548
|
+
},
|
1549
|
+
|
1550
|
+
/**
|
1551
|
+
* Retrieve a layer element by its index, or null if not present
|
1552
|
+
* @param {number} index
|
1553
|
+
* @return {Element}
|
1554
|
+
*/
|
1555
|
+
getLayer: function( index ) {
|
1556
|
+
var layers = this._layers;
|
1557
|
+
return layers && layers[index] || null;
|
1558
|
+
},
|
1559
|
+
|
1560
|
+
/**
|
1561
|
+
* Remove a layer element by its index
|
1562
|
+
* @param {number} index
|
1563
|
+
*/
|
1564
|
+
removeLayer: function( index ) {
|
1565
|
+
var layer = this.getLayer( index ),
|
1566
|
+
box = this._box;
|
1567
|
+
if( layer && box ) {
|
1568
|
+
box.removeChild( layer );
|
1569
|
+
this._layers[index] = null;
|
1570
|
+
}
|
1571
|
+
},
|
1572
|
+
|
1573
|
+
|
1574
|
+
/**
|
1575
|
+
* Get a VML shape by name, creating it if necessary.
|
1576
|
+
* @param {string} name A name identifying the element
|
1577
|
+
* @param {string=} subElName If specified a subelement of the shape will be created with this tag name
|
1578
|
+
* @param {Element} parent The parent element for the shape; will be ignored if 'group' is specified
|
1579
|
+
* @param {number=} group If specified, an ordinal group for the shape. 1 or greater. Groups are rendered
|
1580
|
+
* using container elements in the correct order, to get correct z stacking without z-index.
|
1581
|
+
*/
|
1582
|
+
getShape: function( name, subElName, parent, group ) {
|
1583
|
+
var shapes = this._shapes || ( this._shapes = {} ),
|
1584
|
+
shape = shapes[ name ],
|
1585
|
+
s;
|
1586
|
+
|
1587
|
+
if( !shape ) {
|
1588
|
+
shape = shapes[ name ] = PIE.Util.createVmlElement( 'shape' );
|
1589
|
+
if( subElName ) {
|
1590
|
+
shape.appendChild( shape[ subElName ] = PIE.Util.createVmlElement( subElName ) );
|
1591
|
+
}
|
1592
|
+
|
1593
|
+
if( group ) {
|
1594
|
+
parent = this.getLayer( group );
|
1595
|
+
if( !parent ) {
|
1596
|
+
this.addLayer( group, this.element.document.createElement( 'group' + group ) );
|
1597
|
+
parent = this.getLayer( group );
|
1598
|
+
}
|
1599
|
+
}
|
1600
|
+
|
1601
|
+
parent.appendChild( shape );
|
1602
|
+
|
1603
|
+
s = shape.style;
|
1604
|
+
s.position = 'absolute';
|
1605
|
+
s.left = s.top = 0;
|
1606
|
+
s['behavior'] = 'url(#default#VML)';
|
1607
|
+
}
|
1608
|
+
return shape;
|
1609
|
+
},
|
1610
|
+
|
1611
|
+
/**
|
1612
|
+
* Delete a named shape which was created by getShape(). Returns true if a shape with the
|
1613
|
+
* given name was found and deleted, or false if there was no shape of that name.
|
1614
|
+
* @param {string} name
|
1615
|
+
* @return {boolean}
|
1616
|
+
*/
|
1617
|
+
deleteShape: function( name ) {
|
1618
|
+
var shapes = this._shapes,
|
1619
|
+
shape = shapes && shapes[ name ];
|
1620
|
+
if( shape ) {
|
1621
|
+
shape.parentNode.removeChild( shape );
|
1622
|
+
delete shapes[ name ];
|
1623
|
+
}
|
1624
|
+
return !!shape;
|
1625
|
+
},
|
1626
|
+
|
1627
|
+
|
1628
|
+
/**
|
1629
|
+
* For a given set of border radius length/percentage values, convert them to concrete pixel
|
1630
|
+
* values based on the current size of the target element.
|
1631
|
+
* @param {Object} radii
|
1632
|
+
* @return {Object}
|
1633
|
+
*/
|
1634
|
+
getRadiiPixels: function( radii ) {
|
1635
|
+
var el = this.element,
|
1636
|
+
w = el.offsetWidth,
|
1637
|
+
h = el.offsetHeight,
|
1638
|
+
tlX, tlY, trX, trY, brX, brY, blX, blY, f;
|
1639
|
+
|
1640
|
+
tlX = radii.x['tl'].pixels( el, w );
|
1641
|
+
tlY = radii.y['tl'].pixels( el, h );
|
1642
|
+
trX = radii.x['tr'].pixels( el, w );
|
1643
|
+
trY = radii.y['tr'].pixels( el, h );
|
1644
|
+
brX = radii.x['br'].pixels( el, w );
|
1645
|
+
brY = radii.y['br'].pixels( el, h );
|
1646
|
+
blX = radii.x['bl'].pixels( el, w );
|
1647
|
+
blY = radii.y['bl'].pixels( el, h );
|
1648
|
+
|
1649
|
+
// If any corner ellipses overlap, reduce them all by the appropriate factor. This formula
|
1650
|
+
// is taken straight from the CSS3 Backgrounds and Borders spec.
|
1651
|
+
f = Math.min(
|
1652
|
+
w / ( tlX + trX ),
|
1653
|
+
h / ( trY + brY ),
|
1654
|
+
w / ( blX + brX ),
|
1655
|
+
h / ( tlY + blY )
|
1656
|
+
);
|
1657
|
+
if( f < 1 ) {
|
1658
|
+
tlX *= f;
|
1659
|
+
tlY *= f;
|
1660
|
+
trX *= f;
|
1661
|
+
trY *= f;
|
1662
|
+
brX *= f;
|
1663
|
+
brY *= f;
|
1664
|
+
blX *= f;
|
1665
|
+
blY *= f;
|
1666
|
+
}
|
1667
|
+
|
1668
|
+
return {
|
1669
|
+
x: {
|
1670
|
+
'tl': tlX,
|
1671
|
+
'tr': trX,
|
1672
|
+
'br': brX,
|
1673
|
+
'bl': blX
|
1674
|
+
},
|
1675
|
+
y: {
|
1676
|
+
'tl': tlY,
|
1677
|
+
'tr': trY,
|
1678
|
+
'br': brY,
|
1679
|
+
'bl': blY
|
1680
|
+
}
|
1681
|
+
}
|
1682
|
+
},
|
1683
|
+
|
1684
|
+
/**
|
1685
|
+
* Return the VML path string for the element's background box, with corners rounded.
|
1686
|
+
* @param {Object.<{t:number, r:number, b:number, l:number}>} shrink - if present, specifies number of
|
1687
|
+
* pixels to shrink the box path inward from the element's four sides.
|
1688
|
+
* @param {number=} mult If specified, all coordinates will be multiplied by this number
|
1689
|
+
* @param {Object=} radii If specified, this will be used for the corner radii instead of the properties
|
1690
|
+
* from this renderer's borderRadiusInfo object.
|
1691
|
+
* @return {string} the VML path
|
1692
|
+
*/
|
1693
|
+
getBoxPath: function( shrink, mult, radii ) {
|
1694
|
+
mult = mult || 1;
|
1695
|
+
|
1696
|
+
var r, str,
|
1697
|
+
el = this.element,
|
1698
|
+
w = el.offsetWidth * mult,
|
1699
|
+
h = el.offsetHeight * mult,
|
1700
|
+
radInfo = this.styleInfos.borderRadiusInfo,
|
1701
|
+
floor = Math.floor, ceil = Math.ceil,
|
1702
|
+
shrinkT = shrink ? shrink.t * mult : 0,
|
1703
|
+
shrinkR = shrink ? shrink.r * mult : 0,
|
1704
|
+
shrinkB = shrink ? shrink.b * mult : 0,
|
1705
|
+
shrinkL = shrink ? shrink.l * mult : 0,
|
1706
|
+
tlX, tlY, trX, trY, brX, brY, blX, blY;
|
1707
|
+
|
1708
|
+
if( radii || radInfo.isActive() ) {
|
1709
|
+
r = this.getRadiiPixels( radii || radInfo.getProps() );
|
1710
|
+
|
1711
|
+
tlX = r.x['tl'] * mult;
|
1712
|
+
tlY = r.y['tl'] * mult;
|
1713
|
+
trX = r.x['tr'] * mult;
|
1714
|
+
trY = r.y['tr'] * mult;
|
1715
|
+
brX = r.x['br'] * mult;
|
1716
|
+
brY = r.y['br'] * mult;
|
1717
|
+
blX = r.x['bl'] * mult;
|
1718
|
+
blY = r.y['bl'] * mult;
|
1719
|
+
|
1720
|
+
str = 'm' + floor( shrinkL ) + ',' + floor( tlY ) +
|
1721
|
+
'qy' + floor( tlX ) + ',' + floor( shrinkT ) +
|
1722
|
+
'l' + ceil( w - trX ) + ',' + floor( shrinkT ) +
|
1723
|
+
'qx' + ceil( w - shrinkR ) + ',' + floor( trY ) +
|
1724
|
+
'l' + ceil( w - shrinkR ) + ',' + ceil( h - brY ) +
|
1725
|
+
'qy' + ceil( w - brX ) + ',' + ceil( h - shrinkB ) +
|
1726
|
+
'l' + floor( blX ) + ',' + ceil( h - shrinkB ) +
|
1727
|
+
'qx' + floor( shrinkL ) + ',' + ceil( h - blY ) + ' x e';
|
1728
|
+
} else {
|
1729
|
+
// simplified path for non-rounded box
|
1730
|
+
str = 'm' + floor( shrinkL ) + ',' + floor( shrinkT ) +
|
1731
|
+
'l' + ceil( w - shrinkR ) + ',' + floor( shrinkT ) +
|
1732
|
+
'l' + ceil( w - shrinkR ) + ',' + ceil( h - shrinkB ) +
|
1733
|
+
'l' + floor( shrinkL ) + ',' + ceil( h - shrinkB ) +
|
1734
|
+
'xe';
|
1735
|
+
}
|
1736
|
+
return str;
|
1737
|
+
},
|
1738
|
+
|
1739
|
+
|
1740
|
+
/**
|
1741
|
+
* Get the container element for the shapes, creating it if necessary.
|
1742
|
+
*/
|
1743
|
+
getBox: function() {
|
1744
|
+
var box = this.parent.getLayer( this.zIndex ), s;
|
1745
|
+
|
1746
|
+
if( !box ) {
|
1747
|
+
box = this.element.document.createElement( this.boxName );
|
1748
|
+
s = box.style;
|
1749
|
+
s.position = 'absolute';
|
1750
|
+
s.top = s.left = 0;
|
1751
|
+
this.parent.addLayer( this.zIndex, box );
|
1752
|
+
}
|
1753
|
+
|
1754
|
+
return box;
|
1755
|
+
},
|
1756
|
+
|
1757
|
+
|
1758
|
+
/**
|
1759
|
+
* Destroy the rendered objects. This is a base implementation which handles common renderer
|
1760
|
+
* structures, but individual renderers may override as necessary.
|
1761
|
+
*/
|
1762
|
+
destroy: function() {
|
1763
|
+
this.parent.removeLayer( this.zIndex );
|
1764
|
+
delete this._shapes;
|
1765
|
+
delete this._layers;
|
1766
|
+
}
|
1767
|
+
};
|
1768
|
+
/**
|
1769
|
+
* Root renderer; creates the outermost container element and handles keeping it aligned
|
1770
|
+
* with the target element's size and position.
|
1771
|
+
* @param {Element} el The target element
|
1772
|
+
* @param {Object} styleInfos The StyleInfo objects
|
1773
|
+
*/
|
1774
|
+
PIE.RootRenderer = PIE.RendererBase.newRenderer( {
|
1775
|
+
|
1776
|
+
isActive: function() {
|
1777
|
+
var infos = this.styleInfos;
|
1778
|
+
for( var i in infos ) {
|
1779
|
+
if( infos.hasOwnProperty( i ) && infos[ i ].isActive() ) {
|
1780
|
+
return true;
|
1781
|
+
}
|
1782
|
+
}
|
1783
|
+
return false;
|
1784
|
+
},
|
1785
|
+
|
1786
|
+
needsUpdate: function() {
|
1787
|
+
return this.styleInfos.visibilityInfo.changed();
|
1788
|
+
},
|
1789
|
+
|
1790
|
+
updatePos: function() {
|
1791
|
+
if( this.isActive() ) {
|
1792
|
+
var el = this.element,
|
1793
|
+
par = el,
|
1794
|
+
docEl,
|
1795
|
+
elRect, parRect,
|
1796
|
+
s = this.getBox().style, cs,
|
1797
|
+
x = 0, y = 0;
|
1798
|
+
|
1799
|
+
// Get the element's offsets from its nearest positioned ancestor. Uses
|
1800
|
+
// getBoundingClientRect for accuracy and speed.
|
1801
|
+
do {
|
1802
|
+
par = par.offsetParent;
|
1803
|
+
} while( par && par.currentStyle.position === 'static' );
|
1804
|
+
elRect = el.getBoundingClientRect();
|
1805
|
+
if( par ) {
|
1806
|
+
parRect = par.getBoundingClientRect();
|
1807
|
+
cs = par.currentStyle;
|
1808
|
+
x = elRect.left - parRect.left - ( parseFloat(cs.borderLeftWidth) || 0 );
|
1809
|
+
y = elRect.top - parRect.top - ( parseFloat(cs.borderTopWidth) || 0 );
|
1810
|
+
} else {
|
1811
|
+
docEl = el.document.documentElement;
|
1812
|
+
x = elRect.left + docEl.scrollLeft - docEl.clientLeft;
|
1813
|
+
y = elRect.top + docEl.scrollTop - docEl.clientTop;
|
1814
|
+
}
|
1815
|
+
|
1816
|
+
s.left = x;
|
1817
|
+
s.top = y;
|
1818
|
+
s.zIndex = el.currentStyle.position === 'static' ? -1 : el.currentStyle.zIndex;
|
1819
|
+
}
|
1820
|
+
},
|
1821
|
+
|
1822
|
+
updateSize: function() {
|
1823
|
+
// NO-OP
|
1824
|
+
},
|
1825
|
+
|
1826
|
+
updateVisibility: function() {
|
1827
|
+
var vis = this.styleInfos.visibilityInfo.getProps();
|
1828
|
+
this.getBox().style.display = ( vis.visible && vis.displayed ) ? '' : 'none';
|
1829
|
+
},
|
1830
|
+
|
1831
|
+
updateProps: function() {
|
1832
|
+
if( this.isActive() ) {
|
1833
|
+
this.updateVisibility();
|
1834
|
+
} else {
|
1835
|
+
this.destroy();
|
1836
|
+
}
|
1837
|
+
},
|
1838
|
+
|
1839
|
+
getBox: function() {
|
1840
|
+
var box = this._box, el, s;
|
1841
|
+
if( !box ) {
|
1842
|
+
el = this.element;
|
1843
|
+
box = this._box = el.document.createElement( 'css3-container' );
|
1844
|
+
s = box.style;
|
1845
|
+
|
1846
|
+
s.position = el.currentStyle.position === 'fixed' ? 'fixed' : 'absolute';
|
1847
|
+
this.updateVisibility();
|
1848
|
+
|
1849
|
+
el.parentNode.insertBefore( box, el );
|
1850
|
+
}
|
1851
|
+
return box;
|
1852
|
+
},
|
1853
|
+
|
1854
|
+
destroy: function() {
|
1855
|
+
var box = this._box;
|
1856
|
+
if( box && box.parentNode ) {
|
1857
|
+
box.parentNode.removeChild( box );
|
1858
|
+
}
|
1859
|
+
delete this._box;
|
1860
|
+
delete this._layers;
|
1861
|
+
}
|
1862
|
+
|
1863
|
+
} );
|
1864
|
+
/**
|
1865
|
+
* Renderer for element backgrounds.
|
1866
|
+
* @constructor
|
1867
|
+
* @param {Element} el The target element
|
1868
|
+
* @param {Object} styleInfos The StyleInfo objects
|
1869
|
+
* @param {PIE.RootRenderer} parent
|
1870
|
+
*/
|
1871
|
+
PIE.BackgroundRenderer = PIE.RendererBase.newRenderer( {
|
1872
|
+
|
1873
|
+
zIndex: 2,
|
1874
|
+
boxName: 'background',
|
1875
|
+
|
1876
|
+
needsUpdate: function() {
|
1877
|
+
var si = this.styleInfos;
|
1878
|
+
return si.backgroundInfo.changed() || si.borderRadiusInfo.changed();
|
1879
|
+
},
|
1880
|
+
|
1881
|
+
isActive: function() {
|
1882
|
+
var si = this.styleInfos,
|
1883
|
+
el = this.element;
|
1884
|
+
return el.offsetWidth && el.offsetHeight && (
|
1885
|
+
si.borderImageInfo.isActive() ||
|
1886
|
+
si.borderRadiusInfo.isActive() ||
|
1887
|
+
si.backgroundInfo.isActive() ||
|
1888
|
+
( si.boxShadowInfo.isActive() && si.boxShadowInfo.getProps().inset ) );
|
1889
|
+
},
|
1890
|
+
|
1891
|
+
updateSize: function() {
|
1892
|
+
if( this.isActive() ) {
|
1893
|
+
this.draw();
|
1894
|
+
}
|
1895
|
+
},
|
1896
|
+
|
1897
|
+
updateProps: function() {
|
1898
|
+
this.destroy();
|
1899
|
+
if( this.isActive() ) {
|
1900
|
+
this.draw();
|
1901
|
+
}
|
1902
|
+
},
|
1903
|
+
|
1904
|
+
/**
|
1905
|
+
* Draw the shapes
|
1906
|
+
*/
|
1907
|
+
draw: function() {
|
1908
|
+
this.drawBgColor();
|
1909
|
+
this.drawBgImages();
|
1910
|
+
},
|
1911
|
+
|
1912
|
+
/**
|
1913
|
+
* Draw the background color shape
|
1914
|
+
*/
|
1915
|
+
drawBgColor: function() {
|
1916
|
+
var props = this.styleInfos.backgroundInfo.getProps(),
|
1917
|
+
el = this.element,
|
1918
|
+
color = props && props.color && props.color.value( el ),
|
1919
|
+
shape, w, h, s, alpha;
|
1920
|
+
|
1921
|
+
if( color && color !== 'transparent' ) {
|
1922
|
+
this.hideBackground();
|
1923
|
+
|
1924
|
+
shape = this.getShape( 'bgColor', 'fill', this.getBox(), 1 );
|
1925
|
+
w = el.offsetWidth;
|
1926
|
+
h = el.offsetHeight;
|
1927
|
+
shape.stroked = false;
|
1928
|
+
shape.coordsize = w * 2 + ',' + h * 2;
|
1929
|
+
shape.coordorigin = '1,1';
|
1930
|
+
shape.path = this.getBoxPath( null, 2 );
|
1931
|
+
s = shape.style;
|
1932
|
+
s.width = w;
|
1933
|
+
s.height = h;
|
1934
|
+
shape.fill.color = color;
|
1935
|
+
|
1936
|
+
alpha = props.color.alpha();
|
1937
|
+
if( alpha < 1 ) {
|
1938
|
+
shape.fill.opacity = alpha;
|
1939
|
+
}
|
1940
|
+
} else {
|
1941
|
+
this.deleteShape( 'bgColor' );
|
1942
|
+
}
|
1943
|
+
},
|
1944
|
+
|
1945
|
+
/**
|
1946
|
+
* Draw all the background image layers
|
1947
|
+
*/
|
1948
|
+
drawBgImages: function() {
|
1949
|
+
var props = this.styleInfos.backgroundInfo.getProps(),
|
1950
|
+
images = props && props.images,
|
1951
|
+
img, el, shape, w, h, s, i;
|
1952
|
+
|
1953
|
+
if( images ) {
|
1954
|
+
this.hideBackground();
|
1955
|
+
|
1956
|
+
el = this.element;
|
1957
|
+
w = el.offsetWidth,
|
1958
|
+
h = el.offsetHeight,
|
1959
|
+
|
1960
|
+
i = images.length;
|
1961
|
+
while( i-- ) {
|
1962
|
+
img = images[i];
|
1963
|
+
shape = this.getShape( 'bgImage' + i, 'fill', this.getBox(), 2 );
|
1964
|
+
|
1965
|
+
shape.stroked = false;
|
1966
|
+
shape.fill.type = 'tile';
|
1967
|
+
shape.fillcolor = 'none';
|
1968
|
+
shape.coordsize = w * 2 + ',' + h * 2;
|
1969
|
+
shape.coordorigin = '1,1';
|
1970
|
+
shape.path = this.getBoxPath( 0, 2 );
|
1971
|
+
s = shape.style;
|
1972
|
+
s.width = w;
|
1973
|
+
s.height = h;
|
1974
|
+
|
1975
|
+
if( img.type === 'linear-gradient' ) {
|
1976
|
+
this.addLinearGradient( shape, img );
|
1977
|
+
}
|
1978
|
+
else {
|
1979
|
+
shape.fill.src = img.url;
|
1980
|
+
this.positionBgImage( shape, i );
|
1981
|
+
}
|
1982
|
+
}
|
1983
|
+
}
|
1984
|
+
|
1985
|
+
// Delete any bgImage shapes previously created which weren't used above
|
1986
|
+
i = images ? images.length : 0;
|
1987
|
+
while( this.deleteShape( 'bgImage' + i++ ) ) {}
|
1988
|
+
},
|
1989
|
+
|
1990
|
+
|
1991
|
+
/**
|
1992
|
+
* Set the position and clipping of the background image for a layer
|
1993
|
+
* @param {Element} shape
|
1994
|
+
* @param {number} index
|
1995
|
+
*/
|
1996
|
+
positionBgImage: function( shape, index ) {
|
1997
|
+
PIE.Util.withImageSize( shape.fill.src, function( size ) {
|
1998
|
+
var fill = shape.fill,
|
1999
|
+
el = this.element,
|
2000
|
+
elW = el.offsetWidth,
|
2001
|
+
elH = el.offsetHeight,
|
2002
|
+
cs = el.currentStyle,
|
2003
|
+
si = this.styleInfos,
|
2004
|
+
border = si.borderInfo.getProps(),
|
2005
|
+
bw = border && border.widths,
|
2006
|
+
bwT = bw ? bw['t'].pixels( el ) : 0,
|
2007
|
+
bwR = bw ? bw['r'].pixels( el ) : 0,
|
2008
|
+
bwB = bw ? bw['b'].pixels( el ) : 0,
|
2009
|
+
bwL = bw ? bw['l'].pixels( el ) : 0,
|
2010
|
+
bg = si.backgroundInfo.getProps().images[ index ],
|
2011
|
+
bgPos = bg.position ? bg.position.coords( el, elW - size.w - bwL - bwR, elH - size.h - bwT - bwB ) : { x:0, y:0 },
|
2012
|
+
repeat = bg.repeat,
|
2013
|
+
pxX, pxY,
|
2014
|
+
clipT = 0, clipL = 0,
|
2015
|
+
clipR = elW + 1, clipB = elH + 1, //make sure the default clip region is not inside the box (by a subpixel)
|
2016
|
+
clipAdjust = PIE.isIE8 ? 0 : 1; //prior to IE8 requires 1 extra pixel in the image clip region
|
2017
|
+
|
2018
|
+
// Positioning - find the pixel offset from the top/left and convert to a ratio
|
2019
|
+
// The position is shifted by half a pixel, to adjust for the half-pixel coordorigin shift which is
|
2020
|
+
// needed to fix antialiasing but makes the bg image fuzzy.
|
2021
|
+
pxX = bgPos.x + bwL + 0.5;
|
2022
|
+
pxY = bgPos.y + bwT + 0.5;
|
2023
|
+
fill.position = ( pxX / elW ) + ',' + ( pxY / elH );
|
2024
|
+
|
2025
|
+
// Repeating - clip the image shape
|
2026
|
+
if( repeat && repeat !== 'repeat' ) {
|
2027
|
+
if( repeat === 'repeat-x' || repeat === 'no-repeat' ) {
|
2028
|
+
clipT = pxY + 1;
|
2029
|
+
clipB = pxY + size.h + clipAdjust;
|
2030
|
+
}
|
2031
|
+
if( repeat === 'repeat-y' || repeat === 'no-repeat' ) {
|
2032
|
+
clipL = pxX + 1;
|
2033
|
+
clipR = pxX + size.w + clipAdjust;
|
2034
|
+
}
|
2035
|
+
shape.style.clip = 'rect(' + clipT + 'px,' + clipR + 'px,' + clipB + 'px,' + clipL + 'px)';
|
2036
|
+
}
|
2037
|
+
}, this );
|
2038
|
+
},
|
2039
|
+
|
2040
|
+
|
2041
|
+
/**
|
2042
|
+
* Draw the linear gradient for a gradient layer
|
2043
|
+
* @param {Element} shape
|
2044
|
+
* @param {Object} info The object holding the information about the gradient
|
2045
|
+
*/
|
2046
|
+
addLinearGradient: function( shape, info ) {
|
2047
|
+
var el = this.element,
|
2048
|
+
w = el.offsetWidth,
|
2049
|
+
h = el.offsetHeight,
|
2050
|
+
fill = shape.fill,
|
2051
|
+
angle = info.angle,
|
2052
|
+
startPos = info.gradientStart,
|
2053
|
+
stops = info.stops,
|
2054
|
+
stopCount = stops.length,
|
2055
|
+
PI = Math.PI,
|
2056
|
+
startX, startY,
|
2057
|
+
endX, endY,
|
2058
|
+
startCornerX, startCornerY,
|
2059
|
+
endCornerX, endCornerY,
|
2060
|
+
vmlAngle, vmlGradientLength, vmlColors,
|
2061
|
+
deltaX, deltaY, lineLength,
|
2062
|
+
stopPx, vmlOffsetPct,
|
2063
|
+
p, i, j, before, after;
|
2064
|
+
|
2065
|
+
/**
|
2066
|
+
* Find the point along a given line (defined by a starting point and an angle), at which
|
2067
|
+
* that line is intersected by a perpendicular line extending through another point.
|
2068
|
+
* @param x1 - x coord of the starting point
|
2069
|
+
* @param y1 - y coord of the starting point
|
2070
|
+
* @param angle - angle of the line extending from the starting point (in degrees)
|
2071
|
+
* @param x2 - x coord of point along the perpendicular line
|
2072
|
+
* @param y2 - y coord of point along the perpendicular line
|
2073
|
+
* @return [ x, y ]
|
2074
|
+
*/
|
2075
|
+
function perpendicularIntersect( x1, y1, angle, x2, y2 ) {
|
2076
|
+
// Handle straight vertical and horizontal angles, for performance and to avoid
|
2077
|
+
// divide-by-zero errors.
|
2078
|
+
if( angle === 0 || angle === 180 ) {
|
2079
|
+
return [ x2, y1 ];
|
2080
|
+
}
|
2081
|
+
else if( angle === 90 || angle === 270 ) {
|
2082
|
+
return [ x1, y2 ];
|
2083
|
+
}
|
2084
|
+
else {
|
2085
|
+
// General approach: determine the Ax+By=C formula for each line (the slope of the second
|
2086
|
+
// line is the negative inverse of the first) and then solve for where both formulas have
|
2087
|
+
// the same x/y values.
|
2088
|
+
var a1 = Math.tan( -angle * PI / 180 ),
|
2089
|
+
c1 = a1 * x1 - y1,
|
2090
|
+
a2 = -1 / a1,
|
2091
|
+
c2 = a2 * x2 - y2,
|
2092
|
+
d = a2 - a1,
|
2093
|
+
endX = ( c2 - c1 ) / d,
|
2094
|
+
endY = ( a1 * c2 - a2 * c1 ) / d;
|
2095
|
+
return [ endX, endY ];
|
2096
|
+
}
|
2097
|
+
}
|
2098
|
+
|
2099
|
+
// Find the "start" and "end" corners; these are the corners furthest along the gradient line.
|
2100
|
+
// This is used below to find the start/end positions of the CSS3 gradient-line, and also in finding
|
2101
|
+
// the total length of the VML rendered gradient-line corner to corner.
|
2102
|
+
function findCorners() {
|
2103
|
+
startCornerX = ( angle >= 90 && angle < 270 ) ? w : 0;
|
2104
|
+
startCornerY = angle < 180 ? h : 0;
|
2105
|
+
endCornerX = w - startCornerX;
|
2106
|
+
endCornerY = h - startCornerY;
|
2107
|
+
}
|
2108
|
+
|
2109
|
+
// Normalize the angle to a value between [0, 360)
|
2110
|
+
function normalizeAngle() {
|
2111
|
+
if( angle < 0 ) {
|
2112
|
+
angle += 360;
|
2113
|
+
}
|
2114
|
+
angle = angle % 360;
|
2115
|
+
}
|
2116
|
+
|
2117
|
+
// Find the distance between two points
|
2118
|
+
function distance( p1, p2 ) {
|
2119
|
+
var dx = p2[0] - p1[0],
|
2120
|
+
dy = p2[1] - p1[1];
|
2121
|
+
return Math.abs(
|
2122
|
+
dx === 0 ? dy :
|
2123
|
+
dy === 0 ? dx :
|
2124
|
+
Math.sqrt( dx * dx + dy * dy )
|
2125
|
+
);
|
2126
|
+
}
|
2127
|
+
|
2128
|
+
// Find the start and end points of the gradient
|
2129
|
+
if( startPos ) {
|
2130
|
+
startPos = startPos.coords( el, w, h );
|
2131
|
+
startX = startPos.x;
|
2132
|
+
startY = startPos.y;
|
2133
|
+
}
|
2134
|
+
if( angle ) {
|
2135
|
+
angle = angle.degrees();
|
2136
|
+
|
2137
|
+
normalizeAngle();
|
2138
|
+
findCorners();
|
2139
|
+
|
2140
|
+
// If no start position was specified, then choose a corner as the starting point.
|
2141
|
+
if( !startPos ) {
|
2142
|
+
startX = startCornerX;
|
2143
|
+
startY = startCornerY;
|
2144
|
+
}
|
2145
|
+
|
2146
|
+
// Find the end position by extending a perpendicular line from the gradient-line which
|
2147
|
+
// intersects the corner opposite from the starting corner.
|
2148
|
+
p = perpendicularIntersect( startX, startY, angle, endCornerX, endCornerY );
|
2149
|
+
endX = p[0];
|
2150
|
+
endY = p[1];
|
2151
|
+
}
|
2152
|
+
else if( startPos ) {
|
2153
|
+
// Start position but no angle specified: find the end point by rotating 180deg around the center
|
2154
|
+
endX = w - startX;
|
2155
|
+
endY = h - startY;
|
2156
|
+
}
|
2157
|
+
else {
|
2158
|
+
// Neither position nor angle specified; create vertical gradient from top to bottom
|
2159
|
+
startX = startY = endX = 0;
|
2160
|
+
endY = h;
|
2161
|
+
}
|
2162
|
+
deltaX = endX - startX;
|
2163
|
+
deltaY = endY - startY;
|
2164
|
+
|
2165
|
+
if( angle === undefined ) {
|
2166
|
+
angle = -Math.atan2( deltaY, deltaX ) / PI * 180;
|
2167
|
+
normalizeAngle();
|
2168
|
+
findCorners();
|
2169
|
+
}
|
2170
|
+
|
2171
|
+
|
2172
|
+
// In VML land, the angle of the rendered gradient depends on the aspect ratio of the shape's
|
2173
|
+
// bounding box; for example specifying a 45 deg angle actually results in a gradient
|
2174
|
+
// drawn diagonally from one corner to its opposite corner, which will only appear to the
|
2175
|
+
// viewer as 45 degrees if the shape is equilateral. We adjust for this by taking the x/y deltas
|
2176
|
+
// between the start and end points, multiply one of them by the shape's aspect ratio,
|
2177
|
+
// and get their arctangent, resulting in an appropriate VML angle.
|
2178
|
+
vmlAngle = Math.atan2( deltaX * w / h, deltaY ) / PI * 180;
|
2179
|
+
|
2180
|
+
// VML angles are 180 degrees offset from CSS angles
|
2181
|
+
vmlAngle += 180;
|
2182
|
+
vmlAngle = vmlAngle % 360;
|
2183
|
+
|
2184
|
+
// Add all the stops to the VML 'colors' list, including the first and last stops.
|
2185
|
+
// For each, we find its pixel offset along the gradient-line; if the offset of a stop is less
|
2186
|
+
// than that of its predecessor we increase it to be equal. We then map that pixel offset to a
|
2187
|
+
// percentage along the VML gradient-line, which runs from shape corner to corner.
|
2188
|
+
lineLength = distance( [ startX, startY ], [ endX, endY ] );
|
2189
|
+
vmlGradientLength = distance( [ startCornerX, startCornerY ], perpendicularIntersect( startCornerX, startCornerY, angle, endCornerX, endCornerY ) );
|
2190
|
+
vmlColors = [];
|
2191
|
+
vmlOffsetPct = distance( [ startX, startY ], perpendicularIntersect( startX, startY, angle, startCornerX, startCornerY ) ) / vmlGradientLength * 100;
|
2192
|
+
|
2193
|
+
// Find the pixel offsets along the CSS3 gradient-line for each stop.
|
2194
|
+
stopPx = [];
|
2195
|
+
for( i = 0; i < stopCount; i++ ) {
|
2196
|
+
stopPx.push( stops[i].offset ? stops[i].offset.pixels( el, lineLength ) :
|
2197
|
+
i === 0 ? 0 : i === stopCount - 1 ? lineLength : null );
|
2198
|
+
}
|
2199
|
+
// Fill in gaps with evenly-spaced offsets
|
2200
|
+
for( i = 1; i < stopCount; i++ ) {
|
2201
|
+
if( stopPx[ i ] === null ) {
|
2202
|
+
before = stopPx[ i - 1 ];
|
2203
|
+
j = i;
|
2204
|
+
do {
|
2205
|
+
after = stopPx[ ++j ];
|
2206
|
+
} while( after === null );
|
2207
|
+
stopPx[ i ] = before + ( after - before ) / ( j - i + 1 );
|
2208
|
+
}
|
2209
|
+
// Make sure each stop's offset is no less than the one before it
|
2210
|
+
stopPx[ i ] = Math.max( stopPx[ i ], stopPx[ i - 1 ] );
|
2211
|
+
}
|
2212
|
+
|
2213
|
+
// Convert to percentage along the VML gradient line and add to the VML 'colors' value
|
2214
|
+
for( i = 0; i < stopCount; i++ ) {
|
2215
|
+
vmlColors.push(
|
2216
|
+
( vmlOffsetPct + ( stopPx[ i ] / vmlGradientLength * 100 ) ) + '% ' + stops[i].color.value( el )
|
2217
|
+
);
|
2218
|
+
}
|
2219
|
+
|
2220
|
+
// Now, finally, we're ready to render the gradient fill. Set the start and end colors to
|
2221
|
+
// the first and last stop colors; this just sets outer bounds for the gradient.
|
2222
|
+
fill['angle'] = vmlAngle;
|
2223
|
+
fill['type'] = 'gradient';
|
2224
|
+
fill['method'] = 'sigma';
|
2225
|
+
fill['color'] = stops[0].color.value( el );
|
2226
|
+
fill['color2'] = stops[stopCount - 1].color.value( el );
|
2227
|
+
fill['colors'].value = vmlColors.join( ',' );
|
2228
|
+
},
|
2229
|
+
|
2230
|
+
|
2231
|
+
/**
|
2232
|
+
* Hide the actual background image and color of the element.
|
2233
|
+
*/
|
2234
|
+
hideBackground: function() {
|
2235
|
+
var rs = this.element.runtimeStyle;
|
2236
|
+
rs.backgroundImage = 'url(about:blank)'; //ensures the background area reacts to mouse events
|
2237
|
+
rs.backgroundColor = 'transparent';
|
2238
|
+
},
|
2239
|
+
|
2240
|
+
destroy: function() {
|
2241
|
+
PIE.RendererBase.destroy.call( this );
|
2242
|
+
var rs = this.element.runtimeStyle;
|
2243
|
+
rs.backgroundImage = rs.backgroundColor = '';
|
2244
|
+
}
|
2245
|
+
|
2246
|
+
} );
|
2247
|
+
/**
|
2248
|
+
* Renderer for element borders.
|
2249
|
+
* @constructor
|
2250
|
+
* @param {Element} el The target element
|
2251
|
+
* @param {Object} styleInfos The StyleInfo objects
|
2252
|
+
* @param {PIE.RootRenderer} parent
|
2253
|
+
*/
|
2254
|
+
PIE.BorderRenderer = PIE.RendererBase.newRenderer( {
|
2255
|
+
|
2256
|
+
zIndex: 4,
|
2257
|
+
boxName: 'border',
|
2258
|
+
|
2259
|
+
needsUpdate: function() {
|
2260
|
+
var si = this.styleInfos;
|
2261
|
+
return si.borderInfo.changed() || si.borderRadiusInfo.changed();
|
2262
|
+
},
|
2263
|
+
|
2264
|
+
isActive: function() {
|
2265
|
+
var si = this.styleInfos;
|
2266
|
+
return si.borderImageInfo.isActive() ||
|
2267
|
+
si.borderRadiusInfo.isActive() ||
|
2268
|
+
si.backgroundInfo.isActive();
|
2269
|
+
},
|
2270
|
+
|
2271
|
+
updateSize: function() {
|
2272
|
+
if( this.isActive() ) {
|
2273
|
+
this.drawBorder();
|
2274
|
+
}
|
2275
|
+
},
|
2276
|
+
|
2277
|
+
updateProps: function() {
|
2278
|
+
this.destroy();
|
2279
|
+
if( this.isActive() ) {
|
2280
|
+
this.drawBorder();
|
2281
|
+
}
|
2282
|
+
},
|
2283
|
+
|
2284
|
+
|
2285
|
+
/**
|
2286
|
+
* Draw the border shape(s)
|
2287
|
+
*/
|
2288
|
+
drawBorder: function() {
|
2289
|
+
var el = this.element,
|
2290
|
+
cs = el.currentStyle,
|
2291
|
+
w = el.offsetWidth,
|
2292
|
+
h = el.offsetHeight,
|
2293
|
+
props = this.styleInfos.borderInfo.getProps(),
|
2294
|
+
side, shape, stroke, bColor, bWidth, bStyle, s,
|
2295
|
+
segments, seg, i, len;
|
2296
|
+
|
2297
|
+
if( props ) {
|
2298
|
+
this.hideBorder();
|
2299
|
+
|
2300
|
+
segments = this.getBorderSegments( 2 );
|
2301
|
+
for( i = 0, len = segments.length; i < len; i++) {
|
2302
|
+
seg = segments[i];
|
2303
|
+
shape = this.getShape( 'borderPiece' + i, seg.stroke ? 'stroke' : 'fill', this.getBox() );
|
2304
|
+
shape.coordsize = w * 2 + ',' + h * 2;
|
2305
|
+
shape.coordorigin = '1,1';
|
2306
|
+
shape.path = seg.path;
|
2307
|
+
s = shape.style;
|
2308
|
+
s.width = w;
|
2309
|
+
s.height = h;
|
2310
|
+
|
2311
|
+
shape.filled = !!seg.fill;
|
2312
|
+
shape.stroked = !!seg.stroke;
|
2313
|
+
if( seg.stroke ) {
|
2314
|
+
stroke = shape.stroke;
|
2315
|
+
stroke['weight'] = seg.weight + 'px';
|
2316
|
+
stroke.color = seg.color.value( el );
|
2317
|
+
stroke['dashstyle'] = seg.stroke === 'dashed' ? '2 2' : seg.stroke === 'dotted' ? '1 1' : 'solid';
|
2318
|
+
stroke['linestyle'] = seg.stroke === 'double' && seg.weight > 2 ? 'ThinThin' : 'Single';
|
2319
|
+
} else {
|
2320
|
+
shape.fill.color = seg.fill.value( el );
|
2321
|
+
}
|
2322
|
+
}
|
2323
|
+
|
2324
|
+
// remove any previously-created border shapes which didn't get used above
|
2325
|
+
while( this.deleteShape( 'borderPiece' + i++ ) ) {}
|
2326
|
+
}
|
2327
|
+
},
|
2328
|
+
|
2329
|
+
/**
|
2330
|
+
* Hide the actual border of the element. In IE7 and up we can just set its color to transparent;
|
2331
|
+
* however IE6 does not support transparent borders so we have to get tricky with it. Also, some elements
|
2332
|
+
* like form buttons require removing the border width altogether, so for those we increase the padding
|
2333
|
+
* by the border size.
|
2334
|
+
*/
|
2335
|
+
hideBorder: function() {
|
2336
|
+
var el = this.element,
|
2337
|
+
cs = el.currentStyle,
|
2338
|
+
rs = el.runtimeStyle,
|
2339
|
+
tag = el.tagName,
|
2340
|
+
sides, side, i;
|
2341
|
+
|
2342
|
+
if( tag === 'BUTTON' || ( tag === 'INPUT' && el.type in { 'submit':1, 'button':1, 'reset':1 } ) ) {
|
2343
|
+
rs.borderWidth = '';
|
2344
|
+
sides = this.styleInfos.borderInfo.sides;
|
2345
|
+
for( i = sides.length; i--; ) {
|
2346
|
+
side = sides[ i ];
|
2347
|
+
rs[ 'padding' + side ] = '';
|
2348
|
+
rs[ 'padding' + side ] = parseInt( cs[ 'padding' + side ] ) +
|
2349
|
+
parseInt( cs[ 'border' + side + 'Width' ] ) +
|
2350
|
+
( !PIE.isIE8 && i % 2 ? 1 : 0 ); //needs an extra horizontal pixel to counteract the extra "inner border" going away
|
2351
|
+
}
|
2352
|
+
rs.borderWidth = 0;
|
2353
|
+
}
|
2354
|
+
else if( PIE.isIE6 ) {
|
2355
|
+
// Wrap all the element's children in a custom element, set the element to visiblity:hidden,
|
2356
|
+
// and set the wrapper element to visiblity:visible. This hides the outer element's decorations
|
2357
|
+
// (background and border) but displays all the contents.
|
2358
|
+
// TODO find a better way to do this that doesn't mess up the DOM parent-child relationship,
|
2359
|
+
// as this can interfere with other author scripts which add/modify/delete children. Also, this
|
2360
|
+
// won't work for elements which cannot take children, e.g. input/button/textarea/img/etc. Look into
|
2361
|
+
// using a compositor filter or some other filter which masks the border.
|
2362
|
+
if( el.childNodes.length !== 1 || el.firstChild.tagName !== 'ie6-mask' ) {
|
2363
|
+
var cont = el.document.createElement( 'ie6-mask' ),
|
2364
|
+
s = cont.style, child;
|
2365
|
+
s.visibility = 'visible';
|
2366
|
+
s.zoom = 1;
|
2367
|
+
while( child = el.firstChild ) {
|
2368
|
+
cont.appendChild( child );
|
2369
|
+
}
|
2370
|
+
el.appendChild( cont );
|
2371
|
+
rs.visibility = 'hidden';
|
2372
|
+
}
|
2373
|
+
}
|
2374
|
+
else {
|
2375
|
+
rs.borderColor = 'transparent';
|
2376
|
+
}
|
2377
|
+
},
|
2378
|
+
|
2379
|
+
|
2380
|
+
/**
|
2381
|
+
* Get the VML path definitions for the border segment(s).
|
2382
|
+
* @param {number=} mult If specified, all coordinates will be multiplied by this number
|
2383
|
+
* @return {Array.<string>}
|
2384
|
+
*/
|
2385
|
+
getBorderSegments: function( mult ) {
|
2386
|
+
var el = this.element,
|
2387
|
+
elW, elH,
|
2388
|
+
borderInfo = this.styleInfos.borderInfo,
|
2389
|
+
segments = [],
|
2390
|
+
floor, ceil, wT, wR, wB, wL,
|
2391
|
+
borderProps, radiusInfo, radii, widths, styles, colors;
|
2392
|
+
|
2393
|
+
if( borderInfo.isActive() ) {
|
2394
|
+
borderProps = borderInfo.getProps();
|
2395
|
+
|
2396
|
+
widths = borderProps.widths;
|
2397
|
+
styles = borderProps.styles;
|
2398
|
+
colors = borderProps.colors;
|
2399
|
+
|
2400
|
+
if( borderProps.widthsSame && borderProps.stylesSame && borderProps.colorsSame ) {
|
2401
|
+
// shortcut for identical border on all sides - only need 1 stroked shape
|
2402
|
+
wT = widths['t'].pixels( el ); //thickness
|
2403
|
+
wR = wT / 2; //shrink
|
2404
|
+
segments.push( {
|
2405
|
+
path: this.getBoxPath( { t: wR, r: wR, b: wR, l: wR }, mult ),
|
2406
|
+
stroke: styles['t'],
|
2407
|
+
color: colors['t'],
|
2408
|
+
weight: wT
|
2409
|
+
} );
|
2410
|
+
}
|
2411
|
+
else {
|
2412
|
+
mult = mult || 1;
|
2413
|
+
elW = el.offsetWidth;
|
2414
|
+
elH = el.offsetHeight;
|
2415
|
+
|
2416
|
+
wT = widths['t'].pixels( el );
|
2417
|
+
wR = widths['r'].pixels( el );
|
2418
|
+
wB = widths['b'].pixels( el );
|
2419
|
+
wL = widths['l'].pixels( el );
|
2420
|
+
var pxWidths = {
|
2421
|
+
't': wT,
|
2422
|
+
'r': wR,
|
2423
|
+
'b': wB,
|
2424
|
+
'l': wL
|
2425
|
+
};
|
2426
|
+
|
2427
|
+
radiusInfo = this.styleInfos.borderRadiusInfo;
|
2428
|
+
if( radiusInfo.isActive() ) {
|
2429
|
+
radii = this.getRadiiPixels( radiusInfo.getProps() );
|
2430
|
+
}
|
2431
|
+
|
2432
|
+
floor = Math.floor;
|
2433
|
+
ceil = Math.ceil;
|
2434
|
+
|
2435
|
+
function radius( xy, corner ) {
|
2436
|
+
return radii ? radii[ xy ][ corner ] : 0;
|
2437
|
+
}
|
2438
|
+
|
2439
|
+
function curve( corner, shrinkX, shrinkY, startAngle, ccw, doMove ) {
|
2440
|
+
var rx = radius( 'x', corner),
|
2441
|
+
ry = radius( 'y', corner),
|
2442
|
+
deg = 65535,
|
2443
|
+
isRight = corner.charAt( 1 ) === 'r',
|
2444
|
+
isBottom = corner.charAt( 0 ) === 'b';
|
2445
|
+
return ( rx > 0 && ry > 0 ) ?
|
2446
|
+
( doMove ? 'al' : 'ae' ) +
|
2447
|
+
( isRight ? ceil( elW - rx ) : floor( rx ) ) * mult + ',' + // center x
|
2448
|
+
( isBottom ? ceil( elH - ry ) : floor( ry ) ) * mult + ',' + // center y
|
2449
|
+
( floor( rx ) - shrinkX ) * mult + ',' + // width
|
2450
|
+
( floor( ry ) - shrinkY ) * mult + ',' + // height
|
2451
|
+
( startAngle * deg ) + ',' + // start angle
|
2452
|
+
( 45 * deg * ( ccw ? 1 : -1 ) // angle change
|
2453
|
+
) : (
|
2454
|
+
( doMove ? 'm' : 'l' ) +
|
2455
|
+
( isRight ? elW - shrinkX : shrinkX ) * mult + ',' +
|
2456
|
+
( isBottom ? elH - shrinkY : shrinkY ) * mult
|
2457
|
+
);
|
2458
|
+
}
|
2459
|
+
|
2460
|
+
function line( side, shrink, ccw, doMove ) {
|
2461
|
+
var
|
2462
|
+
start = (
|
2463
|
+
side === 't' ?
|
2464
|
+
floor( radius( 'x', 'tl') ) * mult + ',' + ceil( shrink ) * mult :
|
2465
|
+
side === 'r' ?
|
2466
|
+
ceil( elW - shrink ) * mult + ',' + floor( radius( 'y', 'tr') ) * mult :
|
2467
|
+
side === 'b' ?
|
2468
|
+
ceil( elW - radius( 'x', 'br') ) * mult + ',' + floor( elH - shrink ) * mult :
|
2469
|
+
// side === 'l' ?
|
2470
|
+
floor( shrink ) * mult + ',' + ceil( elH - radius( 'y', 'bl') ) * mult
|
2471
|
+
),
|
2472
|
+
end = (
|
2473
|
+
side === 't' ?
|
2474
|
+
ceil( elW - radius( 'x', 'tr') ) * mult + ',' + ceil( shrink ) * mult :
|
2475
|
+
side === 'r' ?
|
2476
|
+
ceil( elW - shrink ) * mult + ',' + ceil( elH - radius( 'y', 'br') ) * mult :
|
2477
|
+
side === 'b' ?
|
2478
|
+
floor( radius( 'x', 'bl') ) * mult + ',' + floor( elH - shrink ) * mult :
|
2479
|
+
// side === 'l' ?
|
2480
|
+
floor( shrink ) * mult + ',' + floor( radius( 'y', 'tl') ) * mult
|
2481
|
+
);
|
2482
|
+
return ccw ? ( doMove ? 'm' + end : '' ) + 'l' + start :
|
2483
|
+
( doMove ? 'm' + start : '' ) + 'l' + end;
|
2484
|
+
}
|
2485
|
+
|
2486
|
+
|
2487
|
+
function addSide( side, sideBefore, sideAfter, cornerBefore, cornerAfter, baseAngle ) {
|
2488
|
+
var vert = side === 'l' || side === 'r',
|
2489
|
+
sideW = pxWidths[ side ],
|
2490
|
+
beforeX, beforeY, afterX, afterY;
|
2491
|
+
|
2492
|
+
if( sideW > 0 && styles[ side ] !== 'none' ) {
|
2493
|
+
beforeX = pxWidths[ vert ? side : sideBefore ];
|
2494
|
+
beforeY = pxWidths[ vert ? sideBefore : side ];
|
2495
|
+
afterX = pxWidths[ vert ? side : sideAfter ];
|
2496
|
+
afterY = pxWidths[ vert ? sideAfter : side ];
|
2497
|
+
|
2498
|
+
if( styles[ side ] === 'dashed' || styles[ side ] === 'dotted' ) {
|
2499
|
+
segments.push( {
|
2500
|
+
path: curve( cornerBefore, beforeX, beforeY, baseAngle + 45, 0, 1 ) +
|
2501
|
+
curve( cornerBefore, 0, 0, baseAngle, 1, 0 ),
|
2502
|
+
fill: colors[ side ]
|
2503
|
+
} );
|
2504
|
+
segments.push( {
|
2505
|
+
path: line( side, sideW / 2, 0, 1 ),
|
2506
|
+
stroke: styles[ side ],
|
2507
|
+
weight: sideW,
|
2508
|
+
color: colors[ side ]
|
2509
|
+
} );
|
2510
|
+
segments.push( {
|
2511
|
+
path: curve( cornerAfter, afterX, afterY, baseAngle, 0, 1 ) +
|
2512
|
+
curve( cornerAfter, 0, 0, baseAngle - 45, 1, 0 ),
|
2513
|
+
fill: colors[ side ]
|
2514
|
+
} );
|
2515
|
+
}
|
2516
|
+
else {
|
2517
|
+
segments.push( {
|
2518
|
+
path: curve( cornerBefore, beforeX, beforeY, baseAngle + 45, 0, 1 ) +
|
2519
|
+
line( side, sideW, 0, 0 ) +
|
2520
|
+
curve( cornerAfter, afterX, afterY, baseAngle, 0, 0 ) +
|
2521
|
+
|
2522
|
+
( styles[ side ] === 'double' && sideW > 2 ?
|
2523
|
+
curve( cornerAfter, afterX - floor( afterX / 3 ), afterY - floor( afterY / 3 ), baseAngle - 45, 1, 0 ) +
|
2524
|
+
line( side, ceil( sideW / 3 * 2 ), 1, 0 ) +
|
2525
|
+
curve( cornerBefore, beforeX - floor( beforeX / 3 ), beforeY - floor( beforeY / 3 ), baseAngle, 1, 0 ) +
|
2526
|
+
'x ' +
|
2527
|
+
curve( cornerBefore, floor( beforeX / 3 ), floor( beforeY / 3 ), baseAngle + 45, 0, 1 ) +
|
2528
|
+
line( side, floor( sideW / 3 ), 1, 0 ) +
|
2529
|
+
curve( cornerAfter, floor( afterX / 3 ), floor( afterY / 3 ), baseAngle, 0, 0 )
|
2530
|
+
: '' ) +
|
2531
|
+
|
2532
|
+
curve( cornerAfter, 0, 0, baseAngle - 45, 1, 0 ) +
|
2533
|
+
line( side, 0, 1, 0 ) +
|
2534
|
+
curve( cornerBefore, 0, 0, baseAngle, 1, 0 ),
|
2535
|
+
fill: colors[ side ]
|
2536
|
+
} );
|
2537
|
+
}
|
2538
|
+
}
|
2539
|
+
}
|
2540
|
+
|
2541
|
+
addSide( 't', 'l', 'r', 'tl', 'tr', 90 );
|
2542
|
+
addSide( 'r', 't', 'b', 'tr', 'br', 0 );
|
2543
|
+
addSide( 'b', 'r', 'l', 'br', 'bl', -90 );
|
2544
|
+
addSide( 'l', 'b', 't', 'bl', 'tl', -180 );
|
2545
|
+
}
|
2546
|
+
}
|
2547
|
+
|
2548
|
+
return segments;
|
2549
|
+
},
|
2550
|
+
|
2551
|
+
destroy: function() {
|
2552
|
+
PIE.RendererBase.destroy.call( this );
|
2553
|
+
this.element.runtimeStyle.borderColor = '';
|
2554
|
+
}
|
2555
|
+
|
2556
|
+
|
2557
|
+
} );
|
2558
|
+
/**
|
2559
|
+
* Renderer for border-image
|
2560
|
+
* @constructor
|
2561
|
+
* @param {Element} el The target element
|
2562
|
+
* @param {Object} styleInfos The StyleInfo objects
|
2563
|
+
* @param {PIE.RootRenderer} parent
|
2564
|
+
*/
|
2565
|
+
PIE.BorderImageRenderer = PIE.RendererBase.newRenderer( {
|
2566
|
+
|
2567
|
+
zIndex: 5,
|
2568
|
+
pieceNames: [ 't', 'tr', 'r', 'br', 'b', 'bl', 'l', 'tl', 'c' ],
|
2569
|
+
|
2570
|
+
needsUpdate: function() {
|
2571
|
+
var si = this.styleInfos;
|
2572
|
+
return si.borderImageInfo.changed() || si.borderImageInfo.changed();
|
2573
|
+
},
|
2574
|
+
|
2575
|
+
isActive: function() {
|
2576
|
+
return this.styleInfos.borderImageInfo.isActive();
|
2577
|
+
},
|
2578
|
+
|
2579
|
+
updateSize: function() {
|
2580
|
+
if( this.isActive() ) {
|
2581
|
+
var props = this.styleInfos.borderImageInfo.getProps(),
|
2582
|
+
box = this.getBox(), //make sure pieces are created
|
2583
|
+
el = this.element,
|
2584
|
+
pieces = this.pieces;
|
2585
|
+
|
2586
|
+
PIE.Util.withImageSize( props.src, function( imgSize ) {
|
2587
|
+
var elW = el.offsetWidth,
|
2588
|
+
elH = el.offsetHeight,
|
2589
|
+
|
2590
|
+
widths = props.width,
|
2591
|
+
widthT = widths.t.pixels( el ),
|
2592
|
+
widthR = widths.r.pixels( el ),
|
2593
|
+
widthB = widths.b.pixels( el ),
|
2594
|
+
widthL = widths.l.pixels( el ),
|
2595
|
+
slices = props.slice,
|
2596
|
+
sliceT = slices.t.pixels( el ),
|
2597
|
+
sliceR = slices.r.pixels( el ),
|
2598
|
+
sliceB = slices.b.pixels( el ),
|
2599
|
+
sliceL = slices.l.pixels( el );
|
2600
|
+
|
2601
|
+
// Piece positions and sizes
|
2602
|
+
function setSizeAndPos( piece, w, h, x, y ) {
|
2603
|
+
var s = pieces[piece].style;
|
2604
|
+
s.width = w;
|
2605
|
+
s.height = h;
|
2606
|
+
s.left = x;
|
2607
|
+
s.top = y;
|
2608
|
+
}
|
2609
|
+
setSizeAndPos( 'tl', widthL, widthT, 0, 0 );
|
2610
|
+
setSizeAndPos( 't', elW - widthL - widthR, widthT, widthL, 0 );
|
2611
|
+
setSizeAndPos( 'tr', widthR, widthT, elW - widthR, 0 );
|
2612
|
+
setSizeAndPos( 'r', widthR, elH - widthT - widthB, elW - widthR, widthT );
|
2613
|
+
setSizeAndPos( 'br', widthR, widthB, elW - widthR, elH - widthB );
|
2614
|
+
setSizeAndPos( 'b', elW - widthL - widthR, widthB, widthL, elH - widthB );
|
2615
|
+
setSizeAndPos( 'bl', widthL, widthB, 0, elH - widthB );
|
2616
|
+
setSizeAndPos( 'l', widthL, elH - widthT - widthB, 0, widthT );
|
2617
|
+
setSizeAndPos( 'c', elW - widthL - widthR, elH - widthT - widthB, widthL, widthT );
|
2618
|
+
|
2619
|
+
|
2620
|
+
// image croppings
|
2621
|
+
function setCrops( sides, crop, val ) {
|
2622
|
+
for( var i=0, len=sides.length; i < len; i++ ) {
|
2623
|
+
pieces[ sides[i] ]['imagedata'][ crop ] = val;
|
2624
|
+
}
|
2625
|
+
}
|
2626
|
+
|
2627
|
+
// corners
|
2628
|
+
setCrops( [ 'tl', 't', 'tr' ], 'cropBottom', ( imgSize.h - sliceT ) / imgSize.h );
|
2629
|
+
setCrops( [ 'tl', 'l', 'bl' ], 'cropRight', ( imgSize.w - sliceL ) / imgSize.w );
|
2630
|
+
setCrops( [ 'bl', 'b', 'br' ], 'cropTop', ( imgSize.h - sliceB ) / imgSize.h );
|
2631
|
+
setCrops( [ 'tr', 'r', 'br' ], 'cropLeft', ( imgSize.w - sliceR ) / imgSize.w );
|
2632
|
+
|
2633
|
+
// edges and center
|
2634
|
+
if( props.repeat.v === 'stretch' ) {
|
2635
|
+
setCrops( [ 'l', 'r', 'c' ], 'cropTop', sliceT / imgSize.h );
|
2636
|
+
setCrops( [ 'l', 'r', 'c' ], 'cropBottom', sliceB / imgSize.h );
|
2637
|
+
}
|
2638
|
+
if( props.repeat.h === 'stretch' ) {
|
2639
|
+
setCrops( [ 't', 'b', 'c' ], 'cropLeft', sliceL / imgSize.w );
|
2640
|
+
setCrops( [ 't', 'b', 'c' ], 'cropRight', sliceR / imgSize.w );
|
2641
|
+
}
|
2642
|
+
|
2643
|
+
// center fill
|
2644
|
+
pieces['c'].style.display = props.fill ? '' : 'none';
|
2645
|
+
}, this );
|
2646
|
+
} else {
|
2647
|
+
this.destroy();
|
2648
|
+
}
|
2649
|
+
},
|
2650
|
+
|
2651
|
+
updateProps: function() {
|
2652
|
+
this.destroy();
|
2653
|
+
if( this.isActive() ) {
|
2654
|
+
this.updateSize();
|
2655
|
+
}
|
2656
|
+
},
|
2657
|
+
|
2658
|
+
getBox: function() {
|
2659
|
+
var box = this._box, s, piece, i,
|
2660
|
+
pieceNames = this.pieceNames,
|
2661
|
+
len = pieceNames.length;
|
2662
|
+
|
2663
|
+
if( !box ) {
|
2664
|
+
box = this._box = this.element.document.createElement( 'border-image' );
|
2665
|
+
s = box.style;
|
2666
|
+
s.position = 'absolute';
|
2667
|
+
|
2668
|
+
this.pieces = {};
|
2669
|
+
|
2670
|
+
for( i = 0; i < len; i++ ) {
|
2671
|
+
piece = this.pieces[ pieceNames[i] ] = PIE.Util.createVmlElement( 'rect' );
|
2672
|
+
piece.appendChild( PIE.Util.createVmlElement( 'imagedata' ) );
|
2673
|
+
s = piece.style;
|
2674
|
+
s['behavior'] = 'url(#default#VML)';
|
2675
|
+
s.position = "absolute";
|
2676
|
+
s.top = s.left = 0;
|
2677
|
+
piece['imagedata'].src = this.styleInfos.borderImageInfo.getProps().src;
|
2678
|
+
piece.stroked = false;
|
2679
|
+
piece.filled = false;
|
2680
|
+
box.appendChild( piece );
|
2681
|
+
}
|
2682
|
+
|
2683
|
+
this.parent.addLayer( this.zIndex, box )
|
2684
|
+
}
|
2685
|
+
|
2686
|
+
return box;
|
2687
|
+
}
|
2688
|
+
|
2689
|
+
} );
|
2690
|
+
/**
|
2691
|
+
* Renderer for outset box-shadows
|
2692
|
+
* @constructor
|
2693
|
+
* @param {Element} el The target element
|
2694
|
+
* @param {Object} styleInfos The StyleInfo objects
|
2695
|
+
* @param {PIE.RootRenderer} parent
|
2696
|
+
*/
|
2697
|
+
PIE.BoxShadowOutsetRenderer = PIE.RendererBase.newRenderer( {
|
2698
|
+
|
2699
|
+
zIndex: 1,
|
2700
|
+
boxName: 'outset-box-shadow',
|
2701
|
+
|
2702
|
+
needsUpdate: function() {
|
2703
|
+
var si = this.styleInfos;
|
2704
|
+
return si.boxShadowInfo.changed() || si.borderRadiusInfo.changed();
|
2705
|
+
},
|
2706
|
+
|
2707
|
+
isActive: function() {
|
2708
|
+
var boxShadowInfo = this.styleInfos.boxShadowInfo;
|
2709
|
+
return boxShadowInfo.isActive() && boxShadowInfo.getProps().outset[0];
|
2710
|
+
},
|
2711
|
+
|
2712
|
+
updateSize: function() {
|
2713
|
+
if( this.isActive() ) {
|
2714
|
+
var me = this,
|
2715
|
+
el = this.element,
|
2716
|
+
box = this.getBox(),
|
2717
|
+
styleInfos = this.styleInfos,
|
2718
|
+
shadowInfos = styleInfos.boxShadowInfo.getProps().outset,
|
2719
|
+
radii = styleInfos.borderRadiusInfo.getProps(),
|
2720
|
+
len = shadowInfos.length,
|
2721
|
+
i = len, j,
|
2722
|
+
w = el.offsetWidth,
|
2723
|
+
h = el.offsetHeight,
|
2724
|
+
clipAdjust = PIE.isIE8 ? 1 : 0, //workaround for IE8 bug where VML leaks out top/left of clip region by 1px
|
2725
|
+
corners = [ 'tl', 'tr', 'br', 'bl' ], corner,
|
2726
|
+
shadowInfo, shape, fill, ss, xOff, yOff, spread, blur, shrink, color, alpha, path,
|
2727
|
+
totalW, totalH, focusX, focusY, isBottom, isRight;
|
2728
|
+
|
2729
|
+
|
2730
|
+
function getShadowShape( index, corner, xOff, yOff, color, blur, path ) {
|
2731
|
+
var shape = me.getShape( 'shadow' + index + corner, 'fill', box, len - index ),
|
2732
|
+
ss = shape.style,
|
2733
|
+
fill = shape.fill;
|
2734
|
+
|
2735
|
+
// Position and size
|
2736
|
+
ss.left = xOff;
|
2737
|
+
ss.top = yOff;
|
2738
|
+
shape['coordsize'] = w * 2 + ',' + h * 2;
|
2739
|
+
shape['coordorigin'] = '1,1';
|
2740
|
+
|
2741
|
+
// Color and opacity
|
2742
|
+
shape['stroked'] = false;
|
2743
|
+
shape['filled'] = true;
|
2744
|
+
fill.color = color.value( el );
|
2745
|
+
if( blur ) {
|
2746
|
+
fill['type'] = 'gradienttitle'; //makes the VML gradient follow the shape's outline - hooray for undocumented features?!?!
|
2747
|
+
fill['color2'] = fill.color;
|
2748
|
+
fill['opacity'] = 0;
|
2749
|
+
}
|
2750
|
+
|
2751
|
+
// Path
|
2752
|
+
shape.path = path;
|
2753
|
+
|
2754
|
+
// This needs to go last for some reason, to prevent rendering at incorrect size
|
2755
|
+
ss.width = w;
|
2756
|
+
ss.height = h;
|
2757
|
+
|
2758
|
+
return shape;
|
2759
|
+
}
|
2760
|
+
|
2761
|
+
|
2762
|
+
while( i-- ) {
|
2763
|
+
shadowInfo = shadowInfos[ i ];
|
2764
|
+
xOff = shadowInfo.xOffset.pixels( el );
|
2765
|
+
yOff = shadowInfo.yOffset.pixels( el );
|
2766
|
+
spread = shadowInfo.spread.pixels( el ),
|
2767
|
+
blur = shadowInfo.blur.pixels( el );
|
2768
|
+
color = shadowInfo.color;
|
2769
|
+
// Shape path
|
2770
|
+
shrink = -spread - blur;
|
2771
|
+
if( !radii && blur ) {
|
2772
|
+
// If blurring, use a non-null border radius info object so that getBoxPath will
|
2773
|
+
// round the corners of the expanded shadow shape rather than squaring them off.
|
2774
|
+
radii = PIE.BorderRadiusStyleInfo.ALL_ZERO;
|
2775
|
+
}
|
2776
|
+
path = this.getBoxPath( { t: shrink, r: shrink, b: shrink, l: shrink }, 2, radii );
|
2777
|
+
|
2778
|
+
if( blur ) {
|
2779
|
+
totalW = ( spread + blur ) * 2 + w;
|
2780
|
+
totalH = ( spread + blur ) * 2 + h;
|
2781
|
+
focusX = blur * 2 / totalW;
|
2782
|
+
focusY = blur * 2 / totalH;
|
2783
|
+
if( blur - spread > w / 2 || blur - spread > h / 2 ) {
|
2784
|
+
// If the blur is larger than half the element's narrowest dimension, we cannot do
|
2785
|
+
// this with a single shape gradient, because its focussize would have to be less than
|
2786
|
+
// zero which results in ugly artifacts. Instead we create four shapes, each with its
|
2787
|
+
// gradient focus past center, and then clip them so each only shows the quadrant
|
2788
|
+
// opposite the focus.
|
2789
|
+
for( j = 4; j--; ) {
|
2790
|
+
corner = corners[j];
|
2791
|
+
isBottom = corner.charAt( 0 ) === 'b';
|
2792
|
+
isRight = corner.charAt( 1 ) === 'r';
|
2793
|
+
shape = getShadowShape( i, corner, xOff, yOff, color, blur, path );
|
2794
|
+
fill = shape.fill;
|
2795
|
+
fill['focusposition'] = ( isRight ? 1 - focusX : focusX ) + ',' +
|
2796
|
+
( isBottom ? 1 - focusY : focusY );
|
2797
|
+
fill['focussize'] = '0,0';
|
2798
|
+
|
2799
|
+
// Clip to show only the appropriate quadrant. Add 1px to the top/left clip values
|
2800
|
+
// in IE8 to prevent a bug where IE8 displays one pixel outside the clip region.
|
2801
|
+
shape.style.clip = 'rect(' + ( ( isBottom ? totalH / 2 : 0 ) + clipAdjust ) + 'px,' +
|
2802
|
+
( isRight ? totalW : totalW / 2 ) + 'px,' +
|
2803
|
+
( isBottom ? totalH : totalH / 2 ) + 'px,' +
|
2804
|
+
( ( isRight ? totalW / 2 : 0 ) + clipAdjust ) + 'px)';
|
2805
|
+
}
|
2806
|
+
} else {
|
2807
|
+
// TODO delete old quadrant shapes if resizing expands past the barrier
|
2808
|
+
shape = getShadowShape( i, '', xOff, yOff, color, blur, path );
|
2809
|
+
fill = shape.fill;
|
2810
|
+
fill['focusposition'] = focusX + ',' + focusY;
|
2811
|
+
fill['focussize'] = ( 1 - focusX * 2 ) + ',' + ( 1 - focusY * 2 );
|
2812
|
+
}
|
2813
|
+
} else {
|
2814
|
+
shape = getShadowShape( i, '', xOff, yOff, color, blur, path );
|
2815
|
+
alpha = color.alpha();
|
2816
|
+
if( alpha < 1 ) {
|
2817
|
+
// shape.style.filter = 'alpha(opacity=' + ( alpha * 100 ) + ')';
|
2818
|
+
// ss.filter = 'progid:DXImageTransform.Microsoft.BasicImage(opacity=' + ( alpha ) + ')';
|
2819
|
+
shape.fill.opacity = alpha;
|
2820
|
+
}
|
2821
|
+
}
|
2822
|
+
}
|
2823
|
+
} else {
|
2824
|
+
this.destroy();
|
2825
|
+
}
|
2826
|
+
},
|
2827
|
+
|
2828
|
+
updateProps: function() {
|
2829
|
+
this.destroy();
|
2830
|
+
this.updateSize();
|
2831
|
+
}
|
2832
|
+
|
2833
|
+
} );
|
2834
|
+
/**
|
2835
|
+
* Renderer for inset box-shadows
|
2836
|
+
* @constructor
|
2837
|
+
* @param {Element} el The target element
|
2838
|
+
* @param {Object} styleInfos The StyleInfo objects
|
2839
|
+
* @param {PIE.RootRenderer} parent
|
2840
|
+
*/
|
2841
|
+
PIE.BoxShadowInsetRenderer = PIE.RendererBase.newRenderer( {
|
2842
|
+
|
2843
|
+
zIndex: 3,
|
2844
|
+
boxName: 'inset-box-shadow',
|
2845
|
+
|
2846
|
+
needsUpdate: function() {
|
2847
|
+
var si = this.styleInfos;
|
2848
|
+
return si.boxShadowInfo.changed() || si.borderRadiusInfo.changed();
|
2849
|
+
},
|
2850
|
+
|
2851
|
+
isActive: function() {
|
2852
|
+
var boxShadowInfo = this.styleInfos.boxShadowInfo;
|
2853
|
+
return boxShadowInfo.isActive() && boxShadowInfo.getProps().inset[0];
|
2854
|
+
},
|
2855
|
+
|
2856
|
+
updateSize: function() {
|
2857
|
+
// TODO
|
2858
|
+
},
|
2859
|
+
|
2860
|
+
updateProps: function() {
|
2861
|
+
// TODO
|
2862
|
+
}
|
2863
|
+
|
2864
|
+
} );
|
2865
|
+
} // if( !PIE )
|
2866
|
+
var lastW, lastH, lastX, lastY,
|
2867
|
+
renderers,
|
2868
|
+
styleInfos,
|
2869
|
+
ancestors;
|
2870
|
+
|
2871
|
+
/**
|
2872
|
+
* Update position and/or size as necessary. Both move and resize events call
|
2873
|
+
* this rather than the updatePos/Size functions because sometimes, particularly
|
2874
|
+
* during page load, one will fire but the other won't.
|
2875
|
+
*/
|
2876
|
+
function update() {
|
2877
|
+
init();
|
2878
|
+
|
2879
|
+
/* TODO just using getBoundingClientRect may not always be accurate; it's possible that
|
2880
|
+
an element will actually move relative to its positioning parent, but its position
|
2881
|
+
relative to the viewport will stay the same. Need to come up with a better way to
|
2882
|
+
track movement. The most accurate would be the same logic used in RootRenderer.updatePos()
|
2883
|
+
but that is a more expensive operation since it does some DOM walking, and we want this
|
2884
|
+
check to be as fast as possible. */
|
2885
|
+
var rect = element.getBoundingClientRect(),
|
2886
|
+
x = rect.left,
|
2887
|
+
y = rect.top,
|
2888
|
+
w = rect.right - x,
|
2889
|
+
h = rect.bottom - y,
|
2890
|
+
i, len;
|
2891
|
+
|
2892
|
+
if( x !== lastX || y !== lastY ) {
|
2893
|
+
for( i = 0, len = renderers.length; i < len; i++ ) {
|
2894
|
+
renderers[i].updatePos();
|
2895
|
+
}
|
2896
|
+
lastX = x;
|
2897
|
+
lastY = y;
|
2898
|
+
}
|
2899
|
+
if( w !== lastW || h !== lastH ) {
|
2900
|
+
for( i = 0, len = renderers.length; i < len; i++ ) {
|
2901
|
+
renderers[i].updateSize();
|
2902
|
+
}
|
2903
|
+
lastW = w;
|
2904
|
+
lastH = h;
|
2905
|
+
}
|
2906
|
+
}
|
2907
|
+
|
2908
|
+
/**
|
2909
|
+
* Handle property changes to trigger update when appropriate.
|
2910
|
+
*/
|
2911
|
+
function propChanged() {
|
2912
|
+
init();
|
2913
|
+
var i, len,
|
2914
|
+
toUpdate = [];
|
2915
|
+
for( i = 0, len = renderers.length; i < len; i++ ) {
|
2916
|
+
if( renderers[i].needsUpdate() ) {
|
2917
|
+
toUpdate.push( renderers[i] );
|
2918
|
+
}
|
2919
|
+
}
|
2920
|
+
for( i = 0, len = toUpdate.length; i < len; i++ ) {
|
2921
|
+
toUpdate[i].updateProps();
|
2922
|
+
}
|
2923
|
+
}
|
2924
|
+
|
2925
|
+
|
2926
|
+
/**
|
2927
|
+
* Handle mouseenter events. Adds a custom class to the element to allow IE6 to add
|
2928
|
+
* hover styles to non-link elements.
|
2929
|
+
*/
|
2930
|
+
function mouseEntered() {
|
2931
|
+
var el = event.srcElement;
|
2932
|
+
el.className += ' ' + PIE.CLASS_PREFIX + 'hover';
|
2933
|
+
//must delay this because the mouseleave event fires before the :hover styles are added.
|
2934
|
+
setTimeout( propChanged, 0 );
|
2935
|
+
}
|
2936
|
+
/**
|
2937
|
+
* Handle mouseleave events
|
2938
|
+
*/
|
2939
|
+
function mouseLeft() {
|
2940
|
+
var el = event.srcElement;
|
2941
|
+
el.className = el.className.replace( new RegExp( '\\b' + PIE.CLASS_PREFIX + 'hover\\b', 'g' ), '' );
|
2942
|
+
//must delay this because the mouseleave event fires before the :hover styles are removed.
|
2943
|
+
setTimeout( propChanged, 0 );
|
2944
|
+
}
|
2945
|
+
|
2946
|
+
|
2947
|
+
/**
|
2948
|
+
* Handle property changes on ancestors of the element; see initAncestorPropChangeListeners()
|
2949
|
+
* which adds these listeners as requested with the -pie-watch-ancestors CSS property.
|
2950
|
+
*/
|
2951
|
+
function ancestorPropChanged() {
|
2952
|
+
var name = event.propertyName;
|
2953
|
+
if( name === 'className' || name === 'id' ) {
|
2954
|
+
propChanged();
|
2955
|
+
}
|
2956
|
+
}
|
2957
|
+
|
2958
|
+
|
2959
|
+
/**
|
2960
|
+
* Clean everything up when the behavior is removed from the element, or the element
|
2961
|
+
* is destroyed.
|
2962
|
+
*/
|
2963
|
+
function cleanup() {
|
2964
|
+
var i, len;
|
2965
|
+
|
2966
|
+
// destroy any active renderers
|
2967
|
+
if( renderers ) {
|
2968
|
+
for( i = 0, len = renderers.length; i < len; i++ ) {
|
2969
|
+
renderers[i].destroy();
|
2970
|
+
}
|
2971
|
+
renderers = null;
|
2972
|
+
}
|
2973
|
+
|
2974
|
+
styleInfos = null;
|
2975
|
+
|
2976
|
+
// remove any ancestor propertychange listeners
|
2977
|
+
if( ancestors ) {
|
2978
|
+
for( i = 0, len = ancestors.length; i < len; i++ ) {
|
2979
|
+
ancestors[i].detachEvent( 'onpropertychange', ancestorPropChanged );
|
2980
|
+
ancestors[i].detachEvent( 'onmouseenter', mouseEntered );
|
2981
|
+
ancestors[i].detachEvent( 'onmouseleave', mouseLeft );
|
2982
|
+
}
|
2983
|
+
ancestors = null;
|
2984
|
+
}
|
2985
|
+
|
2986
|
+
// Add to list of polled elements in IE8
|
2987
|
+
if( PIE.ie8DocMode === 8 ) {
|
2988
|
+
PIE.ie8Poller.remove( update );
|
2989
|
+
}
|
2990
|
+
}
|
2991
|
+
|
2992
|
+
|
2993
|
+
/**
|
2994
|
+
* If requested via the custom -pie-watch-ancestors CSS property, add onpropertychange listeners
|
2995
|
+
* to ancestor(s) of the element so we can pick up style changes based on CSS rules using
|
2996
|
+
* descendant selectors.
|
2997
|
+
*/
|
2998
|
+
function initAncestorPropChangeListeners() {
|
2999
|
+
var el = element,
|
3000
|
+
watch = el.currentStyle.getAttribute( PIE.CSS_PREFIX + 'watch-ancestors' ),
|
3001
|
+
i, a;
|
3002
|
+
if( watch ) {
|
3003
|
+
ancestors = [];
|
3004
|
+
watch = parseInt( watch, 10 );
|
3005
|
+
i = 0;
|
3006
|
+
a = el.parentNode;
|
3007
|
+
while( a && ( watch === 'NaN' || i++ < watch ) ) {
|
3008
|
+
ancestors.push( a );
|
3009
|
+
a.attachEvent( 'onpropertychange', ancestorPropChanged );
|
3010
|
+
a.attachEvent( 'onmouseenter', mouseEntered );
|
3011
|
+
a.attachEvent( 'onmouseleave', mouseLeft );
|
3012
|
+
a = a.parentNode;
|
3013
|
+
}
|
3014
|
+
}
|
3015
|
+
}
|
3016
|
+
|
3017
|
+
|
3018
|
+
/**
|
3019
|
+
* Initialize PIE for this element.
|
3020
|
+
*/
|
3021
|
+
function init() {
|
3022
|
+
if( !renderers ) {
|
3023
|
+
var el = element;
|
3024
|
+
|
3025
|
+
// force layout so move/resize events will fire
|
3026
|
+
el.runtimeStyle.zoom = 1;
|
3027
|
+
|
3028
|
+
// Create the style infos and renderers
|
3029
|
+
styleInfos = {
|
3030
|
+
backgroundInfo: new PIE.BackgroundStyleInfo( el ),
|
3031
|
+
borderInfo: new PIE.BorderStyleInfo( el ),
|
3032
|
+
borderImageInfo: new PIE.BorderImageStyleInfo( el ),
|
3033
|
+
borderRadiusInfo: new PIE.BorderRadiusStyleInfo( el ),
|
3034
|
+
boxShadowInfo: new PIE.BoxShadowStyleInfo( el ),
|
3035
|
+
visibilityInfo: new PIE.VisibilityStyleInfo( el )
|
3036
|
+
};
|
3037
|
+
|
3038
|
+
var rootRenderer = new PIE.RootRenderer( el, styleInfos );
|
3039
|
+
renderers = [
|
3040
|
+
rootRenderer,
|
3041
|
+
new PIE.BoxShadowOutsetRenderer( el, styleInfos, rootRenderer ),
|
3042
|
+
new PIE.BackgroundRenderer( el, styleInfos, rootRenderer ),
|
3043
|
+
new PIE.BoxShadowInsetRenderer( el, styleInfos, rootRenderer ),
|
3044
|
+
new PIE.BorderRenderer( el, styleInfos, rootRenderer ),
|
3045
|
+
new PIE.BorderImageRenderer( el, styleInfos, rootRenderer )
|
3046
|
+
];
|
3047
|
+
|
3048
|
+
// Add property change listeners to ancestors if requested
|
3049
|
+
initAncestorPropChangeListeners();
|
3050
|
+
|
3051
|
+
// Add to list of polled elements in IE8
|
3052
|
+
if( PIE.ie8DocMode === 8 ) {
|
3053
|
+
PIE.ie8Poller.add( update );
|
3054
|
+
}
|
3055
|
+
}
|
3056
|
+
}
|
3057
|
+
|
3058
|
+
|
3059
|
+
if( element.readyState === 'complete' ) {
|
3060
|
+
update();
|
3061
|
+
}
|
3062
|
+
</script>
|
3063
|
+
|
3064
|
+
</PUBLIC:COMPONENT>
|