From 208dc81870632f6a20068a84e83d63074d172c82 Mon Sep 17 00:00:00 2001 From: Aaron Ayres Date: Thu, 17 Dec 2020 08:47:11 -0600 Subject: [PATCH 1/2] move current documentation into 4.5 --- .../appscripting/action/start_recording_action.html | 0 .../appscripting/action/stop_recording_action.html | 0 .../_modules}/apptools/appscripting/bind_event.html | 0 .../apptools/appscripting/i_bind_event.html | 0 .../apptools/appscripting/i_script_manager.html | 0 .../apptools/appscripting/lazy_namespace.html | 0 .../apptools/appscripting/package_globals.html | 0 .../apptools/appscripting/script_manager.html | 0 .../_modules}/apptools/appscripting/scriptable.html | 0 .../apptools/appscripting/scriptable_type.html | 0 .../help/help_plugin/action/demo_action.html | 0 .../help/help_plugin/action/doc_action.html | 0 .../help/help_plugin/action/example_action.html | 0 .../help/help_plugin/action/load_url_action.html | 0 .../apptools/help/help_plugin/action/util.html | 0 .../help/help_plugin/examples_preferences.html | 0 .../apptools/help/help_plugin/help_code.html | 0 .../apptools/help/help_plugin/help_doc.html | 0 .../apptools/help/help_plugin/help_plugin.html | 0 .../help/help_plugin/help_submenu_manager.html | 0 .../apptools/help/help_plugin/i_help_code.html | 0 .../apptools/help/help_plugin/i_help_doc.html | 0 .../help/help_plugin/preferences_pages.html | 0 {_modules => 4.5/_modules}/apptools/io/file.html | 0 .../_modules}/apptools/io/h5/dict_node.html | 0 {_modules => 4.5/_modules}/apptools/io/h5/file.html | 0 .../_modules}/apptools/io/h5/table_node.html | 0 .../_modules}/apptools/io/h5/utils.html | 0 .../apptools/logger/agent/attachments.html | 0 .../apptools/logger/agent/quality_agent_mailer.html | 0 .../apptools/logger/agent/quality_agent_view.html | 0 .../apptools/logger/custom_excepthook.html | 0 .../apptools/logger/filtering_handler.html | 0 .../_modules}/apptools/logger/log_point.html | 0 .../apptools/logger/log_queue_handler.html | 0 .../_modules}/apptools/logger/logger.html | 0 .../_modules}/apptools/logger/null_handler.html | 0 .../apptools/logger/plugin/logger_plugin.html | 0 .../apptools/logger/plugin/logger_preferences.html | 0 .../apptools/logger/plugin/logger_service.html | 0 .../logger/plugin/view/logger_preferences_page.html | 0 .../apptools/logger/plugin/view/logger_view.html | 0 .../_modules}/apptools/logger/ring_buffer.html | 0 .../_modules}/apptools/logger/util.html | 0 .../_modules}/apptools/lru_cache/lru_cache.html | 0 .../naming/adapter/dict_context_adapter.html | 0 .../adapter/dict_context_adapter_factory.html | 0 .../naming/adapter/instance_context_adapter.html | 0 .../adapter/instance_context_adapter_factory.html | 0 .../naming/adapter/list_context_adapter.html | 0 .../adapter/list_context_adapter_factory.html | 0 .../naming/adapter/trait_dict_context_adapter.html | 0 .../adapter/trait_dict_context_adapter_factory.html | 0 .../naming/adapter/trait_list_context_adapter.html | 0 .../adapter/trait_list_context_adapter_factory.html | 0 .../naming/adapter/tuple_context_adapter.html | 0 .../adapter/tuple_context_adapter_factory.html | 0 .../_modules}/apptools/naming/address.html | 0 .../_modules}/apptools/naming/binding.html | 0 .../_modules}/apptools/naming/context.html | 0 .../_modules}/apptools/naming/context_adapter.html | 0 .../apptools/naming/context_adapter_factory.html | 0 .../_modules}/apptools/naming/dir_context.html | 0 .../_modules}/apptools/naming/dynamic_context.html | 0 .../_modules}/apptools/naming/exception.html | 0 .../_modules}/apptools/naming/initial_context.html | 0 .../apptools/naming/initial_context_factory.html | 0 .../_modules}/apptools/naming/naming_event.html | 0 .../_modules}/apptools/naming/naming_manager.html | 0 .../_modules}/apptools/naming/object_factory.html | 0 .../apptools/naming/object_serializer.html | 0 .../_modules}/apptools/naming/py_context.html | 0 .../apptools/naming/py_object_factory.html | 0 .../_modules}/apptools/naming/pyfs_context.html | 0 .../apptools/naming/pyfs_context_factory.html | 0 .../naming/pyfs_initial_context_factory.html | 0 .../apptools/naming/pyfs_object_factory.html | 0 .../apptools/naming/pyfs_state_factory.html | 0 .../_modules}/apptools/naming/reference.html | 0 .../_modules}/apptools/naming/referenceable.html | 0 .../naming/referenceable_state_factory.html | 0 .../_modules}/apptools/naming/state_factory.html | 0 .../apptools/naming/trait_defs/naming_traits.html | 0 .../apptools/naming/ui/context_monitor.html | 0 .../apptools/naming/ui/context_node_type.html | 0 .../_modules}/apptools/naming/ui/explorer.html | 0 .../apptools/naming/ui/naming_node_manager.html | 0 .../_modules}/apptools/naming/ui/naming_tree.html | 0 .../apptools/naming/ui/naming_tree_model.html | 0 .../apptools/naming/ui/object_node_type.html | 0 .../_modules}/apptools/naming/unique_name.html | 0 .../apptools/permissions/action/login_action.html | 0 .../apptools/permissions/action/logout_action.html | 0 .../permissions/action/user_menu_manager.html | 0 .../apptools/permissions/adapter_base.html | 0 .../permissions/adapters/pyface_action.html | 0 .../permissions/default/i_policy_storage.html | 0 .../permissions/default/i_user_database.html | 0 .../permissions/default/i_user_storage.html | 0 .../apptools/permissions/default/persistent.html | 0 .../apptools/permissions/default/policy_data.html | 0 .../permissions/default/policy_manager.html | 0 .../permissions/default/policy_storage.html | 0 .../permissions/default/role_assignment.html | 0 .../permissions/default/role_definition.html | 0 .../apptools/permissions/default/select_role.html | 0 .../apptools/permissions/default/select_user.html | 0 .../apptools/permissions/default/user_database.html | 0 .../apptools/permissions/default/user_manager.html | 0 .../apptools/permissions/default/user_storage.html | 0 .../apptools/permissions/i_policy_manager.html | 0 .../_modules}/apptools/permissions/i_user.html | 0 .../apptools/permissions/i_user_manager.html | 0 .../apptools/permissions/package_globals.html | 0 .../_modules}/apptools/permissions/permission.html | 0 .../apptools/permissions/permissions_manager.html | 0 .../apptools/permissions/secure_proxy.html | 0 .../_modules}/apptools/persistence/file_path.html | 0 .../apptools/persistence/project_loader.html | 0 .../apptools/persistence/state_pickler.html | 0 .../_modules}/apptools/persistence/updater.html | 0 .../apptools/persistence/version_registry.html | 0 .../apptools/persistence/versioned_unpickler.html | 0 .../apptools/preferences/i_preferences.html | 0 .../apptools/preferences/package_globals.html | 0 .../apptools/preferences/preference_binding.html | 0 .../_modules}/apptools/preferences/preferences.html | 0 .../apptools/preferences/preferences_helper.html | 0 .../apptools/preferences/scoped_preferences.html | 0 .../apptools/preferences/ui/i_preferences_page.html | 0 .../preferences/ui/preferences_manager.html | 0 .../apptools/preferences/ui/preferences_node.html | 0 .../apptools/preferences/ui/preferences_page.html | 0 .../apptools/preferences/ui/tree_item.html | 0 .../apptools/scripting/package_globals.html | 0 .../_modules}/apptools/scripting/recordable.html | 0 .../_modules}/apptools/scripting/recorder.html | 0 .../apptools/scripting/recorder_with_ui.html | 0 .../_modules}/apptools/scripting/util.html | 0 .../_modules}/apptools/selection/errors.html | 0 .../_modules}/apptools/selection/i_selection.html | 0 .../apptools/selection/i_selection_provider.html | 0 .../apptools/selection/list_selection.html | 0 .../apptools/selection/selection_service.html | 0 .../_modules}/apptools/sweet_pickle.html | 0 .../apptools/sweet_pickle/global_registry.html | 0 .../apptools/sweet_pickle/placeholder.html | 0 .../_modules}/apptools/sweet_pickle/updater.html | 0 .../apptools/sweet_pickle/versioned_unpickler.html | 0 .../template/impl/any_context_data_name_item.html | 0 .../apptools/template/impl/any_data_name_item.html | 0 .../template/impl/context_data_name_item.html | 0 .../_modules}/apptools/template/impl/helper.html | 0 .../template/impl/template_data_context.html | 0 .../template/impl/template_data_source.html | 0 .../template/impl/value_data_name_item.html | 0 .../template/impl/value_nd_data_name_item.html | 0 .../apptools/template/imutable_template.html | 0 .../_modules}/apptools/template/itemplate.html | 0 .../apptools/template/itemplate_choice.html | 0 .../apptools/template/itemplate_data_context.html | 0 .../apptools/template/itemplate_data_name_item.html | 0 .../apptools/template/itemplate_data_source.html | 0 .../apptools/template/mutable_template.html | 0 .../apptools/template/template_choice.html | 0 .../apptools/template/template_data_name.html | 0 .../_modules}/apptools/template/template_impl.html | 0 .../apptools/template/template_traits.html | 0 .../type_manager/abstract_adapter_factory.html | 0 .../apptools/type_manager/abstract_factory.html | 0 .../apptools/type_manager/abstract_type_system.html | 0 .../_modules}/apptools/type_manager/adaptable.html | 0 .../_modules}/apptools/type_manager/adapter.html | 0 .../apptools/type_manager/adapter_factory.html | 0 .../apptools/type_manager/adapter_manager.html | 0 .../_modules}/apptools/type_manager/factory.html | 0 .../_modules}/apptools/type_manager/hook.html | 0 .../apptools/type_manager/python_type_system.html | 0 .../apptools/type_manager/type_manager.html | 0 .../_modules}/apptools/type_manager/util.html | 0 .../apptools/type_registry/type_registry.html | 0 .../_modules}/apptools/undo/abstract_command.html | 0 .../undo/action/abstract_command_stack_action.html | 0 .../apptools/undo/action/command_action.html | 0 .../_modules}/apptools/undo/action/redo_action.html | 0 .../_modules}/apptools/undo/action/undo_action.html | 0 .../_modules}/apptools/undo/command_stack.html | 0 .../_modules}/apptools/undo/i_command.html | 0 .../_modules}/apptools/undo/i_command_stack.html | 0 .../_modules}/apptools/undo/i_undo_manager.html | 0 .../_modules}/apptools/undo/undo_manager.html | 0 {_modules => 4.5/_modules}/index.html | 0 {_modules => 4.5/_modules}/traits/trait_types.html | 0 {_modules => 4.5/_modules}/traits/traits.html | 0 .../_modules}/traitsui/editors/code_editor.html | 0 .../_modules}/traitsui/editors/tabular_editor.html | 0 {_modules => 4.5/_modules}/traitsui/view.html | 0 {_sources => 4.5/_sources}/api.rst.txt | 0 .../api/apptools.appscripting.action.rst.txt | 0 .../_sources}/api/apptools.appscripting.rst.txt | 0 .../api/apptools.help.help_plugin.action.rst.txt | 0 .../_sources}/api/apptools.help.help_plugin.rst.txt | 0 .../_sources}/api/apptools.help.rst.txt | 0 .../_sources}/api/apptools.io.h5.rst.txt | 0 {_sources => 4.5/_sources}/api/apptools.io.rst.txt | 0 .../_sources}/api/apptools.logger.agent.rst.txt | 0 .../_sources}/api/apptools.logger.plugin.rst.txt | 0 .../api/apptools.logger.plugin.view.rst.txt | 0 .../_sources}/api/apptools.logger.rst.txt | 0 .../_sources}/api/apptools.lru_cache.rst.txt | 0 .../_sources}/api/apptools.naming.adapter.rst.txt | 0 .../_sources}/api/apptools.naming.rst.txt | 0 .../api/apptools.naming.trait_defs.rst.txt | 0 .../_sources}/api/apptools.naming.ui.rst.txt | 0 .../api/apptools.permissions.action.rst.txt | 0 .../api/apptools.permissions.adapters.rst.txt | 0 .../api/apptools.permissions.default.rst.txt | 0 .../_sources}/api/apptools.permissions.rst.txt | 0 .../_sources}/api/apptools.persistence.rst.txt | 0 .../_sources}/api/apptools.preferences.rst.txt | 0 .../_sources}/api/apptools.preferences.ui.rst.txt | 0 {_sources => 4.5/_sources}/api/apptools.rst.txt | 0 .../_sources}/api/apptools.scripting.rst.txt | 0 .../_sources}/api/apptools.selection.rst.txt | 0 .../_sources}/api/apptools.sweet_pickle.rst.txt | 0 .../_sources}/api/apptools.template.impl.rst.txt | 0 .../_sources}/api/apptools.template.rst.txt | 0 .../_sources}/api/apptools.template.test.rst.txt | 0 .../_sources}/api/apptools.type_manager.rst.txt | 0 .../_sources}/api/apptools.type_registry.rst.txt | 0 .../_sources}/api/apptools.undo.action.rst.txt | 0 .../_sources}/api/apptools.undo.rst.txt | 0 {_sources => 4.5/_sources}/api/modules.rst.txt | 0 .../_sources}/appscripting/Introduction.rst.txt | 0 {_sources => 4.5/_sources}/index.rst.txt | 0 .../_sources}/permissions/ApplicationAPI.rst.txt | 0 .../permissions/DefaultPolicyManagerDataAPI.rst.txt | 0 .../permissions/DefaultUserManagerDataAPI.rst.txt | 0 .../_sources}/permissions/Introduction.rst.txt | 0 .../_sources}/preferences/Preferences.rst.txt | 0 .../preferences/PreferencesInEnvisage.rst.txt | 0 .../_sources}/scripting/introduction.rst.txt | 0 .../_sources}/selection/selection.rst.txt | 0 .../_sources}/undo/Introduction.rst.txt | 0 {_static => 4.5/_static}/ajax-loader.gif | Bin {_static => 4.5/_static}/basic.css | 0 {_static => 4.5/_static}/comment-bright.png | Bin {_static => 4.5/_static}/comment-close.png | Bin {_static => 4.5/_static}/comment.png | Bin {_static => 4.5/_static}/css/pygments.css | 0 {_static => 4.5/_static}/css/spc-bootstrap.css | 0 {_static => 4.5/_static}/css/spc-extend.css | 0 {_static => 4.5/_static}/doctools.js | 0 {_static => 4.5/_static}/documentation_options.js | 0 {_static => 4.5/_static}/down-pressed.png | Bin {_static => 4.5/_static}/down.png | Bin {_static => 4.5/_static}/enthought.css | 0 {_static => 4.5/_static}/file.png | Bin .../_static}/fonts/SourceCodePro-Regular.otf.woff | Bin .../_static}/fonts/SourceCodePro-Semibold.otf.woff | Bin .../_static}/fonts/SourceSansPro-It.otf.woff | Bin .../_static}/fonts/SourceSansPro-Regular.otf.woff | Bin .../_static}/fonts/SourceSansPro-Semibold.otf.woff | Bin .../fonts/SourceSansPro-SemiboldIt.otf.woff | Bin {_static => 4.5/_static}/img/contents.png | Bin {_static => 4.5/_static}/img/e-logo.png | Bin {_static => 4.5/_static}/img/favicon.ico | Bin .../_static}/img/glyphicons-halflings-white.png | Bin .../_static}/img/glyphicons-halflings.png | Bin {_static => 4.5/_static}/img/navigation.png | Bin {_static => 4.5/_static}/img/transparent-pixel.gif | Bin .../_static}/img/ui-anim_basic_16x16.gif | Bin {_static => 4.5/_static}/jquery-3.2.1.js | 0 {_static => 4.5/_static}/jquery.js | 0 {_static => 4.5/_static}/js/copybutton.js | 0 {_static => 4.5/_static}/js/wrap_on_dot.js | 0 {_static => 4.5/_static}/language_data.js | 0 {_static => 4.5/_static}/minus.png | Bin {_static => 4.5/_static}/plus.png | Bin {_static => 4.5/_static}/pygments.css | 0 {_static => 4.5/_static}/searchtools.js | 0 {_static => 4.5/_static}/underscore-1.3.1.js | 0 {_static => 4.5/_static}/underscore.js | 0 {_static => 4.5/_static}/up-pressed.png | Bin {_static => 4.5/_static}/up.png | Bin {_static => 4.5/_static}/websupport.js | 0 api.html => 4.5/api.html | 0 {api => 4.5/api}/apptools.appscripting.action.html | 0 {api => 4.5/api}/apptools.appscripting.html | 0 .../api}/apptools.help.help_plugin.action.html | 0 {api => 4.5/api}/apptools.help.help_plugin.html | 0 {api => 4.5/api}/apptools.help.html | 0 {api => 4.5/api}/apptools.html | 0 {api => 4.5/api}/apptools.io.h5.html | 0 {api => 4.5/api}/apptools.io.html | 0 {api => 4.5/api}/apptools.logger.agent.html | 0 {api => 4.5/api}/apptools.logger.html | 0 {api => 4.5/api}/apptools.logger.plugin.html | 0 {api => 4.5/api}/apptools.logger.plugin.view.html | 0 {api => 4.5/api}/apptools.lru_cache.html | 0 {api => 4.5/api}/apptools.naming.adapter.html | 0 {api => 4.5/api}/apptools.naming.html | 0 {api => 4.5/api}/apptools.naming.trait_defs.html | 0 {api => 4.5/api}/apptools.naming.ui.html | 0 {api => 4.5/api}/apptools.permissions.action.html | 0 {api => 4.5/api}/apptools.permissions.adapters.html | 0 {api => 4.5/api}/apptools.permissions.default.html | 0 {api => 4.5/api}/apptools.permissions.html | 0 {api => 4.5/api}/apptools.persistence.html | 0 {api => 4.5/api}/apptools.preferences.html | 0 {api => 4.5/api}/apptools.preferences.ui.html | 0 {api => 4.5/api}/apptools.scripting.html | 0 {api => 4.5/api}/apptools.selection.html | 0 {api => 4.5/api}/apptools.sweet_pickle.html | 0 {api => 4.5/api}/apptools.template.html | 0 {api => 4.5/api}/apptools.template.impl.html | 0 {api => 4.5/api}/apptools.template.test.html | 0 {api => 4.5/api}/apptools.type_manager.html | 0 {api => 4.5/api}/apptools.type_registry.html | 0 {api => 4.5/api}/apptools.undo.action.html | 0 {api => 4.5/api}/apptools.undo.html | 0 {api => 4.5/api}/modules.html | 0 .../appscripting}/Introduction.html | 0 genindex.html => 4.5/genindex.html | 0 index.html => 4.5/index.html | 0 objects.inv => 4.5/objects.inv | Bin .../permissions}/ApplicationAPI.html | 0 .../permissions}/DefaultPolicyManagerDataAPI.html | 0 .../permissions}/DefaultUserManagerDataAPI.html | 0 {permissions => 4.5/permissions}/Introduction.html | 0 {preferences => 4.5/preferences}/Preferences.html | 0 .../preferences}/PreferencesInEnvisage.html | 0 py-modindex.html => 4.5/py-modindex.html | 0 {scripting => 4.5/scripting}/introduction.html | 0 search.html => 4.5/search.html | 0 searchindex.js => 4.5/searchindex.js | 0 {selection => 4.5/selection}/selection.html | 0 {undo => 4.5/undo}/Introduction.html | 0 338 files changed, 0 insertions(+), 0 deletions(-) rename {_modules => 4.5/_modules}/apptools/appscripting/action/start_recording_action.html (100%) rename {_modules => 4.5/_modules}/apptools/appscripting/action/stop_recording_action.html (100%) rename {_modules => 4.5/_modules}/apptools/appscripting/bind_event.html (100%) rename {_modules => 4.5/_modules}/apptools/appscripting/i_bind_event.html (100%) rename {_modules => 4.5/_modules}/apptools/appscripting/i_script_manager.html (100%) rename {_modules => 4.5/_modules}/apptools/appscripting/lazy_namespace.html (100%) rename {_modules => 4.5/_modules}/apptools/appscripting/package_globals.html (100%) rename {_modules => 4.5/_modules}/apptools/appscripting/script_manager.html (100%) rename {_modules => 4.5/_modules}/apptools/appscripting/scriptable.html (100%) rename {_modules => 4.5/_modules}/apptools/appscripting/scriptable_type.html (100%) rename {_modules => 4.5/_modules}/apptools/help/help_plugin/action/demo_action.html (100%) rename {_modules => 4.5/_modules}/apptools/help/help_plugin/action/doc_action.html (100%) rename {_modules => 4.5/_modules}/apptools/help/help_plugin/action/example_action.html (100%) rename {_modules => 4.5/_modules}/apptools/help/help_plugin/action/load_url_action.html (100%) rename {_modules => 4.5/_modules}/apptools/help/help_plugin/action/util.html (100%) rename {_modules => 4.5/_modules}/apptools/help/help_plugin/examples_preferences.html (100%) rename {_modules => 4.5/_modules}/apptools/help/help_plugin/help_code.html (100%) rename {_modules => 4.5/_modules}/apptools/help/help_plugin/help_doc.html (100%) rename {_modules => 4.5/_modules}/apptools/help/help_plugin/help_plugin.html (100%) rename {_modules => 4.5/_modules}/apptools/help/help_plugin/help_submenu_manager.html (100%) rename {_modules => 4.5/_modules}/apptools/help/help_plugin/i_help_code.html (100%) rename {_modules => 4.5/_modules}/apptools/help/help_plugin/i_help_doc.html (100%) rename {_modules => 4.5/_modules}/apptools/help/help_plugin/preferences_pages.html (100%) rename {_modules => 4.5/_modules}/apptools/io/file.html (100%) rename {_modules => 4.5/_modules}/apptools/io/h5/dict_node.html (100%) rename {_modules => 4.5/_modules}/apptools/io/h5/file.html (100%) rename {_modules => 4.5/_modules}/apptools/io/h5/table_node.html (100%) rename {_modules => 4.5/_modules}/apptools/io/h5/utils.html (100%) rename {_modules => 4.5/_modules}/apptools/logger/agent/attachments.html (100%) rename {_modules => 4.5/_modules}/apptools/logger/agent/quality_agent_mailer.html (100%) rename {_modules => 4.5/_modules}/apptools/logger/agent/quality_agent_view.html (100%) rename {_modules => 4.5/_modules}/apptools/logger/custom_excepthook.html (100%) rename {_modules => 4.5/_modules}/apptools/logger/filtering_handler.html (100%) rename {_modules => 4.5/_modules}/apptools/logger/log_point.html (100%) rename {_modules => 4.5/_modules}/apptools/logger/log_queue_handler.html (100%) rename {_modules => 4.5/_modules}/apptools/logger/logger.html (100%) rename {_modules => 4.5/_modules}/apptools/logger/null_handler.html (100%) rename {_modules => 4.5/_modules}/apptools/logger/plugin/logger_plugin.html (100%) rename {_modules => 4.5/_modules}/apptools/logger/plugin/logger_preferences.html (100%) rename {_modules => 4.5/_modules}/apptools/logger/plugin/logger_service.html (100%) rename {_modules => 4.5/_modules}/apptools/logger/plugin/view/logger_preferences_page.html (100%) rename {_modules => 4.5/_modules}/apptools/logger/plugin/view/logger_view.html (100%) rename {_modules => 4.5/_modules}/apptools/logger/ring_buffer.html (100%) rename {_modules => 4.5/_modules}/apptools/logger/util.html (100%) rename {_modules => 4.5/_modules}/apptools/lru_cache/lru_cache.html (100%) rename {_modules => 4.5/_modules}/apptools/naming/adapter/dict_context_adapter.html (100%) rename {_modules => 4.5/_modules}/apptools/naming/adapter/dict_context_adapter_factory.html (100%) rename {_modules => 4.5/_modules}/apptools/naming/adapter/instance_context_adapter.html (100%) rename {_modules => 4.5/_modules}/apptools/naming/adapter/instance_context_adapter_factory.html (100%) rename {_modules => 4.5/_modules}/apptools/naming/adapter/list_context_adapter.html (100%) rename {_modules => 4.5/_modules}/apptools/naming/adapter/list_context_adapter_factory.html (100%) rename {_modules => 4.5/_modules}/apptools/naming/adapter/trait_dict_context_adapter.html (100%) rename {_modules => 4.5/_modules}/apptools/naming/adapter/trait_dict_context_adapter_factory.html (100%) rename {_modules => 4.5/_modules}/apptools/naming/adapter/trait_list_context_adapter.html (100%) rename {_modules => 4.5/_modules}/apptools/naming/adapter/trait_list_context_adapter_factory.html (100%) rename {_modules => 4.5/_modules}/apptools/naming/adapter/tuple_context_adapter.html (100%) rename {_modules => 4.5/_modules}/apptools/naming/adapter/tuple_context_adapter_factory.html (100%) rename {_modules => 4.5/_modules}/apptools/naming/address.html (100%) rename {_modules => 4.5/_modules}/apptools/naming/binding.html (100%) rename {_modules => 4.5/_modules}/apptools/naming/context.html (100%) rename {_modules => 4.5/_modules}/apptools/naming/context_adapter.html (100%) rename {_modules => 4.5/_modules}/apptools/naming/context_adapter_factory.html (100%) rename {_modules => 4.5/_modules}/apptools/naming/dir_context.html (100%) rename {_modules => 4.5/_modules}/apptools/naming/dynamic_context.html (100%) rename {_modules => 4.5/_modules}/apptools/naming/exception.html (100%) rename {_modules => 4.5/_modules}/apptools/naming/initial_context.html (100%) rename {_modules => 4.5/_modules}/apptools/naming/initial_context_factory.html (100%) rename {_modules => 4.5/_modules}/apptools/naming/naming_event.html (100%) rename {_modules => 4.5/_modules}/apptools/naming/naming_manager.html (100%) rename {_modules => 4.5/_modules}/apptools/naming/object_factory.html (100%) rename {_modules => 4.5/_modules}/apptools/naming/object_serializer.html (100%) rename {_modules => 4.5/_modules}/apptools/naming/py_context.html (100%) rename {_modules => 4.5/_modules}/apptools/naming/py_object_factory.html (100%) rename {_modules => 4.5/_modules}/apptools/naming/pyfs_context.html (100%) rename {_modules => 4.5/_modules}/apptools/naming/pyfs_context_factory.html (100%) rename {_modules => 4.5/_modules}/apptools/naming/pyfs_initial_context_factory.html (100%) rename {_modules => 4.5/_modules}/apptools/naming/pyfs_object_factory.html (100%) rename {_modules => 4.5/_modules}/apptools/naming/pyfs_state_factory.html (100%) rename {_modules => 4.5/_modules}/apptools/naming/reference.html (100%) rename {_modules => 4.5/_modules}/apptools/naming/referenceable.html (100%) rename {_modules => 4.5/_modules}/apptools/naming/referenceable_state_factory.html (100%) rename {_modules => 4.5/_modules}/apptools/naming/state_factory.html (100%) rename {_modules => 4.5/_modules}/apptools/naming/trait_defs/naming_traits.html (100%) rename {_modules => 4.5/_modules}/apptools/naming/ui/context_monitor.html (100%) rename {_modules => 4.5/_modules}/apptools/naming/ui/context_node_type.html (100%) rename {_modules => 4.5/_modules}/apptools/naming/ui/explorer.html (100%) rename {_modules => 4.5/_modules}/apptools/naming/ui/naming_node_manager.html (100%) rename {_modules => 4.5/_modules}/apptools/naming/ui/naming_tree.html (100%) rename {_modules => 4.5/_modules}/apptools/naming/ui/naming_tree_model.html (100%) rename {_modules => 4.5/_modules}/apptools/naming/ui/object_node_type.html (100%) rename {_modules => 4.5/_modules}/apptools/naming/unique_name.html (100%) rename {_modules => 4.5/_modules}/apptools/permissions/action/login_action.html (100%) rename {_modules => 4.5/_modules}/apptools/permissions/action/logout_action.html (100%) rename {_modules => 4.5/_modules}/apptools/permissions/action/user_menu_manager.html (100%) rename {_modules => 4.5/_modules}/apptools/permissions/adapter_base.html (100%) rename {_modules => 4.5/_modules}/apptools/permissions/adapters/pyface_action.html (100%) rename {_modules => 4.5/_modules}/apptools/permissions/default/i_policy_storage.html (100%) rename {_modules => 4.5/_modules}/apptools/permissions/default/i_user_database.html (100%) rename {_modules => 4.5/_modules}/apptools/permissions/default/i_user_storage.html (100%) rename {_modules => 4.5/_modules}/apptools/permissions/default/persistent.html (100%) rename {_modules => 4.5/_modules}/apptools/permissions/default/policy_data.html (100%) rename {_modules => 4.5/_modules}/apptools/permissions/default/policy_manager.html (100%) rename {_modules => 4.5/_modules}/apptools/permissions/default/policy_storage.html (100%) rename {_modules => 4.5/_modules}/apptools/permissions/default/role_assignment.html (100%) rename {_modules => 4.5/_modules}/apptools/permissions/default/role_definition.html (100%) rename {_modules => 4.5/_modules}/apptools/permissions/default/select_role.html (100%) rename {_modules => 4.5/_modules}/apptools/permissions/default/select_user.html (100%) rename {_modules => 4.5/_modules}/apptools/permissions/default/user_database.html (100%) rename {_modules => 4.5/_modules}/apptools/permissions/default/user_manager.html (100%) rename {_modules => 4.5/_modules}/apptools/permissions/default/user_storage.html (100%) rename {_modules => 4.5/_modules}/apptools/permissions/i_policy_manager.html (100%) rename {_modules => 4.5/_modules}/apptools/permissions/i_user.html (100%) rename {_modules => 4.5/_modules}/apptools/permissions/i_user_manager.html (100%) rename {_modules => 4.5/_modules}/apptools/permissions/package_globals.html (100%) rename {_modules => 4.5/_modules}/apptools/permissions/permission.html (100%) rename {_modules => 4.5/_modules}/apptools/permissions/permissions_manager.html (100%) rename {_modules => 4.5/_modules}/apptools/permissions/secure_proxy.html (100%) rename {_modules => 4.5/_modules}/apptools/persistence/file_path.html (100%) rename {_modules => 4.5/_modules}/apptools/persistence/project_loader.html (100%) rename {_modules => 4.5/_modules}/apptools/persistence/state_pickler.html (100%) rename {_modules => 4.5/_modules}/apptools/persistence/updater.html (100%) rename {_modules => 4.5/_modules}/apptools/persistence/version_registry.html (100%) rename {_modules => 4.5/_modules}/apptools/persistence/versioned_unpickler.html (100%) rename {_modules => 4.5/_modules}/apptools/preferences/i_preferences.html (100%) rename {_modules => 4.5/_modules}/apptools/preferences/package_globals.html (100%) rename {_modules => 4.5/_modules}/apptools/preferences/preference_binding.html (100%) rename {_modules => 4.5/_modules}/apptools/preferences/preferences.html (100%) rename {_modules => 4.5/_modules}/apptools/preferences/preferences_helper.html (100%) rename {_modules => 4.5/_modules}/apptools/preferences/scoped_preferences.html (100%) rename {_modules => 4.5/_modules}/apptools/preferences/ui/i_preferences_page.html (100%) rename {_modules => 4.5/_modules}/apptools/preferences/ui/preferences_manager.html (100%) rename {_modules => 4.5/_modules}/apptools/preferences/ui/preferences_node.html (100%) rename {_modules => 4.5/_modules}/apptools/preferences/ui/preferences_page.html (100%) rename {_modules => 4.5/_modules}/apptools/preferences/ui/tree_item.html (100%) rename {_modules => 4.5/_modules}/apptools/scripting/package_globals.html (100%) rename {_modules => 4.5/_modules}/apptools/scripting/recordable.html (100%) rename {_modules => 4.5/_modules}/apptools/scripting/recorder.html (100%) rename {_modules => 4.5/_modules}/apptools/scripting/recorder_with_ui.html (100%) rename {_modules => 4.5/_modules}/apptools/scripting/util.html (100%) rename {_modules => 4.5/_modules}/apptools/selection/errors.html (100%) rename {_modules => 4.5/_modules}/apptools/selection/i_selection.html (100%) rename {_modules => 4.5/_modules}/apptools/selection/i_selection_provider.html (100%) rename {_modules => 4.5/_modules}/apptools/selection/list_selection.html (100%) rename {_modules => 4.5/_modules}/apptools/selection/selection_service.html (100%) rename {_modules => 4.5/_modules}/apptools/sweet_pickle.html (100%) rename {_modules => 4.5/_modules}/apptools/sweet_pickle/global_registry.html (100%) rename {_modules => 4.5/_modules}/apptools/sweet_pickle/placeholder.html (100%) rename {_modules => 4.5/_modules}/apptools/sweet_pickle/updater.html (100%) rename {_modules => 4.5/_modules}/apptools/sweet_pickle/versioned_unpickler.html (100%) rename {_modules => 4.5/_modules}/apptools/template/impl/any_context_data_name_item.html (100%) rename {_modules => 4.5/_modules}/apptools/template/impl/any_data_name_item.html (100%) rename {_modules => 4.5/_modules}/apptools/template/impl/context_data_name_item.html (100%) rename {_modules => 4.5/_modules}/apptools/template/impl/helper.html (100%) rename {_modules => 4.5/_modules}/apptools/template/impl/template_data_context.html (100%) rename {_modules => 4.5/_modules}/apptools/template/impl/template_data_source.html (100%) rename {_modules => 4.5/_modules}/apptools/template/impl/value_data_name_item.html (100%) rename {_modules => 4.5/_modules}/apptools/template/impl/value_nd_data_name_item.html (100%) rename {_modules => 4.5/_modules}/apptools/template/imutable_template.html (100%) rename {_modules => 4.5/_modules}/apptools/template/itemplate.html (100%) rename {_modules => 4.5/_modules}/apptools/template/itemplate_choice.html (100%) rename {_modules => 4.5/_modules}/apptools/template/itemplate_data_context.html (100%) rename {_modules => 4.5/_modules}/apptools/template/itemplate_data_name_item.html (100%) rename {_modules => 4.5/_modules}/apptools/template/itemplate_data_source.html (100%) rename {_modules => 4.5/_modules}/apptools/template/mutable_template.html (100%) rename {_modules => 4.5/_modules}/apptools/template/template_choice.html (100%) rename {_modules => 4.5/_modules}/apptools/template/template_data_name.html (100%) rename {_modules => 4.5/_modules}/apptools/template/template_impl.html (100%) rename {_modules => 4.5/_modules}/apptools/template/template_traits.html (100%) rename {_modules => 4.5/_modules}/apptools/type_manager/abstract_adapter_factory.html (100%) rename {_modules => 4.5/_modules}/apptools/type_manager/abstract_factory.html (100%) rename {_modules => 4.5/_modules}/apptools/type_manager/abstract_type_system.html (100%) rename {_modules => 4.5/_modules}/apptools/type_manager/adaptable.html (100%) rename {_modules => 4.5/_modules}/apptools/type_manager/adapter.html (100%) rename {_modules => 4.5/_modules}/apptools/type_manager/adapter_factory.html (100%) rename {_modules => 4.5/_modules}/apptools/type_manager/adapter_manager.html (100%) rename {_modules => 4.5/_modules}/apptools/type_manager/factory.html (100%) rename {_modules => 4.5/_modules}/apptools/type_manager/hook.html (100%) rename {_modules => 4.5/_modules}/apptools/type_manager/python_type_system.html (100%) rename {_modules => 4.5/_modules}/apptools/type_manager/type_manager.html (100%) rename {_modules => 4.5/_modules}/apptools/type_manager/util.html (100%) rename {_modules => 4.5/_modules}/apptools/type_registry/type_registry.html (100%) rename {_modules => 4.5/_modules}/apptools/undo/abstract_command.html (100%) rename {_modules => 4.5/_modules}/apptools/undo/action/abstract_command_stack_action.html (100%) rename {_modules => 4.5/_modules}/apptools/undo/action/command_action.html (100%) rename {_modules => 4.5/_modules}/apptools/undo/action/redo_action.html (100%) rename {_modules => 4.5/_modules}/apptools/undo/action/undo_action.html (100%) rename {_modules => 4.5/_modules}/apptools/undo/command_stack.html (100%) rename {_modules => 4.5/_modules}/apptools/undo/i_command.html (100%) rename {_modules => 4.5/_modules}/apptools/undo/i_command_stack.html (100%) rename {_modules => 4.5/_modules}/apptools/undo/i_undo_manager.html (100%) rename {_modules => 4.5/_modules}/apptools/undo/undo_manager.html (100%) rename {_modules => 4.5/_modules}/index.html (100%) rename {_modules => 4.5/_modules}/traits/trait_types.html (100%) rename {_modules => 4.5/_modules}/traits/traits.html (100%) rename {_modules => 4.5/_modules}/traitsui/editors/code_editor.html (100%) rename {_modules => 4.5/_modules}/traitsui/editors/tabular_editor.html (100%) rename {_modules => 4.5/_modules}/traitsui/view.html (100%) rename {_sources => 4.5/_sources}/api.rst.txt (100%) rename {_sources => 4.5/_sources}/api/apptools.appscripting.action.rst.txt (100%) rename {_sources => 4.5/_sources}/api/apptools.appscripting.rst.txt (100%) rename {_sources => 4.5/_sources}/api/apptools.help.help_plugin.action.rst.txt (100%) rename {_sources => 4.5/_sources}/api/apptools.help.help_plugin.rst.txt (100%) rename {_sources => 4.5/_sources}/api/apptools.help.rst.txt (100%) rename {_sources => 4.5/_sources}/api/apptools.io.h5.rst.txt (100%) rename {_sources => 4.5/_sources}/api/apptools.io.rst.txt (100%) rename {_sources => 4.5/_sources}/api/apptools.logger.agent.rst.txt (100%) rename {_sources => 4.5/_sources}/api/apptools.logger.plugin.rst.txt (100%) rename {_sources => 4.5/_sources}/api/apptools.logger.plugin.view.rst.txt (100%) rename {_sources => 4.5/_sources}/api/apptools.logger.rst.txt (100%) rename {_sources => 4.5/_sources}/api/apptools.lru_cache.rst.txt (100%) rename {_sources => 4.5/_sources}/api/apptools.naming.adapter.rst.txt (100%) rename {_sources => 4.5/_sources}/api/apptools.naming.rst.txt (100%) rename {_sources => 4.5/_sources}/api/apptools.naming.trait_defs.rst.txt (100%) rename {_sources => 4.5/_sources}/api/apptools.naming.ui.rst.txt (100%) rename {_sources => 4.5/_sources}/api/apptools.permissions.action.rst.txt (100%) rename {_sources => 4.5/_sources}/api/apptools.permissions.adapters.rst.txt (100%) rename {_sources => 4.5/_sources}/api/apptools.permissions.default.rst.txt (100%) rename {_sources => 4.5/_sources}/api/apptools.permissions.rst.txt (100%) rename {_sources => 4.5/_sources}/api/apptools.persistence.rst.txt (100%) rename {_sources => 4.5/_sources}/api/apptools.preferences.rst.txt (100%) rename {_sources => 4.5/_sources}/api/apptools.preferences.ui.rst.txt (100%) rename {_sources => 4.5/_sources}/api/apptools.rst.txt (100%) rename {_sources => 4.5/_sources}/api/apptools.scripting.rst.txt (100%) rename {_sources => 4.5/_sources}/api/apptools.selection.rst.txt (100%) rename {_sources => 4.5/_sources}/api/apptools.sweet_pickle.rst.txt (100%) rename {_sources => 4.5/_sources}/api/apptools.template.impl.rst.txt (100%) rename {_sources => 4.5/_sources}/api/apptools.template.rst.txt (100%) rename {_sources => 4.5/_sources}/api/apptools.template.test.rst.txt (100%) rename {_sources => 4.5/_sources}/api/apptools.type_manager.rst.txt (100%) rename {_sources => 4.5/_sources}/api/apptools.type_registry.rst.txt (100%) rename {_sources => 4.5/_sources}/api/apptools.undo.action.rst.txt (100%) rename {_sources => 4.5/_sources}/api/apptools.undo.rst.txt (100%) rename {_sources => 4.5/_sources}/api/modules.rst.txt (100%) rename {_sources => 4.5/_sources}/appscripting/Introduction.rst.txt (100%) rename {_sources => 4.5/_sources}/index.rst.txt (100%) rename {_sources => 4.5/_sources}/permissions/ApplicationAPI.rst.txt (100%) rename {_sources => 4.5/_sources}/permissions/DefaultPolicyManagerDataAPI.rst.txt (100%) rename {_sources => 4.5/_sources}/permissions/DefaultUserManagerDataAPI.rst.txt (100%) rename {_sources => 4.5/_sources}/permissions/Introduction.rst.txt (100%) rename {_sources => 4.5/_sources}/preferences/Preferences.rst.txt (100%) rename {_sources => 4.5/_sources}/preferences/PreferencesInEnvisage.rst.txt (100%) rename {_sources => 4.5/_sources}/scripting/introduction.rst.txt (100%) rename {_sources => 4.5/_sources}/selection/selection.rst.txt (100%) rename {_sources => 4.5/_sources}/undo/Introduction.rst.txt (100%) rename {_static => 4.5/_static}/ajax-loader.gif (100%) rename {_static => 4.5/_static}/basic.css (100%) rename {_static => 4.5/_static}/comment-bright.png (100%) rename {_static => 4.5/_static}/comment-close.png (100%) rename {_static => 4.5/_static}/comment.png (100%) rename {_static => 4.5/_static}/css/pygments.css (100%) rename {_static => 4.5/_static}/css/spc-bootstrap.css (100%) rename {_static => 4.5/_static}/css/spc-extend.css (100%) rename {_static => 4.5/_static}/doctools.js (100%) rename {_static => 4.5/_static}/documentation_options.js (100%) rename {_static => 4.5/_static}/down-pressed.png (100%) rename {_static => 4.5/_static}/down.png (100%) rename {_static => 4.5/_static}/enthought.css (100%) rename {_static => 4.5/_static}/file.png (100%) rename {_static => 4.5/_static}/fonts/SourceCodePro-Regular.otf.woff (100%) rename {_static => 4.5/_static}/fonts/SourceCodePro-Semibold.otf.woff (100%) rename {_static => 4.5/_static}/fonts/SourceSansPro-It.otf.woff (100%) rename {_static => 4.5/_static}/fonts/SourceSansPro-Regular.otf.woff (100%) rename {_static => 4.5/_static}/fonts/SourceSansPro-Semibold.otf.woff (100%) rename {_static => 4.5/_static}/fonts/SourceSansPro-SemiboldIt.otf.woff (100%) rename {_static => 4.5/_static}/img/contents.png (100%) rename {_static => 4.5/_static}/img/e-logo.png (100%) rename {_static => 4.5/_static}/img/favicon.ico (100%) rename {_static => 4.5/_static}/img/glyphicons-halflings-white.png (100%) rename {_static => 4.5/_static}/img/glyphicons-halflings.png (100%) rename {_static => 4.5/_static}/img/navigation.png (100%) rename {_static => 4.5/_static}/img/transparent-pixel.gif (100%) rename {_static => 4.5/_static}/img/ui-anim_basic_16x16.gif (100%) rename {_static => 4.5/_static}/jquery-3.2.1.js (100%) rename {_static => 4.5/_static}/jquery.js (100%) rename {_static => 4.5/_static}/js/copybutton.js (100%) rename {_static => 4.5/_static}/js/wrap_on_dot.js (100%) rename {_static => 4.5/_static}/language_data.js (100%) rename {_static => 4.5/_static}/minus.png (100%) rename {_static => 4.5/_static}/plus.png (100%) rename {_static => 4.5/_static}/pygments.css (100%) rename {_static => 4.5/_static}/searchtools.js (100%) rename {_static => 4.5/_static}/underscore-1.3.1.js (100%) rename {_static => 4.5/_static}/underscore.js (100%) rename {_static => 4.5/_static}/up-pressed.png (100%) rename {_static => 4.5/_static}/up.png (100%) rename {_static => 4.5/_static}/websupport.js (100%) rename api.html => 4.5/api.html (100%) rename {api => 4.5/api}/apptools.appscripting.action.html (100%) rename {api => 4.5/api}/apptools.appscripting.html (100%) rename {api => 4.5/api}/apptools.help.help_plugin.action.html (100%) rename {api => 4.5/api}/apptools.help.help_plugin.html (100%) rename {api => 4.5/api}/apptools.help.html (100%) rename {api => 4.5/api}/apptools.html (100%) rename {api => 4.5/api}/apptools.io.h5.html (100%) rename {api => 4.5/api}/apptools.io.html (100%) rename {api => 4.5/api}/apptools.logger.agent.html (100%) rename {api => 4.5/api}/apptools.logger.html (100%) rename {api => 4.5/api}/apptools.logger.plugin.html (100%) rename {api => 4.5/api}/apptools.logger.plugin.view.html (100%) rename {api => 4.5/api}/apptools.lru_cache.html (100%) rename {api => 4.5/api}/apptools.naming.adapter.html (100%) rename {api => 4.5/api}/apptools.naming.html (100%) rename {api => 4.5/api}/apptools.naming.trait_defs.html (100%) rename {api => 4.5/api}/apptools.naming.ui.html (100%) rename {api => 4.5/api}/apptools.permissions.action.html (100%) rename {api => 4.5/api}/apptools.permissions.adapters.html (100%) rename {api => 4.5/api}/apptools.permissions.default.html (100%) rename {api => 4.5/api}/apptools.permissions.html (100%) rename {api => 4.5/api}/apptools.persistence.html (100%) rename {api => 4.5/api}/apptools.preferences.html (100%) rename {api => 4.5/api}/apptools.preferences.ui.html (100%) rename {api => 4.5/api}/apptools.scripting.html (100%) rename {api => 4.5/api}/apptools.selection.html (100%) rename {api => 4.5/api}/apptools.sweet_pickle.html (100%) rename {api => 4.5/api}/apptools.template.html (100%) rename {api => 4.5/api}/apptools.template.impl.html (100%) rename {api => 4.5/api}/apptools.template.test.html (100%) rename {api => 4.5/api}/apptools.type_manager.html (100%) rename {api => 4.5/api}/apptools.type_registry.html (100%) rename {api => 4.5/api}/apptools.undo.action.html (100%) rename {api => 4.5/api}/apptools.undo.html (100%) rename {api => 4.5/api}/modules.html (100%) rename {appscripting => 4.5/appscripting}/Introduction.html (100%) rename genindex.html => 4.5/genindex.html (100%) rename index.html => 4.5/index.html (100%) rename objects.inv => 4.5/objects.inv (100%) rename {permissions => 4.5/permissions}/ApplicationAPI.html (100%) rename {permissions => 4.5/permissions}/DefaultPolicyManagerDataAPI.html (100%) rename {permissions => 4.5/permissions}/DefaultUserManagerDataAPI.html (100%) rename {permissions => 4.5/permissions}/Introduction.html (100%) rename {preferences => 4.5/preferences}/Preferences.html (100%) rename {preferences => 4.5/preferences}/PreferencesInEnvisage.html (100%) rename py-modindex.html => 4.5/py-modindex.html (100%) rename {scripting => 4.5/scripting}/introduction.html (100%) rename search.html => 4.5/search.html (100%) rename searchindex.js => 4.5/searchindex.js (100%) rename {selection => 4.5/selection}/selection.html (100%) rename {undo => 4.5/undo}/Introduction.html (100%) diff --git a/_modules/apptools/appscripting/action/start_recording_action.html b/4.5/_modules/apptools/appscripting/action/start_recording_action.html similarity index 100% rename from _modules/apptools/appscripting/action/start_recording_action.html rename to 4.5/_modules/apptools/appscripting/action/start_recording_action.html diff --git a/_modules/apptools/appscripting/action/stop_recording_action.html b/4.5/_modules/apptools/appscripting/action/stop_recording_action.html similarity index 100% rename from _modules/apptools/appscripting/action/stop_recording_action.html rename to 4.5/_modules/apptools/appscripting/action/stop_recording_action.html diff --git a/_modules/apptools/appscripting/bind_event.html b/4.5/_modules/apptools/appscripting/bind_event.html similarity index 100% rename from _modules/apptools/appscripting/bind_event.html rename to 4.5/_modules/apptools/appscripting/bind_event.html diff --git a/_modules/apptools/appscripting/i_bind_event.html b/4.5/_modules/apptools/appscripting/i_bind_event.html similarity index 100% rename from _modules/apptools/appscripting/i_bind_event.html rename to 4.5/_modules/apptools/appscripting/i_bind_event.html diff --git a/_modules/apptools/appscripting/i_script_manager.html b/4.5/_modules/apptools/appscripting/i_script_manager.html similarity index 100% rename from _modules/apptools/appscripting/i_script_manager.html rename to 4.5/_modules/apptools/appscripting/i_script_manager.html diff --git a/_modules/apptools/appscripting/lazy_namespace.html b/4.5/_modules/apptools/appscripting/lazy_namespace.html similarity index 100% rename from _modules/apptools/appscripting/lazy_namespace.html rename to 4.5/_modules/apptools/appscripting/lazy_namespace.html diff --git a/_modules/apptools/appscripting/package_globals.html b/4.5/_modules/apptools/appscripting/package_globals.html similarity index 100% rename from _modules/apptools/appscripting/package_globals.html rename to 4.5/_modules/apptools/appscripting/package_globals.html diff --git a/_modules/apptools/appscripting/script_manager.html b/4.5/_modules/apptools/appscripting/script_manager.html similarity index 100% rename from _modules/apptools/appscripting/script_manager.html rename to 4.5/_modules/apptools/appscripting/script_manager.html diff --git a/_modules/apptools/appscripting/scriptable.html b/4.5/_modules/apptools/appscripting/scriptable.html similarity index 100% rename from _modules/apptools/appscripting/scriptable.html rename to 4.5/_modules/apptools/appscripting/scriptable.html diff --git a/_modules/apptools/appscripting/scriptable_type.html b/4.5/_modules/apptools/appscripting/scriptable_type.html similarity index 100% rename from _modules/apptools/appscripting/scriptable_type.html rename to 4.5/_modules/apptools/appscripting/scriptable_type.html diff --git a/_modules/apptools/help/help_plugin/action/demo_action.html b/4.5/_modules/apptools/help/help_plugin/action/demo_action.html similarity index 100% rename from _modules/apptools/help/help_plugin/action/demo_action.html rename to 4.5/_modules/apptools/help/help_plugin/action/demo_action.html diff --git a/_modules/apptools/help/help_plugin/action/doc_action.html b/4.5/_modules/apptools/help/help_plugin/action/doc_action.html similarity index 100% rename from _modules/apptools/help/help_plugin/action/doc_action.html rename to 4.5/_modules/apptools/help/help_plugin/action/doc_action.html diff --git a/_modules/apptools/help/help_plugin/action/example_action.html b/4.5/_modules/apptools/help/help_plugin/action/example_action.html similarity index 100% rename from _modules/apptools/help/help_plugin/action/example_action.html rename to 4.5/_modules/apptools/help/help_plugin/action/example_action.html diff --git a/_modules/apptools/help/help_plugin/action/load_url_action.html b/4.5/_modules/apptools/help/help_plugin/action/load_url_action.html similarity index 100% rename from _modules/apptools/help/help_plugin/action/load_url_action.html rename to 4.5/_modules/apptools/help/help_plugin/action/load_url_action.html diff --git a/_modules/apptools/help/help_plugin/action/util.html b/4.5/_modules/apptools/help/help_plugin/action/util.html similarity index 100% rename from _modules/apptools/help/help_plugin/action/util.html rename to 4.5/_modules/apptools/help/help_plugin/action/util.html diff --git a/_modules/apptools/help/help_plugin/examples_preferences.html b/4.5/_modules/apptools/help/help_plugin/examples_preferences.html similarity index 100% rename from _modules/apptools/help/help_plugin/examples_preferences.html rename to 4.5/_modules/apptools/help/help_plugin/examples_preferences.html diff --git a/_modules/apptools/help/help_plugin/help_code.html b/4.5/_modules/apptools/help/help_plugin/help_code.html similarity index 100% rename from _modules/apptools/help/help_plugin/help_code.html rename to 4.5/_modules/apptools/help/help_plugin/help_code.html diff --git a/_modules/apptools/help/help_plugin/help_doc.html b/4.5/_modules/apptools/help/help_plugin/help_doc.html similarity index 100% rename from _modules/apptools/help/help_plugin/help_doc.html rename to 4.5/_modules/apptools/help/help_plugin/help_doc.html diff --git a/_modules/apptools/help/help_plugin/help_plugin.html b/4.5/_modules/apptools/help/help_plugin/help_plugin.html similarity index 100% rename from _modules/apptools/help/help_plugin/help_plugin.html rename to 4.5/_modules/apptools/help/help_plugin/help_plugin.html diff --git a/_modules/apptools/help/help_plugin/help_submenu_manager.html b/4.5/_modules/apptools/help/help_plugin/help_submenu_manager.html similarity index 100% rename from _modules/apptools/help/help_plugin/help_submenu_manager.html rename to 4.5/_modules/apptools/help/help_plugin/help_submenu_manager.html diff --git a/_modules/apptools/help/help_plugin/i_help_code.html b/4.5/_modules/apptools/help/help_plugin/i_help_code.html similarity index 100% rename from _modules/apptools/help/help_plugin/i_help_code.html rename to 4.5/_modules/apptools/help/help_plugin/i_help_code.html diff --git a/_modules/apptools/help/help_plugin/i_help_doc.html b/4.5/_modules/apptools/help/help_plugin/i_help_doc.html similarity index 100% rename from _modules/apptools/help/help_plugin/i_help_doc.html rename to 4.5/_modules/apptools/help/help_plugin/i_help_doc.html diff --git a/_modules/apptools/help/help_plugin/preferences_pages.html b/4.5/_modules/apptools/help/help_plugin/preferences_pages.html similarity index 100% rename from _modules/apptools/help/help_plugin/preferences_pages.html rename to 4.5/_modules/apptools/help/help_plugin/preferences_pages.html diff --git a/_modules/apptools/io/file.html b/4.5/_modules/apptools/io/file.html similarity index 100% rename from _modules/apptools/io/file.html rename to 4.5/_modules/apptools/io/file.html diff --git a/_modules/apptools/io/h5/dict_node.html b/4.5/_modules/apptools/io/h5/dict_node.html similarity index 100% rename from _modules/apptools/io/h5/dict_node.html rename to 4.5/_modules/apptools/io/h5/dict_node.html diff --git a/_modules/apptools/io/h5/file.html b/4.5/_modules/apptools/io/h5/file.html similarity index 100% rename from _modules/apptools/io/h5/file.html rename to 4.5/_modules/apptools/io/h5/file.html diff --git a/_modules/apptools/io/h5/table_node.html b/4.5/_modules/apptools/io/h5/table_node.html similarity index 100% rename from _modules/apptools/io/h5/table_node.html rename to 4.5/_modules/apptools/io/h5/table_node.html diff --git a/_modules/apptools/io/h5/utils.html b/4.5/_modules/apptools/io/h5/utils.html similarity index 100% rename from _modules/apptools/io/h5/utils.html rename to 4.5/_modules/apptools/io/h5/utils.html diff --git a/_modules/apptools/logger/agent/attachments.html b/4.5/_modules/apptools/logger/agent/attachments.html similarity index 100% rename from _modules/apptools/logger/agent/attachments.html rename to 4.5/_modules/apptools/logger/agent/attachments.html diff --git a/_modules/apptools/logger/agent/quality_agent_mailer.html b/4.5/_modules/apptools/logger/agent/quality_agent_mailer.html similarity index 100% rename from _modules/apptools/logger/agent/quality_agent_mailer.html rename to 4.5/_modules/apptools/logger/agent/quality_agent_mailer.html diff --git a/_modules/apptools/logger/agent/quality_agent_view.html b/4.5/_modules/apptools/logger/agent/quality_agent_view.html similarity index 100% rename from _modules/apptools/logger/agent/quality_agent_view.html rename to 4.5/_modules/apptools/logger/agent/quality_agent_view.html diff --git a/_modules/apptools/logger/custom_excepthook.html b/4.5/_modules/apptools/logger/custom_excepthook.html similarity index 100% rename from _modules/apptools/logger/custom_excepthook.html rename to 4.5/_modules/apptools/logger/custom_excepthook.html diff --git a/_modules/apptools/logger/filtering_handler.html b/4.5/_modules/apptools/logger/filtering_handler.html similarity index 100% rename from _modules/apptools/logger/filtering_handler.html rename to 4.5/_modules/apptools/logger/filtering_handler.html diff --git a/_modules/apptools/logger/log_point.html b/4.5/_modules/apptools/logger/log_point.html similarity index 100% rename from _modules/apptools/logger/log_point.html rename to 4.5/_modules/apptools/logger/log_point.html diff --git a/_modules/apptools/logger/log_queue_handler.html b/4.5/_modules/apptools/logger/log_queue_handler.html similarity index 100% rename from _modules/apptools/logger/log_queue_handler.html rename to 4.5/_modules/apptools/logger/log_queue_handler.html diff --git a/_modules/apptools/logger/logger.html b/4.5/_modules/apptools/logger/logger.html similarity index 100% rename from _modules/apptools/logger/logger.html rename to 4.5/_modules/apptools/logger/logger.html diff --git a/_modules/apptools/logger/null_handler.html b/4.5/_modules/apptools/logger/null_handler.html similarity index 100% rename from _modules/apptools/logger/null_handler.html rename to 4.5/_modules/apptools/logger/null_handler.html diff --git a/_modules/apptools/logger/plugin/logger_plugin.html b/4.5/_modules/apptools/logger/plugin/logger_plugin.html similarity index 100% rename from _modules/apptools/logger/plugin/logger_plugin.html rename to 4.5/_modules/apptools/logger/plugin/logger_plugin.html diff --git a/_modules/apptools/logger/plugin/logger_preferences.html b/4.5/_modules/apptools/logger/plugin/logger_preferences.html similarity index 100% rename from _modules/apptools/logger/plugin/logger_preferences.html rename to 4.5/_modules/apptools/logger/plugin/logger_preferences.html diff --git a/_modules/apptools/logger/plugin/logger_service.html b/4.5/_modules/apptools/logger/plugin/logger_service.html similarity index 100% rename from _modules/apptools/logger/plugin/logger_service.html rename to 4.5/_modules/apptools/logger/plugin/logger_service.html diff --git a/_modules/apptools/logger/plugin/view/logger_preferences_page.html b/4.5/_modules/apptools/logger/plugin/view/logger_preferences_page.html similarity index 100% rename from _modules/apptools/logger/plugin/view/logger_preferences_page.html rename to 4.5/_modules/apptools/logger/plugin/view/logger_preferences_page.html diff --git a/_modules/apptools/logger/plugin/view/logger_view.html b/4.5/_modules/apptools/logger/plugin/view/logger_view.html similarity index 100% rename from _modules/apptools/logger/plugin/view/logger_view.html rename to 4.5/_modules/apptools/logger/plugin/view/logger_view.html diff --git a/_modules/apptools/logger/ring_buffer.html b/4.5/_modules/apptools/logger/ring_buffer.html similarity index 100% rename from _modules/apptools/logger/ring_buffer.html rename to 4.5/_modules/apptools/logger/ring_buffer.html diff --git a/_modules/apptools/logger/util.html b/4.5/_modules/apptools/logger/util.html similarity index 100% rename from _modules/apptools/logger/util.html rename to 4.5/_modules/apptools/logger/util.html diff --git a/_modules/apptools/lru_cache/lru_cache.html b/4.5/_modules/apptools/lru_cache/lru_cache.html similarity index 100% rename from _modules/apptools/lru_cache/lru_cache.html rename to 4.5/_modules/apptools/lru_cache/lru_cache.html diff --git a/_modules/apptools/naming/adapter/dict_context_adapter.html b/4.5/_modules/apptools/naming/adapter/dict_context_adapter.html similarity index 100% rename from _modules/apptools/naming/adapter/dict_context_adapter.html rename to 4.5/_modules/apptools/naming/adapter/dict_context_adapter.html diff --git a/_modules/apptools/naming/adapter/dict_context_adapter_factory.html b/4.5/_modules/apptools/naming/adapter/dict_context_adapter_factory.html similarity index 100% rename from _modules/apptools/naming/adapter/dict_context_adapter_factory.html rename to 4.5/_modules/apptools/naming/adapter/dict_context_adapter_factory.html diff --git a/_modules/apptools/naming/adapter/instance_context_adapter.html b/4.5/_modules/apptools/naming/adapter/instance_context_adapter.html similarity index 100% rename from _modules/apptools/naming/adapter/instance_context_adapter.html rename to 4.5/_modules/apptools/naming/adapter/instance_context_adapter.html diff --git a/_modules/apptools/naming/adapter/instance_context_adapter_factory.html b/4.5/_modules/apptools/naming/adapter/instance_context_adapter_factory.html similarity index 100% rename from _modules/apptools/naming/adapter/instance_context_adapter_factory.html rename to 4.5/_modules/apptools/naming/adapter/instance_context_adapter_factory.html diff --git a/_modules/apptools/naming/adapter/list_context_adapter.html b/4.5/_modules/apptools/naming/adapter/list_context_adapter.html similarity index 100% rename from _modules/apptools/naming/adapter/list_context_adapter.html rename to 4.5/_modules/apptools/naming/adapter/list_context_adapter.html diff --git a/_modules/apptools/naming/adapter/list_context_adapter_factory.html b/4.5/_modules/apptools/naming/adapter/list_context_adapter_factory.html similarity index 100% rename from _modules/apptools/naming/adapter/list_context_adapter_factory.html rename to 4.5/_modules/apptools/naming/adapter/list_context_adapter_factory.html diff --git a/_modules/apptools/naming/adapter/trait_dict_context_adapter.html b/4.5/_modules/apptools/naming/adapter/trait_dict_context_adapter.html similarity index 100% rename from _modules/apptools/naming/adapter/trait_dict_context_adapter.html rename to 4.5/_modules/apptools/naming/adapter/trait_dict_context_adapter.html diff --git a/_modules/apptools/naming/adapter/trait_dict_context_adapter_factory.html b/4.5/_modules/apptools/naming/adapter/trait_dict_context_adapter_factory.html similarity index 100% rename from _modules/apptools/naming/adapter/trait_dict_context_adapter_factory.html rename to 4.5/_modules/apptools/naming/adapter/trait_dict_context_adapter_factory.html diff --git a/_modules/apptools/naming/adapter/trait_list_context_adapter.html b/4.5/_modules/apptools/naming/adapter/trait_list_context_adapter.html similarity index 100% rename from _modules/apptools/naming/adapter/trait_list_context_adapter.html rename to 4.5/_modules/apptools/naming/adapter/trait_list_context_adapter.html diff --git a/_modules/apptools/naming/adapter/trait_list_context_adapter_factory.html b/4.5/_modules/apptools/naming/adapter/trait_list_context_adapter_factory.html similarity index 100% rename from _modules/apptools/naming/adapter/trait_list_context_adapter_factory.html rename to 4.5/_modules/apptools/naming/adapter/trait_list_context_adapter_factory.html diff --git a/_modules/apptools/naming/adapter/tuple_context_adapter.html b/4.5/_modules/apptools/naming/adapter/tuple_context_adapter.html similarity index 100% rename from _modules/apptools/naming/adapter/tuple_context_adapter.html rename to 4.5/_modules/apptools/naming/adapter/tuple_context_adapter.html diff --git a/_modules/apptools/naming/adapter/tuple_context_adapter_factory.html b/4.5/_modules/apptools/naming/adapter/tuple_context_adapter_factory.html similarity index 100% rename from _modules/apptools/naming/adapter/tuple_context_adapter_factory.html rename to 4.5/_modules/apptools/naming/adapter/tuple_context_adapter_factory.html diff --git a/_modules/apptools/naming/address.html b/4.5/_modules/apptools/naming/address.html similarity index 100% rename from _modules/apptools/naming/address.html rename to 4.5/_modules/apptools/naming/address.html diff --git a/_modules/apptools/naming/binding.html b/4.5/_modules/apptools/naming/binding.html similarity index 100% rename from _modules/apptools/naming/binding.html rename to 4.5/_modules/apptools/naming/binding.html diff --git a/_modules/apptools/naming/context.html b/4.5/_modules/apptools/naming/context.html similarity index 100% rename from _modules/apptools/naming/context.html rename to 4.5/_modules/apptools/naming/context.html diff --git a/_modules/apptools/naming/context_adapter.html b/4.5/_modules/apptools/naming/context_adapter.html similarity index 100% rename from _modules/apptools/naming/context_adapter.html rename to 4.5/_modules/apptools/naming/context_adapter.html diff --git a/_modules/apptools/naming/context_adapter_factory.html b/4.5/_modules/apptools/naming/context_adapter_factory.html similarity index 100% rename from _modules/apptools/naming/context_adapter_factory.html rename to 4.5/_modules/apptools/naming/context_adapter_factory.html diff --git a/_modules/apptools/naming/dir_context.html b/4.5/_modules/apptools/naming/dir_context.html similarity index 100% rename from _modules/apptools/naming/dir_context.html rename to 4.5/_modules/apptools/naming/dir_context.html diff --git a/_modules/apptools/naming/dynamic_context.html b/4.5/_modules/apptools/naming/dynamic_context.html similarity index 100% rename from _modules/apptools/naming/dynamic_context.html rename to 4.5/_modules/apptools/naming/dynamic_context.html diff --git a/_modules/apptools/naming/exception.html b/4.5/_modules/apptools/naming/exception.html similarity index 100% rename from _modules/apptools/naming/exception.html rename to 4.5/_modules/apptools/naming/exception.html diff --git a/_modules/apptools/naming/initial_context.html b/4.5/_modules/apptools/naming/initial_context.html similarity index 100% rename from _modules/apptools/naming/initial_context.html rename to 4.5/_modules/apptools/naming/initial_context.html diff --git a/_modules/apptools/naming/initial_context_factory.html b/4.5/_modules/apptools/naming/initial_context_factory.html similarity index 100% rename from _modules/apptools/naming/initial_context_factory.html rename to 4.5/_modules/apptools/naming/initial_context_factory.html diff --git a/_modules/apptools/naming/naming_event.html b/4.5/_modules/apptools/naming/naming_event.html similarity index 100% rename from _modules/apptools/naming/naming_event.html rename to 4.5/_modules/apptools/naming/naming_event.html diff --git a/_modules/apptools/naming/naming_manager.html b/4.5/_modules/apptools/naming/naming_manager.html similarity index 100% rename from _modules/apptools/naming/naming_manager.html rename to 4.5/_modules/apptools/naming/naming_manager.html diff --git a/_modules/apptools/naming/object_factory.html b/4.5/_modules/apptools/naming/object_factory.html similarity index 100% rename from _modules/apptools/naming/object_factory.html rename to 4.5/_modules/apptools/naming/object_factory.html diff --git a/_modules/apptools/naming/object_serializer.html b/4.5/_modules/apptools/naming/object_serializer.html similarity index 100% rename from _modules/apptools/naming/object_serializer.html rename to 4.5/_modules/apptools/naming/object_serializer.html diff --git a/_modules/apptools/naming/py_context.html b/4.5/_modules/apptools/naming/py_context.html similarity index 100% rename from _modules/apptools/naming/py_context.html rename to 4.5/_modules/apptools/naming/py_context.html diff --git a/_modules/apptools/naming/py_object_factory.html b/4.5/_modules/apptools/naming/py_object_factory.html similarity index 100% rename from _modules/apptools/naming/py_object_factory.html rename to 4.5/_modules/apptools/naming/py_object_factory.html diff --git a/_modules/apptools/naming/pyfs_context.html b/4.5/_modules/apptools/naming/pyfs_context.html similarity index 100% rename from _modules/apptools/naming/pyfs_context.html rename to 4.5/_modules/apptools/naming/pyfs_context.html diff --git a/_modules/apptools/naming/pyfs_context_factory.html b/4.5/_modules/apptools/naming/pyfs_context_factory.html similarity index 100% rename from _modules/apptools/naming/pyfs_context_factory.html rename to 4.5/_modules/apptools/naming/pyfs_context_factory.html diff --git a/_modules/apptools/naming/pyfs_initial_context_factory.html b/4.5/_modules/apptools/naming/pyfs_initial_context_factory.html similarity index 100% rename from _modules/apptools/naming/pyfs_initial_context_factory.html rename to 4.5/_modules/apptools/naming/pyfs_initial_context_factory.html diff --git a/_modules/apptools/naming/pyfs_object_factory.html b/4.5/_modules/apptools/naming/pyfs_object_factory.html similarity index 100% rename from _modules/apptools/naming/pyfs_object_factory.html rename to 4.5/_modules/apptools/naming/pyfs_object_factory.html diff --git a/_modules/apptools/naming/pyfs_state_factory.html b/4.5/_modules/apptools/naming/pyfs_state_factory.html similarity index 100% rename from _modules/apptools/naming/pyfs_state_factory.html rename to 4.5/_modules/apptools/naming/pyfs_state_factory.html diff --git a/_modules/apptools/naming/reference.html b/4.5/_modules/apptools/naming/reference.html similarity index 100% rename from _modules/apptools/naming/reference.html rename to 4.5/_modules/apptools/naming/reference.html diff --git a/_modules/apptools/naming/referenceable.html b/4.5/_modules/apptools/naming/referenceable.html similarity index 100% rename from _modules/apptools/naming/referenceable.html rename to 4.5/_modules/apptools/naming/referenceable.html diff --git a/_modules/apptools/naming/referenceable_state_factory.html b/4.5/_modules/apptools/naming/referenceable_state_factory.html similarity index 100% rename from _modules/apptools/naming/referenceable_state_factory.html rename to 4.5/_modules/apptools/naming/referenceable_state_factory.html diff --git a/_modules/apptools/naming/state_factory.html b/4.5/_modules/apptools/naming/state_factory.html similarity index 100% rename from _modules/apptools/naming/state_factory.html rename to 4.5/_modules/apptools/naming/state_factory.html diff --git a/_modules/apptools/naming/trait_defs/naming_traits.html b/4.5/_modules/apptools/naming/trait_defs/naming_traits.html similarity index 100% rename from _modules/apptools/naming/trait_defs/naming_traits.html rename to 4.5/_modules/apptools/naming/trait_defs/naming_traits.html diff --git a/_modules/apptools/naming/ui/context_monitor.html b/4.5/_modules/apptools/naming/ui/context_monitor.html similarity index 100% rename from _modules/apptools/naming/ui/context_monitor.html rename to 4.5/_modules/apptools/naming/ui/context_monitor.html diff --git a/_modules/apptools/naming/ui/context_node_type.html b/4.5/_modules/apptools/naming/ui/context_node_type.html similarity index 100% rename from _modules/apptools/naming/ui/context_node_type.html rename to 4.5/_modules/apptools/naming/ui/context_node_type.html diff --git a/_modules/apptools/naming/ui/explorer.html b/4.5/_modules/apptools/naming/ui/explorer.html similarity index 100% rename from _modules/apptools/naming/ui/explorer.html rename to 4.5/_modules/apptools/naming/ui/explorer.html diff --git a/_modules/apptools/naming/ui/naming_node_manager.html b/4.5/_modules/apptools/naming/ui/naming_node_manager.html similarity index 100% rename from _modules/apptools/naming/ui/naming_node_manager.html rename to 4.5/_modules/apptools/naming/ui/naming_node_manager.html diff --git a/_modules/apptools/naming/ui/naming_tree.html b/4.5/_modules/apptools/naming/ui/naming_tree.html similarity index 100% rename from _modules/apptools/naming/ui/naming_tree.html rename to 4.5/_modules/apptools/naming/ui/naming_tree.html diff --git a/_modules/apptools/naming/ui/naming_tree_model.html b/4.5/_modules/apptools/naming/ui/naming_tree_model.html similarity index 100% rename from _modules/apptools/naming/ui/naming_tree_model.html rename to 4.5/_modules/apptools/naming/ui/naming_tree_model.html diff --git a/_modules/apptools/naming/ui/object_node_type.html b/4.5/_modules/apptools/naming/ui/object_node_type.html similarity index 100% rename from _modules/apptools/naming/ui/object_node_type.html rename to 4.5/_modules/apptools/naming/ui/object_node_type.html diff --git a/_modules/apptools/naming/unique_name.html b/4.5/_modules/apptools/naming/unique_name.html similarity index 100% rename from _modules/apptools/naming/unique_name.html rename to 4.5/_modules/apptools/naming/unique_name.html diff --git a/_modules/apptools/permissions/action/login_action.html b/4.5/_modules/apptools/permissions/action/login_action.html similarity index 100% rename from _modules/apptools/permissions/action/login_action.html rename to 4.5/_modules/apptools/permissions/action/login_action.html diff --git a/_modules/apptools/permissions/action/logout_action.html b/4.5/_modules/apptools/permissions/action/logout_action.html similarity index 100% rename from _modules/apptools/permissions/action/logout_action.html rename to 4.5/_modules/apptools/permissions/action/logout_action.html diff --git a/_modules/apptools/permissions/action/user_menu_manager.html b/4.5/_modules/apptools/permissions/action/user_menu_manager.html similarity index 100% rename from _modules/apptools/permissions/action/user_menu_manager.html rename to 4.5/_modules/apptools/permissions/action/user_menu_manager.html diff --git a/_modules/apptools/permissions/adapter_base.html b/4.5/_modules/apptools/permissions/adapter_base.html similarity index 100% rename from _modules/apptools/permissions/adapter_base.html rename to 4.5/_modules/apptools/permissions/adapter_base.html diff --git a/_modules/apptools/permissions/adapters/pyface_action.html b/4.5/_modules/apptools/permissions/adapters/pyface_action.html similarity index 100% rename from _modules/apptools/permissions/adapters/pyface_action.html rename to 4.5/_modules/apptools/permissions/adapters/pyface_action.html diff --git a/_modules/apptools/permissions/default/i_policy_storage.html b/4.5/_modules/apptools/permissions/default/i_policy_storage.html similarity index 100% rename from _modules/apptools/permissions/default/i_policy_storage.html rename to 4.5/_modules/apptools/permissions/default/i_policy_storage.html diff --git a/_modules/apptools/permissions/default/i_user_database.html b/4.5/_modules/apptools/permissions/default/i_user_database.html similarity index 100% rename from _modules/apptools/permissions/default/i_user_database.html rename to 4.5/_modules/apptools/permissions/default/i_user_database.html diff --git a/_modules/apptools/permissions/default/i_user_storage.html b/4.5/_modules/apptools/permissions/default/i_user_storage.html similarity index 100% rename from _modules/apptools/permissions/default/i_user_storage.html rename to 4.5/_modules/apptools/permissions/default/i_user_storage.html diff --git a/_modules/apptools/permissions/default/persistent.html b/4.5/_modules/apptools/permissions/default/persistent.html similarity index 100% rename from _modules/apptools/permissions/default/persistent.html rename to 4.5/_modules/apptools/permissions/default/persistent.html diff --git a/_modules/apptools/permissions/default/policy_data.html b/4.5/_modules/apptools/permissions/default/policy_data.html similarity index 100% rename from _modules/apptools/permissions/default/policy_data.html rename to 4.5/_modules/apptools/permissions/default/policy_data.html diff --git a/_modules/apptools/permissions/default/policy_manager.html b/4.5/_modules/apptools/permissions/default/policy_manager.html similarity index 100% rename from _modules/apptools/permissions/default/policy_manager.html rename to 4.5/_modules/apptools/permissions/default/policy_manager.html diff --git a/_modules/apptools/permissions/default/policy_storage.html b/4.5/_modules/apptools/permissions/default/policy_storage.html similarity index 100% rename from _modules/apptools/permissions/default/policy_storage.html rename to 4.5/_modules/apptools/permissions/default/policy_storage.html diff --git a/_modules/apptools/permissions/default/role_assignment.html b/4.5/_modules/apptools/permissions/default/role_assignment.html similarity index 100% rename from _modules/apptools/permissions/default/role_assignment.html rename to 4.5/_modules/apptools/permissions/default/role_assignment.html diff --git a/_modules/apptools/permissions/default/role_definition.html b/4.5/_modules/apptools/permissions/default/role_definition.html similarity index 100% rename from _modules/apptools/permissions/default/role_definition.html rename to 4.5/_modules/apptools/permissions/default/role_definition.html diff --git a/_modules/apptools/permissions/default/select_role.html b/4.5/_modules/apptools/permissions/default/select_role.html similarity index 100% rename from _modules/apptools/permissions/default/select_role.html rename to 4.5/_modules/apptools/permissions/default/select_role.html diff --git a/_modules/apptools/permissions/default/select_user.html b/4.5/_modules/apptools/permissions/default/select_user.html similarity index 100% rename from _modules/apptools/permissions/default/select_user.html rename to 4.5/_modules/apptools/permissions/default/select_user.html diff --git a/_modules/apptools/permissions/default/user_database.html b/4.5/_modules/apptools/permissions/default/user_database.html similarity index 100% rename from _modules/apptools/permissions/default/user_database.html rename to 4.5/_modules/apptools/permissions/default/user_database.html diff --git a/_modules/apptools/permissions/default/user_manager.html b/4.5/_modules/apptools/permissions/default/user_manager.html similarity index 100% rename from _modules/apptools/permissions/default/user_manager.html rename to 4.5/_modules/apptools/permissions/default/user_manager.html diff --git a/_modules/apptools/permissions/default/user_storage.html b/4.5/_modules/apptools/permissions/default/user_storage.html similarity index 100% rename from _modules/apptools/permissions/default/user_storage.html rename to 4.5/_modules/apptools/permissions/default/user_storage.html diff --git a/_modules/apptools/permissions/i_policy_manager.html b/4.5/_modules/apptools/permissions/i_policy_manager.html similarity index 100% rename from _modules/apptools/permissions/i_policy_manager.html rename to 4.5/_modules/apptools/permissions/i_policy_manager.html diff --git a/_modules/apptools/permissions/i_user.html b/4.5/_modules/apptools/permissions/i_user.html similarity index 100% rename from _modules/apptools/permissions/i_user.html rename to 4.5/_modules/apptools/permissions/i_user.html diff --git a/_modules/apptools/permissions/i_user_manager.html b/4.5/_modules/apptools/permissions/i_user_manager.html similarity index 100% rename from _modules/apptools/permissions/i_user_manager.html rename to 4.5/_modules/apptools/permissions/i_user_manager.html diff --git a/_modules/apptools/permissions/package_globals.html b/4.5/_modules/apptools/permissions/package_globals.html similarity index 100% rename from _modules/apptools/permissions/package_globals.html rename to 4.5/_modules/apptools/permissions/package_globals.html diff --git a/_modules/apptools/permissions/permission.html b/4.5/_modules/apptools/permissions/permission.html similarity index 100% rename from _modules/apptools/permissions/permission.html rename to 4.5/_modules/apptools/permissions/permission.html diff --git a/_modules/apptools/permissions/permissions_manager.html b/4.5/_modules/apptools/permissions/permissions_manager.html similarity index 100% rename from _modules/apptools/permissions/permissions_manager.html rename to 4.5/_modules/apptools/permissions/permissions_manager.html diff --git a/_modules/apptools/permissions/secure_proxy.html b/4.5/_modules/apptools/permissions/secure_proxy.html similarity index 100% rename from _modules/apptools/permissions/secure_proxy.html rename to 4.5/_modules/apptools/permissions/secure_proxy.html diff --git a/_modules/apptools/persistence/file_path.html b/4.5/_modules/apptools/persistence/file_path.html similarity index 100% rename from _modules/apptools/persistence/file_path.html rename to 4.5/_modules/apptools/persistence/file_path.html diff --git a/_modules/apptools/persistence/project_loader.html b/4.5/_modules/apptools/persistence/project_loader.html similarity index 100% rename from _modules/apptools/persistence/project_loader.html rename to 4.5/_modules/apptools/persistence/project_loader.html diff --git a/_modules/apptools/persistence/state_pickler.html b/4.5/_modules/apptools/persistence/state_pickler.html similarity index 100% rename from _modules/apptools/persistence/state_pickler.html rename to 4.5/_modules/apptools/persistence/state_pickler.html diff --git a/_modules/apptools/persistence/updater.html b/4.5/_modules/apptools/persistence/updater.html similarity index 100% rename from _modules/apptools/persistence/updater.html rename to 4.5/_modules/apptools/persistence/updater.html diff --git a/_modules/apptools/persistence/version_registry.html b/4.5/_modules/apptools/persistence/version_registry.html similarity index 100% rename from _modules/apptools/persistence/version_registry.html rename to 4.5/_modules/apptools/persistence/version_registry.html diff --git a/_modules/apptools/persistence/versioned_unpickler.html b/4.5/_modules/apptools/persistence/versioned_unpickler.html similarity index 100% rename from _modules/apptools/persistence/versioned_unpickler.html rename to 4.5/_modules/apptools/persistence/versioned_unpickler.html diff --git a/_modules/apptools/preferences/i_preferences.html b/4.5/_modules/apptools/preferences/i_preferences.html similarity index 100% rename from _modules/apptools/preferences/i_preferences.html rename to 4.5/_modules/apptools/preferences/i_preferences.html diff --git a/_modules/apptools/preferences/package_globals.html b/4.5/_modules/apptools/preferences/package_globals.html similarity index 100% rename from _modules/apptools/preferences/package_globals.html rename to 4.5/_modules/apptools/preferences/package_globals.html diff --git a/_modules/apptools/preferences/preference_binding.html b/4.5/_modules/apptools/preferences/preference_binding.html similarity index 100% rename from _modules/apptools/preferences/preference_binding.html rename to 4.5/_modules/apptools/preferences/preference_binding.html diff --git a/_modules/apptools/preferences/preferences.html b/4.5/_modules/apptools/preferences/preferences.html similarity index 100% rename from _modules/apptools/preferences/preferences.html rename to 4.5/_modules/apptools/preferences/preferences.html diff --git a/_modules/apptools/preferences/preferences_helper.html b/4.5/_modules/apptools/preferences/preferences_helper.html similarity index 100% rename from _modules/apptools/preferences/preferences_helper.html rename to 4.5/_modules/apptools/preferences/preferences_helper.html diff --git a/_modules/apptools/preferences/scoped_preferences.html b/4.5/_modules/apptools/preferences/scoped_preferences.html similarity index 100% rename from _modules/apptools/preferences/scoped_preferences.html rename to 4.5/_modules/apptools/preferences/scoped_preferences.html diff --git a/_modules/apptools/preferences/ui/i_preferences_page.html b/4.5/_modules/apptools/preferences/ui/i_preferences_page.html similarity index 100% rename from _modules/apptools/preferences/ui/i_preferences_page.html rename to 4.5/_modules/apptools/preferences/ui/i_preferences_page.html diff --git a/_modules/apptools/preferences/ui/preferences_manager.html b/4.5/_modules/apptools/preferences/ui/preferences_manager.html similarity index 100% rename from _modules/apptools/preferences/ui/preferences_manager.html rename to 4.5/_modules/apptools/preferences/ui/preferences_manager.html diff --git a/_modules/apptools/preferences/ui/preferences_node.html b/4.5/_modules/apptools/preferences/ui/preferences_node.html similarity index 100% rename from _modules/apptools/preferences/ui/preferences_node.html rename to 4.5/_modules/apptools/preferences/ui/preferences_node.html diff --git a/_modules/apptools/preferences/ui/preferences_page.html b/4.5/_modules/apptools/preferences/ui/preferences_page.html similarity index 100% rename from _modules/apptools/preferences/ui/preferences_page.html rename to 4.5/_modules/apptools/preferences/ui/preferences_page.html diff --git a/_modules/apptools/preferences/ui/tree_item.html b/4.5/_modules/apptools/preferences/ui/tree_item.html similarity index 100% rename from _modules/apptools/preferences/ui/tree_item.html rename to 4.5/_modules/apptools/preferences/ui/tree_item.html diff --git a/_modules/apptools/scripting/package_globals.html b/4.5/_modules/apptools/scripting/package_globals.html similarity index 100% rename from _modules/apptools/scripting/package_globals.html rename to 4.5/_modules/apptools/scripting/package_globals.html diff --git a/_modules/apptools/scripting/recordable.html b/4.5/_modules/apptools/scripting/recordable.html similarity index 100% rename from _modules/apptools/scripting/recordable.html rename to 4.5/_modules/apptools/scripting/recordable.html diff --git a/_modules/apptools/scripting/recorder.html b/4.5/_modules/apptools/scripting/recorder.html similarity index 100% rename from _modules/apptools/scripting/recorder.html rename to 4.5/_modules/apptools/scripting/recorder.html diff --git a/_modules/apptools/scripting/recorder_with_ui.html b/4.5/_modules/apptools/scripting/recorder_with_ui.html similarity index 100% rename from _modules/apptools/scripting/recorder_with_ui.html rename to 4.5/_modules/apptools/scripting/recorder_with_ui.html diff --git a/_modules/apptools/scripting/util.html b/4.5/_modules/apptools/scripting/util.html similarity index 100% rename from _modules/apptools/scripting/util.html rename to 4.5/_modules/apptools/scripting/util.html diff --git a/_modules/apptools/selection/errors.html b/4.5/_modules/apptools/selection/errors.html similarity index 100% rename from _modules/apptools/selection/errors.html rename to 4.5/_modules/apptools/selection/errors.html diff --git a/_modules/apptools/selection/i_selection.html b/4.5/_modules/apptools/selection/i_selection.html similarity index 100% rename from _modules/apptools/selection/i_selection.html rename to 4.5/_modules/apptools/selection/i_selection.html diff --git a/_modules/apptools/selection/i_selection_provider.html b/4.5/_modules/apptools/selection/i_selection_provider.html similarity index 100% rename from _modules/apptools/selection/i_selection_provider.html rename to 4.5/_modules/apptools/selection/i_selection_provider.html diff --git a/_modules/apptools/selection/list_selection.html b/4.5/_modules/apptools/selection/list_selection.html similarity index 100% rename from _modules/apptools/selection/list_selection.html rename to 4.5/_modules/apptools/selection/list_selection.html diff --git a/_modules/apptools/selection/selection_service.html b/4.5/_modules/apptools/selection/selection_service.html similarity index 100% rename from _modules/apptools/selection/selection_service.html rename to 4.5/_modules/apptools/selection/selection_service.html diff --git a/_modules/apptools/sweet_pickle.html b/4.5/_modules/apptools/sweet_pickle.html similarity index 100% rename from _modules/apptools/sweet_pickle.html rename to 4.5/_modules/apptools/sweet_pickle.html diff --git a/_modules/apptools/sweet_pickle/global_registry.html b/4.5/_modules/apptools/sweet_pickle/global_registry.html similarity index 100% rename from _modules/apptools/sweet_pickle/global_registry.html rename to 4.5/_modules/apptools/sweet_pickle/global_registry.html diff --git a/_modules/apptools/sweet_pickle/placeholder.html b/4.5/_modules/apptools/sweet_pickle/placeholder.html similarity index 100% rename from _modules/apptools/sweet_pickle/placeholder.html rename to 4.5/_modules/apptools/sweet_pickle/placeholder.html diff --git a/_modules/apptools/sweet_pickle/updater.html b/4.5/_modules/apptools/sweet_pickle/updater.html similarity index 100% rename from _modules/apptools/sweet_pickle/updater.html rename to 4.5/_modules/apptools/sweet_pickle/updater.html diff --git a/_modules/apptools/sweet_pickle/versioned_unpickler.html b/4.5/_modules/apptools/sweet_pickle/versioned_unpickler.html similarity index 100% rename from _modules/apptools/sweet_pickle/versioned_unpickler.html rename to 4.5/_modules/apptools/sweet_pickle/versioned_unpickler.html diff --git a/_modules/apptools/template/impl/any_context_data_name_item.html b/4.5/_modules/apptools/template/impl/any_context_data_name_item.html similarity index 100% rename from _modules/apptools/template/impl/any_context_data_name_item.html rename to 4.5/_modules/apptools/template/impl/any_context_data_name_item.html diff --git a/_modules/apptools/template/impl/any_data_name_item.html b/4.5/_modules/apptools/template/impl/any_data_name_item.html similarity index 100% rename from _modules/apptools/template/impl/any_data_name_item.html rename to 4.5/_modules/apptools/template/impl/any_data_name_item.html diff --git a/_modules/apptools/template/impl/context_data_name_item.html b/4.5/_modules/apptools/template/impl/context_data_name_item.html similarity index 100% rename from _modules/apptools/template/impl/context_data_name_item.html rename to 4.5/_modules/apptools/template/impl/context_data_name_item.html diff --git a/_modules/apptools/template/impl/helper.html b/4.5/_modules/apptools/template/impl/helper.html similarity index 100% rename from _modules/apptools/template/impl/helper.html rename to 4.5/_modules/apptools/template/impl/helper.html diff --git a/_modules/apptools/template/impl/template_data_context.html b/4.5/_modules/apptools/template/impl/template_data_context.html similarity index 100% rename from _modules/apptools/template/impl/template_data_context.html rename to 4.5/_modules/apptools/template/impl/template_data_context.html diff --git a/_modules/apptools/template/impl/template_data_source.html b/4.5/_modules/apptools/template/impl/template_data_source.html similarity index 100% rename from _modules/apptools/template/impl/template_data_source.html rename to 4.5/_modules/apptools/template/impl/template_data_source.html diff --git a/_modules/apptools/template/impl/value_data_name_item.html b/4.5/_modules/apptools/template/impl/value_data_name_item.html similarity index 100% rename from _modules/apptools/template/impl/value_data_name_item.html rename to 4.5/_modules/apptools/template/impl/value_data_name_item.html diff --git a/_modules/apptools/template/impl/value_nd_data_name_item.html b/4.5/_modules/apptools/template/impl/value_nd_data_name_item.html similarity index 100% rename from _modules/apptools/template/impl/value_nd_data_name_item.html rename to 4.5/_modules/apptools/template/impl/value_nd_data_name_item.html diff --git a/_modules/apptools/template/imutable_template.html b/4.5/_modules/apptools/template/imutable_template.html similarity index 100% rename from _modules/apptools/template/imutable_template.html rename to 4.5/_modules/apptools/template/imutable_template.html diff --git a/_modules/apptools/template/itemplate.html b/4.5/_modules/apptools/template/itemplate.html similarity index 100% rename from _modules/apptools/template/itemplate.html rename to 4.5/_modules/apptools/template/itemplate.html diff --git a/_modules/apptools/template/itemplate_choice.html b/4.5/_modules/apptools/template/itemplate_choice.html similarity index 100% rename from _modules/apptools/template/itemplate_choice.html rename to 4.5/_modules/apptools/template/itemplate_choice.html diff --git a/_modules/apptools/template/itemplate_data_context.html b/4.5/_modules/apptools/template/itemplate_data_context.html similarity index 100% rename from _modules/apptools/template/itemplate_data_context.html rename to 4.5/_modules/apptools/template/itemplate_data_context.html diff --git a/_modules/apptools/template/itemplate_data_name_item.html b/4.5/_modules/apptools/template/itemplate_data_name_item.html similarity index 100% rename from _modules/apptools/template/itemplate_data_name_item.html rename to 4.5/_modules/apptools/template/itemplate_data_name_item.html diff --git a/_modules/apptools/template/itemplate_data_source.html b/4.5/_modules/apptools/template/itemplate_data_source.html similarity index 100% rename from _modules/apptools/template/itemplate_data_source.html rename to 4.5/_modules/apptools/template/itemplate_data_source.html diff --git a/_modules/apptools/template/mutable_template.html b/4.5/_modules/apptools/template/mutable_template.html similarity index 100% rename from _modules/apptools/template/mutable_template.html rename to 4.5/_modules/apptools/template/mutable_template.html diff --git a/_modules/apptools/template/template_choice.html b/4.5/_modules/apptools/template/template_choice.html similarity index 100% rename from _modules/apptools/template/template_choice.html rename to 4.5/_modules/apptools/template/template_choice.html diff --git a/_modules/apptools/template/template_data_name.html b/4.5/_modules/apptools/template/template_data_name.html similarity index 100% rename from _modules/apptools/template/template_data_name.html rename to 4.5/_modules/apptools/template/template_data_name.html diff --git a/_modules/apptools/template/template_impl.html b/4.5/_modules/apptools/template/template_impl.html similarity index 100% rename from _modules/apptools/template/template_impl.html rename to 4.5/_modules/apptools/template/template_impl.html diff --git a/_modules/apptools/template/template_traits.html b/4.5/_modules/apptools/template/template_traits.html similarity index 100% rename from _modules/apptools/template/template_traits.html rename to 4.5/_modules/apptools/template/template_traits.html diff --git a/_modules/apptools/type_manager/abstract_adapter_factory.html b/4.5/_modules/apptools/type_manager/abstract_adapter_factory.html similarity index 100% rename from _modules/apptools/type_manager/abstract_adapter_factory.html rename to 4.5/_modules/apptools/type_manager/abstract_adapter_factory.html diff --git a/_modules/apptools/type_manager/abstract_factory.html b/4.5/_modules/apptools/type_manager/abstract_factory.html similarity index 100% rename from _modules/apptools/type_manager/abstract_factory.html rename to 4.5/_modules/apptools/type_manager/abstract_factory.html diff --git a/_modules/apptools/type_manager/abstract_type_system.html b/4.5/_modules/apptools/type_manager/abstract_type_system.html similarity index 100% rename from _modules/apptools/type_manager/abstract_type_system.html rename to 4.5/_modules/apptools/type_manager/abstract_type_system.html diff --git a/_modules/apptools/type_manager/adaptable.html b/4.5/_modules/apptools/type_manager/adaptable.html similarity index 100% rename from _modules/apptools/type_manager/adaptable.html rename to 4.5/_modules/apptools/type_manager/adaptable.html diff --git a/_modules/apptools/type_manager/adapter.html b/4.5/_modules/apptools/type_manager/adapter.html similarity index 100% rename from _modules/apptools/type_manager/adapter.html rename to 4.5/_modules/apptools/type_manager/adapter.html diff --git a/_modules/apptools/type_manager/adapter_factory.html b/4.5/_modules/apptools/type_manager/adapter_factory.html similarity index 100% rename from _modules/apptools/type_manager/adapter_factory.html rename to 4.5/_modules/apptools/type_manager/adapter_factory.html diff --git a/_modules/apptools/type_manager/adapter_manager.html b/4.5/_modules/apptools/type_manager/adapter_manager.html similarity index 100% rename from _modules/apptools/type_manager/adapter_manager.html rename to 4.5/_modules/apptools/type_manager/adapter_manager.html diff --git a/_modules/apptools/type_manager/factory.html b/4.5/_modules/apptools/type_manager/factory.html similarity index 100% rename from _modules/apptools/type_manager/factory.html rename to 4.5/_modules/apptools/type_manager/factory.html diff --git a/_modules/apptools/type_manager/hook.html b/4.5/_modules/apptools/type_manager/hook.html similarity index 100% rename from _modules/apptools/type_manager/hook.html rename to 4.5/_modules/apptools/type_manager/hook.html diff --git a/_modules/apptools/type_manager/python_type_system.html b/4.5/_modules/apptools/type_manager/python_type_system.html similarity index 100% rename from _modules/apptools/type_manager/python_type_system.html rename to 4.5/_modules/apptools/type_manager/python_type_system.html diff --git a/_modules/apptools/type_manager/type_manager.html b/4.5/_modules/apptools/type_manager/type_manager.html similarity index 100% rename from _modules/apptools/type_manager/type_manager.html rename to 4.5/_modules/apptools/type_manager/type_manager.html diff --git a/_modules/apptools/type_manager/util.html b/4.5/_modules/apptools/type_manager/util.html similarity index 100% rename from _modules/apptools/type_manager/util.html rename to 4.5/_modules/apptools/type_manager/util.html diff --git a/_modules/apptools/type_registry/type_registry.html b/4.5/_modules/apptools/type_registry/type_registry.html similarity index 100% rename from _modules/apptools/type_registry/type_registry.html rename to 4.5/_modules/apptools/type_registry/type_registry.html diff --git a/_modules/apptools/undo/abstract_command.html b/4.5/_modules/apptools/undo/abstract_command.html similarity index 100% rename from _modules/apptools/undo/abstract_command.html rename to 4.5/_modules/apptools/undo/abstract_command.html diff --git a/_modules/apptools/undo/action/abstract_command_stack_action.html b/4.5/_modules/apptools/undo/action/abstract_command_stack_action.html similarity index 100% rename from _modules/apptools/undo/action/abstract_command_stack_action.html rename to 4.5/_modules/apptools/undo/action/abstract_command_stack_action.html diff --git a/_modules/apptools/undo/action/command_action.html b/4.5/_modules/apptools/undo/action/command_action.html similarity index 100% rename from _modules/apptools/undo/action/command_action.html rename to 4.5/_modules/apptools/undo/action/command_action.html diff --git a/_modules/apptools/undo/action/redo_action.html b/4.5/_modules/apptools/undo/action/redo_action.html similarity index 100% rename from _modules/apptools/undo/action/redo_action.html rename to 4.5/_modules/apptools/undo/action/redo_action.html diff --git a/_modules/apptools/undo/action/undo_action.html b/4.5/_modules/apptools/undo/action/undo_action.html similarity index 100% rename from _modules/apptools/undo/action/undo_action.html rename to 4.5/_modules/apptools/undo/action/undo_action.html diff --git a/_modules/apptools/undo/command_stack.html b/4.5/_modules/apptools/undo/command_stack.html similarity index 100% rename from _modules/apptools/undo/command_stack.html rename to 4.5/_modules/apptools/undo/command_stack.html diff --git a/_modules/apptools/undo/i_command.html b/4.5/_modules/apptools/undo/i_command.html similarity index 100% rename from _modules/apptools/undo/i_command.html rename to 4.5/_modules/apptools/undo/i_command.html diff --git a/_modules/apptools/undo/i_command_stack.html b/4.5/_modules/apptools/undo/i_command_stack.html similarity index 100% rename from _modules/apptools/undo/i_command_stack.html rename to 4.5/_modules/apptools/undo/i_command_stack.html diff --git a/_modules/apptools/undo/i_undo_manager.html b/4.5/_modules/apptools/undo/i_undo_manager.html similarity index 100% rename from _modules/apptools/undo/i_undo_manager.html rename to 4.5/_modules/apptools/undo/i_undo_manager.html diff --git a/_modules/apptools/undo/undo_manager.html b/4.5/_modules/apptools/undo/undo_manager.html similarity index 100% rename from _modules/apptools/undo/undo_manager.html rename to 4.5/_modules/apptools/undo/undo_manager.html diff --git a/_modules/index.html b/4.5/_modules/index.html similarity index 100% rename from _modules/index.html rename to 4.5/_modules/index.html diff --git a/_modules/traits/trait_types.html b/4.5/_modules/traits/trait_types.html similarity index 100% rename from _modules/traits/trait_types.html rename to 4.5/_modules/traits/trait_types.html diff --git a/_modules/traits/traits.html b/4.5/_modules/traits/traits.html similarity index 100% rename from _modules/traits/traits.html rename to 4.5/_modules/traits/traits.html diff --git a/_modules/traitsui/editors/code_editor.html b/4.5/_modules/traitsui/editors/code_editor.html similarity index 100% rename from _modules/traitsui/editors/code_editor.html rename to 4.5/_modules/traitsui/editors/code_editor.html diff --git a/_modules/traitsui/editors/tabular_editor.html b/4.5/_modules/traitsui/editors/tabular_editor.html similarity index 100% rename from _modules/traitsui/editors/tabular_editor.html rename to 4.5/_modules/traitsui/editors/tabular_editor.html diff --git a/_modules/traitsui/view.html b/4.5/_modules/traitsui/view.html similarity index 100% rename from _modules/traitsui/view.html rename to 4.5/_modules/traitsui/view.html diff --git a/_sources/api.rst.txt b/4.5/_sources/api.rst.txt similarity index 100% rename from _sources/api.rst.txt rename to 4.5/_sources/api.rst.txt diff --git a/_sources/api/apptools.appscripting.action.rst.txt b/4.5/_sources/api/apptools.appscripting.action.rst.txt similarity index 100% rename from _sources/api/apptools.appscripting.action.rst.txt rename to 4.5/_sources/api/apptools.appscripting.action.rst.txt diff --git a/_sources/api/apptools.appscripting.rst.txt b/4.5/_sources/api/apptools.appscripting.rst.txt similarity index 100% rename from _sources/api/apptools.appscripting.rst.txt rename to 4.5/_sources/api/apptools.appscripting.rst.txt diff --git a/_sources/api/apptools.help.help_plugin.action.rst.txt b/4.5/_sources/api/apptools.help.help_plugin.action.rst.txt similarity index 100% rename from _sources/api/apptools.help.help_plugin.action.rst.txt rename to 4.5/_sources/api/apptools.help.help_plugin.action.rst.txt diff --git a/_sources/api/apptools.help.help_plugin.rst.txt b/4.5/_sources/api/apptools.help.help_plugin.rst.txt similarity index 100% rename from _sources/api/apptools.help.help_plugin.rst.txt rename to 4.5/_sources/api/apptools.help.help_plugin.rst.txt diff --git a/_sources/api/apptools.help.rst.txt b/4.5/_sources/api/apptools.help.rst.txt similarity index 100% rename from _sources/api/apptools.help.rst.txt rename to 4.5/_sources/api/apptools.help.rst.txt diff --git a/_sources/api/apptools.io.h5.rst.txt b/4.5/_sources/api/apptools.io.h5.rst.txt similarity index 100% rename from _sources/api/apptools.io.h5.rst.txt rename to 4.5/_sources/api/apptools.io.h5.rst.txt diff --git a/_sources/api/apptools.io.rst.txt b/4.5/_sources/api/apptools.io.rst.txt similarity index 100% rename from _sources/api/apptools.io.rst.txt rename to 4.5/_sources/api/apptools.io.rst.txt diff --git a/_sources/api/apptools.logger.agent.rst.txt b/4.5/_sources/api/apptools.logger.agent.rst.txt similarity index 100% rename from _sources/api/apptools.logger.agent.rst.txt rename to 4.5/_sources/api/apptools.logger.agent.rst.txt diff --git a/_sources/api/apptools.logger.plugin.rst.txt b/4.5/_sources/api/apptools.logger.plugin.rst.txt similarity index 100% rename from _sources/api/apptools.logger.plugin.rst.txt rename to 4.5/_sources/api/apptools.logger.plugin.rst.txt diff --git a/_sources/api/apptools.logger.plugin.view.rst.txt b/4.5/_sources/api/apptools.logger.plugin.view.rst.txt similarity index 100% rename from _sources/api/apptools.logger.plugin.view.rst.txt rename to 4.5/_sources/api/apptools.logger.plugin.view.rst.txt diff --git a/_sources/api/apptools.logger.rst.txt b/4.5/_sources/api/apptools.logger.rst.txt similarity index 100% rename from _sources/api/apptools.logger.rst.txt rename to 4.5/_sources/api/apptools.logger.rst.txt diff --git a/_sources/api/apptools.lru_cache.rst.txt b/4.5/_sources/api/apptools.lru_cache.rst.txt similarity index 100% rename from _sources/api/apptools.lru_cache.rst.txt rename to 4.5/_sources/api/apptools.lru_cache.rst.txt diff --git a/_sources/api/apptools.naming.adapter.rst.txt b/4.5/_sources/api/apptools.naming.adapter.rst.txt similarity index 100% rename from _sources/api/apptools.naming.adapter.rst.txt rename to 4.5/_sources/api/apptools.naming.adapter.rst.txt diff --git a/_sources/api/apptools.naming.rst.txt b/4.5/_sources/api/apptools.naming.rst.txt similarity index 100% rename from _sources/api/apptools.naming.rst.txt rename to 4.5/_sources/api/apptools.naming.rst.txt diff --git a/_sources/api/apptools.naming.trait_defs.rst.txt b/4.5/_sources/api/apptools.naming.trait_defs.rst.txt similarity index 100% rename from _sources/api/apptools.naming.trait_defs.rst.txt rename to 4.5/_sources/api/apptools.naming.trait_defs.rst.txt diff --git a/_sources/api/apptools.naming.ui.rst.txt b/4.5/_sources/api/apptools.naming.ui.rst.txt similarity index 100% rename from _sources/api/apptools.naming.ui.rst.txt rename to 4.5/_sources/api/apptools.naming.ui.rst.txt diff --git a/_sources/api/apptools.permissions.action.rst.txt b/4.5/_sources/api/apptools.permissions.action.rst.txt similarity index 100% rename from _sources/api/apptools.permissions.action.rst.txt rename to 4.5/_sources/api/apptools.permissions.action.rst.txt diff --git a/_sources/api/apptools.permissions.adapters.rst.txt b/4.5/_sources/api/apptools.permissions.adapters.rst.txt similarity index 100% rename from _sources/api/apptools.permissions.adapters.rst.txt rename to 4.5/_sources/api/apptools.permissions.adapters.rst.txt diff --git a/_sources/api/apptools.permissions.default.rst.txt b/4.5/_sources/api/apptools.permissions.default.rst.txt similarity index 100% rename from _sources/api/apptools.permissions.default.rst.txt rename to 4.5/_sources/api/apptools.permissions.default.rst.txt diff --git a/_sources/api/apptools.permissions.rst.txt b/4.5/_sources/api/apptools.permissions.rst.txt similarity index 100% rename from _sources/api/apptools.permissions.rst.txt rename to 4.5/_sources/api/apptools.permissions.rst.txt diff --git a/_sources/api/apptools.persistence.rst.txt b/4.5/_sources/api/apptools.persistence.rst.txt similarity index 100% rename from _sources/api/apptools.persistence.rst.txt rename to 4.5/_sources/api/apptools.persistence.rst.txt diff --git a/_sources/api/apptools.preferences.rst.txt b/4.5/_sources/api/apptools.preferences.rst.txt similarity index 100% rename from _sources/api/apptools.preferences.rst.txt rename to 4.5/_sources/api/apptools.preferences.rst.txt diff --git a/_sources/api/apptools.preferences.ui.rst.txt b/4.5/_sources/api/apptools.preferences.ui.rst.txt similarity index 100% rename from _sources/api/apptools.preferences.ui.rst.txt rename to 4.5/_sources/api/apptools.preferences.ui.rst.txt diff --git a/_sources/api/apptools.rst.txt b/4.5/_sources/api/apptools.rst.txt similarity index 100% rename from _sources/api/apptools.rst.txt rename to 4.5/_sources/api/apptools.rst.txt diff --git a/_sources/api/apptools.scripting.rst.txt b/4.5/_sources/api/apptools.scripting.rst.txt similarity index 100% rename from _sources/api/apptools.scripting.rst.txt rename to 4.5/_sources/api/apptools.scripting.rst.txt diff --git a/_sources/api/apptools.selection.rst.txt b/4.5/_sources/api/apptools.selection.rst.txt similarity index 100% rename from _sources/api/apptools.selection.rst.txt rename to 4.5/_sources/api/apptools.selection.rst.txt diff --git a/_sources/api/apptools.sweet_pickle.rst.txt b/4.5/_sources/api/apptools.sweet_pickle.rst.txt similarity index 100% rename from _sources/api/apptools.sweet_pickle.rst.txt rename to 4.5/_sources/api/apptools.sweet_pickle.rst.txt diff --git a/_sources/api/apptools.template.impl.rst.txt b/4.5/_sources/api/apptools.template.impl.rst.txt similarity index 100% rename from _sources/api/apptools.template.impl.rst.txt rename to 4.5/_sources/api/apptools.template.impl.rst.txt diff --git a/_sources/api/apptools.template.rst.txt b/4.5/_sources/api/apptools.template.rst.txt similarity index 100% rename from _sources/api/apptools.template.rst.txt rename to 4.5/_sources/api/apptools.template.rst.txt diff --git a/_sources/api/apptools.template.test.rst.txt b/4.5/_sources/api/apptools.template.test.rst.txt similarity index 100% rename from _sources/api/apptools.template.test.rst.txt rename to 4.5/_sources/api/apptools.template.test.rst.txt diff --git a/_sources/api/apptools.type_manager.rst.txt b/4.5/_sources/api/apptools.type_manager.rst.txt similarity index 100% rename from _sources/api/apptools.type_manager.rst.txt rename to 4.5/_sources/api/apptools.type_manager.rst.txt diff --git a/_sources/api/apptools.type_registry.rst.txt b/4.5/_sources/api/apptools.type_registry.rst.txt similarity index 100% rename from _sources/api/apptools.type_registry.rst.txt rename to 4.5/_sources/api/apptools.type_registry.rst.txt diff --git a/_sources/api/apptools.undo.action.rst.txt b/4.5/_sources/api/apptools.undo.action.rst.txt similarity index 100% rename from _sources/api/apptools.undo.action.rst.txt rename to 4.5/_sources/api/apptools.undo.action.rst.txt diff --git a/_sources/api/apptools.undo.rst.txt b/4.5/_sources/api/apptools.undo.rst.txt similarity index 100% rename from _sources/api/apptools.undo.rst.txt rename to 4.5/_sources/api/apptools.undo.rst.txt diff --git a/_sources/api/modules.rst.txt b/4.5/_sources/api/modules.rst.txt similarity index 100% rename from _sources/api/modules.rst.txt rename to 4.5/_sources/api/modules.rst.txt diff --git a/_sources/appscripting/Introduction.rst.txt b/4.5/_sources/appscripting/Introduction.rst.txt similarity index 100% rename from _sources/appscripting/Introduction.rst.txt rename to 4.5/_sources/appscripting/Introduction.rst.txt diff --git a/_sources/index.rst.txt b/4.5/_sources/index.rst.txt similarity index 100% rename from _sources/index.rst.txt rename to 4.5/_sources/index.rst.txt diff --git a/_sources/permissions/ApplicationAPI.rst.txt b/4.5/_sources/permissions/ApplicationAPI.rst.txt similarity index 100% rename from _sources/permissions/ApplicationAPI.rst.txt rename to 4.5/_sources/permissions/ApplicationAPI.rst.txt diff --git a/_sources/permissions/DefaultPolicyManagerDataAPI.rst.txt b/4.5/_sources/permissions/DefaultPolicyManagerDataAPI.rst.txt similarity index 100% rename from _sources/permissions/DefaultPolicyManagerDataAPI.rst.txt rename to 4.5/_sources/permissions/DefaultPolicyManagerDataAPI.rst.txt diff --git a/_sources/permissions/DefaultUserManagerDataAPI.rst.txt b/4.5/_sources/permissions/DefaultUserManagerDataAPI.rst.txt similarity index 100% rename from _sources/permissions/DefaultUserManagerDataAPI.rst.txt rename to 4.5/_sources/permissions/DefaultUserManagerDataAPI.rst.txt diff --git a/_sources/permissions/Introduction.rst.txt b/4.5/_sources/permissions/Introduction.rst.txt similarity index 100% rename from _sources/permissions/Introduction.rst.txt rename to 4.5/_sources/permissions/Introduction.rst.txt diff --git a/_sources/preferences/Preferences.rst.txt b/4.5/_sources/preferences/Preferences.rst.txt similarity index 100% rename from _sources/preferences/Preferences.rst.txt rename to 4.5/_sources/preferences/Preferences.rst.txt diff --git a/_sources/preferences/PreferencesInEnvisage.rst.txt b/4.5/_sources/preferences/PreferencesInEnvisage.rst.txt similarity index 100% rename from _sources/preferences/PreferencesInEnvisage.rst.txt rename to 4.5/_sources/preferences/PreferencesInEnvisage.rst.txt diff --git a/_sources/scripting/introduction.rst.txt b/4.5/_sources/scripting/introduction.rst.txt similarity index 100% rename from _sources/scripting/introduction.rst.txt rename to 4.5/_sources/scripting/introduction.rst.txt diff --git a/_sources/selection/selection.rst.txt b/4.5/_sources/selection/selection.rst.txt similarity index 100% rename from _sources/selection/selection.rst.txt rename to 4.5/_sources/selection/selection.rst.txt diff --git a/_sources/undo/Introduction.rst.txt b/4.5/_sources/undo/Introduction.rst.txt similarity index 100% rename from _sources/undo/Introduction.rst.txt rename to 4.5/_sources/undo/Introduction.rst.txt diff --git a/_static/ajax-loader.gif b/4.5/_static/ajax-loader.gif similarity index 100% rename from _static/ajax-loader.gif rename to 4.5/_static/ajax-loader.gif diff --git a/_static/basic.css b/4.5/_static/basic.css similarity index 100% rename from _static/basic.css rename to 4.5/_static/basic.css diff --git a/_static/comment-bright.png b/4.5/_static/comment-bright.png similarity index 100% rename from _static/comment-bright.png rename to 4.5/_static/comment-bright.png diff --git a/_static/comment-close.png b/4.5/_static/comment-close.png similarity index 100% rename from _static/comment-close.png rename to 4.5/_static/comment-close.png diff --git a/_static/comment.png b/4.5/_static/comment.png similarity index 100% rename from _static/comment.png rename to 4.5/_static/comment.png diff --git a/_static/css/pygments.css b/4.5/_static/css/pygments.css similarity index 100% rename from _static/css/pygments.css rename to 4.5/_static/css/pygments.css diff --git a/_static/css/spc-bootstrap.css b/4.5/_static/css/spc-bootstrap.css similarity index 100% rename from _static/css/spc-bootstrap.css rename to 4.5/_static/css/spc-bootstrap.css diff --git a/_static/css/spc-extend.css b/4.5/_static/css/spc-extend.css similarity index 100% rename from _static/css/spc-extend.css rename to 4.5/_static/css/spc-extend.css diff --git a/_static/doctools.js b/4.5/_static/doctools.js similarity index 100% rename from _static/doctools.js rename to 4.5/_static/doctools.js diff --git a/_static/documentation_options.js b/4.5/_static/documentation_options.js similarity index 100% rename from _static/documentation_options.js rename to 4.5/_static/documentation_options.js diff --git a/_static/down-pressed.png b/4.5/_static/down-pressed.png similarity index 100% rename from _static/down-pressed.png rename to 4.5/_static/down-pressed.png diff --git a/_static/down.png b/4.5/_static/down.png similarity index 100% rename from _static/down.png rename to 4.5/_static/down.png diff --git a/_static/enthought.css b/4.5/_static/enthought.css similarity index 100% rename from _static/enthought.css rename to 4.5/_static/enthought.css diff --git a/_static/file.png b/4.5/_static/file.png similarity index 100% rename from _static/file.png rename to 4.5/_static/file.png diff --git a/_static/fonts/SourceCodePro-Regular.otf.woff b/4.5/_static/fonts/SourceCodePro-Regular.otf.woff similarity index 100% rename from _static/fonts/SourceCodePro-Regular.otf.woff rename to 4.5/_static/fonts/SourceCodePro-Regular.otf.woff diff --git a/_static/fonts/SourceCodePro-Semibold.otf.woff b/4.5/_static/fonts/SourceCodePro-Semibold.otf.woff similarity index 100% rename from _static/fonts/SourceCodePro-Semibold.otf.woff rename to 4.5/_static/fonts/SourceCodePro-Semibold.otf.woff diff --git a/_static/fonts/SourceSansPro-It.otf.woff b/4.5/_static/fonts/SourceSansPro-It.otf.woff similarity index 100% rename from _static/fonts/SourceSansPro-It.otf.woff rename to 4.5/_static/fonts/SourceSansPro-It.otf.woff diff --git a/_static/fonts/SourceSansPro-Regular.otf.woff b/4.5/_static/fonts/SourceSansPro-Regular.otf.woff similarity index 100% rename from _static/fonts/SourceSansPro-Regular.otf.woff rename to 4.5/_static/fonts/SourceSansPro-Regular.otf.woff diff --git a/_static/fonts/SourceSansPro-Semibold.otf.woff b/4.5/_static/fonts/SourceSansPro-Semibold.otf.woff similarity index 100% rename from _static/fonts/SourceSansPro-Semibold.otf.woff rename to 4.5/_static/fonts/SourceSansPro-Semibold.otf.woff diff --git a/_static/fonts/SourceSansPro-SemiboldIt.otf.woff b/4.5/_static/fonts/SourceSansPro-SemiboldIt.otf.woff similarity index 100% rename from _static/fonts/SourceSansPro-SemiboldIt.otf.woff rename to 4.5/_static/fonts/SourceSansPro-SemiboldIt.otf.woff diff --git a/_static/img/contents.png b/4.5/_static/img/contents.png similarity index 100% rename from _static/img/contents.png rename to 4.5/_static/img/contents.png diff --git a/_static/img/e-logo.png b/4.5/_static/img/e-logo.png similarity index 100% rename from _static/img/e-logo.png rename to 4.5/_static/img/e-logo.png diff --git a/_static/img/favicon.ico b/4.5/_static/img/favicon.ico similarity index 100% rename from _static/img/favicon.ico rename to 4.5/_static/img/favicon.ico diff --git a/_static/img/glyphicons-halflings-white.png b/4.5/_static/img/glyphicons-halflings-white.png similarity index 100% rename from _static/img/glyphicons-halflings-white.png rename to 4.5/_static/img/glyphicons-halflings-white.png diff --git a/_static/img/glyphicons-halflings.png b/4.5/_static/img/glyphicons-halflings.png similarity index 100% rename from _static/img/glyphicons-halflings.png rename to 4.5/_static/img/glyphicons-halflings.png diff --git a/_static/img/navigation.png b/4.5/_static/img/navigation.png similarity index 100% rename from _static/img/navigation.png rename to 4.5/_static/img/navigation.png diff --git a/_static/img/transparent-pixel.gif b/4.5/_static/img/transparent-pixel.gif similarity index 100% rename from _static/img/transparent-pixel.gif rename to 4.5/_static/img/transparent-pixel.gif diff --git a/_static/img/ui-anim_basic_16x16.gif b/4.5/_static/img/ui-anim_basic_16x16.gif similarity index 100% rename from _static/img/ui-anim_basic_16x16.gif rename to 4.5/_static/img/ui-anim_basic_16x16.gif diff --git a/_static/jquery-3.2.1.js b/4.5/_static/jquery-3.2.1.js similarity index 100% rename from _static/jquery-3.2.1.js rename to 4.5/_static/jquery-3.2.1.js diff --git a/_static/jquery.js b/4.5/_static/jquery.js similarity index 100% rename from _static/jquery.js rename to 4.5/_static/jquery.js diff --git a/_static/js/copybutton.js b/4.5/_static/js/copybutton.js similarity index 100% rename from _static/js/copybutton.js rename to 4.5/_static/js/copybutton.js diff --git a/_static/js/wrap_on_dot.js b/4.5/_static/js/wrap_on_dot.js similarity index 100% rename from _static/js/wrap_on_dot.js rename to 4.5/_static/js/wrap_on_dot.js diff --git a/_static/language_data.js b/4.5/_static/language_data.js similarity index 100% rename from _static/language_data.js rename to 4.5/_static/language_data.js diff --git a/_static/minus.png b/4.5/_static/minus.png similarity index 100% rename from _static/minus.png rename to 4.5/_static/minus.png diff --git a/_static/plus.png b/4.5/_static/plus.png similarity index 100% rename from _static/plus.png rename to 4.5/_static/plus.png diff --git a/_static/pygments.css b/4.5/_static/pygments.css similarity index 100% rename from _static/pygments.css rename to 4.5/_static/pygments.css diff --git a/_static/searchtools.js b/4.5/_static/searchtools.js similarity index 100% rename from _static/searchtools.js rename to 4.5/_static/searchtools.js diff --git a/_static/underscore-1.3.1.js b/4.5/_static/underscore-1.3.1.js similarity index 100% rename from _static/underscore-1.3.1.js rename to 4.5/_static/underscore-1.3.1.js diff --git a/_static/underscore.js b/4.5/_static/underscore.js similarity index 100% rename from _static/underscore.js rename to 4.5/_static/underscore.js diff --git a/_static/up-pressed.png b/4.5/_static/up-pressed.png similarity index 100% rename from _static/up-pressed.png rename to 4.5/_static/up-pressed.png diff --git a/_static/up.png b/4.5/_static/up.png similarity index 100% rename from _static/up.png rename to 4.5/_static/up.png diff --git a/_static/websupport.js b/4.5/_static/websupport.js similarity index 100% rename from _static/websupport.js rename to 4.5/_static/websupport.js diff --git a/api.html b/4.5/api.html similarity index 100% rename from api.html rename to 4.5/api.html diff --git a/api/apptools.appscripting.action.html b/4.5/api/apptools.appscripting.action.html similarity index 100% rename from api/apptools.appscripting.action.html rename to 4.5/api/apptools.appscripting.action.html diff --git a/api/apptools.appscripting.html b/4.5/api/apptools.appscripting.html similarity index 100% rename from api/apptools.appscripting.html rename to 4.5/api/apptools.appscripting.html diff --git a/api/apptools.help.help_plugin.action.html b/4.5/api/apptools.help.help_plugin.action.html similarity index 100% rename from api/apptools.help.help_plugin.action.html rename to 4.5/api/apptools.help.help_plugin.action.html diff --git a/api/apptools.help.help_plugin.html b/4.5/api/apptools.help.help_plugin.html similarity index 100% rename from api/apptools.help.help_plugin.html rename to 4.5/api/apptools.help.help_plugin.html diff --git a/api/apptools.help.html b/4.5/api/apptools.help.html similarity index 100% rename from api/apptools.help.html rename to 4.5/api/apptools.help.html diff --git a/api/apptools.html b/4.5/api/apptools.html similarity index 100% rename from api/apptools.html rename to 4.5/api/apptools.html diff --git a/api/apptools.io.h5.html b/4.5/api/apptools.io.h5.html similarity index 100% rename from api/apptools.io.h5.html rename to 4.5/api/apptools.io.h5.html diff --git a/api/apptools.io.html b/4.5/api/apptools.io.html similarity index 100% rename from api/apptools.io.html rename to 4.5/api/apptools.io.html diff --git a/api/apptools.logger.agent.html b/4.5/api/apptools.logger.agent.html similarity index 100% rename from api/apptools.logger.agent.html rename to 4.5/api/apptools.logger.agent.html diff --git a/api/apptools.logger.html b/4.5/api/apptools.logger.html similarity index 100% rename from api/apptools.logger.html rename to 4.5/api/apptools.logger.html diff --git a/api/apptools.logger.plugin.html b/4.5/api/apptools.logger.plugin.html similarity index 100% rename from api/apptools.logger.plugin.html rename to 4.5/api/apptools.logger.plugin.html diff --git a/api/apptools.logger.plugin.view.html b/4.5/api/apptools.logger.plugin.view.html similarity index 100% rename from api/apptools.logger.plugin.view.html rename to 4.5/api/apptools.logger.plugin.view.html diff --git a/api/apptools.lru_cache.html b/4.5/api/apptools.lru_cache.html similarity index 100% rename from api/apptools.lru_cache.html rename to 4.5/api/apptools.lru_cache.html diff --git a/api/apptools.naming.adapter.html b/4.5/api/apptools.naming.adapter.html similarity index 100% rename from api/apptools.naming.adapter.html rename to 4.5/api/apptools.naming.adapter.html diff --git a/api/apptools.naming.html b/4.5/api/apptools.naming.html similarity index 100% rename from api/apptools.naming.html rename to 4.5/api/apptools.naming.html diff --git a/api/apptools.naming.trait_defs.html b/4.5/api/apptools.naming.trait_defs.html similarity index 100% rename from api/apptools.naming.trait_defs.html rename to 4.5/api/apptools.naming.trait_defs.html diff --git a/api/apptools.naming.ui.html b/4.5/api/apptools.naming.ui.html similarity index 100% rename from api/apptools.naming.ui.html rename to 4.5/api/apptools.naming.ui.html diff --git a/api/apptools.permissions.action.html b/4.5/api/apptools.permissions.action.html similarity index 100% rename from api/apptools.permissions.action.html rename to 4.5/api/apptools.permissions.action.html diff --git a/api/apptools.permissions.adapters.html b/4.5/api/apptools.permissions.adapters.html similarity index 100% rename from api/apptools.permissions.adapters.html rename to 4.5/api/apptools.permissions.adapters.html diff --git a/api/apptools.permissions.default.html b/4.5/api/apptools.permissions.default.html similarity index 100% rename from api/apptools.permissions.default.html rename to 4.5/api/apptools.permissions.default.html diff --git a/api/apptools.permissions.html b/4.5/api/apptools.permissions.html similarity index 100% rename from api/apptools.permissions.html rename to 4.5/api/apptools.permissions.html diff --git a/api/apptools.persistence.html b/4.5/api/apptools.persistence.html similarity index 100% rename from api/apptools.persistence.html rename to 4.5/api/apptools.persistence.html diff --git a/api/apptools.preferences.html b/4.5/api/apptools.preferences.html similarity index 100% rename from api/apptools.preferences.html rename to 4.5/api/apptools.preferences.html diff --git a/api/apptools.preferences.ui.html b/4.5/api/apptools.preferences.ui.html similarity index 100% rename from api/apptools.preferences.ui.html rename to 4.5/api/apptools.preferences.ui.html diff --git a/api/apptools.scripting.html b/4.5/api/apptools.scripting.html similarity index 100% rename from api/apptools.scripting.html rename to 4.5/api/apptools.scripting.html diff --git a/api/apptools.selection.html b/4.5/api/apptools.selection.html similarity index 100% rename from api/apptools.selection.html rename to 4.5/api/apptools.selection.html diff --git a/api/apptools.sweet_pickle.html b/4.5/api/apptools.sweet_pickle.html similarity index 100% rename from api/apptools.sweet_pickle.html rename to 4.5/api/apptools.sweet_pickle.html diff --git a/api/apptools.template.html b/4.5/api/apptools.template.html similarity index 100% rename from api/apptools.template.html rename to 4.5/api/apptools.template.html diff --git a/api/apptools.template.impl.html b/4.5/api/apptools.template.impl.html similarity index 100% rename from api/apptools.template.impl.html rename to 4.5/api/apptools.template.impl.html diff --git a/api/apptools.template.test.html b/4.5/api/apptools.template.test.html similarity index 100% rename from api/apptools.template.test.html rename to 4.5/api/apptools.template.test.html diff --git a/api/apptools.type_manager.html b/4.5/api/apptools.type_manager.html similarity index 100% rename from api/apptools.type_manager.html rename to 4.5/api/apptools.type_manager.html diff --git a/api/apptools.type_registry.html b/4.5/api/apptools.type_registry.html similarity index 100% rename from api/apptools.type_registry.html rename to 4.5/api/apptools.type_registry.html diff --git a/api/apptools.undo.action.html b/4.5/api/apptools.undo.action.html similarity index 100% rename from api/apptools.undo.action.html rename to 4.5/api/apptools.undo.action.html diff --git a/api/apptools.undo.html b/4.5/api/apptools.undo.html similarity index 100% rename from api/apptools.undo.html rename to 4.5/api/apptools.undo.html diff --git a/api/modules.html b/4.5/api/modules.html similarity index 100% rename from api/modules.html rename to 4.5/api/modules.html diff --git a/appscripting/Introduction.html b/4.5/appscripting/Introduction.html similarity index 100% rename from appscripting/Introduction.html rename to 4.5/appscripting/Introduction.html diff --git a/genindex.html b/4.5/genindex.html similarity index 100% rename from genindex.html rename to 4.5/genindex.html diff --git a/index.html b/4.5/index.html similarity index 100% rename from index.html rename to 4.5/index.html diff --git a/objects.inv b/4.5/objects.inv similarity index 100% rename from objects.inv rename to 4.5/objects.inv diff --git a/permissions/ApplicationAPI.html b/4.5/permissions/ApplicationAPI.html similarity index 100% rename from permissions/ApplicationAPI.html rename to 4.5/permissions/ApplicationAPI.html diff --git a/permissions/DefaultPolicyManagerDataAPI.html b/4.5/permissions/DefaultPolicyManagerDataAPI.html similarity index 100% rename from permissions/DefaultPolicyManagerDataAPI.html rename to 4.5/permissions/DefaultPolicyManagerDataAPI.html diff --git a/permissions/DefaultUserManagerDataAPI.html b/4.5/permissions/DefaultUserManagerDataAPI.html similarity index 100% rename from permissions/DefaultUserManagerDataAPI.html rename to 4.5/permissions/DefaultUserManagerDataAPI.html diff --git a/permissions/Introduction.html b/4.5/permissions/Introduction.html similarity index 100% rename from permissions/Introduction.html rename to 4.5/permissions/Introduction.html diff --git a/preferences/Preferences.html b/4.5/preferences/Preferences.html similarity index 100% rename from preferences/Preferences.html rename to 4.5/preferences/Preferences.html diff --git a/preferences/PreferencesInEnvisage.html b/4.5/preferences/PreferencesInEnvisage.html similarity index 100% rename from preferences/PreferencesInEnvisage.html rename to 4.5/preferences/PreferencesInEnvisage.html diff --git a/py-modindex.html b/4.5/py-modindex.html similarity index 100% rename from py-modindex.html rename to 4.5/py-modindex.html diff --git a/scripting/introduction.html b/4.5/scripting/introduction.html similarity index 100% rename from scripting/introduction.html rename to 4.5/scripting/introduction.html diff --git a/search.html b/4.5/search.html similarity index 100% rename from search.html rename to 4.5/search.html diff --git a/searchindex.js b/4.5/searchindex.js similarity index 100% rename from searchindex.js rename to 4.5/searchindex.js diff --git a/selection/selection.html b/4.5/selection/selection.html similarity index 100% rename from selection/selection.html rename to 4.5/selection/selection.html diff --git a/undo/Introduction.html b/4.5/undo/Introduction.html similarity index 100% rename from undo/Introduction.html rename to 4.5/undo/Introduction.html From 9f83b4d465b871bf7659c138d9c09994d2ada660 Mon Sep 17 00:00:00 2001 From: Aaron Ayres Date: Thu, 17 Dec 2020 08:47:54 -0600 Subject: [PATCH 2/2] create new 5.0 folder and add current docs there and to root directory --- 5.0/_modules/apptools/io/file.html | 443 + 5.0/_modules/apptools/io/h5/dict_node.html | 362 + 5.0/_modules/apptools/io/h5/file.html | 671 + 5.0/_modules/apptools/io/h5/table_node.html | 287 + 5.0/_modules/apptools/io/h5/utils.html | 166 + .../apptools/logger/agent/attachments.html | 235 + .../logger/agent/quality_agent_mailer.html | 244 + .../logger/agent/quality_agent_view.html | 510 + .../apptools/logger/custom_excepthook.html | 165 + 5.0/_modules/apptools/logger/log_point.html | 170 + .../apptools/logger/log_queue_handler.html | 198 + 5.0/_modules/apptools/logger/logger.html | 188 + .../apptools/logger/plugin/logger_plugin.html | 238 + .../logger/plugin/logger_preferences.html | 169 + .../logger/plugin/logger_service.html | 310 + .../plugin/view/logger_preferences_page.html | 226 + .../logger/plugin/view/logger_view.html | 343 + 5.0/_modules/apptools/logger/ring_buffer.html | 183 + 5.0/_modules/apptools/naming/address.html | 158 + 5.0/_modules/apptools/naming/binding.html | 233 + 5.0/_modules/apptools/naming/context.html | 813 ++ 5.0/_modules/apptools/naming/dir_context.html | 293 + .../apptools/naming/dynamic_context.html | 308 + 5.0/_modules/apptools/naming/exception.html | 184 + .../apptools/naming/initial_context.html | 185 + .../naming/initial_context_factory.html | 159 + .../apptools/naming/naming_event.html | 157 + .../apptools/naming/naming_manager.html | 213 + .../apptools/naming/object_factory.html | 166 + .../apptools/naming/object_serializer.html | 222 + 5.0/_modules/apptools/naming/py_context.html | 311 + .../apptools/naming/py_object_factory.html | 171 + .../apptools/naming/pyfs_context.html | 674 + .../apptools/naming/pyfs_context_factory.html | 165 + .../naming/pyfs_initial_context_factory.html | 174 + .../apptools/naming/pyfs_object_factory.html | 166 + .../apptools/naming/pyfs_state_factory.html | 172 + 5.0/_modules/apptools/naming/reference.html | 173 + .../apptools/naming/referenceable.html | 155 + .../naming/referenceable_state_factory.html | 164 + .../apptools/naming/state_factory.html | 166 + .../naming/trait_defs/naming_traits.html | 300 + 5.0/_modules/apptools/naming/unique_name.html | 164 + .../apptools/persistence/file_path.html | 218 + .../apptools/persistence/project_loader.html | 262 + .../apptools/persistence/state_pickler.html | 1168 ++ .../apptools/persistence/updater.html | 174 + .../persistence/version_registry.html | 239 + .../persistence/versioned_unpickler.html | 342 + .../apptools/preferences/i_preferences.html | 310 + .../apptools/preferences/package_globals.html | 167 + .../preferences/preference_binding.html | 299 + .../apptools/preferences/preferences.html | 687 + .../preferences/preferences_helper.html | 336 + .../preferences/scoped_preferences.html | 582 + .../preferences/ui/i_preferences_page.html | 162 + .../preferences/ui/preferences_manager.html | 418 + .../preferences/ui/preferences_node.html | 221 + .../preferences/ui/preferences_page.html | 233 + .../apptools/preferences/ui/tree_item.html | 269 + .../preferences/ui/widget_editor.html | 232 + .../apptools/scripting/package_globals.html | 159 + .../apptools/scripting/recordable.html | 186 + 5.0/_modules/apptools/scripting/recorder.html | 874 ++ .../apptools/scripting/recorder_with_ui.html | 226 + 5.0/_modules/apptools/scripting/util.html | 186 + 5.0/_modules/apptools/selection/errors.html | 172 + .../apptools/selection/i_selection.html | 159 + .../selection/i_selection_provider.html | 176 + .../apptools/selection/list_selection.html | 196 + .../apptools/selection/selection_service.html | 325 + .../apptools/type_registry/type_registry.html | 403 + .../apptools/undo/abstract_command.html | 205 + .../action/abstract_command_stack_action.html | 212 + .../apptools/undo/action/command_action.html | 187 + .../apptools/undo/action/redo_action.html | 178 + .../apptools/undo/action/undo_action.html | 176 + 5.0/_modules/apptools/undo/command_stack.html | 448 + 5.0/_modules/apptools/undo/i_command.html | 194 + .../apptools/undo/i_command_stack.html | 220 + .../apptools/undo/i_undo_manager.html | 192 + 5.0/_modules/apptools/undo/undo_manager.html | 251 + 5.0/_modules/index.html | 215 + 5.0/_modules/traits/trait_types.html | 4427 +++++++ 5.0/_modules/traits/traits.html | 833 ++ .../traitsui/editors/code_editor.html | 243 + .../traitsui/editors/tabular_editor.html | 290 + 5.0/_modules/traitsui/view.html | 610 + 5.0/_sources/api.rst.txt | 11 + 5.0/_sources/api/apptools.io.h5.rst.txt | 46 + 5.0/_sources/api/apptools.io.rst.txt | 37 + .../api/apptools.logger.agent.rst.txt | 38 + .../api/apptools.logger.plugin.rst.txt | 45 + .../api/apptools.logger.plugin.view.rst.txt | 30 + 5.0/_sources/api/apptools.logger.rst.txt | 70 + 5.0/_sources/api/apptools.naming.rst.txt | 221 + .../api/apptools.naming.trait_defs.rst.txt | 30 + 5.0/_sources/api/apptools.persistence.rst.txt | 62 + 5.0/_sources/api/apptools.preferences.rst.txt | 77 + .../api/apptools.preferences.ui.rst.txt | 70 + 5.0/_sources/api/apptools.rst.txt | 25 + 5.0/_sources/api/apptools.scripting.rst.txt | 62 + 5.0/_sources/api/apptools.selection.rst.txt | 62 + .../api/apptools.type_registry.rst.txt | 30 + 5.0/_sources/api/apptools.undo.action.rst.txt | 54 + 5.0/_sources/api/apptools.undo.rst.txt | 77 + 5.0/_sources/api/modules.rst.txt | 7 + 5.0/_sources/index.rst.txt | 16 + 5.0/_sources/io/introduction.rst.txt | 28 + 5.0/_sources/naming/Introduction.rst.txt | 7 + 5.0/_sources/preferences/Preferences.rst.txt | 329 + .../preferences/PreferencesInEnvisage.rst.txt | 52 + 5.0/_sources/scripting/introduction.rst.txt | 198 + 5.0/_sources/selection/selection.rst.txt | 144 + 5.0/_sources/undo/Introduction.rst.txt | 275 + 5.0/_static/basic.css | 768 ++ 5.0/_static/css/pygments.css | 87 + 5.0/_static/css/spc-bootstrap.css | 6288 +++++++++ 5.0/_static/css/spc-extend.css | 92 + 5.0/_static/doctools.js | 314 + 5.0/_static/documentation_options.js | 11 + 5.0/_static/enthought.css | 412 + 5.0/_static/file.png | Bin 0 -> 286 bytes .../fonts/SourceCodePro-Regular.otf.woff | Bin 0 -> 57712 bytes .../fonts/SourceCodePro-Semibold.otf.woff | Bin 0 -> 57200 bytes 5.0/_static/fonts/SourceSansPro-It.otf.woff | Bin 0 -> 53852 bytes .../fonts/SourceSansPro-Regular.otf.woff | Bin 0 -> 133352 bytes .../fonts/SourceSansPro-Semibold.otf.woff | Bin 0 -> 134540 bytes .../fonts/SourceSansPro-SemiboldIt.otf.woff | Bin 0 -> 54260 bytes 5.0/_static/img/contents.png | Bin 0 -> 202 bytes 5.0/_static/img/e-logo.png | Bin 0 -> 5124 bytes 5.0/_static/img/favicon.ico | Bin 0 -> 4286 bytes .../img/glyphicons-halflings-white.png | Bin 0 -> 8777 bytes 5.0/_static/img/glyphicons-halflings.png | Bin 0 -> 12799 bytes 5.0/_static/img/navigation.png | Bin 0 -> 218 bytes 5.0/_static/img/transparent-pixel.gif | Bin 0 -> 43 bytes 5.0/_static/img/ui-anim_basic_16x16.gif | Bin 0 -> 1459 bytes 5.0/_static/jquery-3.4.1.js | 10598 ++++++++++++++++ 5.0/_static/jquery.js | 2 + 5.0/_static/js/copybutton.js | 60 + 5.0/_static/js/wrap_on_dot.js | 7 + 5.0/_static/language_data.js | 297 + 5.0/_static/minus.png | Bin 0 -> 90 bytes 5.0/_static/plus.png | Bin 0 -> 90 bytes 5.0/_static/pygments.css | 69 + 5.0/_static/searchtools.js | 506 + 5.0/_static/underscore-1.3.1.js | 999 ++ 5.0/_static/underscore.js | 31 + 5.0/api.html | 159 + 5.0/api/apptools.html | 367 + 5.0/api/apptools.io.h5.html | 705 + 5.0/api/apptools.io.html | 256 + 5.0/api/apptools.logger.agent.html | 286 + 5.0/api/apptools.logger.html | 323 + 5.0/api/apptools.logger.plugin.html | 305 + 5.0/api/apptools.logger.plugin.view.html | 303 + 5.0/api/apptools.naming.html | 840 ++ 5.0/api/apptools.naming.trait_defs.html | 271 + 5.0/api/apptools.persistence.html | 794 ++ 5.0/api/apptools.preferences.html | 653 + 5.0/api/apptools.preferences.ui.html | 405 + 5.0/api/apptools.scripting.html | 414 + 5.0/api/apptools.selection.html | 482 + 5.0/api/apptools.type_registry.html | 344 + 5.0/api/apptools.undo.action.html | 241 + 5.0/api/apptools.undo.html | 468 + 5.0/api/modules.html | 280 + 5.0/genindex.html | 1536 +++ 5.0/index.html | 179 + 5.0/io/introduction.html | 169 + 5.0/naming/Introduction.html | 154 + 5.0/objects.inv | Bin 0 -> 4807 bytes 5.0/preferences/Preferences.html | 466 + 5.0/preferences/PreferencesInEnvisage.html | 189 + 5.0/py-modindex.html | 692 + 5.0/scripting/introduction.html | 336 + 5.0/search.html | 143 + 5.0/searchindex.js | 1 + 5.0/selection/selection.html | 282 + 5.0/undo/Introduction.html | 386 + _modules/apptools/io/file.html | 443 + _modules/apptools/io/h5/dict_node.html | 362 + _modules/apptools/io/h5/file.html | 671 + _modules/apptools/io/h5/table_node.html | 287 + _modules/apptools/io/h5/utils.html | 166 + .../apptools/logger/agent/attachments.html | 235 + .../logger/agent/quality_agent_mailer.html | 244 + .../logger/agent/quality_agent_view.html | 510 + .../apptools/logger/custom_excepthook.html | 165 + _modules/apptools/logger/log_point.html | 170 + .../apptools/logger/log_queue_handler.html | 198 + _modules/apptools/logger/logger.html | 188 + .../apptools/logger/plugin/logger_plugin.html | 238 + .../logger/plugin/logger_preferences.html | 169 + .../logger/plugin/logger_service.html | 310 + .../plugin/view/logger_preferences_page.html | 226 + .../logger/plugin/view/logger_view.html | 343 + _modules/apptools/logger/ring_buffer.html | 183 + _modules/apptools/naming/address.html | 158 + _modules/apptools/naming/binding.html | 233 + _modules/apptools/naming/context.html | 813 ++ _modules/apptools/naming/dir_context.html | 293 + _modules/apptools/naming/dynamic_context.html | 308 + _modules/apptools/naming/exception.html | 184 + _modules/apptools/naming/initial_context.html | 185 + .../naming/initial_context_factory.html | 159 + _modules/apptools/naming/naming_event.html | 157 + _modules/apptools/naming/naming_manager.html | 213 + _modules/apptools/naming/object_factory.html | 166 + .../apptools/naming/object_serializer.html | 222 + _modules/apptools/naming/py_context.html | 311 + .../apptools/naming/py_object_factory.html | 171 + _modules/apptools/naming/pyfs_context.html | 674 + .../apptools/naming/pyfs_context_factory.html | 165 + .../naming/pyfs_initial_context_factory.html | 174 + .../apptools/naming/pyfs_object_factory.html | 166 + .../apptools/naming/pyfs_state_factory.html | 172 + _modules/apptools/naming/reference.html | 173 + _modules/apptools/naming/referenceable.html | 155 + .../naming/referenceable_state_factory.html | 164 + _modules/apptools/naming/state_factory.html | 166 + .../naming/trait_defs/naming_traits.html | 300 + _modules/apptools/naming/unique_name.html | 164 + _modules/apptools/persistence/file_path.html | 218 + .../apptools/persistence/project_loader.html | 262 + .../apptools/persistence/state_pickler.html | 1168 ++ _modules/apptools/persistence/updater.html | 174 + .../persistence/version_registry.html | 239 + .../persistence/versioned_unpickler.html | 342 + .../apptools/preferences/i_preferences.html | 310 + .../apptools/preferences/package_globals.html | 167 + .../preferences/preference_binding.html | 299 + .../apptools/preferences/preferences.html | 687 + .../preferences/preferences_helper.html | 336 + .../preferences/scoped_preferences.html | 582 + .../preferences/ui/i_preferences_page.html | 162 + .../preferences/ui/preferences_manager.html | 418 + .../preferences/ui/preferences_node.html | 221 + .../preferences/ui/preferences_page.html | 233 + .../apptools/preferences/ui/tree_item.html | 269 + .../preferences/ui/widget_editor.html | 232 + .../apptools/scripting/package_globals.html | 159 + _modules/apptools/scripting/recordable.html | 186 + _modules/apptools/scripting/recorder.html | 874 ++ .../apptools/scripting/recorder_with_ui.html | 226 + _modules/apptools/scripting/util.html | 186 + _modules/apptools/selection/errors.html | 172 + _modules/apptools/selection/i_selection.html | 159 + .../selection/i_selection_provider.html | 176 + .../apptools/selection/list_selection.html | 196 + .../apptools/selection/selection_service.html | 325 + .../apptools/type_registry/type_registry.html | 403 + _modules/apptools/undo/abstract_command.html | 205 + .../action/abstract_command_stack_action.html | 212 + .../apptools/undo/action/command_action.html | 187 + .../apptools/undo/action/redo_action.html | 178 + .../apptools/undo/action/undo_action.html | 176 + _modules/apptools/undo/command_stack.html | 448 + _modules/apptools/undo/i_command.html | 194 + _modules/apptools/undo/i_command_stack.html | 220 + _modules/apptools/undo/i_undo_manager.html | 192 + _modules/apptools/undo/undo_manager.html | 251 + _modules/index.html | 215 + _modules/traits/trait_types.html | 4427 +++++++ _modules/traits/traits.html | 833 ++ _modules/traitsui/editors/code_editor.html | 243 + _modules/traitsui/editors/tabular_editor.html | 290 + _modules/traitsui/view.html | 610 + _sources/api.rst.txt | 11 + _sources/api/apptools.io.h5.rst.txt | 46 + _sources/api/apptools.io.rst.txt | 37 + _sources/api/apptools.logger.agent.rst.txt | 38 + _sources/api/apptools.logger.plugin.rst.txt | 45 + .../api/apptools.logger.plugin.view.rst.txt | 30 + _sources/api/apptools.logger.rst.txt | 70 + _sources/api/apptools.naming.rst.txt | 221 + .../api/apptools.naming.trait_defs.rst.txt | 30 + _sources/api/apptools.persistence.rst.txt | 62 + _sources/api/apptools.preferences.rst.txt | 77 + _sources/api/apptools.preferences.ui.rst.txt | 70 + _sources/api/apptools.rst.txt | 25 + _sources/api/apptools.scripting.rst.txt | 62 + _sources/api/apptools.selection.rst.txt | 62 + _sources/api/apptools.type_registry.rst.txt | 30 + _sources/api/apptools.undo.action.rst.txt | 54 + _sources/api/apptools.undo.rst.txt | 77 + _sources/api/modules.rst.txt | 7 + _sources/index.rst.txt | 16 + _sources/io/introduction.rst.txt | 28 + _sources/naming/Introduction.rst.txt | 7 + _sources/preferences/Preferences.rst.txt | 329 + .../preferences/PreferencesInEnvisage.rst.txt | 52 + _sources/scripting/introduction.rst.txt | 198 + _sources/selection/selection.rst.txt | 144 + _sources/undo/Introduction.rst.txt | 275 + _static/basic.css | 768 ++ _static/css/pygments.css | 87 + _static/css/spc-bootstrap.css | 6288 +++++++++ _static/css/spc-extend.css | 92 + _static/doctools.js | 314 + _static/documentation_options.js | 11 + _static/enthought.css | 412 + _static/file.png | Bin 0 -> 286 bytes _static/fonts/SourceCodePro-Regular.otf.woff | Bin 0 -> 57712 bytes _static/fonts/SourceCodePro-Semibold.otf.woff | Bin 0 -> 57200 bytes _static/fonts/SourceSansPro-It.otf.woff | Bin 0 -> 53852 bytes _static/fonts/SourceSansPro-Regular.otf.woff | Bin 0 -> 133352 bytes _static/fonts/SourceSansPro-Semibold.otf.woff | Bin 0 -> 134540 bytes .../fonts/SourceSansPro-SemiboldIt.otf.woff | Bin 0 -> 54260 bytes _static/img/contents.png | Bin 0 -> 202 bytes _static/img/e-logo.png | Bin 0 -> 5124 bytes _static/img/favicon.ico | Bin 0 -> 4286 bytes _static/img/glyphicons-halflings-white.png | Bin 0 -> 8777 bytes _static/img/glyphicons-halflings.png | Bin 0 -> 12799 bytes _static/img/navigation.png | Bin 0 -> 218 bytes _static/img/transparent-pixel.gif | Bin 0 -> 43 bytes _static/img/ui-anim_basic_16x16.gif | Bin 0 -> 1459 bytes _static/jquery-3.4.1.js | 10598 ++++++++++++++++ _static/jquery.js | 2 + _static/js/copybutton.js | 60 + _static/js/wrap_on_dot.js | 7 + _static/language_data.js | 297 + _static/minus.png | Bin 0 -> 90 bytes _static/plus.png | Bin 0 -> 90 bytes _static/pygments.css | 69 + _static/searchtools.js | 506 + _static/underscore-1.3.1.js | 999 ++ _static/underscore.js | 31 + api.html | 159 + api/apptools.html | 367 + api/apptools.io.h5.html | 705 + api/apptools.io.html | 256 + api/apptools.logger.agent.html | 286 + api/apptools.logger.html | 323 + api/apptools.logger.plugin.html | 305 + api/apptools.logger.plugin.view.html | 303 + api/apptools.naming.html | 840 ++ api/apptools.naming.trait_defs.html | 271 + api/apptools.persistence.html | 794 ++ api/apptools.preferences.html | 653 + api/apptools.preferences.ui.html | 405 + api/apptools.scripting.html | 414 + api/apptools.selection.html | 482 + api/apptools.type_registry.html | 344 + api/apptools.undo.action.html | 241 + api/apptools.undo.html | 468 + api/modules.html | 280 + genindex.html | 1536 +++ index.html | 179 + io/introduction.html | 169 + naming/Introduction.html | 154 + objects.inv | Bin 0 -> 4807 bytes preferences/Preferences.html | 466 + preferences/PreferencesInEnvisage.html | 189 + py-modindex.html | 692 + scripting/introduction.html | 336 + search.html | 143 + searchindex.js | 1 + selection/selection.html | 282 + undo/Introduction.html | 386 + 360 files changed, 128978 insertions(+) create mode 100644 5.0/_modules/apptools/io/file.html create mode 100644 5.0/_modules/apptools/io/h5/dict_node.html create mode 100644 5.0/_modules/apptools/io/h5/file.html create mode 100644 5.0/_modules/apptools/io/h5/table_node.html create mode 100644 5.0/_modules/apptools/io/h5/utils.html create mode 100644 5.0/_modules/apptools/logger/agent/attachments.html create mode 100644 5.0/_modules/apptools/logger/agent/quality_agent_mailer.html create mode 100644 5.0/_modules/apptools/logger/agent/quality_agent_view.html create mode 100644 5.0/_modules/apptools/logger/custom_excepthook.html create mode 100644 5.0/_modules/apptools/logger/log_point.html create mode 100644 5.0/_modules/apptools/logger/log_queue_handler.html create mode 100644 5.0/_modules/apptools/logger/logger.html create mode 100644 5.0/_modules/apptools/logger/plugin/logger_plugin.html create mode 100644 5.0/_modules/apptools/logger/plugin/logger_preferences.html create mode 100644 5.0/_modules/apptools/logger/plugin/logger_service.html create mode 100644 5.0/_modules/apptools/logger/plugin/view/logger_preferences_page.html create mode 100644 5.0/_modules/apptools/logger/plugin/view/logger_view.html create mode 100644 5.0/_modules/apptools/logger/ring_buffer.html create mode 100644 5.0/_modules/apptools/naming/address.html create mode 100644 5.0/_modules/apptools/naming/binding.html create mode 100644 5.0/_modules/apptools/naming/context.html create mode 100644 5.0/_modules/apptools/naming/dir_context.html create mode 100644 5.0/_modules/apptools/naming/dynamic_context.html create mode 100644 5.0/_modules/apptools/naming/exception.html create mode 100644 5.0/_modules/apptools/naming/initial_context.html create mode 100644 5.0/_modules/apptools/naming/initial_context_factory.html create mode 100644 5.0/_modules/apptools/naming/naming_event.html create mode 100644 5.0/_modules/apptools/naming/naming_manager.html create mode 100644 5.0/_modules/apptools/naming/object_factory.html create mode 100644 5.0/_modules/apptools/naming/object_serializer.html create mode 100644 5.0/_modules/apptools/naming/py_context.html create mode 100644 5.0/_modules/apptools/naming/py_object_factory.html create mode 100644 5.0/_modules/apptools/naming/pyfs_context.html create mode 100644 5.0/_modules/apptools/naming/pyfs_context_factory.html create mode 100644 5.0/_modules/apptools/naming/pyfs_initial_context_factory.html create mode 100644 5.0/_modules/apptools/naming/pyfs_object_factory.html create mode 100644 5.0/_modules/apptools/naming/pyfs_state_factory.html create mode 100644 5.0/_modules/apptools/naming/reference.html create mode 100644 5.0/_modules/apptools/naming/referenceable.html create mode 100644 5.0/_modules/apptools/naming/referenceable_state_factory.html create mode 100644 5.0/_modules/apptools/naming/state_factory.html create mode 100644 5.0/_modules/apptools/naming/trait_defs/naming_traits.html create mode 100644 5.0/_modules/apptools/naming/unique_name.html create mode 100644 5.0/_modules/apptools/persistence/file_path.html create mode 100644 5.0/_modules/apptools/persistence/project_loader.html create mode 100644 5.0/_modules/apptools/persistence/state_pickler.html create mode 100644 5.0/_modules/apptools/persistence/updater.html create mode 100644 5.0/_modules/apptools/persistence/version_registry.html create mode 100644 5.0/_modules/apptools/persistence/versioned_unpickler.html create mode 100644 5.0/_modules/apptools/preferences/i_preferences.html create mode 100644 5.0/_modules/apptools/preferences/package_globals.html create mode 100644 5.0/_modules/apptools/preferences/preference_binding.html create mode 100644 5.0/_modules/apptools/preferences/preferences.html create mode 100644 5.0/_modules/apptools/preferences/preferences_helper.html create mode 100644 5.0/_modules/apptools/preferences/scoped_preferences.html create mode 100644 5.0/_modules/apptools/preferences/ui/i_preferences_page.html create mode 100644 5.0/_modules/apptools/preferences/ui/preferences_manager.html create mode 100644 5.0/_modules/apptools/preferences/ui/preferences_node.html create mode 100644 5.0/_modules/apptools/preferences/ui/preferences_page.html create mode 100644 5.0/_modules/apptools/preferences/ui/tree_item.html create mode 100644 5.0/_modules/apptools/preferences/ui/widget_editor.html create mode 100644 5.0/_modules/apptools/scripting/package_globals.html create mode 100644 5.0/_modules/apptools/scripting/recordable.html create mode 100644 5.0/_modules/apptools/scripting/recorder.html create mode 100644 5.0/_modules/apptools/scripting/recorder_with_ui.html create mode 100644 5.0/_modules/apptools/scripting/util.html create mode 100644 5.0/_modules/apptools/selection/errors.html create mode 100644 5.0/_modules/apptools/selection/i_selection.html create mode 100644 5.0/_modules/apptools/selection/i_selection_provider.html create mode 100644 5.0/_modules/apptools/selection/list_selection.html create mode 100644 5.0/_modules/apptools/selection/selection_service.html create mode 100644 5.0/_modules/apptools/type_registry/type_registry.html create mode 100644 5.0/_modules/apptools/undo/abstract_command.html create mode 100644 5.0/_modules/apptools/undo/action/abstract_command_stack_action.html create mode 100644 5.0/_modules/apptools/undo/action/command_action.html create mode 100644 5.0/_modules/apptools/undo/action/redo_action.html create mode 100644 5.0/_modules/apptools/undo/action/undo_action.html create mode 100644 5.0/_modules/apptools/undo/command_stack.html create mode 100644 5.0/_modules/apptools/undo/i_command.html create mode 100644 5.0/_modules/apptools/undo/i_command_stack.html create mode 100644 5.0/_modules/apptools/undo/i_undo_manager.html create mode 100644 5.0/_modules/apptools/undo/undo_manager.html create mode 100644 5.0/_modules/index.html create mode 100644 5.0/_modules/traits/trait_types.html create mode 100644 5.0/_modules/traits/traits.html create mode 100644 5.0/_modules/traitsui/editors/code_editor.html create mode 100644 5.0/_modules/traitsui/editors/tabular_editor.html create mode 100644 5.0/_modules/traitsui/view.html create mode 100644 5.0/_sources/api.rst.txt create mode 100644 5.0/_sources/api/apptools.io.h5.rst.txt create mode 100644 5.0/_sources/api/apptools.io.rst.txt create mode 100644 5.0/_sources/api/apptools.logger.agent.rst.txt create mode 100644 5.0/_sources/api/apptools.logger.plugin.rst.txt create mode 100644 5.0/_sources/api/apptools.logger.plugin.view.rst.txt create mode 100644 5.0/_sources/api/apptools.logger.rst.txt create mode 100644 5.0/_sources/api/apptools.naming.rst.txt create mode 100644 5.0/_sources/api/apptools.naming.trait_defs.rst.txt create mode 100644 5.0/_sources/api/apptools.persistence.rst.txt create mode 100644 5.0/_sources/api/apptools.preferences.rst.txt create mode 100644 5.0/_sources/api/apptools.preferences.ui.rst.txt create mode 100644 5.0/_sources/api/apptools.rst.txt create mode 100644 5.0/_sources/api/apptools.scripting.rst.txt create mode 100644 5.0/_sources/api/apptools.selection.rst.txt create mode 100644 5.0/_sources/api/apptools.type_registry.rst.txt create mode 100644 5.0/_sources/api/apptools.undo.action.rst.txt create mode 100644 5.0/_sources/api/apptools.undo.rst.txt create mode 100644 5.0/_sources/api/modules.rst.txt create mode 100644 5.0/_sources/index.rst.txt create mode 100644 5.0/_sources/io/introduction.rst.txt create mode 100644 5.0/_sources/naming/Introduction.rst.txt create mode 100644 5.0/_sources/preferences/Preferences.rst.txt create mode 100644 5.0/_sources/preferences/PreferencesInEnvisage.rst.txt create mode 100644 5.0/_sources/scripting/introduction.rst.txt create mode 100644 5.0/_sources/selection/selection.rst.txt create mode 100644 5.0/_sources/undo/Introduction.rst.txt create mode 100644 5.0/_static/basic.css create mode 100644 5.0/_static/css/pygments.css create mode 100644 5.0/_static/css/spc-bootstrap.css create mode 100644 5.0/_static/css/spc-extend.css create mode 100644 5.0/_static/doctools.js create mode 100644 5.0/_static/documentation_options.js create mode 100644 5.0/_static/enthought.css create mode 100644 5.0/_static/file.png create mode 100644 5.0/_static/fonts/SourceCodePro-Regular.otf.woff create mode 100644 5.0/_static/fonts/SourceCodePro-Semibold.otf.woff create mode 100644 5.0/_static/fonts/SourceSansPro-It.otf.woff create mode 100644 5.0/_static/fonts/SourceSansPro-Regular.otf.woff create mode 100644 5.0/_static/fonts/SourceSansPro-Semibold.otf.woff create mode 100644 5.0/_static/fonts/SourceSansPro-SemiboldIt.otf.woff create mode 100644 5.0/_static/img/contents.png create mode 100644 5.0/_static/img/e-logo.png create mode 100644 5.0/_static/img/favicon.ico create mode 100644 5.0/_static/img/glyphicons-halflings-white.png create mode 100644 5.0/_static/img/glyphicons-halflings.png create mode 100644 5.0/_static/img/navigation.png create mode 100644 5.0/_static/img/transparent-pixel.gif create mode 100644 5.0/_static/img/ui-anim_basic_16x16.gif create mode 100644 5.0/_static/jquery-3.4.1.js create mode 100644 5.0/_static/jquery.js create mode 100644 5.0/_static/js/copybutton.js create mode 100644 5.0/_static/js/wrap_on_dot.js create mode 100644 5.0/_static/language_data.js create mode 100644 5.0/_static/minus.png create mode 100644 5.0/_static/plus.png create mode 100644 5.0/_static/pygments.css create mode 100644 5.0/_static/searchtools.js create mode 100644 5.0/_static/underscore-1.3.1.js create mode 100644 5.0/_static/underscore.js create mode 100644 5.0/api.html create mode 100644 5.0/api/apptools.html create mode 100644 5.0/api/apptools.io.h5.html create mode 100644 5.0/api/apptools.io.html create mode 100644 5.0/api/apptools.logger.agent.html create mode 100644 5.0/api/apptools.logger.html create mode 100644 5.0/api/apptools.logger.plugin.html create mode 100644 5.0/api/apptools.logger.plugin.view.html create mode 100644 5.0/api/apptools.naming.html create mode 100644 5.0/api/apptools.naming.trait_defs.html create mode 100644 5.0/api/apptools.persistence.html create mode 100644 5.0/api/apptools.preferences.html create mode 100644 5.0/api/apptools.preferences.ui.html create mode 100644 5.0/api/apptools.scripting.html create mode 100644 5.0/api/apptools.selection.html create mode 100644 5.0/api/apptools.type_registry.html create mode 100644 5.0/api/apptools.undo.action.html create mode 100644 5.0/api/apptools.undo.html create mode 100644 5.0/api/modules.html create mode 100644 5.0/genindex.html create mode 100644 5.0/index.html create mode 100644 5.0/io/introduction.html create mode 100644 5.0/naming/Introduction.html create mode 100644 5.0/objects.inv create mode 100644 5.0/preferences/Preferences.html create mode 100644 5.0/preferences/PreferencesInEnvisage.html create mode 100644 5.0/py-modindex.html create mode 100644 5.0/scripting/introduction.html create mode 100644 5.0/search.html create mode 100644 5.0/searchindex.js create mode 100644 5.0/selection/selection.html create mode 100644 5.0/undo/Introduction.html create mode 100644 _modules/apptools/io/file.html create mode 100644 _modules/apptools/io/h5/dict_node.html create mode 100644 _modules/apptools/io/h5/file.html create mode 100644 _modules/apptools/io/h5/table_node.html create mode 100644 _modules/apptools/io/h5/utils.html create mode 100644 _modules/apptools/logger/agent/attachments.html create mode 100644 _modules/apptools/logger/agent/quality_agent_mailer.html create mode 100644 _modules/apptools/logger/agent/quality_agent_view.html create mode 100644 _modules/apptools/logger/custom_excepthook.html create mode 100644 _modules/apptools/logger/log_point.html create mode 100644 _modules/apptools/logger/log_queue_handler.html create mode 100644 _modules/apptools/logger/logger.html create mode 100644 _modules/apptools/logger/plugin/logger_plugin.html create mode 100644 _modules/apptools/logger/plugin/logger_preferences.html create mode 100644 _modules/apptools/logger/plugin/logger_service.html create mode 100644 _modules/apptools/logger/plugin/view/logger_preferences_page.html create mode 100644 _modules/apptools/logger/plugin/view/logger_view.html create mode 100644 _modules/apptools/logger/ring_buffer.html create mode 100644 _modules/apptools/naming/address.html create mode 100644 _modules/apptools/naming/binding.html create mode 100644 _modules/apptools/naming/context.html create mode 100644 _modules/apptools/naming/dir_context.html create mode 100644 _modules/apptools/naming/dynamic_context.html create mode 100644 _modules/apptools/naming/exception.html create mode 100644 _modules/apptools/naming/initial_context.html create mode 100644 _modules/apptools/naming/initial_context_factory.html create mode 100644 _modules/apptools/naming/naming_event.html create mode 100644 _modules/apptools/naming/naming_manager.html create mode 100644 _modules/apptools/naming/object_factory.html create mode 100644 _modules/apptools/naming/object_serializer.html create mode 100644 _modules/apptools/naming/py_context.html create mode 100644 _modules/apptools/naming/py_object_factory.html create mode 100644 _modules/apptools/naming/pyfs_context.html create mode 100644 _modules/apptools/naming/pyfs_context_factory.html create mode 100644 _modules/apptools/naming/pyfs_initial_context_factory.html create mode 100644 _modules/apptools/naming/pyfs_object_factory.html create mode 100644 _modules/apptools/naming/pyfs_state_factory.html create mode 100644 _modules/apptools/naming/reference.html create mode 100644 _modules/apptools/naming/referenceable.html create mode 100644 _modules/apptools/naming/referenceable_state_factory.html create mode 100644 _modules/apptools/naming/state_factory.html create mode 100644 _modules/apptools/naming/trait_defs/naming_traits.html create mode 100644 _modules/apptools/naming/unique_name.html create mode 100644 _modules/apptools/persistence/file_path.html create mode 100644 _modules/apptools/persistence/project_loader.html create mode 100644 _modules/apptools/persistence/state_pickler.html create mode 100644 _modules/apptools/persistence/updater.html create mode 100644 _modules/apptools/persistence/version_registry.html create mode 100644 _modules/apptools/persistence/versioned_unpickler.html create mode 100644 _modules/apptools/preferences/i_preferences.html create mode 100644 _modules/apptools/preferences/package_globals.html create mode 100644 _modules/apptools/preferences/preference_binding.html create mode 100644 _modules/apptools/preferences/preferences.html create mode 100644 _modules/apptools/preferences/preferences_helper.html create mode 100644 _modules/apptools/preferences/scoped_preferences.html create mode 100644 _modules/apptools/preferences/ui/i_preferences_page.html create mode 100644 _modules/apptools/preferences/ui/preferences_manager.html create mode 100644 _modules/apptools/preferences/ui/preferences_node.html create mode 100644 _modules/apptools/preferences/ui/preferences_page.html create mode 100644 _modules/apptools/preferences/ui/tree_item.html create mode 100644 _modules/apptools/preferences/ui/widget_editor.html create mode 100644 _modules/apptools/scripting/package_globals.html create mode 100644 _modules/apptools/scripting/recordable.html create mode 100644 _modules/apptools/scripting/recorder.html create mode 100644 _modules/apptools/scripting/recorder_with_ui.html create mode 100644 _modules/apptools/scripting/util.html create mode 100644 _modules/apptools/selection/errors.html create mode 100644 _modules/apptools/selection/i_selection.html create mode 100644 _modules/apptools/selection/i_selection_provider.html create mode 100644 _modules/apptools/selection/list_selection.html create mode 100644 _modules/apptools/selection/selection_service.html create mode 100644 _modules/apptools/type_registry/type_registry.html create mode 100644 _modules/apptools/undo/abstract_command.html create mode 100644 _modules/apptools/undo/action/abstract_command_stack_action.html create mode 100644 _modules/apptools/undo/action/command_action.html create mode 100644 _modules/apptools/undo/action/redo_action.html create mode 100644 _modules/apptools/undo/action/undo_action.html create mode 100644 _modules/apptools/undo/command_stack.html create mode 100644 _modules/apptools/undo/i_command.html create mode 100644 _modules/apptools/undo/i_command_stack.html create mode 100644 _modules/apptools/undo/i_undo_manager.html create mode 100644 _modules/apptools/undo/undo_manager.html create mode 100644 _modules/index.html create mode 100644 _modules/traits/trait_types.html create mode 100644 _modules/traits/traits.html create mode 100644 _modules/traitsui/editors/code_editor.html create mode 100644 _modules/traitsui/editors/tabular_editor.html create mode 100644 _modules/traitsui/view.html create mode 100644 _sources/api.rst.txt create mode 100644 _sources/api/apptools.io.h5.rst.txt create mode 100644 _sources/api/apptools.io.rst.txt create mode 100644 _sources/api/apptools.logger.agent.rst.txt create mode 100644 _sources/api/apptools.logger.plugin.rst.txt create mode 100644 _sources/api/apptools.logger.plugin.view.rst.txt create mode 100644 _sources/api/apptools.logger.rst.txt create mode 100644 _sources/api/apptools.naming.rst.txt create mode 100644 _sources/api/apptools.naming.trait_defs.rst.txt create mode 100644 _sources/api/apptools.persistence.rst.txt create mode 100644 _sources/api/apptools.preferences.rst.txt create mode 100644 _sources/api/apptools.preferences.ui.rst.txt create mode 100644 _sources/api/apptools.rst.txt create mode 100644 _sources/api/apptools.scripting.rst.txt create mode 100644 _sources/api/apptools.selection.rst.txt create mode 100644 _sources/api/apptools.type_registry.rst.txt create mode 100644 _sources/api/apptools.undo.action.rst.txt create mode 100644 _sources/api/apptools.undo.rst.txt create mode 100644 _sources/api/modules.rst.txt create mode 100644 _sources/index.rst.txt create mode 100644 _sources/io/introduction.rst.txt create mode 100644 _sources/naming/Introduction.rst.txt create mode 100644 _sources/preferences/Preferences.rst.txt create mode 100644 _sources/preferences/PreferencesInEnvisage.rst.txt create mode 100644 _sources/scripting/introduction.rst.txt create mode 100644 _sources/selection/selection.rst.txt create mode 100644 _sources/undo/Introduction.rst.txt create mode 100644 _static/basic.css create mode 100644 _static/css/pygments.css create mode 100644 _static/css/spc-bootstrap.css create mode 100644 _static/css/spc-extend.css create mode 100644 _static/doctools.js create mode 100644 _static/documentation_options.js create mode 100644 _static/enthought.css create mode 100644 _static/file.png create mode 100644 _static/fonts/SourceCodePro-Regular.otf.woff create mode 100644 _static/fonts/SourceCodePro-Semibold.otf.woff create mode 100644 _static/fonts/SourceSansPro-It.otf.woff create mode 100644 _static/fonts/SourceSansPro-Regular.otf.woff create mode 100644 _static/fonts/SourceSansPro-Semibold.otf.woff create mode 100644 _static/fonts/SourceSansPro-SemiboldIt.otf.woff create mode 100644 _static/img/contents.png create mode 100644 _static/img/e-logo.png create mode 100644 _static/img/favicon.ico create mode 100644 _static/img/glyphicons-halflings-white.png create mode 100644 _static/img/glyphicons-halflings.png create mode 100644 _static/img/navigation.png create mode 100644 _static/img/transparent-pixel.gif create mode 100644 _static/img/ui-anim_basic_16x16.gif create mode 100644 _static/jquery-3.4.1.js create mode 100644 _static/jquery.js create mode 100644 _static/js/copybutton.js create mode 100644 _static/js/wrap_on_dot.js create mode 100644 _static/language_data.js create mode 100644 _static/minus.png create mode 100644 _static/plus.png create mode 100644 _static/pygments.css create mode 100644 _static/searchtools.js create mode 100644 _static/underscore-1.3.1.js create mode 100644 _static/underscore.js create mode 100644 api.html create mode 100644 api/apptools.html create mode 100644 api/apptools.io.h5.html create mode 100644 api/apptools.io.html create mode 100644 api/apptools.logger.agent.html create mode 100644 api/apptools.logger.html create mode 100644 api/apptools.logger.plugin.html create mode 100644 api/apptools.logger.plugin.view.html create mode 100644 api/apptools.naming.html create mode 100644 api/apptools.naming.trait_defs.html create mode 100644 api/apptools.persistence.html create mode 100644 api/apptools.preferences.html create mode 100644 api/apptools.preferences.ui.html create mode 100644 api/apptools.scripting.html create mode 100644 api/apptools.selection.html create mode 100644 api/apptools.type_registry.html create mode 100644 api/apptools.undo.action.html create mode 100644 api/apptools.undo.html create mode 100644 api/modules.html create mode 100644 genindex.html create mode 100644 index.html create mode 100644 io/introduction.html create mode 100644 naming/Introduction.html create mode 100644 objects.inv create mode 100644 preferences/Preferences.html create mode 100644 preferences/PreferencesInEnvisage.html create mode 100644 py-modindex.html create mode 100644 scripting/introduction.html create mode 100644 search.html create mode 100644 searchindex.js create mode 100644 selection/selection.html create mode 100644 undo/Introduction.html diff --git a/5.0/_modules/apptools/io/file.html b/5.0/_modules/apptools/io/file.html new file mode 100644 index 000000000..a996811e5 --- /dev/null +++ b/5.0/_modules/apptools/io/file.html @@ -0,0 +1,443 @@ + + + + + + + apptools.io.file — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.io.file

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" A representation of files and folders in a file system. """
+
+
+# Standard/built-in imports.
+import mimetypes
+import os
+import shutil
+import stat
+
+# Enthought library imports.
+from traits.api import Bool, HasPrivateTraits, Instance, List, Property
+from traits.api import Str
+
+
+
[docs]class File(HasPrivateTraits): + """ A representation of files and folders in a file system. """ + + #### 'File' interface ##################################################### + + # The absolute path name of this file/folder. + absolute_path = Property(Str) + + # The folder's children (for files this is always None). + children = Property(List("File")) + + # The file extension (for folders this is always the empty string). + # + # fixme: Currently the extension includes the '.' (ie. we have '.py' and + # not 'py'). This is because things like 'os.path.splitext' leave the '.' + # on, but I'm not sure that this is a good idea! + ext = Property(Str) + + # Does the file/folder exist? + exists = Property(Bool) + + # Is this an existing file? + is_file = Property(Bool) + + # Is this an existing folder? + is_folder = Property(Bool) + + # Is this a Python package (ie. a folder contaning an '__init__.py' file. + is_package = Property(Bool) + + # Is the file/folder readonly? + is_readonly = Property(Bool) + + # The MIME type of the file (for a folder this will always be + # 'context/unknown' (is that what it should be?)). + mime_type = Property(Str) + + # The last component of the path without the extension. + name = Property(Str) + + # The parent of this file/folder (None if it has no parent). + parent = Property(Instance("File")) + + # The path name of this file/folder. + path = Str + + # A URL reference to the file. + url = Property(Str) + + ########################################################################### + # 'object' interface. + ########################################################################### + + def __init__(self, path, **traits): + """ Creates a new representation of the specified path. """ + + super(File, self).__init__(path=path, **traits) + + def __str__(self): + """ Returns an 'informal' string representation of the object. """ + + return "File(%s)" % self.path + + ########################################################################### + # 'File' interface. + ########################################################################### + + #### Properties ########################################################### + + def _get_absolute_path(self): + """ Returns the absolute path of this file/folder. """ + + return os.path.abspath(self.path) + + def _get_children(self): + """Returns the folder's children. + + Returns None if the path does not exist or is not a folder. + + """ + + if self.is_folder: + children = [] + for name in os.listdir(self.path): + children.append(File(os.path.join(self.path, name))) + + else: + children = None + + return children + + def _get_exists(self): + """ Returns True if the file exists, otherwise False. """ + + return os.path.exists(self.path) + + def _get_ext(self): + """ Returns the file extension. """ + + name, ext = os.path.splitext(self.path) + + return ext + + def _get_is_file(self): + """ Returns True if the path exists and is a file. """ + + return self.exists and os.path.isfile(self.path) + + def _get_is_folder(self): + """ Returns True if the path exists and is a folder. """ + + return self.exists and os.path.isdir(self.path) + + def _get_is_package(self): + """ Returns True if the path exists and is a Python package. """ + + return self.is_folder and "__init__.py" in os.listdir(self.path) + + def _get_is_readonly(self): + """ Returns True if the file/folder is readonly, otherwise False. """ + + # If the File object is a folder, os.access cannot be used because it + # returns True for both read-only and writable folders on Windows + # systems. + if self.is_folder: + + # Mask for the write-permission bits on the folder. If these bits + # are set to zero, the folder is read-only. + WRITE_MASK = 0x92 + permissions = os.stat(self.path)[0] + + if permissions & WRITE_MASK == 0: + readonly = True + else: + readonly = False + + elif self.is_file: + readonly = not os.access(self.path, os.W_OK) + + else: + readonly = False + + return readonly + + def _get_mime_type(self): + """ Returns the mime-type of this file/folder. """ + + mime_type, encoding = mimetypes.guess_type(self.path) + if mime_type is None: + mime_type = "content/unknown" + + return mime_type + + def _get_name(self): + """ Returns the last component of the path without the extension. """ + + basename = os.path.basename(self.path) + + name, ext = os.path.splitext(basename) + + return name + + def _get_parent(self): + """ Returns the parent of this file/folder. """ + + return File(os.path.dirname(self.path)) + + def _get_url(self): + """ Returns the path as a URL. """ + + # Strip out the leading slash on POSIX systems. + return "file:///%s" % self.absolute_path.lstrip("/") + + #### Methods ############################################################## + +
[docs] def copy(self, destination): + """ Copies this file/folder. """ + + # Allow the destination to be a string. + if not isinstance(destination, File): + destination = File(destination) + + if self.is_folder: + shutil.copytree(self.path, destination.path) + + elif self.is_file: + shutil.copyfile(self.path, destination.path)
+ +
[docs] def create_file(self, contents=""): + """ Creates a file at this path. """ + + if self.exists: + raise ValueError("file %s already exists" % self.path) + + f = open(self.path, "w") + f.write(contents) + f.close()
+ +
[docs] def create_folder(self): + """Creates a folder at this path. + + All intermediate folders MUST already exist. + + """ + + if self.exists: + raise ValueError("folder %s already exists" % self.path) + + os.mkdir(self.path)
+ +
[docs] def create_folders(self): + """Creates a folder at this path. + + This will attempt to create any missing intermediate folders. + + """ + + if self.exists: + raise ValueError("folder %s already exists" % self.path) + + os.makedirs(self.path)
+ +
[docs] def create_package(self): + """Creates a package at this path. + + All intermediate folders/packages MUST already exist. + + """ + + if self.exists: + raise ValueError("package %s already exists" % self.path) + + os.mkdir(self.path) + + # Create the '__init__.py' file that actually turns the folder into a + # package! + init = File(os.path.join(self.path, "__init__.py")) + init.create_file()
+ +
[docs] def delete(self): + """Deletes this file/folder. + + Does nothing if the file/folder does not exist. + + """ + + if self.is_folder: + # Try to make sure that everything in the folder is writeable. + self.make_writeable() + + # Delete it! + shutil.rmtree(self.path) + + elif self.is_file: + # Try to make sure that the file is writeable. + self.make_writeable() + + # Delete it! + os.remove(self.path)
+ +
[docs] def make_writeable(self): + """ Attempt to make the file/folder writeable. """ + + if self.is_folder: + # Try to make sure that everything in the folder is writeable + # (i.e., can be deleted!). This comes in especially handy when + # deleting '.svn' directories. + for path, dirnames, filenames in os.walk(self.path): + for name in dirnames + filenames: + filename = os.path.join(path, name) + if not os.access(filename, os.W_OK): + os.chmod(filename, stat.S_IWUSR) + + elif self.is_file: + # Try to make sure that the file is writeable (i.e., can be + # deleted!). + if not os.access(self.path, os.W_OK): + os.chmod(self.path, stat.S_IWUSR)
+ +
[docs] def move(self, destination): + """ Moves this file/folder. """ + + # Allow the destination to be a string. + if not isinstance(destination, File): + destination = File(destination) + + # Try to make sure that everything in the directory is writeable. + self.make_writeable() + + # Move it! + shutil.move(self.path, destination.path)
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/io/h5/dict_node.html b/5.0/_modules/apptools/io/h5/dict_node.html new file mode 100644 index 000000000..1497f2caa --- /dev/null +++ b/5.0/_modules/apptools/io/h5/dict_node.html @@ -0,0 +1,362 @@ + + + + + + + apptools.io.h5.dict_node — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.io.h5.dict_node

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+from contextlib import closing
+import json
+
+from numpy import ndarray
+
+from tables import Group as PyTablesGroup
+from tables.nodes import filenode
+
+
+#: The key name which identifies array objects in the JSON dict.
+ARRAY_PROXY_KEY = "__array__"
+NODE_KEY = "node_name"
+
+
+
[docs]class H5DictNode(object): + """Dictionary-like node interface. + + Data for the dict is stored as a JSON file in a PyTables FileNode. This + allows easy storage of Python objects, such as dictionaries and lists of + different data types. + + Note that this is implemented using a group-node assuming that arrays are + valid inputs and will be stored as H5 array nodes. + + Parameters + ---------- + h5_group : H5Group instance + Group node which will be used as a dictionary store. + auto_flush : bool + If True, write data to disk whenever the dict data is altered. + Otherwise, call `flush()` explicitly to write data to disk. + """ + + #: Name of filenode where dict data is stored. + _pyobject_data_node = "_pyobject_data" + + def __init__(self, h5_group, auto_flush=True): + assert self.is_dict_node(h5_group) + + h5_group = self._get_pyt_group(h5_group) + self._h5_group = h5_group + self.auto_flush = auto_flush + + # Load dict data from the file node. + dict_node = getattr(h5_group, self._pyobject_data_node) + with closing(filenode.open_node(dict_node)) as f: + self._pyobject_data = json.loads( + f.read().decode("ascii"), object_hook=self._object_hook + ) + + # -------------------------------------------------------------------------- + # Dictionary interface + # -------------------------------------------------------------------------- + + def __getitem__(self, key): + return self.data[key] + + def __setitem__(self, key, value): + self.data[key] = value + if self.auto_flush: + self.flush() + + def __delitem__(self, key): + del self.data[key] + if self.auto_flush: + self.flush() + + def __contains__(self, key): + return key in self.data + +
[docs] def keys(self): + return self.data.keys()
+ + # -------------------------------------------------------------------------- + # Public interface + # -------------------------------------------------------------------------- + + @property + def data(self): + return self._pyobject_data + + @data.setter + def data(self, new_data_dict): + self._pyobject_data = new_data_dict + if self.auto_flush: + self.flush() + +
[docs] def flush(self): + """ Write buffered data to disk. """ + self._remove_pyobject_node() + self._write_pyobject_node()
+ +
[docs] @classmethod + def add_to_h5file(cls, h5, node_path, data=None, **kwargs): + """Add dict node to an H5 file at the specified path. + + Parameters + ---------- + h5 : H5File + The H5 file where the dictionary data will be stored. + node_path : str + Path to node where data is stored (e.g. '/path/to/my_dict') + data : dict + Data for initialization, if desired. + """ + h5.create_group(node_path) + group = h5[node_path] + + cls._create_pyobject_node(h5._h5, node_path, data=data) + return cls(group, **kwargs)
+ +
[docs] @classmethod + def is_dict_node(cls, pytables_node): + """Return True if PyTables node looks like an H5DictNode. + + NOTE: That this returns False if the node is an `H5DictNode` instance, + since the input node should be a normal PyTables Group node. + """ + # Import here to prevent circular imports + from .file import H5Group + + if isinstance(pytables_node, H5Group): + pytables_node = cls._get_pyt_group(pytables_node) + + if not isinstance(pytables_node, PyTablesGroup): + return False + + return cls._pyobject_data_node in pytables_node._v_children
+ + # -------------------------------------------------------------------------- + # Private interface + # -------------------------------------------------------------------------- + + def _f_remove(self): + """This is called by H5File whenever a node is removed. + + All nodes in `_h5_group` will be removed. + """ + for name in self._h5_group._v_children.keys(): + if name != self._pyobject_data_node: + self._h5_group.__getattr__(name)._f_remove() + # Remove the dict node + self._remove_pyobject_node() + # Remove the group node + self._h5_group._f_remove() + + def _object_hook(self, dct): + """This gets passed object dictionaries by `json.load(s)` and if it + finds `ARRAY_PROXY_KEY` in the object description it returns the + proxied array object. + """ + if ARRAY_PROXY_KEY in dct: + node_name = dct[NODE_KEY] + return getattr(self._h5_group, node_name)[:] + return dct + + def _remove_pyobject_node(self): + node = getattr(self._h5_group, self._pyobject_data_node) + node._f_remove() + + def _write_pyobject_node(self): + pyt_file = self._h5_group._v_file + node_path = self._h5_group._v_pathname + self._create_pyobject_node(pyt_file, node_path, self.data) + + @classmethod + def _create_pyobject_node(cls, pyt_file, node_path, data=None): + if data is None: + data = {} + + # Stash the array values in their own h5 nodes and return a dictionary + # which is appropriate for JSON serialization. + out_data = cls._handle_array_values(pyt_file, node_path, data) + + kwargs = dict(where=node_path, name=cls._pyobject_data_node) + with closing(filenode.new_node(pyt_file, **kwargs)) as f: + f.write(json.dumps(out_data).encode("ascii")) + + @classmethod + def _get_pyt_group(self, group): + if hasattr(group, "_h5_group"): + group = group._h5_group + return group + + @classmethod + def _array_proxy(cls, pyt_file, group, key, array): + """Stores an array as a normal H5 node and returns the proxy object + which will be serialized to JSON. + + `ARRAY_PROXY_KEY` marks the object dictionary as an array proxy so that + `_object_hook` can recognize it. `NODE_KEY` stores the node name of the + array so that `_object_hook` can load the array data when the dict node + is deserialized. + + """ + if key in group: + pyt_file.remove_node(group, key) + pyt_file.create_array(group, key, array) + return {ARRAY_PROXY_KEY: True, NODE_KEY: key} + + @classmethod + def _handle_array_values(cls, pyt_file, group_path, data): + group = pyt_file.get_node(group_path) + + # Convert numpy array values to H5 array nodes. + out_data = {} + for key in data.keys(): + value = data[key] + if isinstance(value, ndarray): + out_data[key] = cls._array_proxy(pyt_file, group, key, value) + else: + out_data[key] = value + + # Remove stored arrays which are no longer in the data dictionary. + pyt_children = group._v_children + nodes_to_remove = [] + for key in pyt_children.keys(): + if key not in data: + nodes_to_remove.append(key) + + for key in nodes_to_remove: + pyt_file.remove_node(group, key) + + return out_data
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/io/h5/file.html b/5.0/_modules/apptools/io/h5/file.html new file mode 100644 index 000000000..5fdccad23 --- /dev/null +++ b/5.0/_modules/apptools/io/h5/file.html @@ -0,0 +1,671 @@ + + + + + + + apptools.io.h5.file — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.io.h5.file

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+from collections import Mapping, MutableMapping
+from functools import partial
+import inspect
+
+import numpy as np
+import tables
+
+from .dict_node import H5DictNode
+from .table_node import H5TableNode
+
+
+
[docs]def get_atom(dtype): + """Return a PyTables Atom for the given dtype or dtype string.""" + return tables.Atom.from_dtype(np.dtype(dtype))
+ + +
[docs]def iterator_length(iterator): + return sum(1 for _ in iterator)
+ + +def _update_wrapped_docstring(wrapped, original=None): + PREAMBLE = """\ +** H5Group wrapper for H5File.{func_name}: ** +Note that the first argument is a nodepath relative to the group, rather than +an absolute path. Below is the original docstring: + +""".format( + func_name=wrapped.__name__ + ) + wrapped.__doc__ = PREAMBLE + inspect.cleandoc(original.__doc__) + return wrapped + + +
[docs]def h5_group_wrapper(original): + return partial(_update_wrapped_docstring, original=original)
+ + +
[docs]class H5File(Mapping): + """File object for HDF5 files. + + This class wraps PyTables to provide a cleaner, but only implements an + interface for accessing arrays. + + Parameters + ---------- + filename : str or a `tables.File` instance + Filename for an HDF5 file, or a PyTables `File` object. + mode : str + Mode to open the file: + + 'r' : Read-only + 'w' : Write; create new file (an existing file would be deleted). + 'a' : Read and write to file; create if not existing + 'r+': Read and write to file; must already exist + + delete_existing : bool + If True, an existing node will be deleted when a `create_*` method is + called. Otherwise, a ValueError will be raise. + auto_groups : bool + If True, `create_array` will automatically create parent groups. + auto_open : bool + If True, open the file automatically on initialization. Otherwise, + you can call `H5File.open()` explicitly after initialization. + chunked : bool + If True, the default behavior of `create_array` will be a chunked + array (see PyTables `create_carray`). + + """ + + exists_error = ( + "'{}' exists in '{}'; set `delete_existing` attribute " + "to True to overwrite existing calculations." + ) + + def __init__( + self, + filename, + mode="r+", + delete_existing=False, + auto_groups=True, + auto_open=True, + h5filters=None, + ): + self.mode = mode + self.delete_existing = delete_existing + self.auto_groups = auto_groups + if h5filters is None: + self.h5filters = tables.Filters( + complib="blosc", complevel=5, shuffle=True + ) + self._h5 = None + + if isinstance(filename, tables.File): + pyt_file = filename + filename = pyt_file.filename + if pyt_file.isopen: + self._h5 = pyt_file + + self.filename = filename + if auto_open: + self.open() + +
[docs] def open(self): + if not self.is_open: + self._h5 = tables.open_file(self.filename, mode=self.mode)
+ +
[docs] def close(self): + if self.is_open: + self._h5.close() + self._h5 = None
+ + @property + def root(self): + return self["/"] + + @property + def is_open(self): + return self._h5 is not None + + def __str__(self): + return str(self._h5) + + def __repr__(self): + return repr(self._h5) + + def __contains__(self, node_path): + return node_path in self._h5 + + def __getitem__(self, node_path): + try: + node = self._h5.get_node(node_path) + except tables.NoSuchNodeError: + msg = "Node {0!r} not found in {1!r}" + raise NameError(msg.format(node_path, self.filename)) + return _wrap_node(node) + + def __iter__(self): + return (_wrap_node(n) for n in self._h5.iter_nodes(where="/")) + + def __len__(self): + return iterator_length(self) + +
[docs] def iteritems(self, path="/"): + """ Iterate over node paths and nodes of the h5 file. """ + for node in self._h5.walk_nodes(where=path): + node_path = node._v_pathname + yield node_path, _wrap_node(node)
+ +
[docs] def create_array( + self, + node_path, + array_or_shape, + dtype=None, + chunked=False, + extendable=False, + **kwargs + ): + """Create node to store an array. + + Parameters + ---------- + node_path : str + PyTable node path; e.g. '/path/to/node'. + array_or_shape : array or shape tuple + Array or shape tuple for an array. If given a shape tuple, the + `dtype` parameter must also specified. + dtype : str or numpy.dtype + Data type of array. Only necessary if `array_or_shape` is a shape. + chunked : bool + Controls whether the array is chunked. + extendable : {None | bool} + Controls whether the array is extendable. + kwargs : key/value pairs + Keyword args passed to PyTables `File.create_(c|e)array`. + """ + self._check_node(node_path) + self._assert_valid_path(node_path) + + h5 = self._h5 + + if isinstance(array_or_shape, tuple): + if dtype is None: + msg = "`dtype` must be specified if only given array shape." + raise ValueError(msg) + array = None + dtype = dtype + shape = array_or_shape + else: + array = array_or_shape + dtype = array.dtype.name + shape = array.shape + + path, name = self.split_path(node_path) + if extendable: + shape = (0,) + shape[1:] + atom = get_atom(dtype) + node = h5.create_earray( + path, name, atom, shape, filters=self.h5filters, **kwargs + ) + if array is not None: + node.append(array) + elif chunked: + atom = get_atom(dtype) + node = h5.create_carray( + path, name, atom, shape, filters=self.h5filters, **kwargs + ) + if array is not None: + node[:] = array + else: + if array is None: + array = np.zeros(shape, dtype=dtype) + node = h5.create_array(path, name, array, **kwargs) + return node
+ +
[docs] def create_group(self, group_path, **kwargs): + """Create group. + + Parameters + ---------- + group_path : str + PyTable group path; e.g. '/path/to/group'. + kwargs : key/value pairs + Keyword args passed to PyTables `File.create_group`. + """ + self._check_node(group_path) + self._assert_valid_path(group_path) + path, name = self.split_path(group_path) + self._h5.create_group(path, name, **kwargs) + return self[group_path]
+ +
[docs] def create_dict(self, node_path, data=None, **kwargs): + """Create dict node at the specified path. + + Parameters + ---------- + node_path : str + Path to node where data is stored (e.g. '/path/to/my_dict') + data : dict + Data for initialization, if desired. + """ + self._check_node(node_path) + self._assert_valid_path(node_path) + H5DictNode.add_to_h5file(self, node_path, data=data, **kwargs) + return self[node_path]
+ +
[docs] def create_table(self, node_path, description, **kwargs): + """Create table node at the specified path. + + Parameters + ---------- + node_path : str + Path to node where data is stored (e.g. '/path/to/my_dict') + description : dict or numpy dtype object + The description of the columns in the table. This is either a dict + of column name -> dtype items or a numpy record array dtype. For + more information, see the documentation for Table in pytables. + """ + self._check_node(node_path) + self._assert_valid_path(node_path) + H5TableNode.add_to_h5file(self, node_path, description, **kwargs) + return self[node_path]
+ + def _check_node(self, node_path): + """Check if node exists and create parent groups if necessary. + + Either raise error or delete depending on `delete_existing` attribute. + """ + if self.auto_groups: + path, name = self.split_path(node_path) + self._create_required_groups(path) + + if node_path in self: + if self.delete_existing: + if isinstance(self[node_path], H5Group): + self.remove_group(node_path, recursive=True) + else: + self.remove_node(node_path) + else: + msg = self.exists_error.format(node_path, self.filename) + raise ValueError(msg) + + def _create_required_groups(self, path): + if path not in self: + parent, missing = self.split_path(path) + # Call recursively to ensure that all parent groups exist. + self._create_required_groups(parent) + self.create_group(path) + +
[docs] def remove_node(self, node_path): + """Remove node + + Parameters + ---------- + node_path : str + PyTable node path; e.g. '/path/to/node'. + """ + node = self[node_path] + if isinstance(node, H5Group): + msg = "{!r} is a group. Use `remove_group` to remove group nodes." + raise ValueError(msg.format(node.pathname)) + node._f_remove()
+ +
[docs] def remove_group(self, group_path, **kwargs): + """Remove group + + Parameters + ---------- + group_path : str + PyTable group path; e.g. '/path/to/group'. + """ + self[group_path]._h5_group._g_remove(**kwargs)
+ + @classmethod + def _assert_valid_path(self, node_path): + if "attrs" in node_path.split("/"): + raise ValueError("'attrs' is an invalid node name.") + +
[docs] @classmethod + def split_path(cls, node_path): + """Split node path returning the base path and node name. + + For example: '/path/to/node' will return '/path/to' and 'node' + + Parameters + ---------- + node_path : str + PyTable node path; e.g. '/path/to/node'. + """ + i = node_path.rfind("/") + if i == 0: + return "/", node_path[1:] + else: + return node_path[:i], node_path[i + 1:]
+ +
[docs] @classmethod + def join_path(cls, *args): + """Join parts of an h5 path. + + For example, the 3 argmuments 'path', 'to', 'node' will return + '/path/to/node'. + + Parameters + ---------- + args : str + Parts of path to be joined. + """ + path = "/".join(part.strip("/") for part in args) + if not path.startswith("/"): + path = "/" + path + return path
+ + +
[docs]class H5Attrs(MutableMapping): + """An attributes dictionary for an h5 node. + + This intercepts `__setitem__` so that python sequences can be converted to + numpy arrays. This helps preserve the readability of our HDF5 files by + other (non-python) programs. + """ + + def __init__(self, node_attrs): + self._node_attrs = node_attrs + + def __delitem__(self, key): + del self._node_attrs[key] + + def __getitem__(self, key): + return self._node_attrs[key] + + def __iter__(self): + return iter(self.keys()) + + def __len__(self): + return len(self._node_attrs._f_list()) + + def __setitem__(self, key, value): + if isinstance(value, tuple) or isinstance(value, list): + value = np.array(value) + self._node_attrs[key] = value + +
[docs] def get(self, key, default=None): + return default if key not in self else self[key]
+ +
[docs] def keys(self): + return self._node_attrs._f_list()
+ +
[docs] def values(self): + return [self[k] for k in self.keys()]
+ +
[docs] def items(self): + return [(k, self[k]) for k in self.keys()]
+ + +
[docs]class H5Group(Mapping): + """A group node in an H5File. + + This is a thin wrapper around PyTables' Group object to expose attributes + and maintain the dict interface of H5File. + """ + + def __init__(self, pytables_group): + self._h5_group = pytables_group + self.attrs = H5Attrs(self._h5_group._v_attrs) + + def __contains__(self, node_path): + return node_path in self._h5_group + + def __str__(self): + return str(self._h5_group) + + def __repr__(self): + return repr(self._h5_group) + + def __getitem__(self, node_path): + parts = node_path.split("/") + # PyTables stores children as attributes + node = self._h5_group.__getattr__(parts[0]) + node = _wrap_node(node) + if len(parts) == 1: + return node + else: + return node["/".join(parts[1:])] + + def __iter__(self): + return (_wrap_node(c) for c in self._h5_group) + + def __len__(self): + return iterator_length(self) + + @property + def pathname(self): + return self._h5_group._v_pathname + + @property + def name(self): + return self._h5_group._v_name + + @property + def filename(self): + return self._h5_group._v_file.filename + + @property + def root(self): + return _wrap_node(self._h5_group._v_file.root) + + @property + def children_names(self): + return list(self._h5_group._v_children.keys()) + + @property + def subgroup_names(self): + return list(self._h5_group._v_groups.keys()) + +
[docs] def iter_groups(self): + """ Iterate over `H5Group` nodes that are children of this group. """ + groups = self._h5_group._v_groups + + # not using the groups.values() method here, because groups is a + # `proxydict` object whose .values() method is non-lazy. Related: + # PyTables/PyTables#784. + return (_wrap_node(groups[group_name]) for group_name in groups)
+ +
[docs] @h5_group_wrapper(H5File.create_group) + def create_group(self, group_subpath, delete_existing=False, **kwargs): + return self._delegate_to_h5file( + "create_group", + group_subpath, + delete_existing=delete_existing, + **kwargs + )
+ +
[docs] @h5_group_wrapper(H5File.remove_group) + def remove_group(self, group_subpath, **kwargs): + return self._delegate_to_h5file( + "remove_group", group_subpath, **kwargs + )
+ +
[docs] @h5_group_wrapper(H5File.create_array) + def create_array( + self, + node_subpath, + array_or_shape, + dtype=None, + chunked=False, + extendable=False, + **kwargs + ): + return self._delegate_to_h5file( + "create_array", + node_subpath, + array_or_shape, + dtype=dtype, + chunked=chunked, + extendable=extendable, + **kwargs + )
+ +
[docs] @h5_group_wrapper(H5File.create_table) + def create_table(self, node_subpath, description, *args, **kwargs): + return self._delegate_to_h5file( + "create_table", node_subpath, description, *args, **kwargs + )
+ +
[docs] @h5_group_wrapper(H5File.create_dict) + def create_dict(self, node_subpath, data=None, **kwargs): + return self._delegate_to_h5file( + "create_dict", node_subpath, data=data, **kwargs + )
+ +
[docs] @h5_group_wrapper(H5File.remove_node) + def remove_node(self, node_subpath, **kwargs): + return self._delegate_to_h5file("remove_node", node_subpath, **kwargs)
+ + def _delegate_to_h5file( + self, function_name, node_subpath, *args, **kwargs + ): + delete_existing = kwargs.pop("delete_existing", False) + h5 = H5File(self._h5_group._v_file, delete_existing=delete_existing) + group_path = h5.join_path(self.pathname, node_subpath) + func = getattr(h5, function_name) + return func(group_path, *args, **kwargs)
+ + +def _wrap_node(node): + """ Wrap PyTables node object, if necessary. """ + if isinstance(node, tables.Group): + if H5DictNode.is_dict_node(node): + node = H5DictNode(node) + else: + node = H5Group(node) + elif H5TableNode.is_table_node(node): + node = H5TableNode(node) + return node +
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/io/h5/table_node.html b/5.0/_modules/apptools/io/h5/table_node.html new file mode 100644 index 000000000..755c69191 --- /dev/null +++ b/5.0/_modules/apptools/io/h5/table_node.html @@ -0,0 +1,287 @@ + + + + + + + apptools.io.h5.table_node — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.io.h5.table_node

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+import numpy as np
+
+from tables.table import Table as PyTablesTable
+
+
+class _TableRowAccessor(object):
+    """A simple object which provides read access to the rows in a Table."""
+
+    def __init__(self, h5_table):
+        self._h5_table = h5_table
+
+    def __getitem__(self, key):
+        return self._h5_table[key]
+
+
+
[docs]class H5TableNode(object): + """A wrapper for PyTables Table nodes. + + Parameters + ---------- + node : tables.Table instance + An H5 node which is a pytables.Table or H5TableNode instance + """ + + def __init__(self, node): + # Avoid a circular import + from .file import H5Attrs + + assert self.is_table_node(node) + self._h5_table = node._h5_table if hasattr(node, "_h5_table") else node + self.attrs = H5Attrs(self._h5_table._v_attrs) + + # -------------------------------------------------------------------------- + # Creation methods + # -------------------------------------------------------------------------- + +
[docs] @classmethod + def add_to_h5file(cls, h5, node_path, description, **kwargs): + """Add table node to an H5 file at the specified path. + + Parameters + ---------- + h5 : H5File + The H5 file where the table node will be stored. + node_path : str + Path to node where data is stored (e.g. '/path/to/my_table') + description : list of tuples or numpy dtype object + The description of the columns in the table. This is either a list + of (column name, dtype, [, shape or itemsize]) tuples or a numpy + record array dtype. For more information, see the documentation for + `Table` in PyTables. + **kwargs : dict + Additional keyword arguments to pass to pytables.File.create_table + """ + if isinstance(description, (tuple, list)): + description = np.dtype(description) + + cls._create_pytables_node(h5, node_path, description, **kwargs) + node = h5[node_path] + + return cls(node)
+ +
[docs] @classmethod + def is_table_node(cls, pytables_node): + """Return True if pytables_node is a pytables.Table or a H5TableNode. + """ + return isinstance(pytables_node, (PyTablesTable, H5TableNode))
+ + # -------------------------------------------------------------------------- + # Public interface + # -------------------------------------------------------------------------- + +
[docs] def append(self, data): + """Add some data to the table. + + Parameters + ---------- + data : dict + A dictionary of column name -> values items + """ + rows = list(zip(*[data[name] for name in self.keys()])) + self._h5_table.append(rows)
+ + def __getitem__(self, col_or_cols): + """Return one or more columns of data from the table. + + Parameters + ---------- + col_or_cols : str or list of str + A single column name or a list of column names + + Return + ------ + data : ndarray + An array of column data with the column order matching that of + `col_or_cols`. + """ + if isinstance(col_or_cols, str): + return self._h5_table.col(col_or_cols) + + column_data = [self._h5_table.col(name) for name in col_or_cols] + return np.column_stack(column_data) + + @property + def ix(self): + """Return an object which provides access to row data.""" + return _TableRowAccessor(self._h5_table) + +
[docs] def keys(self): + return self._h5_table.colnames
+ +
[docs] def to_dataframe(self): + """Return table data as a pandas `DataFrame`. + + XXX: This does not work if the table contains a multidimensional column + + This method requires pandas to have been installed in the environment. + """ + from pandas import DataFrame + + # Slicing rows gives a numpy struct array, which DataFrame understands. + return DataFrame(self.ix[:])
+ + # -------------------------------------------------------------------------- + # Object interface + # -------------------------------------------------------------------------- + + def __repr__(self): + return repr(self._h5_table) + + def __len__(self): + return self._h5_table.nrows + + # -------------------------------------------------------------------------- + # Private interface + # -------------------------------------------------------------------------- + + def _f_remove(self): + """Implement the PyTables `Node._f_remove` method so that H5File + doesn't choke when trying to remove our node. + """ + self._h5_table._f_remove() + self._h5_table = None + + @classmethod + def _create_pytables_node(cls, h5, node_path, description, **kwargs): + path, name = h5.split_path(node_path) + pyt_file = h5._h5 + pyt_file.create_table(path, name, description, **kwargs)
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/io/h5/utils.html b/5.0/_modules/apptools/io/h5/utils.html new file mode 100644 index 000000000..a35477daa --- /dev/null +++ b/5.0/_modules/apptools/io/h5/utils.html @@ -0,0 +1,166 @@ + + + + + + + apptools.io.h5.utils — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.io.h5.utils

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+from contextlib import contextmanager
+
+from .file import H5File
+
+
+
[docs]@contextmanager +def open_h5file(filename, mode="r+", **kwargs): + """Context manager for reading an HDF5 file as an H5File object. + + Parameters + ---------- + filename : str + HDF5 file name. + mode : str + Mode to open the file: + + 'r' : Read-only + 'w' : Write; create new file (an existing file would be deleted). + 'a' : Read and write to file; create if not existing + 'r+': Read and write to file; must already exist + + See `H5File` for additional keyword arguments. + """ + h5 = H5File(filename, mode=mode, **kwargs) + try: + yield h5 + finally: + h5.close()
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/logger/agent/attachments.html b/5.0/_modules/apptools/logger/agent/attachments.html new file mode 100644 index 000000000..f0d664cbb --- /dev/null +++ b/5.0/_modules/apptools/logger/agent/attachments.html @@ -0,0 +1,235 @@ + + + + + + + apptools.logger.agent.attachments — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.logger.agent.attachments

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" Attach relevant project files.
+
+FIXME: there are no public project plugins for Envisage 3, yet. In any case,
+this stuff should not be hard-coded, but extensible via extension points. The
+code remains here because we can reuse the zip utility code in that extensible
+rewrite.
+"""
+
+import logging
+import os.path
+from email import encoders
+from email.mime.base import MIMEBase
+
+from traits.api import Any, HasTraits
+
+
+logger = logging.getLogger(__name__)
+
+
+
[docs]class Attachments(HasTraits): + + application = Any() + message = Any() + + def __init__(self, message, **traits): + traits = traits.copy() + traits["message"] = message + super(Attachments, self).__init__(**traits) + + # FIXME: all of the package_*() methods refer to deprecated project plugins + +
[docs] def package_workspace(self): + if self.application is None: + pass + + workspace = self.application.get_service("envisage.project.IWorkspace") + if workspace is not None: + dir = workspace.path + self._attach_directory(dir)
+ +
[docs] def package_single_project(self): + if self.application is None: + pass + + single_project = self.application.get_service( + "envisage.single_project.ModelService" + ) + if single_project is not None: + dir = single_project.location + self._attach_directory(dir)
+ +
[docs] def package_any_relevant_files(self): + self.package_workspace() + self.package_single_project()
+ + def _attach_directory(self, dir): + relpath = os.path.basename(dir) + + import zipfile + from io import BytesIO + + ctype = "application/octet-stream" + maintype, subtype = ctype.split("/", 1) + msg = MIMEBase(maintype, subtype) + + file_object = BytesIO() + zip = zipfile.ZipFile(file_object, "w") + _append_to_zip_archive(zip, dir, relpath) + zip.close() + + msg.set_payload(file_object.getvalue()) + + encoders.encode_base64(msg) # Encode the payload using Base64 + msg.add_header( + "Content-Disposition", "attachment", filename="project.zip" + ) + + self.message.attach(msg) + + file_object.close()
+ + +def _append_to_zip_archive(zip, dir, relpath): + """ Add all files in and below directory dir into zip archive""" + for filename in os.listdir(dir): + path = os.path.join(dir, filename) + + if os.path.isfile(path): + name = os.path.join(relpath, filename) + zip.write(path, name) + logger.debug("adding %s to error report" % path) + else: + if filename != ".svn": # skip svn files if any + subdir = os.path.join(dir, filename) + _append_to_zip_archive( + zip, subdir, os.path.join(relpath, filename) + ) +
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/logger/agent/quality_agent_mailer.html b/5.0/_modules/apptools/logger/agent/quality_agent_mailer.html new file mode 100644 index 000000000..d6d7b3c5c --- /dev/null +++ b/5.0/_modules/apptools/logger/agent/quality_agent_mailer.html @@ -0,0 +1,244 @@ + + + + + + + apptools.logger.agent.quality_agent_mailer — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.logger.agent.quality_agent_mailer

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+
+# Standard library imports.
+import logging
+import os
+
+# Enthought library imports.
+from traits.util.home_directory import get_home_directory
+
+
+# Setup a logger for this module.
+logger = logging.getLogger(__name__)
+
+
+
[docs]def create_email_message( + fromaddr, + toaddrs, + ccaddrs, + subject, + priority, + include_project=False, + stack_trace="", + comments="", +): + # format a message suitable to be sent to the Roundup bug tracker + + from email.MIMEMultipart import MIMEMultipart + from email.MIMEText import MIMEText + from email.MIMEBase import MIMEBase + + message = MIMEMultipart() + message["Subject"] = "%s [priority=%s]" % (subject, priority) + message["To"] = ", ".join(toaddrs) + message["Cc"] = ", ".join(ccaddrs) + message["From"] = fromaddr + message.preamble = "You will not see this in a MIME-aware mail reader.\n" + message.epilogue = " " # To guarantee the message ends with a newline + + # First section is simple ASCII data ... + m = [] + m.append("Bug Report") + m.append("==============================") + m.append("") + + if len(comments) > 0: + m.append("Comments:") + m.append("========") + m.append(comments) + m.append("") + + if len(stack_trace) > 0: + m.append("Stack Trace:") + m.append("===========") + m.append(stack_trace) + m.append("") + + msg = MIMEText("\n".join(m)) + message.attach(msg) + + # Include the log file ... + if True: + try: + log = os.path.join(get_home_directory(), "envisage.log") + f = open(log, "r") + entries = f.readlines() + f.close() + + ctype = "application/octet-stream" + maintype, subtype = ctype.split("/", 1) + msg = MIMEBase(maintype, subtype) + + msg = MIMEText("".join(entries)) + msg.add_header( + "Content-Disposition", "attachment", filename="logfile.txt" + ) + message.attach(msg) + except Exception: + logger.exception("Failed to include log file with message") + + # Include the environment variables ... + if True: + """ + Transmit the user's environment settings as well. Main purpose is to + work out the user name to help with following up on bug reports and + in future we should probably send less data. + """ + try: + entries = [] + for key, value in os.environ.items(): + entries.append("%30s : %s\n" % (key, value)) + + ctype = "application/octet-stream" + maintype, subtype = ctype.split("/", 1) + msg = MIMEBase(maintype, subtype) + + msg = MIMEText("".join(entries)) + msg.add_header( + "Content-Disposition", "attachment", filename="environment.txt" + ) + message.attach(msg) + + except Exception: + logger.exception( + "Failed to include environment variables with message" + ) + + return message
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/logger/agent/quality_agent_view.html b/5.0/_modules/apptools/logger/agent/quality_agent_view.html new file mode 100644 index 000000000..8a09b4bfa --- /dev/null +++ b/5.0/_modules/apptools/logger/agent/quality_agent_view.html @@ -0,0 +1,510 @@ + + + + + + + apptools.logger.agent.quality_agent_view — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.logger.agent.quality_agent_view

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+
+# Standard library imports.
+import logging
+
+# Enthought library imports.
+from pyface.api import Dialog
+from traits.api import Any, Str, Tuple
+
+
+# Setup a logger for this module.
+logger = logging.getLogger(__name__)
+
+
+priority_levels = ["Low", "Medium", "High", "Critical"]
+
+
+
[docs]class QualityAgentView(Dialog): + + size = Tuple((700, 900)) + title = Str("Quality Agent") + + # The associated LoggerService. + service = Any() + + msg = Str("") + subject = Str("Untitled Error Report") + to_address = Str() + cc_address = Str("") + from_address = Str() + smtp_server = Str() + priority = Str(priority_levels[2]) + comments = Str("None") + include_userdata = Any + + ########################################################################### + # Protected 'Dialog' interface. + ########################################################################### + + # fixme: Ideally, this should be passed in; this topic ID belongs to the + # Enlib help project/plug-in. + help_id = "enlib|HID_Quality_Agent_Dlg" + + def _create_dialog_area(self, parent): + """ Creates the main content of the dialog. """ + import wx + + parent.SetSizeHints(minW=300, minH=575) + + # Add the main panel + sizer = wx.BoxSizer(wx.VERTICAL) + panel = wx.Panel(parent, -1) + panel.SetSizer(sizer) + panel.SetAutoLayout(True) + + # Add a descriptive label at the top ... + label = wx.StaticText(panel, -1, "Send a comment or bug report ...") + sizer.Add(label, 0, wx.ALL, border=5) + + # Add the stack trace view ... + error_panel = self._create_error_panel(panel) + sizer.Add( + error_panel, 1, wx.ALL | wx.EXPAND | wx.CLIP_CHILDREN, border=5 + ) + + # Update the layout: + sizer.Fit(panel) + + # Add the error report view ... + report_panel = self._create_report_panel(panel) + sizer.Add( + report_panel, 2, wx.ALL | wx.EXPAND | wx.CLIP_CHILDREN, border=5 + ) + + # Update the layout: + sizer.Fit(panel) + + return panel + + def _create_buttons(self, parent): + """ Creates the buttons. """ + import wx + + sizer = wx.BoxSizer(wx.HORIZONTAL) + + # 'Send' button. + send = wx.Button(parent, wx.ID_OK, "Send") + wx.EVT_BUTTON(parent, wx.ID_OK, self._on_send) + sizer.Add(send) + send.SetDefault() + + # 'Cancel' button. + cancel = wx.Button(parent, wx.ID_CANCEL, "Cancel") + wx.EVT_BUTTON(parent, wx.ID_CANCEL, self._wx_on_cancel) + sizer.Add(cancel, 0, wx.LEFT, 10) + + # 'Help' button. + if len(self.help_id) > 0: + help = wx.Button(parent, wx.ID_HELP, "Help") + wx.EVT_BUTTON(parent, wx.ID_HELP, self._wx_on_help) + sizer.Add(help, 0, wx.LEFT, 10) + + return sizer + + ### Utility methods ####################################################### + + def _create_error_panel(self, parent): + import wx + + box = wx.StaticBox(parent, -1, "Message:") + sizer = wx.StaticBoxSizer(box, wx.VERTICAL) + + # Print the stack trace + label2 = wx.StaticText( + parent, + -1, + "The following information will be included in the report:", + ) + sizer.Add( + label2, + 0, + wx.LEFT | wx.TOP | wx.BOTTOM | wx.CLIP_CHILDREN, + border=5, + ) + + details = wx.TextCtrl( + parent, + -1, + self.msg, + size=(-1, 75), + style=wx.TE_MULTILINE + | wx.TE_READONLY + | wx.HSCROLL + | wx.VSCROLL + | wx.TE_RICH2 + | wx.CLIP_CHILDREN, + ) + details.SetSizeHints(minW=-1, minH=75) + # Set the font to not be proportional + font = wx.Font(12, wx.MODERN, wx.NORMAL, wx.NORMAL) + details.SetStyle(0, len(self.msg), wx.TextAttr(font=font)) + sizer.Add(details, 1, wx.EXPAND | wx.ALL | wx.CLIP_CHILDREN, 5) + + return sizer + + def _create_report_panel(self, parent): + import wx + + box = wx.StaticBox(parent, -1, "Report Information:") + sizer = wx.StaticBoxSizer(box, wx.VERTICAL) + + # Add email info ... + sizer.Add(self._create_email_info(parent), 0, wx.ALL | wx.EXPAND, 5) + + # Add priority combo: + sizer.Add(self._create_priority_combo(parent), 0, wx.ALL | wx.RIGHT, 5) + + # Extra comments from the user: + label3 = wx.StaticText(parent, -1, "Additional Comments:") + sizer.Add( + label3, 0, wx.LEFT | wx.TOP | wx.BOTTOM | wx.CLIP_CHILDREN, 5 + ) + + comments_field = wx.TextCtrl( + parent, + -1, + self.comments, + size=(-1, 75), + style=wx.TE_MULTILINE | wx.TE_RICH2 | wx.CLIP_CHILDREN, + ) + comments_field.SetSizeHints(minW=-1, minH=75) + font = wx.Font(12, wx.MODERN, wx.NORMAL, wx.NORMAL) + comments_field.SetStyle(0, len(self.comments), wx.TextAttr(font=font)) + sizer.Add(comments_field, 1, wx.ALL | wx.EXPAND | wx.CLIP_CHILDREN, 5) + wx.EVT_TEXT(parent, comments_field.GetId(), self._on_comments) + + # Include the project combobox? + if len(self.service.mail_files) > 0: + sizer.Add(self._create_project_upload(parent), 0, wx.ALL, border=5) + + return sizer + + def _create_email_info(self, parent): + import wx + + # Layout setup .. + sizer = wx.FlexGridSizer(5, 2, 10, 10) + sizer.AddGrowableCol(1) + + title_label = wx.StaticText(parent, -1, "Subject:") + sizer.Add(title_label, 0, wx.ALL | wx.ALIGN_RIGHT) + title_field = wx.TextCtrl(parent, -1, self.subject, wx.Point(-1, -1)) + sizer.Add( + title_field, + 1, + wx.EXPAND | wx.ALL | wx.ALIGN_RIGHT | wx.CLIP_CHILDREN, + ) + wx.EVT_TEXT(parent, title_field.GetId(), self._on_subject) + + to_label = wx.StaticText(parent, -1, "To:") + sizer.Add(to_label, 0, wx.ALL | wx.ALIGN_RIGHT) + to_field = wx.TextCtrl(parent, -1, self.to_address) + sizer.Add( + to_field, 1, wx.EXPAND | wx.ALL | wx.ALIGN_RIGHT | wx.CLIP_CHILDREN + ) + wx.EVT_TEXT(parent, to_field.GetId(), self._on_to) + + cc_label = wx.StaticText(parent, -1, "Cc:") + sizer.Add(cc_label, 0, wx.ALL | wx.ALIGN_RIGHT) + cc_field = wx.TextCtrl(parent, -1, "") + sizer.Add( + cc_field, 1, wx.EXPAND | wx.ALL | wx.ALIGN_RIGHT | wx.CLIP_CHILDREN + ) + wx.EVT_TEXT(parent, cc_field.GetId(), self._on_cc) + + from_label = wx.StaticText(parent, -1, "From:") + sizer.Add(from_label, 0, wx.ALL | wx.ALIGN_RIGHT) + from_field = wx.TextCtrl(parent, -1, self.from_address) + sizer.Add( + from_field, + 1, + wx.EXPAND | wx.ALL | wx.ALIGN_RIGHT | wx.CLIP_CHILDREN, + ) + wx.EVT_TEXT(parent, from_field.GetId(), self._on_from) + + smtp_label = wx.StaticText(parent, -1, "SMTP Server:") + sizer.Add(smtp_label, 0, wx.ALL | wx.ALIGN_RIGHT) + smtp_server_field = wx.TextCtrl(parent, -1, self.smtp_server) + sizer.Add( + smtp_server_field, + 1, + wx.EXPAND | wx.ALL | wx.ALIGN_RIGHT | wx.CLIP_CHILDREN, + ) + wx.EVT_TEXT(parent, smtp_server_field.GetId(), self._on_smtp_server) + + return sizer + + def _create_priority_combo(self, parent): + import wx + + sizer = wx.BoxSizer(wx.HORIZONTAL) + + label = wx.StaticText(parent, -1, "How critical is this issue?") + sizer.Add(label, 0, wx.ALL, border=0) + + cb = wx.ComboBox( + parent, + -1, + self.priority, + wx.Point(90, 50), + wx.Size(95, -1), + priority_levels, + wx.CB_READONLY, + ) + sizer.Add(cb, 1, wx.EXPAND | wx.LEFT | wx.CLIP_CHILDREN, border=10) + + wx.EVT_COMBOBOX(parent, cb.GetId(), self._on_priority) + + return sizer + + def _create_project_upload(self, parent): + import wx + + id = wx.NewId() + cb = wx.CheckBox( + parent, + id, + "Include Workspace Files (will increase email size) ", + wx.Point(65, 80), + wx.Size(-1, 20), + wx.NO_BORDER, + ) + wx.EVT_CHECKBOX(parent, id, self._on_project) + + return cb + + ## UI Listeners ########################################################### + + def _on_subject(self, event): + self.subject = event.GetEventObject().GetValue() + + def _on_to(self, event): + self.to_address = event.GetEventObject().GetValue() + + def _on_cc(self, event): + self.cc_address = event.GetEventObject().GetValue() + + def _on_from(self, event): + self.from_address = event.GetEventObject().GetValue() + + def _on_smtp_server(self, event): + self.smtp_server = event.GetEventObject().GetValue() + + def _on_priority(self, event): + self.priority = event.GetEventObject().GetStringSelection() + + def _on_comments(self, event): + self.comments = event.GetEventObject().GetValue() + + def _on_project(self, event): + self.include_userdata = event.Checked() + cb = event.GetEventObject() + + if event.Checked(): + cb.SetLabel( + "Include Workspace Files (approx. %.2f MBytes)" + % self._compute_project_size() + ) + else: + cb.SetLabel("Include Workspace Files (will increase email size)") + + def _on_send(self, event): + + # Disable the Send button while we go through the possibly + # time-consuming email-sending process. + button = event.GetEventObject() + button.Enable(0) + + fromaddr, toaddrs, ccaddrs = self._create_email_addresses() + message = self._create_email(fromaddr, toaddrs, ccaddrs) + + self.service.send_bug_report( + self.smtp_server, fromaddr, toaddrs, ccaddrs, message + ) + + # save the user's preferences + self.service.preferences.smtp_server = self.smtp_server + self.service.preferences.to_address = self.to_address + self.service.preferences.from_address = self.from_address + + # finally we close the dialog + self._wx_on_ok(event) + + ## Private ################################################################ + + def _create_email_addresses(self): + # utility function map addresses from ui into the standard format + # FIXME: We should use standard To: header parsing instead of this ad + # hoc whitespace-only approach. + fromaddr = self.from_address + if "" == fromaddr.strip(): + fromaddr = "anonymous" + toaddrs = self.to_address.split() + ccaddrs = self.cc_address.split() + + return fromaddr, toaddrs, ccaddrs + + def _compute_project_size(self): + # determine size of email in MBytes + fromaddr, toaddrs, ccaddrs = self._create_email_addresses() + message = self._create_email(fromaddr, toaddrs, ccaddrs) + return len(message.as_string()) / (2.0 ** 20) + + def _create_email(self, fromaddr, toaddrs, ccaddrs): + return self.service.create_email_message( + fromaddr, + toaddrs, + ccaddrs, + self.subject, + self.priority, + self.include_userdata, + self.msg, + self.comments, + ) + + def _to_address_default(self): + return self.service.preferences.to_address + + def _from_address_default(self): + return self.service.preferences.from_address + + def _smtp_server_default(self): + return self.service.preferences.smtp_server
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/logger/custom_excepthook.html b/5.0/_modules/apptools/logger/custom_excepthook.html new file mode 100644 index 000000000..371e3aa28 --- /dev/null +++ b/5.0/_modules/apptools/logger/custom_excepthook.html @@ -0,0 +1,165 @@ + + + + + + + apptools.logger.custom_excepthook — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.logger.custom_excepthook

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+
+
+# Standard library imports.
+import logging
+from traceback import format_exception
+
+
+"""
+    To catch exceptions with our own code this code needs to be added
+    sys.excepthook = custom_excepthook
+"""
+
+
+
[docs]def custom_excepthook(type, value, traceback): + """ Pass on the exception to the logging system. """ + + msg = "Custom - Traceback (most recent call last):\n" + list = format_exception(type, value, traceback) + + msg = "".join(list) + + # Try to find the module that the exception actually came from. + name = getattr(traceback.tb_frame, "f_globals", {}).get( + "__name__", __name__ + ) + logger = logging.getLogger(name) + logger.error(msg)
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/logger/log_point.html b/5.0/_modules/apptools/logger/log_point.html new file mode 100644 index 000000000..b1b3cd71c --- /dev/null +++ b/5.0/_modules/apptools/logger/log_point.html @@ -0,0 +1,170 @@ + + + + + + + apptools.logger.log_point — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.logger.log_point

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" Prints a stack trace every time it is called but does not halt execution
+    of the application.
+
+    Copied from Uche Ogbuji's blog
+"""
+
+# Standard library imports.
+import inspect
+from io import StringIO
+
+
+
[docs]def log_point(msg="\n"): + stack = inspect.stack() + # get rid of logPoint's part of the stack: + stack = stack[1:] + stack.reverse() + output = StringIO() + if msg: + output.write(str(msg) + "\n") + for stackLine in stack: + frame, filename, line, funcname, lines, unknown = stackLine + if filename.endswith("/unittest.py"): + # unittest.py code is a boring part of the traceback + continue + if filename.startswith("./"): + filename = filename[2:] + output.write("%s:%s in %s:\n" % (filename, line, funcname)) + if lines: + output.write(" %s\n" % "".join(lines)[:-1]) + s = output.getvalue() + + return s
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/logger/log_queue_handler.html b/5.0/_modules/apptools/logger/log_queue_handler.html new file mode 100644 index 000000000..30cce403c --- /dev/null +++ b/5.0/_modules/apptools/logger/log_queue_handler.html @@ -0,0 +1,198 @@ + + + + + + + apptools.logger.log_queue_handler — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.logger.log_queue_handler

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+
+# Standard library imports.
+from logging import Handler
+
+# Local imports.
+from .ring_buffer import RingBuffer
+
+
+
[docs]class LogQueueHandler(Handler): + + """Buffers up the log messages so that we can display them later. + This is important on startup when log messages are generated before + the ui has started. By putting them in this queue we can display + them once the ui is ready. + """ + + # The view where updates will go + _view = None + + def __init__(self, size=1000): + Handler.__init__(self) + # only buffer 1000 log records + self.size = size + self.ring = RingBuffer(self.size) + self.dirty = False + +
[docs] def emit(self, record): + """ Actually this is more like an enqueue than an emit().""" + self.ring.append(record) + if self._view is not None: + try: + self._view.update() + except Exception: + pass + self.dirty = True
+ +
[docs] def get(self): + self.dirty = False + + try: + result = self.ring.get() + except Exception: + # we did our best and it won't cause too much damage + # to just return a bogus message + result = [] + + return result
+ +
[docs] def has_new_records(self): + return self.dirty
+ +
[docs] def reset(self): + # start over with a new empty buffer + self.ring = RingBuffer(self.size) + if self._view is not None: + try: + self._view.update() + except Exception: + pass + self.dirty = True
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/logger/logger.html b/5.0/_modules/apptools/logger/logger.html new file mode 100644 index 000000000..7c65ef5cc --- /dev/null +++ b/5.0/_modules/apptools/logger/logger.html @@ -0,0 +1,188 @@ + + + + + + + apptools.logger.logger — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.logger.logger

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" Convenience functions for creating logging handlers etc. """
+
+
+# Standard library imports.
+import logging
+from logging.handlers import RotatingFileHandler
+
+# Local imports.
+from .log_queue_handler import LogQueueHandler
+
+
+# The default logging level.
+LEVEL = logging.DEBUG
+
+# The default formatter.
+FORMATTER = logging.Formatter("%(levelname)s|%(asctime)s|%(message)s")
+
+
+
[docs]class LogFileHandler(RotatingFileHandler): + """The default log file handler.""" + + def __init__( + self, path, maxBytes=1000000, backupCount=3, level=None, formatter=None + ): + RotatingFileHandler.__init__( + self, path, maxBytes=maxBytes, backupCount=3 + ) + + if level is None: + level = LEVEL + if formatter is None: + formatter = FORMATTER + # Set our default formatter and log level. + self.setFormatter(formatter) + self.setLevel(level)
+ + +
[docs]def add_log_queue_handler(logger, level=None, formatter=None): + """Adds a queueing log handler to a logger.""" + if level is None: + level = LEVEL + if formatter is None: + formatter = FORMATTER + + # Add the handler to the root logger. + log_queue_handler = LogQueueHandler() + log_queue_handler.setLevel(level) + log_queue_handler.setFormatter(formatter) + logger.addHandler(log_queue_handler) + return log_queue_handler
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/logger/plugin/logger_plugin.html b/5.0/_modules/apptools/logger/plugin/logger_plugin.html new file mode 100644 index 000000000..467379f49 --- /dev/null +++ b/5.0/_modules/apptools/logger/plugin/logger_plugin.html @@ -0,0 +1,238 @@ + + + + + + + apptools.logger.plugin.logger_plugin — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.logger.plugin.logger_plugin

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" Logger plugin.
+"""
+
+# Standard library imports.
+import logging
+
+# Enthought library imports.
+from envisage.api import ExtensionPoint, Plugin
+from apptools.logger.log_queue_handler import LogQueueHandler
+from traits.api import Callable, List
+
+# Local imports.
+from .logger_preferences import LoggerPreferences
+from .logger_service import LoggerService
+
+
+ID = "apptools.logger"
+ILOGGER = ID + ".plugin.logger_service.LoggerService"
+
+
+
[docs]class LoggerPlugin(Plugin): + """Logger plugin.""" + + id = ID + name = "Logger plugin" + + #### Extension points for this plugin ##################################### + + MAIL_FILES = "apptools.logger.plugin.mail_files" + + mail_files = ExtensionPoint( + List(Callable), + id=MAIL_FILES, + desc=""" + + This extension point allows you to contribute functions which will be + called to add project files to the zip file that the user mails back + with bug reports from the Quality Agent. + + The function will be passed a zipfile.ZipFile object. + + """, + ) + + #### Contributions to extension points made by this plugin ################ + + PREFERENCES = "envisage.preferences" + PREFERENCES_PAGES = "envisage.ui.workbench.preferences_pages" + VIEWS = "envisage.ui.workbench.views" + + preferences = List(contributes_to=PREFERENCES) + preferences_pages = List(contributes_to=PREFERENCES_PAGES) + views = List(contributes_to=VIEWS) + + def _preferences_default(self): + return ["pkgfile://%s/plugin/preferences.ini" % ID] + + def _preferences_pages_default(self): + from apptools.logger.plugin.view.logger_preferences_page import ( + LoggerPreferencesPage, + ) + + return [LoggerPreferencesPage] + + def _views_default(self): + return [self._logger_view_factory] + + #### Plugin interface ##################################################### + +
[docs] def start(self): + """Starts the plugin.""" + preferences = LoggerPreferences() + service = LoggerService( + application=self.application, preferences=preferences + ) + formatter = logging.Formatter("%(levelname)s|%(asctime)s|%(message)s") + handler = LogQueueHandler() + handler.setLevel(preferences.level_) + handler.setFormatter(formatter) + root_logger = logging.getLogger() + root_logger.addHandler(handler) + root_logger.setLevel(preferences.level_) + service.handler = handler + self.application.register_service(ILOGGER, service)
+ +
[docs] def stop(self): + """Stops the plugin.""" + service = self.application.get_service(ILOGGER) + service.save_preferences()
+ + #### LoggerPlugin private interface ####################################### + + def _logger_view_factory(self, **traits): + from apptools.logger.plugin.view.logger_view import LoggerView + + service = self.application.get_service(ILOGGER) + view = LoggerView(service=service, **traits) + # Record the created view on the service. + service.plugin_view = view + return view
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/logger/plugin/logger_preferences.html b/5.0/_modules/apptools/logger/plugin/logger_preferences.html new file mode 100644 index 000000000..d3be2d62e --- /dev/null +++ b/5.0/_modules/apptools/logger/plugin/logger_preferences.html @@ -0,0 +1,169 @@ + + + + + + + apptools.logger.plugin.logger_preferences — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.logger.plugin.logger_preferences

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+import logging
+
+from apptools.preferences.api import PreferencesHelper
+from traits.api import Bool, Str, Trait
+
+
+
[docs]class LoggerPreferences(PreferencesHelper): + """The persistent service exposing the Logger plugin's API.""" + + #### Preferences ########################################################## + + # The log levels + level = Trait( + "Info", + { + "Debug": logging.DEBUG, + "Info": logging.INFO, + "Warning": logging.WARNING, + "Error": logging.ERROR, + "Critical": logging.CRITICAL, + }, + is_str=True, + ) + + enable_agent = Bool(False) + smtp_server = Str() + to_address = Str() + from_address = Str() + + # The path to the preferences node that contains the preferences. + preferences_path = Str("apptools.logger")
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/logger/plugin/logger_service.html b/5.0/_modules/apptools/logger/plugin/logger_service.html new file mode 100644 index 000000000..69028d2ce --- /dev/null +++ b/5.0/_modules/apptools/logger/plugin/logger_service.html @@ -0,0 +1,310 @@ + + + + + + + apptools.logger.plugin.logger_service — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.logger.plugin.logger_service

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+# Standard library imports
+from io import BytesIO
+import logging
+import os
+import zipfile
+
+# Enthought library imports
+from pyface.workbench.api import View as WorkbenchView
+from traits.api import (
+    Any,
+    Callable,
+    HasTraits,
+    Instance,
+    List,
+    Property,
+    Undefined,
+    on_trait_change,
+)
+
+root_logger = logging.getLogger()
+logger = logging.getLogger(__name__)
+
+
+
[docs]class LoggerService(HasTraits): + """The persistent service exposing the Logger plugin's API.""" + + # The Envisage application. + application = Any() + + # The logging Handler we use. + handler = Any() + + # Our associated LoggerPreferences. + preferences = Any() + + # The view we use. + plugin_view = Instance(WorkbenchView) + + # Contributions from other plugins. + mail_files = Property(List(Callable)) + +
[docs] def save_preferences(self): + """Save the preferences.""" + self.preferences.preferences.save()
+ +
[docs] def whole_log_text(self): + """Return all of the logged data as formatted text.""" + lines = [self.handler.format(rec) for rec in self.handler.get()] + # Ensure that we end with a newline. + lines.append("") + text = "\n".join(lines) + return text
+ +
[docs] def create_email_message( + self, + fromaddr, + toaddrs, + ccaddrs, + subject, + priority, + include_userdata=False, + stack_trace="", + comments="", + include_environment=True, + ): + """Format a bug report email from the log files.""" + from email.mime.application import MIMEApplication + from email.mime.multipart import MIMEMultipart + from email.mime.text import MIMEText + + message = MIMEMultipart() + message["Subject"] = "%s [priority=%s]" % (subject, priority) + message["To"] = ", ".join(toaddrs) + message["Cc"] = ", ".join(ccaddrs) + message["From"] = fromaddr + message.preamble = ( + "You will not see this in a MIME-aware mail " "reader.\n" + ) + message.epilogue = " " # To guarantee the message ends with a newline + + # First section is simple ASCII data ... + m = [] + m.append("Bug Report") + m.append("==============================") + m.append("") + + if len(comments) > 0: + m.append("Comments:") + m.append("========") + m.append(comments) + m.append("") + + if len(stack_trace) > 0: + m.append("Stack Trace:") + m.append("===========") + m.append(stack_trace) + m.append("") + + msg = MIMEText("\n".join(m)) + message.attach(msg) + + # Include the log file ... + logtext = self.whole_log_text() + msg = MIMEText(logtext) + msg.add_header( + "Content-Disposition", "attachment", filename="logfile.txt" + ) + message.attach(msg) + + # Include the environment variables ... + # FIXME: ask the user, maybe? + if include_environment: + # Transmit the user's environment settings as well. Main purpose + # is to work out the user name to help with following up on bug + # reports and in future we should probably send less data. + entries = [] + for key, value in sorted(os.environ.items()): + entries.append("%30s : %s\n" % (key, value)) + + msg = MIMEText("".join(entries)) + msg.add_header( + "Content-Disposition", "attachment", filename="environment.txt" + ) + message.attach(msg) + + if include_userdata and len(self.mail_files) != 0: + f = BytesIO() + zf = zipfile.ZipFile(f, "w") + for mf in self.mail_files: + mf(zf) + zf.close() + + msg = MIMEApplication(f.getvalue()) + msg.add_header( + "Content-Disposition", "attachment", filename="userdata.zip" + ) + message.attach(msg) + + return message
+ +
[docs] def send_bug_report( + self, smtp_server, fromaddr, toaddrs, ccaddrs, message + ): + """Send a bug report email.""" + try: + import smtplib + + logger.debug("Connecting to: %s" % smtp_server) + server = smtplib.SMTP(host=smtp_server) + logger.debug("Connected: %s" % server) + # server.set_debuglevel(1) + server.sendmail(fromaddr, toaddrs + ccaddrs, message.as_string()) + server.quit() + except Exception: + logger.exception("Problem sending error report")
+ + #### Traits stuff ######################################################### + + def _get_mail_files(self): + return self.application.get_extensions( + "apptools.logger.plugin.mail_files" + ) + + @on_trait_change("preferences.level_") + def _level_changed(self, new): + if ( + new is not None + and new is not Undefined + and self.handler is not None + ): + root_logger.setLevel(self.preferences.level_) + self.handler.setLevel(self.preferences.level_)
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/logger/plugin/view/logger_preferences_page.html b/5.0/_modules/apptools/logger/plugin/view/logger_preferences_page.html new file mode 100644 index 000000000..a0f07b780 --- /dev/null +++ b/5.0/_modules/apptools/logger/plugin/view/logger_preferences_page.html @@ -0,0 +1,226 @@ + + + + + + + apptools.logger.plugin.view.logger_preferences_page — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.logger.plugin.view.logger_preferences_page

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+
+import logging
+
+from apptools.preferences.ui.api import PreferencesPage
+from traits.api import Bool, Trait, Str
+from traitsui.api import EnumEditor, Group, Item, View
+
+
+
[docs]class LoggerPreferencesPage(PreferencesPage): + """A preference page for the logger plugin.""" + + #### 'PreferencesPage' interface ########################################## + + # The page's category (e.g. 'General/Appearance'). The empty string means + # that this is a top-level page. + category = "" + + # The page's help identifier (optional). If a help Id *is* provided then + # there will be a 'Help' button shown on the preference page. + help_id = "" + + # The page name (this is what is shown in the preferences dialog. + name = "Logger" + + # The path to the preferences node that contains the preferences. + preferences_path = "apptools.logger" + + #### Preferences ########################################################## + + # The log levels + level = Trait( + "Info", + { + "Debug": logging.DEBUG, + "Info": logging.INFO, + "Warning": logging.WARNING, + "Error": logging.ERROR, + "Critical": logging.CRITICAL, + }, + is_str=True, + ) + + enable_agent = Bool(False) + smtp_server = Str + to_address = Str + from_address = Str + + # The view used to change the plugin preferences + traits_view = View( + Group( + Group( + Item( + name="level", + editor=EnumEditor( + values={ + "Debug": "1:Debug", + "Info": "2:Info", + "Warning": "3:Warning", + "Error": "4:Error", + "Critical": "5:Critical", + }, + ), + style="simple", + ), + label="Logger Settings", + show_border=True, + ), + Group(Item(name="10")), + Group( + Group( + Group( + Item( + name="enable_agent", label="Enable quality agent" + ), + show_left=False, + ), + Group( + Item(name="smtp_server", label="SMTP server"), + Item(name="from_address"), + Item(name="to_address"), + enabled_when="enable_agent==True", + ), + ), + label="Quality Agent Settings", + show_border=True, + ), + ), + )
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/logger/plugin/view/logger_view.html b/5.0/_modules/apptools/logger/plugin/view/logger_view.html new file mode 100644 index 000000000..237263148 --- /dev/null +++ b/5.0/_modules/apptools/logger/plugin/view/logger_view.html @@ -0,0 +1,343 @@ + + + + + + + apptools.logger.plugin.view.logger_view — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.logger.plugin.view.logger_view

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+
+# Standard library imports
+from datetime import datetime
+import logging
+
+# Enthought library imports.
+from pyface.api import ImageResource, clipboard
+from pyface.workbench.api import TraitsUIView
+from traits.api import (
+    Button,
+    Instance,
+    List,
+    Property,
+    Str,
+    cached_property,
+    on_trait_change,
+)
+from traitsui.api import View, Group, Item, CodeEditor, TabularEditor, spring
+from traitsui.tabular_adapter import TabularAdapter
+
+# Local imports
+from apptools.logger.agent.quality_agent_view import QualityAgentView
+from apptools.logger.plugin.logger_service import LoggerService
+
+# Constants
+_IMAGE_MAP = {
+    logging.DEBUG: ImageResource("debug"),
+    logging.INFO: ImageResource("info"),
+    logging.WARNING: ImageResource("warning"),
+    logging.ERROR: ImageResource("error"),
+    logging.CRITICAL: ImageResource("crit_error"),
+}
+
+
+
[docs]class LogRecordAdapter(TabularAdapter): + """A TabularEditor adapter for logging.LogRecord objects.""" + + columns = [ + ("Level", "level"), + ("Date", "date"), + ("Time", "time"), + ("Message", "message"), + ] + column_widths = [80, 100, 120, -1] + + level_image = Property + level_text = Property(Str) + date_text = Property(Str) + time_text = Property(Str) + message_text = Property(Str) + +
[docs] def get_width(self, object, trait, column): + return self.column_widths[column]
+ + def _get_level_image(self): + return _IMAGE_MAP[self.item.levelno] + + def _get_level_text(self): + return self.item.levelname.capitalize() + + def _get_date_text(self): + dt = datetime.fromtimestamp(self.item.created) + return dt.date().isoformat() + + def _get_time_text(self): + dt = datetime.fromtimestamp(self.item.created) + return dt.time().isoformat() + + def _get_message_text(self): + # Just display the first line of multiline messages, like stacktraces. + msg = self.item.getMessage() + msgs = msg.strip().split("\n") + if len(msgs) > 1: + suffix = "... [double click for details]" + else: + suffix = "" + abbrev_msg = msgs[0] + suffix + return abbrev_msg
+ + +
[docs]class LoggerView(TraitsUIView): + """The Workbench View showing the list of log items.""" + + id = Str("apptools.logger.plugin.view.logger_view.LoggerView") + name = Str("Logger") + service = Instance(LoggerService) + + log_records = List(Instance(logging.LogRecord)) + formatted_records = Property(Str, depends_on="log_records") + + activated = Instance(logging.LogRecord) + activated_text = Property(Str, depends_on="activated") + reset_button = Button("Reset Logs") + show_button = Button("Complete Text Log") + copy_button = Button("Copy Log to Clipboard") + + code_editor = CodeEditor(lexer="null", show_line_numbers=False) + log_records_editor = TabularEditor( + adapter=LogRecordAdapter(), editable=False, activated="activated" + ) + trait_view = View( + Group( + Item("log_records", editor=log_records_editor), + Group( + Item("reset_button"), + spring, + Item("show_button"), + Item("copy_button"), + orientation="horizontal", + show_labels=False, + ), + show_labels=False, + ) + ) + + ########################################################################### + # LogQueueHandler view interface + ########################################################################### + +
[docs] def update(self, force=False): + """Update 'log_records' if our handler has new records or 'force' is + set. + """ + service = self.service + if service.handler.has_new_records() or force: + log_records = [ + rec + for rec in service.handler.get() + if rec.levelno >= service.preferences.level_ + ] + log_records.reverse() + self.log_records = log_records
+ + ########################################################################### + # Private interface + ########################################################################### + + @on_trait_change("service.preferences.level_") + def _update_log_records(self): + self.service.handler._view = self + self.update(force=True) + + def _reset_button_fired(self): + self.service.handler.reset() + self.log_records = [] + + def _show_button_fired(self): + self.edit_traits( + view=View( + Item( + "formatted_records", + editor=self.code_editor, + style="readonly", + show_label=False, + ), + width=800, + height=600, + resizable=True, + buttons=["OK"], + title="Complete Text Log", + ) + ) + + def _copy_button_fired(self): + clipboard.text_data = self.formatted_records + + @cached_property + def _get_formatted_records(self): + return "\n".join( + [ + self.service.handler.formatter.format(record) + for record in self.log_records + ] + ) + + def _activated_changed(self): + if self.activated is None: + return + msg = self.activated.getMessage() + if self.service.preferences.enable_agent: + dialog = QualityAgentView(msg=msg, service=self.service) + dialog.open() + else: + self.edit_traits( + view=View( + Item( + "activated_text", + editor=self.code_editor, + style="readonly", + show_label=False, + ), + width=800, + height=600, + resizable=True, + buttons=["OK"], + title="Log Message Detail", + ) + ) + + @cached_property + def _get_activated_text(self): + if self.activated is None: + return "" + else: + return self.activated.getMessage()
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/logger/ring_buffer.html b/5.0/_modules/apptools/logger/ring_buffer.html new file mode 100644 index 000000000..bbfb99f9f --- /dev/null +++ b/5.0/_modules/apptools/logger/ring_buffer.html @@ -0,0 +1,183 @@ + + + + + + + apptools.logger.ring_buffer — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.logger.ring_buffer

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+"""
+Copied from Python Cookbook.
+
+"""
+
+
+
[docs]class RingBuffer: + def __init__(self, size_max): + self.max = size_max + self.data = [] + +
[docs] def append(self, x): + """append an element at the end of the buffer""" + self.data.append(x) + if len(self.data) == self.max: + self.cur = 0 + self.__class__ = RingBufferFull
+ +
[docs] def get(self): + """ return a list of elements from the oldest to the newest""" + return self.data
+ + +
[docs]class RingBufferFull: + def __init__(self, n): + raise Exception("you should use RingBuffer") + +
[docs] def append(self, x): + self.data[self.cur] = x + self.cur = (self.cur + 1) % self.max
+ +
[docs] def get(self): + return self.data[self.cur:] + self.data[: self.cur]
+ + +# sample of use +"""x=RingBuffer(5) +x.append(1); x.append(2); x.append(3); x.append(4) +print(x.__class__,x.get()) +x.append(5) +print(x.__class__,x.get()) +x.append(6) +print(x.data,x.get()) +x.append(7); x.append(8); x.append(9); x.append(10) +print(x.data,x.get())""" +
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/naming/address.html b/5.0/_modules/apptools/naming/address.html new file mode 100644 index 000000000..c0bb64dc7 --- /dev/null +++ b/5.0/_modules/apptools/naming/address.html @@ -0,0 +1,158 @@ + + + + + + + apptools.naming.address — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.naming.address

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" The address of a commuications endpoint. """
+
+
+# Enthought library imports.
+from traits.api import Any, HasTraits, Str
+
+
+
[docs]class Address(HasTraits): + """The address of a communications end-point. + + It contains a type that describes the communication mechanism, and the + actual address content. + + """ + + # The type of the address. + type = Str + + # The actual content. + content = Any
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/naming/binding.html b/5.0/_modules/apptools/naming/binding.html new file mode 100644 index 000000000..396897ce3 --- /dev/null +++ b/5.0/_modules/apptools/naming/binding.html @@ -0,0 +1,233 @@ + + + + + + + apptools.naming.binding — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.naming.binding

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" The representation of a name-to-object binding in a context. """
+
+
+# Enthought libary imports.
+from traits.api import Any, HasTraits, Property, Str
+
+
+
[docs]class Binding(HasTraits): + """ The representation of a name-to-object binding in a context. """ + + #### 'Binding' interface ################################################## + + # The class name of the object bound to the name in the binding. + class_name = Property(Str) + + # The name. + name = Str + + # The object bound to the name in the binding. + obj = Any + + #### Experimental 'Binding' interface ##################################### + + # fixme: These is not part of the JNDI spec, but they do seem startlingly + # useful! + # + # fixme: And, errr, startlingly broken! If the context that the binding + # is in is required then just look up the name minus the last component! + # + # The context that the binding came from. + context = Any + + # The name of the bound object within the namespace. + namespace_name = Property(Str) + + #### 'Private' interface ################################################## + + # Shadow trait for the 'class_name' property. + _class_name = Str + + ########################################################################### + # 'object' interface. + ########################################################################### + + def __str__(self): + """ Returns an informal string representation of the object. """ + + return super(Binding, self).__str__() + "(name=%s, obj=%s)" % ( + self.name, + self.obj, + ) + + ########################################################################### + # 'Binding' interface. + ########################################################################### + + #### Properties ########################################################### + + # class_name + def _get_class_name(self): + """ Returns the class name of the object. """ + + if len(self._class_name) == 0: + if self.obj is None: + class_name = None + + else: + klass = self.obj.__class__ + + class_name = "%s.%s" % (klass.__module__, klass.__name__) + + return class_name + + def _set_class_name(self, class_name): + """ Sets the class name of the object. """ + + self._class_name = class_name + + # namespace_name + def _get_namespace_name(self): + """ Returns the name of the context within its own namespace. """ + + if self.context is not None: + base = self.context.namespace_name + + else: + base = "" + + if len(base) > 0: + namespace_name = base + "/" + self.name + + else: + namespace_name = self.name + + return namespace_name
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/naming/context.html b/5.0/_modules/apptools/naming/context.html new file mode 100644 index 000000000..d87dae45b --- /dev/null +++ b/5.0/_modules/apptools/naming/context.html @@ -0,0 +1,813 @@ + + + + + + + apptools.naming.context — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.naming.context

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" The base class for all naming contexts. """
+
+
+# Enthought library imports.
+from traits.api import Any, Dict, Event, HasTraits
+from traits.api import Property, Str
+
+# Local imports.
+from .binding import Binding
+from .exception import InvalidNameError, NameAlreadyBoundError
+from .exception import NameNotFoundError, NotContextError
+from .naming_event import NamingEvent
+from .naming_manager import naming_manager
+from .unique_name import make_unique_name
+
+
+# Constants for environment property keys.
+INITIAL_CONTEXT_FACTORY = "apptools.naming.factory.initial"
+OBJECT_FACTORIES = "apptools.naming.factory.object"
+STATE_FACTORIES = "apptools.naming.factory.state"
+
+
+# The default environment.
+ENVIRONMENT = {
+    # 'Context' properties.
+    OBJECT_FACTORIES: [],
+    STATE_FACTORIES: [],
+}
+
+
+
[docs]class Context(HasTraits): + """ The base class for all naming contexts. """ + + # Keys for environment properties. + INITIAL_CONTEXT_FACTORY = INITIAL_CONTEXT_FACTORY + OBJECT_FACTORIES = OBJECT_FACTORIES + STATE_FACTORIES = STATE_FACTORIES + + #### 'Context' interface ################################################## + + # The naming environment in effect for this context. + environment = Dict(ENVIRONMENT) + + # The name of the context within its own namespace. + namespace_name = Property(Str) + + #### Events #### + + # Fired when an object has been added to the context (either via 'bind' or + # 'create_subcontext'). + object_added = Event(NamingEvent) + + # Fired when an object has been changed (via 'rebind'). + object_changed = Event(NamingEvent) + + # Fired when an object has been removed from the context (either via + # 'unbind' or 'destroy_subcontext'). + object_removed = Event(NamingEvent) + + # Fired when an object in the context has been renamed (via 'rename'). + object_renamed = Event(NamingEvent) + + # Fired when the contents of the context have changed dramatically. + context_changed = Event(NamingEvent) + + #### Protected 'Context' interface ####################################### + + # The bindings in the context. + _bindings = Dict(Str, Any) + + ########################################################################### + # 'Context' interface. + ########################################################################### + + #### Properties ########################################################### + + def _get_namespace_name(self): + """ + Return the name of the context within its own namespace. + + That is the full-path, through the namespace this context participates + in, to get to this context. For example, if the root context of the + namespace was called 'Foo', and there was a subcontext of that called + 'Bar', and we were within that and called 'Baz', then this should + return 'Foo/Bar/Baz'. + + """ + + # FIXME: We'd like to raise an exception and force implementors to + # decide what to do. However, it appears to be pretty common that + # most Context implementations do not override this method -- possibly + # because the comments aren't clear on what this is supposed to be? + # + # Anyway, if we raise an exception then it is impossible to use any + # evaluations when building a Traits UI for a Context. That is, the + # Traits UI can't include items that have a 'visible_when' or + # 'enabled_when' evaluation. This is because the Traits evaluation + # code calls the 'get()' method on the Context which attempts to + # retrieve the current namespace_name value. + # raise OperationNotSupportedError() + return "" + + #### Methods ############################################################## + +
[docs] def bind(self, name, obj, make_contexts=False): + """Binds a name to an object. + + If 'make_contexts' is True then any missing intermediate contexts are + created automatically. + + """ + + if len(name) == 0: + raise InvalidNameError("empty name") + + # Parse the name. + components = self._parse_name(name) + + # If there is exactly one component in the name then the operation + # takes place in this context. + if len(components) == 1: + atom = components[0] + + # Is the name already bound? + if self._is_bound(atom): + raise NameAlreadyBoundError(name) + + # Do the actual bind. + self._bind(atom, obj) + + # Trait event notification. + self.object_added = NamingEvent( + new_binding=Binding(name=name, obj=obj, context=self) + ) + + # Otherwise, attempt to continue resolution into the next context. + else: + if not self._is_bound(components[0]): + if make_contexts: + self._create_subcontext(components[0]) + + else: + raise NameNotFoundError(components[0]) + + next_context = self._get_next_context(components[0]) + next_context.bind("/".join(components[1:]), obj, make_contexts)
+ +
[docs] def rebind(self, name, obj, make_contexts=False): + """Binds an object to a name that may already be bound. + + If 'make_contexts' is True then any missing intermediate contexts are + created automatically. + + The object may be a different object but may also be the same object + that is already bound to the specified name. The name may or may not be + already used. Think of this as a safer version of 'bind' since this + one will never raise an exception regarding a name being used. + + """ + + if len(name) == 0: + raise InvalidNameError("empty name") + + # Parse the name. + components = self._parse_name(name) + + # If there is exactly one component in the name then the operation + # takes place in this context. + if len(components) == 1: + # Do the actual rebind. + self._rebind(components[0], obj) + + # Trait event notification. + self.object_changed = NamingEvent( + new_binding=Binding(name=name, obj=obj, context=self) + ) + + # Otherwise, attempt to continue resolution into the next context. + else: + if not self._is_bound(components[0]): + if make_contexts: + self._create_subcontext(components[0]) + + else: + raise NameNotFoundError(components[0]) + + next_context = self._get_next_context(components[0]) + next_context.rebind("/".join(components[1:]), obj, make_contexts)
+ +
[docs] def unbind(self, name): + """ Unbinds a name. """ + + if len(name) == 0: + raise InvalidNameError("empty name") + + # Parse the name. + components = self._parse_name(name) + + # If there is exactly one component in the name then the operation + # takes place in this context. + if len(components) == 1: + atom = components[0] + + if not self._is_bound(atom): + raise NameNotFoundError(name) + + # Lookup the object that we are unbinding to use in the event + # notification. + obj = self._lookup(atom) + + # Do the actual unbind. + self._unbind(atom) + + # Trait event notification. + self.object_removed = NamingEvent( + old_binding=Binding(name=name, obj=obj, context=self) + ) + + # Otherwise, attempt to continue resolution into the next context. + else: + if not self._is_bound(components[0]): + raise NameNotFoundError(components[0]) + + next_context = self._get_next_context(components[0]) + next_context.unbind("/".join(components[1:]))
+ +
[docs] def rename(self, old_name, new_name): + """ Binds a new name to an object. """ + + if len(old_name) == 0 or len(new_name) == 0: + raise InvalidNameError("empty name") + + # Parse the names. + old_components = self._parse_name(old_name) + new_components = self._parse_name(new_name) + + # If there is axactly one component in BOTH names then the operation + # takes place ENTIRELY in this context. + if len(old_components) == 1 and len(new_components) == 1: + # Is the old name actually bound? + if not self._is_bound(old_name): + raise NameNotFoundError(old_name) + + # Is the new name already bound? + if self._is_bound(new_name): + raise NameAlreadyBoundError(new_name) + + # Do the actual rename. + self._rename(old_name, new_name) + + # Lookup the object that we are renaming to use in the event + # notification. + obj = self._lookup(new_name) + + # Trait event notification. + self.object_renamed = NamingEvent( + old_binding=Binding(name=old_name, obj=obj, context=self), + new_binding=Binding(name=new_name, obj=obj, context=self), + ) + + else: + # fixme: This really needs to be transactional in case the bind + # succeeds but the unbind fails. To be safe should we just not + # support cross-context renaming for now?!?! + # + # Lookup the object. + obj = self.lookup(old_name) + + # Bind the new name. + self.bind(new_name, obj) + + # Unbind the old one. + self.unbind(old_name)
+ +
[docs] def lookup(self, name): + """ Resolves a name relative to this context. """ + + # If the name is empty we return the context itself. + if len(name) == 0: + # fixme: The JNDI spec. says that this should return a COPY of + # the context. + return self + + # Parse the name. + components = self._parse_name(name) + + # If there is exactly one component in the name then the operation + # takes place in this context. + if len(components) == 1: + atom = components[0] + + if not self._is_bound(atom): + raise NameNotFoundError(name) + + # Do the actual lookup. + obj = self._lookup(atom) + + # Otherwise, attempt to continue resolution into the next context. + else: + if not self._is_bound(components[0]): + raise NameNotFoundError(components[0]) + + next_context = self._get_next_context(components[0]) + obj = next_context.lookup("/".join(components[1:])) + + return obj
+ + # fixme: Non-JNDI +
[docs] def lookup_binding(self, name): + """ Looks up the binding for a name relative to this context. """ + + if len(name) == 0: + raise InvalidNameError("empty name") + + # Parse the name. + components = self._parse_name(name) + + # If there is exactly one component in the name then the operation + # takes place in this context. + if len(components) == 1: + atom = components[0] + + if not self._is_bound(atom): + raise NameNotFoundError(name) + + # Do the actual lookup. + binding = self._lookup_binding(atom) + + # Otherwise, attempt to continue resolution into the next context. + else: + if not self._is_bound(components[0]): + raise NameNotFoundError(components[0]) + + next_context = self._get_next_context(components[0]) + binding = next_context.lookup_binding("/".join(components[1:])) + + return binding
+ + # fixme: Non-JNDI +
[docs] def lookup_context(self, name): + """Resolves a name relative to this context. + + The name MUST resolve to a context. + + """ + + # If the name is empty we return the context itself. + if len(name) == 0: + # fixme: The JNDI spec. says that this should return a COPY of + # the context. + return self + + # Parse the name. + components = self._parse_name(name) + + # If there is exactly one component in the name then the operation + # takes place in this context. + if len(components) == 1: + atom = components[0] + + if not self._is_bound(atom): + raise NameNotFoundError(name) + + # Do the actual lookup. + obj = self._get_next_context(atom) + + # Otherwise, attempt to continue resolution into the next context. + else: + if not self._is_bound(components[0]): + raise NameNotFoundError(components[0]) + + next_context = self._get_next_context(components[0]) + obj = next_context.lookup("/".join(components[1:])) + + return obj
+ +
[docs] def create_subcontext(self, name): + """ Creates a sub-context. """ + + if len(name) == 0: + raise InvalidNameError("empty name") + + # Parse the name. + components = self._parse_name(name) + + # If there is exactly one component in the name then the operation + # takes place in this context. + if len(components) == 1: + atom = components[0] + + # Is the name already bound? + if self._is_bound(atom): + raise NameAlreadyBoundError(name) + + # Do the actual creation of the sub-context. + sub = self._create_subcontext(atom) + + # Trait event notification. + self.object_added = NamingEvent( + new_binding=Binding(name=name, obj=sub, context=self) + ) + + # Otherwise, attempt to continue resolution into the next context. + else: + if not self._is_bound(components[0]): + raise NameNotFoundError(components[0]) + + next_context = self._get_next_context(components[0]) + sub = next_context.create_subcontext("/".join(components[1:])) + + return sub
+ +
[docs] def destroy_subcontext(self, name): + """ Destroys a sub-context. """ + + if len(name) == 0: + raise InvalidNameError("empty name") + + # Parse the name. + components = self._parse_name(name) + + # If there is exactly one component in the name then the operation + # takes place in this context. + if len(components) == 1: + atom = components[0] + + if not self._is_bound(atom): + raise NameNotFoundError(name) + + obj = self._lookup(atom) + if not self._is_context(atom): + raise NotContextError(name) + + # Do the actual destruction of the sub-context. + self._destroy_subcontext(atom) + + # Trait event notification. + self.object_removed = NamingEvent( + old_binding=Binding(name=name, obj=obj, context=self) + ) + + # Otherwise, attempt to continue resolution into the next context. + else: + if not self._is_bound(components[0]): + raise NameNotFoundError(components[0]) + + next_context = self._get_next_context(components[0]) + next_context.destroy_subcontext("/".join(components[1:]))
+ + # fixme: Non-JNDI +
[docs] def get_unique_name(self, prefix): + """Returns a name that is unique within the context. + + The name returned will start with the specified prefix. + + """ + + return make_unique_name( + prefix, existing=self.list_names(""), format="%s (%d)" + )
+ +
[docs] def list_names(self, name=""): + """ Lists the names bound in a context. """ + + # If the name is empty then the operation takes place in this context. + if len(name) == 0: + names = self._list_names() + + # Otherwise, attempt to continue resolution into the next context. + else: + # Parse the name. + components = self._parse_name(name) + + if not self._is_bound(components[0]): + raise NameNotFoundError(components[0]) + + next_context = self._get_next_context(components[0]) + names = next_context.list_names("/".join(components[1:])) + + return names
+ +
[docs] def list_bindings(self, name=""): + """ Lists the bindings in a context. """ + + # If the name is empty then the operation takes place in this context. + if len(name) == 0: + bindings = self._list_bindings() + + # Otherwise, attempt to continue resolution into the next context. + else: + # Parse the name. + components = self._parse_name(name) + + if not self._is_bound(components[0]): + raise NameNotFoundError(components[0]) + + next_context = self._get_next_context(components[0]) + bindings = next_context.list_bindings("/".join(components[1:])) + + return bindings
+ + # fixme: Non-JNDI +
[docs] def is_context(self, name): + """ Returns True if the name is bound to a context. """ + + # If the name is empty then it refers to this context. + if len(name) == 0: + is_context = True + + else: + # Parse the name. + components = self._parse_name(name) + + # If there is exactly one component in the name then the operation + # takes place in this context. + if len(components) == 1: + atom = components[0] + + if not self._is_bound(atom): + raise NameNotFoundError(name) + + # Do the actual check. + is_context = self._is_context(atom) + + # Otherwise, attempt to continue resolution into the next context. + else: + if not self._is_bound(components[0]): + raise NameNotFoundError(components[0]) + + next_context = self._get_next_context(components[0]) + is_context = next_context.is_context("/".join(components[1:])) + + return is_context
+ + # fixme: Non-JNDI +
[docs] def search(self, obj): + """ Returns a list of namespace names that are bound to obj. """ + + # don't look for None + if obj is None: + return [] + + # Obj is bound to these names relative to this context + names = [] + + # path contain the name components down to the current context + path = [] + + self._search(obj, names, path, {}) + + return names
+ + ########################################################################### + # Protected 'Context' interface. + ########################################################################### + + def _parse_name(self, name): + """Parse a name into a list of components. + + e.g. 'foo/bar/baz' -> ['foo', 'bar', 'baz'] + + """ + + return name.split("/") + + def _is_bound(self, name): + """ Is a name bound in this context? """ + + return name in self._bindings + + def _lookup(self, name): + """ Looks up a name in this context. """ + + obj = self._bindings[name] + + return naming_manager.get_object_instance(obj, name, self) + + def _lookup_binding(self, name): + """ Looks up the binding for a name in this context. """ + + return Binding(name=name, obj=self._lookup(name), context=self) + + def _bind(self, name, obj): + """ Binds a name to an object in this context. """ + + state = naming_manager.get_state_to_bind(obj, name, self) + self._bindings[name] = state + + def _rebind(self, name, obj): + """ Rebinds a name to an object in this context. """ + + self._bind(name, obj) + + def _unbind(self, name): + """ Unbinds a name from this context. """ + + del self._bindings[name] + + def _rename(self, old_name, new_name): + """ Renames an object in this context. """ + + # Bind the new name. + self._bindings[new_name] = self._bindings[old_name] + + # Unbind the old one. + del self._bindings[old_name] + + def _create_subcontext(self, name): + """ Creates a sub-context of this context. """ + + sub = self.__class__(environment=self.environment) + self._bindings[name] = sub + + return sub + + def _destroy_subcontext(self, name): + """ Destroys a sub-context of this context. """ + + del self._bindings[name] + + def _list_bindings(self): + """ Lists the bindings in this context. """ + + bindings = [] + for name in self._list_names(): + bindings.append( + Binding(name=name, obj=self._lookup(name), context=self) + ) + + return bindings + + def _list_names(self): + """ Lists the names bound in this context. """ + + return list(self._bindings.keys()) + + def _is_context(self, name): + """ Returns True if a name is bound to a context. """ + + return self._get_next_context(name) is not None + + def _get_next_context(self, name): + """ Returns the next context. """ + + obj = self._lookup(name) + + # If the object is a context then everything is just dandy. + if isinstance(obj, Context): + next_context = obj + else: + raise NotContextError(name) + + return next_context + + def _search(self, obj, names, path, searched): + """Append to names any name bound to obj. + Join path and name with '/' to for a complete name from the + top context. + """ + + # Check the bindings recursively. + for binding in self.list_bindings(): + if binding.obj is obj: + path.append(binding.name) + names.append("/".join(path)) + path.pop() + + if ( + isinstance(binding.obj, Context) + and binding.obj not in searched + ): + path.append(binding.name) + searched[binding.obj] = True + binding.obj._search(obj, names, path, searched) + path.pop()
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/naming/dir_context.html b/5.0/_modules/apptools/naming/dir_context.html new file mode 100644 index 000000000..edc6864a6 --- /dev/null +++ b/5.0/_modules/apptools/naming/dir_context.html @@ -0,0 +1,293 @@ + + + + + + + apptools.naming.dir_context — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.naming.dir_context

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" The base class for all directory contexts. """
+
+
+# Enthought library imports.
+from traits.api import Dict
+
+# Local imports.
+from .context import Context
+from .exception import NameNotFoundError
+
+
+
[docs]class DirContext(Context): + """ The base class for all directory contexts. """ + + # The attributes of every object in the context. The attributes for the + # context itself have the empty string as the key. + # + # {str name : dict attributes} + _attributes = Dict + + ########################################################################### + # 'DirContext' interface. + ########################################################################### + +
[docs] def get_attributes(self, name): + """ Returns the attributes associated with a named object. """ + + # If the name is empty then we return the attributes of this context. + if len(name) == 0: + attributes = self._get_attributes(name) + + else: + # Parse the name. + components = self._parse_name(name) + + # If there is exactly one component in the name then the operation + # takes place in this context. + if len(components) == 1: + atom = components[0] + + if not self._is_bound(atom): + raise NameNotFoundError(name) + + # Do the actual get. + attributes = self._get_attributes(atom) + + # Otherwise, attempt to continue resolution into the next context. + else: + if not self._is_bound(components[0]): + raise NameNotFoundError(components[0]) + + next_context = self._get_next_context(components[0]) + attributes = next_context.get_attributes( + "/".join(components[1:]) + ) + + return attributes
+ +
[docs] def set_attributes(self, name, attributes): + """ Sets the attributes associated with a named object. """ + + # If the name is empty then we set the attributes of this context. + if len(name) == 0: + attributes = self._set_attributes(name, attributes) + + else: + # Parse the name. + components = self._parse_name(name) + + # If there is exactly one component in the name then the operation + # takes place in this context. + if len(components) == 1: + atom = components[0] + + if not self._is_bound(atom): + raise NameNotFoundError(name) + + # Do the actual set. + self._set_attributes(atom, attributes) + + # Otherwise, attempt to continue resolution into the next context. + else: + if not self._is_bound(components[0]): + raise NameNotFoundError(components[0]) + + next_context = self._get_next_context(components[0]) + next_context.set_attributes( + "/".join(components[1:]), attributes + )
+ + # fixme: Non-JNDI +
[docs] def find_bindings(self, visitor): + """Find bindings with attributes matching criteria in visitor. + + Visitor is a function that is passed the bindings for each level of the + heirarchy and the attribute dictionary for those bindings. The visitor + examines the bindings and dictionary and returns the bindings it is + interested in. + + """ + + bindings = visitor(self.list_bindings(), self._attributes) + + # recursively check other sub contexts. + for binding in self.list_bindings(): + obj = binding.obj + if isinstance(obj, DirContext): + bindings.extend(obj.find_bindings(visitor)) + + return bindings
+ + ########################################################################### + # Protected 'DirContext' interface. + ########################################################################### + + def _get_attributes(self, name): + """ Returns the attributes of an object in this context. """ + + attributes = self._attributes.setdefault(name, {}) + + return attributes.copy() + + def _set_attributes(self, name, attributes): + """ Sets the attributes of an object in this context. """ + + self._attributes[name] = attributes + + ########################################################################### + # Protected 'Context' interface. + ########################################################################### + + def _unbind(self, name): + """ Unbinds a name from this context. """ + + super(DirContext, self)._unbind(name) + + if name in self._attributes: + del self._attributes[name] + + def _rename(self, old_name, new_name): + """ Renames an object in this context. """ + + super(DirContext, self)._rename(old_name, new_name) + + if old_name in self._attributes: + self._attributes[new_name] = self._attributes[old_name] + del self._attributes[old_name] + + def _destroy_subcontext(self, name): + """ Destroys a sub-context of this context. """ + + super(DirContext, self)._destroy_subcontext(name) + + if name in self._attributes: + del self._attributes[name]
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/naming/dynamic_context.html b/5.0/_modules/apptools/naming/dynamic_context.html new file mode 100644 index 000000000..5b69be61c --- /dev/null +++ b/5.0/_modules/apptools/naming/dynamic_context.html @@ -0,0 +1,308 @@ + + + + + + + apptools.naming.dynamic_context — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.naming.dynamic_context

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+
+""" Provider of a framework that dynamically determines the contents of a
+    context at the time of interaction with the contents rather than at the
+    time a class is written.
+
+    This capability is particularly useful when the object acting as a context
+    is part of a plug-in application -- such as Envisage.  In general, this
+    capability allows the context to be:
+
+    - Extendable by contributions from somewhere other than the original
+      code writer
+    - Dynamic in that the elements it is composed of can change each time
+      someone interacts with the contents of the context.
+
+    It should be noted that this capability is explicitly different from
+    contexts that look at another container to determine their contents, such
+    as a file system context!
+
+    Users of this framework contribute items to a dynamic context by adding
+    traits to the dynamic context instance.  (This addition can happen
+    statically through the use of a Traits Category.)  The trait value is the
+    context item's value and the trait definition's metadata determines how the
+    item is treated within the context.  The support metadata is:
+
+    context_name: A non-empty string
+        Represents the name of the item within this context.  This must be
+        present for the trait to show up as a context item though the value
+        may change over time as the item gets bound to different names.
+    context_order: A float value
+        Indicates the position for the item within this context.  All
+        dynamically contributed context items are sorted by ascending order
+        of this value using the standard list sort function.
+    is_context: A boolean value
+        True if the item is itself a context.
+"""
+
+# Standardlibrary imports
+import logging
+
+# Local imports
+from .binding import Binding
+from .context import Context
+from .exception import OperationNotSupportedError
+
+
+# Setup a logger for this module.
+logger = logging.getLogger(__name__)
+
+
+
[docs]class DynamicContext(Context): + """A framework that dynamically determines the contents of a context at + the time of interaction with the contents rather than at the time a + context class is written. + + It should be noted that this capability is explicitly different from + contexts that look at another container to determine their contents, + such as a file system context! + """ + + ########################################################################## + # 'Context' interface. + ########################################################################## + + ### protected interface ################################################## + + def _is_bound(self, name): + """Is a name bound in this context?""" + + item = self._get_contributed_context_item(name) + result = item != (None, None) + + return result + + def _is_context(self, name): + """Returns True if a name is bound to a context.""" + item = self._get_contributed_context_item(name) + if item != (None, None): + obj, trait = item + result = trait.is_context is True + else: + result = False + + return result + + def _list_bindings(self): + """Lists the bindings in this context.""" + result = [ + Binding(name=n, obj=o, context=self) + for n, o, t in self._get_contributed_context_items() + ] + + return result + + def _list_names(self): + """Lists the names bound in this context.""" + result = [n for n, o, t in self._get_contributed_context_items()] + + return result + + def _lookup(self, name): + """Looks up a name in this context.""" + item = self._get_contributed_context_item(name) + if item != (None, None): + obj, trait = item + result = obj + else: + result = None + + return result + + def _rename(self, old_name, new_name): + """Renames an object in this context.""" + + item = self._get_contributed_context_item(old_name) + if item != (None, None): + obj, trait = item + trait.context_name = new_name + else: + raise ValueError('Name "%s" not in context', old_name) + + def _unbind(self, name): + """Unbinds a name from this context.""" + # It is an error to try to unbind any contributed context items + item = self._get_contributed_context_item(name) + if item != (None, None): + raise OperationNotSupportedError( + "Unable to unbind " + "built-in with name [%s]" % name + ) + + ########################################################################## + # 'DynamicContext' interface. + ########################################################################## + + ### protected interface ################################################## + + def _get_contributed_context_item(self, name): + """If the specified name matches a contributed context item then + returns a tuple of the item's current value and trait definition + (in that order.) Otherwise, returns a tuple of (None, None). + """ + result = (None, None) + + for n, o, t in self._get_contributed_context_items(): + if n == name: + result = (o, t) + + return result + + def _get_contributed_context_items(self): + """Returns an ordered list of items to be treated as part of our + context. + + Each item in the list is a tuple of its name, object, and trait + definition (in that order.) + """ + # Our traits that get treated as context items are those that declare + # themselves via metadata on the trait definition. + filter = {"context_name": lambda v: v is not None and len(v) > 0} + traits = self.traits(**filter) + + # Sort the list of context items according to the name of the item. + traits = [(t.context_order, n, t) for n, t in traits.items()] + traits.sort() + + # Convert these trait definitions into a list of name and object tuples + result = [ + (t.context_name, getattr(self, n), t) for order, n, t in traits + ] + + return result
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/naming/exception.html b/5.0/_modules/apptools/naming/exception.html new file mode 100644 index 000000000..8c73dc462 --- /dev/null +++ b/5.0/_modules/apptools/naming/exception.html @@ -0,0 +1,184 @@ + + + + + + + apptools.naming.exception — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.naming.exception

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" Naming exceptions. """
+
+
+
[docs]class NamingError(Exception): + """Base class for all naming exceptions."""
+ + +
[docs]class InvalidNameError(NamingError): + """Invalid name. + + This exception is thrown when the name passed to a naming operation does + not conform to the syntax of the naming system (or is empty etc). + + """
+ + +
[docs]class NameAlreadyBoundError(NamingError): + """Name already bound. + + This exception is thrown when an attempt is made to bind a name that is + already bound in the current context. + + """
+ + +
[docs]class NameNotFoundError(NamingError): + """Name not found. + + This exception is thrown when a component of a name cannot be resolved + because it is not bound in the current context. + + """
+ + +
[docs]class NotContextError(NamingError): + """Not a context. + + This exception is thrown when a naming operation has reached a point where + a context is required to continue the operation, but the resolved object + is not a context. + + """
+ + +
[docs]class OperationNotSupportedError(NamingError): + """The context does support the requested operation."""
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/naming/initial_context.html b/5.0/_modules/apptools/naming/initial_context.html new file mode 100644 index 000000000..d26ad66b5 --- /dev/null +++ b/5.0/_modules/apptools/naming/initial_context.html @@ -0,0 +1,185 @@ + + + + + + + apptools.naming.initial_context — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.naming.initial_context

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" The starting point for performing naming operations. """
+
+
+# Local imports.
+from .context import Context
+
+
+
[docs]def InitialContext(environment): + """ Creates an initial context for beginning name resolution. """ + + # Get the class name of the factory that will produce the initial context. + klass_name = environment.get(Context.INITIAL_CONTEXT_FACTORY) + if klass_name is None: + raise ValueError("No initial context factory specified") + + # Import the factory class. + klass = _import_symbol(klass_name) + + # Create the factory. + factory = klass() + + # Ask the factory for a context implementation instance. + return factory.get_initial_context(environment)
+ + +# fixme: This is the same code as in the Envisage import manager but we don't +# want naming to be dependent on Envisage, so we need some other package +# for useful 'Python' tools etc. +def _import_symbol(symbol_path): + """Imports the symbol defined by 'symbol_path'. + + 'symbol_path' is a string in the form 'foo.bar.baz' which is turned + into an import statement 'from foo.bar import baz' (ie. the last + component of the name is the symbol name, the rest is the package/ + module path to load it from). + + """ + + components = symbol_path.split(".") + + module_name = ".".join(components[:-1]) + symbol_name = components[-1] + + module = __import__(module_name, globals(), locals(), [symbol_name]) + symbol = getattr(module, symbol_name) + + return symbol +
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/naming/initial_context_factory.html b/5.0/_modules/apptools/naming/initial_context_factory.html new file mode 100644 index 000000000..8c80347bc --- /dev/null +++ b/5.0/_modules/apptools/naming/initial_context_factory.html @@ -0,0 +1,159 @@ + + + + + + + apptools.naming.initial_context_factory — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.naming.initial_context_factory

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" The base class for all initial context factories. """
+
+
+# Enthought library imports.
+from traits.api import HasTraits
+
+# Local imports.
+from .context import Context
+
+
+
[docs]class InitialContextFactory(HasTraits): + """ The base class for all initial context factories. """ + + ########################################################################### + # 'InitialContextFactory' interface. + ########################################################################### + +
[docs] def get_initial_context(self, environment): + """ Creates an initial context for beginning name resolution. """ + + return Context(environment=environment)
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/naming/naming_event.html b/5.0/_modules/apptools/naming/naming_event.html new file mode 100644 index 000000000..e119c59e8 --- /dev/null +++ b/5.0/_modules/apptools/naming/naming_event.html @@ -0,0 +1,157 @@ + + + + + + + apptools.naming.naming_event — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.naming.naming_event

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" The event fired by the tree model when it changes. """
+
+
+# Enthought library imports.
+from traits.api import HasTraits, Instance
+
+# Local imports.
+from .binding import Binding
+
+
+# Classes for event traits.
+
[docs]class NamingEvent(HasTraits): + """ Information about tree model changes. """ + + # The old binding. + old_binding = Instance(Binding) + + # The new binding. + new_binding = Instance(Binding)
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/naming/naming_manager.html b/5.0/_modules/apptools/naming/naming_manager.html new file mode 100644 index 000000000..124e9b884 --- /dev/null +++ b/5.0/_modules/apptools/naming/naming_manager.html @@ -0,0 +1,213 @@ + + + + + + + apptools.naming.naming_manager — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.naming.naming_manager

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" The naming manager. """
+
+
+# Enthought library imports.
+from traits.api import HasTraits
+
+
+
[docs]class NamingManager(HasTraits): + """ The naming manager. """ + + ########################################################################### + # 'NamingManager' interface. + ########################################################################### + +
[docs] def get_state_to_bind(self, obj, name, context): + """Returns the state of an object for binding. + + The naming manager asks the context for its list of STATE factories + and then calls them one by one until it gets a non-None result + indicating that the factory recognised the object and created state + information for it. + + If none of the factories recognize the object (or if the context + has no factories) then the object itself is returned. + + """ + + # Local imports. + from .context import Context + + # We get the state factories from the context's environment. + state_factories = context.environment[Context.STATE_FACTORIES] + + for state_factory in state_factories: + state = state_factory.get_state_to_bind(obj, name, context) + if state is not None: + break + + else: + state = obj + + return state
+ +
[docs] def get_object_instance(self, info, name, context): + """Creates an object using the specified state information. + + The naming manager asks the context for its list of OBJECT factories + and calls them one by one until it gets a non-None result, indicating + that the factory recognised the information and created an object. + + If none of the factories recognize the state information (or if the + context has no factories) then the state information itself is + returned. + + """ + + # Local imports. + from .context import Context + + # We get the object factories from the context's environment. + object_factories = context.environment[Context.OBJECT_FACTORIES] + + for object_factory in object_factories: + obj = object_factory.get_object_instance(info, name, context) + if obj is not None: + break + + else: + obj = info + + return obj
+ + +# Singleton instance. +naming_manager = NamingManager() +
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/naming/object_factory.html b/5.0/_modules/apptools/naming/object_factory.html new file mode 100644 index 000000000..8277c2eec --- /dev/null +++ b/5.0/_modules/apptools/naming/object_factory.html @@ -0,0 +1,166 @@ + + + + + + + apptools.naming.object_factory — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.naming.object_factory

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" The base class for all object factories. """
+
+
+# Enthought library imports.
+from traits.api import HasTraits
+
+
+
[docs]class ObjectFactory(HasTraits): + """The base class for all object factories. + + An object factory accepts some information about how to create an object + (such as a reference) and returns an instance of that object. + + """ + + ########################################################################### + # 'ObjectFactory' interface. + ########################################################################### + +
[docs] def get_object_instance(self, state, name, context): + """Creates an object using the specified state information. + + Returns None if the factory cannot create the object (ie. it does not + recognise the state passed to it). + + """ + + raise NotImplementedError
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/naming/object_serializer.html b/5.0/_modules/apptools/naming/object_serializer.html new file mode 100644 index 000000000..8df9f1aab --- /dev/null +++ b/5.0/_modules/apptools/naming/object_serializer.html @@ -0,0 +1,222 @@ + + + + + + + apptools.naming.object_serializer — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.naming.object_serializer

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" The base class for all object serializers. """
+
+
+# Standard library imports.
+import logging
+from traceback import print_exc
+from os.path import splitext
+import pickle
+
+# Enthought library imports.
+from apptools.persistence.versioned_unpickler import VersionedUnpickler
+from traits.api import HasTraits, Str
+
+
+# Setup a logger for this module.
+logger = logging.getLogger(__name__)
+
+
+
[docs]class ObjectSerializer(HasTraits): + """ The base class for all object serializers. """ + + #### 'ObjectSerializer' interface ######################################### + + # The file extension recognized by this serializer. + ext = Str(".pickle") + + ########################################################################### + # 'ObjectSerializer' interface. + ########################################################################### + +
[docs] def can_load(self, path): + """ Returns True if the serializer can load a file. """ + + rest, ext = splitext(path) + + return ext == self.ext
+ +
[docs] def load(self, path): + """ Loads an object from a file. """ + + # Unpickle the object. + f = open(path, "rb") + try: + try: + obj = VersionedUnpickler(f).load() + except Exception as ex: + print_exc() + logger.exception( + "Failed to load pickle file: %s, %s" % (path, ex) + ) + + raise + finally: + f.close() + + return obj
+ +
[docs] def can_save(self, obj): + """ Returns True if the serializer can save an object. """ + + return True
+ +
[docs] def save(self, path, obj): + """ Saves an object to a file. """ + + if not path.endswith(self.ext): + actual_path = path + self.ext + + else: + actual_path = path + + # Pickle the object. + f = open(actual_path, "wb") + try: + pickle.dump(obj, f, 1) + except Exception as ex: + logger.exception( + "Failed to pickle into file: %s, %s, object:%s" + % (path, ex, obj) + ) + print_exc() + f.close() + + return actual_path
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/naming/py_context.html b/5.0/_modules/apptools/naming/py_context.html new file mode 100644 index 000000000..b3bb934f3 --- /dev/null +++ b/5.0/_modules/apptools/naming/py_context.html @@ -0,0 +1,311 @@ + + + + + + + apptools.naming.py_context — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.naming.py_context

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" A naming context for a Python namespace. """
+
+
+# Enthought library imports.
+from traits.api import Any, Dict, Instance, Property
+
+# Local imports.
+from .address import Address
+from .binding import Binding
+from .context import Context
+from .naming_manager import naming_manager
+from .py_object_factory import PyObjectFactory
+from .reference import Reference
+from .referenceable import Referenceable
+from .referenceable_state_factory import ReferenceableStateFactory
+
+
+# The default environment.
+ENVIRONMENT = {
+    # 'Context' properties.
+    Context.OBJECT_FACTORIES: [PyObjectFactory()],
+    Context.STATE_FACTORIES: [ReferenceableStateFactory()],
+}
+
+
+
[docs]class PyContext(Context, Referenceable): + """ A naming context for a Python namespace. """ + + #### 'Context' interface ################################################## + + # The naming environment in effect for this context. + environment = Dict(ENVIRONMENT) + + #### 'PyContext' interface ################################################ + + # The Python namespace that we represent. + namespace = Any + + # If the namespace is actual a Python object that has a '__dict__' + # attribute, then this will be that object (the namespace will be the + # object's '__dict__'. + obj = Any + + #### 'Referenceable' interface ############################################ + + # The object's reference suitable for binding in a naming context. + reference = Property(Instance(Reference)) + + ########################################################################### + # 'object' interface. + ########################################################################### + + def __init__(self, **traits): + """ Creates a new context. """ + + # Base class constructor. + super(PyContext, self).__init__(**traits) + + if type(self.namespace) is not dict: + if hasattr(self.namespace, "__dict__"): + self.obj = self.namespace + self.namespace = self.namespace.__dict__ + + else: + raise ValueError("Need a dictionary or a __dict__ attribute") + + ########################################################################### + # 'Referenceable' interface. + ########################################################################### + + #### Properties ########################################################### + + def _get_reference(self): + """ Returns a reference to this object suitable for binding. """ + + reference = Reference( + class_name=self.__class__.__name__, + addresses=[Address(type="py_context", content=self.namespace)], + ) + + return reference + + ########################################################################### + # Protected 'Context' interface. + ########################################################################### + + def _is_bound(self, name): + """ Is a name bound in this context? """ + + return name in self.namespace + + def _lookup(self, name): + """ Looks up a name in this context. """ + + obj = self.namespace[name] + + return naming_manager.get_object_instance(obj, name, self) + + def _bind(self, name, obj): + """ Binds a name to an object in this context. """ + + state = naming_manager.get_state_to_bind(obj, name, self) + self.namespace[name] = state + + def _rebind(self, name, obj): + """ Rebinds a name to a object in this context. """ + + self._bind(name, obj) + + def _unbind(self, name): + """ Unbinds a name from this context. """ + + del self.namespace[name] + + # Trait event notification. + self.trait_property_changed("context_changed", None, None) + + def _rename(self, old_name, new_name): + """ Renames an object in this context. """ + + state = self.namespace[old_name] + + # Bind the new name. + self.namespace[new_name] = state + + # Unbind the old one. + del self.namespace[old_name] + + # Trait event notification. + self.context_changed = True + + def _create_subcontext(self, name): + """ Creates a sub-context of this context. """ + + sub = self._context_factory(name, {}) + self.namespace[name] = sub + + # Trait event notification. + self.trait_property_changed("context_changed", None, None) + + return sub + + def _destroy_subcontext(self, name): + """ Destroys a sub-context of this context. """ + + del self.namespace[name] + + # Trait event notification. + self.trait_property_changed("context_changed", None, None) + + def _list_bindings(self): + """ Lists the bindings in this context. """ + + bindings = [] + for name, value in self.namespace.items(): + bindings.append( + Binding(name=name, obj=self._lookup(name), context=self) + ) + return bindings + + def _list_names(self): + """ Lists the names bound in this context. """ + + return list(self.namespace.keys()) + + ########################################################################### + # Private interface. + ########################################################################### + + def _context_factory(self, name, namespace): + """ Create a sub-context. """ + + return self.__class__(namespace=namespace)
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/naming/py_object_factory.html b/5.0/_modules/apptools/naming/py_object_factory.html new file mode 100644 index 000000000..4a049b8ad --- /dev/null +++ b/5.0/_modules/apptools/naming/py_object_factory.html @@ -0,0 +1,171 @@ + + + + + + + apptools.naming.py_object_factory — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.naming.py_object_factory

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" Object factory for Python namespace contexts. """
+
+
+# Local imports.
+from .object_factory import ObjectFactory
+from .reference import Reference
+
+
+
[docs]class PyObjectFactory(ObjectFactory): + """ Object factory for Python namespace contexts. """ + + ########################################################################### + # 'ObjectFactory' interface. + ########################################################################### + +
[docs] def get_object_instance(self, state, name, context): + """ Creates an object using the specified state information. """ + + obj = None + + if isinstance(state, Reference): + if len(state.addresses) > 0: + if state.addresses[0].type == "py_context": + namespace = state.addresses[0].content + obj = context._context_factory(name, namespace) + + elif hasattr(state, "__dict__"): + from apptools.naming.py_context import PyContext + + if not isinstance(state, PyContext): + obj = context._context_factory(name, state) + + return obj
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/naming/pyfs_context.html b/5.0/_modules/apptools/naming/pyfs_context.html new file mode 100644 index 000000000..819d1dbd6 --- /dev/null +++ b/5.0/_modules/apptools/naming/pyfs_context.html @@ -0,0 +1,674 @@ + + + + + + + apptools.naming.pyfs_context — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.naming.pyfs_context

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" A Python File System context. """
+
+
+# Standard library imports.
+import glob
+import logging
+import os
+from os.path import join, splitext
+import pickle
+
+# Enthought library imports.
+from apptools.io.api import File
+from traits.api import Any, Dict, Instance, Property, Str
+
+# Local imports.
+from .address import Address
+from .binding import Binding
+from .context import Context
+from .dir_context import DirContext
+from .naming_event import NamingEvent
+from .naming_manager import naming_manager
+from .object_serializer import ObjectSerializer
+from .pyfs_context_factory import PyFSContextFactory
+from .pyfs_object_factory import PyFSObjectFactory
+from .pyfs_state_factory import PyFSStateFactory
+from .reference import Reference
+from .referenceable import Referenceable
+
+
+# Setup a logger for this module.
+logger = logging.getLogger(__name__)
+
+
+# The name of the 'special' file in which we store object attributes.
+ATTRIBUTES_FILE = "__attributes__"
+
+# Constants for environment property keys.
+FILTERS = "apptools.naming.pyfs.filters"
+OBJECT_SERIALIZERS = "apptools.naming.pyfs.object.serializers"
+
+
+# The default environment.
+ENVIRONMENT = {
+    #### 'Context' properties #################################################
+    # Object factories.
+    Context.OBJECT_FACTORIES: [PyFSObjectFactory(), PyFSContextFactory()],
+    # State factories.
+    Context.STATE_FACTORIES: [PyFSStateFactory()],
+    #### 'PyFSContext' properties #############################################
+    # Object serializers.
+    OBJECT_SERIALIZERS: [ObjectSerializer()],
+    # List of filename patterns to ignore.  These patterns are passed to
+    # 'glob.glob', so things like '*.pyc' will do what you expect.
+    #
+    # fixme: We should have a generalized filter mechanism here, and '.svn'
+    # should be moved elsewhere!
+    FILTERS: [ATTRIBUTES_FILE, ".svn"],
+}
+
+
+
[docs]class PyFSContext(DirContext, Referenceable): + """A Python File System context. + + This context represents a directory on a local file system. + + """ + + # The name of the 'special' file in which we store object attributes. + ATTRIBUTES_FILE = ATTRIBUTES_FILE + + # Environment property keys. + FILTERS = FILTERS + OBJECT_SERIALIZERS = OBJECT_SERIALIZERS + + #### 'Context' interface ################################################## + + # The naming environment in effect for this context. + environment = Dict(ENVIRONMENT) + + # The name of the context within its own namespace. + namespace_name = Property(Str) + + #### 'PyFSContext' interface ############################################## + + # The name of the context (the last component of the path). + name = Str + + # The path name of the directory on the local file system. + path = Str + + #### 'Referenceable' interface ############################################ + + # The object's reference suitable for binding in a naming context. + reference = Property(Instance(Reference)) + + #### Private interface #################################################### + + # A mapping from bound name to the name of the corresponding file or + # directory on the file system. + _name_to_filename_map = Dict # (Str, Str) + + # The attributes of every object in the context. The attributes for the + # context itself have the empty string as the key. + # + # {str name : dict attributes} + # + # fixme: Don't use 'Dict' here as it causes problems when pickling because + # trait dicts have a reference back to the parent object (hence we end up + # pickling all kinds of things that we don't need or want to!). + _attributes = Any + + ########################################################################### + # 'object' interface. + ########################################################################### + + def __init__(self, **traits): + """ Creates a new context. """ + + # Base class constructor. + super(PyFSContext, self).__init__(**traits) + + # We cache each object as it is looked up so that all accesses to a + # serialized Python object return a reference to exactly the same one. + self._cache = {} + + ########################################################################### + # 'PyFSContext' interface. + ########################################################################### + + #### Properties ########################################################### + + def _get_namespace_name(self): + """ Returns the name of the context within its own namespace. """ + + # fixme: clean this up with an initial context API! + if "root" in self.environment: + root = self.environment["root"] + + namespace_name = self.path[len(root) + 1:] + + else: + namespace_name = self.path + + # fixme: This is a bit dodgy 'cos we actually return a name that can + # be looked up, and not the file system name... + namespace_name = "/".join(namespace_name.split(os.path.sep)) + + return namespace_name + + #### methods ############################################################## + +
[docs] def refresh(self): + """ Refresh the context to reflect changes in the file system. """ + + # fixme: This needs more work 'cos if we refresh a context then we + # will load new copies of serialized Python objects! + + # This causes the initializer to run again the next time the trait is + # accessed. + self.reset_traits(["_name_to_filename_map"]) + + # Clear out the cache. + self._cache = {} + + # fixme: This is a bit hacky since the context in the binding may + # not be None! + self.context_changed = NamingEvent( + new_binding=Binding(name=self.name, obj=self, context=None) + )
+ + ########################################################################### + # 'Referenceable' interface. + ########################################################################### + + #### Properties ########################################################### + + def _get_reference(self): + """ Returns a reference to this object suitable for binding. """ + + abspath = os.path.abspath(self.path) + + reference = Reference( + class_name=self.__class__.__name__, + addresses=[Address(type="pyfs_context", content=abspath)], + ) + + return reference + + ########################################################################### + # Protected 'Context' interface. + ########################################################################### + + def _is_bound(self, name): + """ Is a name bound in this context? """ + + return name in self._name_to_filename_map + + def _lookup(self, name): + """ Looks up a name in this context. """ + + if name in self._cache: + obj = self._cache[name] + + else: + # Get the full path to the file. + path = join(self.path, self._name_to_filename_map[name]) + + # If the file contains a serialized Python object then load it. + for serializer in self._get_object_serializers(): + if serializer.can_load(path): + try: + state = serializer.load(path) + + # If the load fails then we create a generic file resource + # (the idea being that it might be useful to have access to + # the file to see what went wrong). + except: # noqa: E722 + state = File(path) + logger.exception("Error loading resource at %s" % path) + + break + + # Otherwise, it must just be a file or folder. + else: + # Directories are contexts. + if os.path.isdir(path): + state = self._context_factory(name, path) + + # Files are just files! + elif os.path.isfile(path): + state = File(path) + + else: + raise ValueError("unrecognized file for %s" % name) + + # Get the actual object from the naming manager. + obj = naming_manager.get_object_instance(state, name, self) + + # Update the cache. + self._cache[name] = obj + + return obj + + def _bind(self, name, obj): + """ Binds a name to an object in this context. """ + + # Get the actual state to bind from the naming manager. + state = naming_manager.get_state_to_bind(obj, name, self) + + # If the object is actually an abstract file then we don't have to + # do anything. + if isinstance(state, File): + if not state.exists: + state.create_file() + + filename = name + + # Otherwise we are binding an arbitrary Python object, so find a + # serializer for it. + else: + for serializer in self._get_object_serializers(): + if serializer.can_save(obj): + path = serializer.save(join(self.path, name), obj) + filename = os.path.basename(path) + break + + else: + raise ValueError("cannot serialize object %s" % name) + + # Update the name to filename map. + self._name_to_filename_map[name] = filename + + # Update the cache. + self._cache[name] = obj + + return state + + def _rebind(self, name, obj): + """ Rebinds a name to an object in this context. """ + + self._bind(name, obj) + + def _unbind(self, name): + """ Unbinds a name from this context. """ + + # Get the full path to the file. + path = join(self.path, self._name_to_filename_map[name]) + + # Remove it! + f = File(path) + f.delete() + + # Update the name to filename map. + del self._name_to_filename_map[name] + + # Update the cache. + if name in self._cache: + del self._cache[name] + + # Remove any attributes. + if name in self._attributes: + del self._attributes[name] + self._save_attributes() + + def _rename(self, old_name, new_name): + """ Renames an object in this context. """ + + # Get the old filename. + old_filename = self._name_to_filename_map[old_name] + old_file = File(join(self.path, old_filename)) + + # Lookup the object bound to the old name. This has the side effect + # of adding the object to the cache under the name 'old_name'. + obj = self._lookup(old_name) + + # We are renaming a LOCAL context (ie. a folder)... + if old_file.is_folder: + # Create the new filename. + new_filename = new_name + new_file = File(join(self.path, new_filename)) + + # Move the folder. + old_file.move(new_file) + + # Update the 'Context' object. + obj.path = new_file.path + + # Update the cache. + self._cache[new_name] = obj + del self._cache[old_name] + + # Refreshing the context makes sure that all of its contents + # reflect the new name (i.e., sub-folders and files have the + # correct path). + # + # fixme: This currently results in new copies of serialized + # Python objects! We need to be a bit more judicious in the + # refresh. + obj.refresh() + + # We are renaming a file... + elif isinstance(obj, File): + # Create the new filename. + new_filename = new_name + new_file = File(join(self.path, new_filename)) + + # Move the file. + old_file.move(new_file) + + # Update the 'File' object. + obj.path = new_file.path + + # Update the cache. + self._cache[new_name] = obj + del self._cache[old_name] + + # We are renaming a serialized Python object... + else: + # Create the new filename. + new_filename = new_name + old_file.ext + new_file = File(join(self.path, new_filename)) + + old_file.delete() + + # Update the cache. + if old_name in self._cache: + self._cache[new_name] = self._cache[old_name] + del self._cache[old_name] + + # Force the creation of the new file. + # + # fixme: I'm not sure that this is really the place for this. We + # do it because often the 'name' of the object is actually an + # attribute of the object itself, and hence we want the serialized + # state to reflect the new name... Hmmm... + self._rebind(new_name, obj) + + # Update the name to filename map. + del self._name_to_filename_map[old_name] + self._name_to_filename_map[new_name] = new_filename + + # Move any attributes over to the new name. + if old_name in self._attributes: + self._attributes[new_name] = self._attributes[old_name] + del self._attributes[old_name] + self._save_attributes() + + def _create_subcontext(self, name): + """ Creates a sub-context of this context. """ + + path = join(self.path, name) + + # Create a directory. + os.mkdir(path) + + # Create a sub-context that represents the directory. + sub = self._context_factory(name, path) + + # Update the name to filename map. + self._name_to_filename_map[name] = name + + # Update the cache. + self._cache[name] = sub + + return sub + + def _destroy_subcontext(self, name): + """ Destroys a sub-context of this context. """ + + return self._unbind(name) + + def _list_names(self): + """ Lists the names bound in this context. """ + + return list(self._name_to_filename_map.keys()) + + # fixme: YFI this is not part of the protected 'Context' interface so + # what is it doing here? +
[docs] def get_unique_name(self, name): + + ext = splitext(name)[1] + + # specially handle '.py' files + if ext != ".py": + return super(PyFSContext, self).get_unique_name(name) + + body = splitext(name)[0] + names = self.list_names() + i = 2 + unique = name + while unique in names: + unique = body + "_" + str(i) + ".py" + i += 1 + + return unique
+ + ########################################################################### + # Protected 'DirContext' interface. + ########################################################################### + + def _get_attributes(self, name): + """ Returns the attributes of an object in this context. """ + + attributes = self._attributes.setdefault(name, {}) + + return attributes.copy() + + def _set_attributes(self, name, attributes): + """ Sets the attributes of an object in this context. """ + + self._attributes[name] = attributes + self._save_attributes() + + ########################################################################### + # Private interface. + ########################################################################### + + def _get_filters(self): + """ Returns the filters for this context. """ + + return self.environment.get(self.FILTERS, []) + + def _get_object_serializers(self): + """ Returns the object serializers for this context. """ + + return self.environment.get(self.OBJECT_SERIALIZERS, []) + + def _context_factory(self, name, path): + """ Create a sub-context. """ + + return self.__class__(path=path, environment=self.environment) + + def _save_attributes(self): + """ Saves all attributes to the attributes file. """ + + path = join(self.path, self.ATTRIBUTES_FILE) + + f = open(path, "wb") + pickle.dump(self._attributes, f, 1) + f.close() + + #### Trait initializers ################################################### + + def __name_to_filename_map_default(self): + """ Initializes the '_name_to_filename' trait. """ + + # fixme: We should have a generalized filter mechanism (instead of + # just 'glob' patterns we should have filter objects that can be a bit + # more flexible in how they do the filtering). + patterns = [join(self.path, filter) for filter in self._get_filters()] + + name_to_filename_map = {} + for filename in os.listdir(self.path): + path = join(self.path, filename) + for pattern in patterns: + if path in glob.glob(pattern): + break + + else: + for serializer in self._get_object_serializers(): + if serializer.can_load(filename): + # fixme: We should probably get the name from the + # serializer instead of assuming that we can just + # drop the file exension. + name, ext = os.path.splitext(filename) + break + + else: + name = filename + + name_to_filename_map[name] = filename + + return name_to_filename_map + + def __attributes_default(self): + """ Initializes the '_attributes' trait. """ + + attributes_file = File(join(self.path, self.ATTRIBUTES_FILE)) + if attributes_file.is_file: + f = open(attributes_file.path, "rb") + attributes = pickle.load(f) + f.close() + + else: + attributes = {} + + return attributes + + #### Trait event handlers ################################################# + + def _path_changed(self): + """ Called when the context's path has changed. """ + + basename = os.path.basename(self.path) + + self.name, ext = os.path.splitext(basename)
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/naming/pyfs_context_factory.html b/5.0/_modules/apptools/naming/pyfs_context_factory.html new file mode 100644 index 000000000..13c30f4ea --- /dev/null +++ b/5.0/_modules/apptools/naming/pyfs_context_factory.html @@ -0,0 +1,165 @@ + + + + + + + apptools.naming.pyfs_context_factory — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.naming.pyfs_context_factory

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" Object factory for Python File System contexts. """
+
+
+# Local imports.
+from .object_factory import ObjectFactory
+from .reference import Reference
+
+
+
[docs]class PyFSContextFactory(ObjectFactory): + """ Object factory for Python File System contexts. """ + + ########################################################################### + # 'ObjectFactory' interface. + ########################################################################### + +
[docs] def get_object_instance(self, state, name, context): + """ Creates an object using the specified state information. """ + + obj = None + + if isinstance(state, Reference): + if len(state.addresses) > 0: + if state.addresses[0].type == "pyfs_context": + path = state.addresses[0].content + obj = context._context_factory(name, path) + + return obj
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/naming/pyfs_initial_context_factory.html b/5.0/_modules/apptools/naming/pyfs_initial_context_factory.html new file mode 100644 index 000000000..6dc9fafdc --- /dev/null +++ b/5.0/_modules/apptools/naming/pyfs_initial_context_factory.html @@ -0,0 +1,174 @@ + + + + + + + apptools.naming.pyfs_initial_context_factory — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.naming.pyfs_initial_context_factory

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" The initial context factory for Python file system contexts. """
+
+
+# Local imports.
+from .context import Context
+from .initial_context_factory import InitialContextFactory
+from .object_serializer import ObjectSerializer
+from .pyfs_context import PyFSContext
+from .pyfs_context_factory import PyFSContextFactory
+from .pyfs_object_factory import PyFSObjectFactory
+from .pyfs_state_factory import PyFSStateFactory
+
+
+
[docs]class PyFSInitialContextFactory(InitialContextFactory): + """ The initial context factory for Python file system contexts. """ + + ########################################################################### + # 'InitialContextFactory' interface. + ########################################################################### + +
[docs] def get_initial_context(self, environment): + """ Creates an initial context for beginning name resolution. """ + + # Object factories. + object_factories = [PyFSObjectFactory(), PyFSContextFactory()] + environment[Context.OBJECT_FACTORIES] = object_factories + + # State factories. + state_factories = [PyFSStateFactory()] + environment[Context.STATE_FACTORIES] = state_factories + + # Object serializers. + object_serializers = [ObjectSerializer()] + environment[PyFSContext.OBJECT_SERIALIZERS] = object_serializers + + return PyFSContext(path=r"", environment=environment)
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/naming/pyfs_object_factory.html b/5.0/_modules/apptools/naming/pyfs_object_factory.html new file mode 100644 index 000000000..394ba4730 --- /dev/null +++ b/5.0/_modules/apptools/naming/pyfs_object_factory.html @@ -0,0 +1,166 @@ + + + + + + + apptools.naming.pyfs_object_factory — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.naming.pyfs_object_factory

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" Object factory for Python File System contexts. """
+
+
+# Enthought library imports.
+from apptools.io.api import File
+
+# Local imports.
+from .object_factory import ObjectFactory
+from .reference import Reference
+
+
+
[docs]class PyFSObjectFactory(ObjectFactory): + """ Object factory for Python File System contexts. """ + + ########################################################################### + # 'ObjectFactory' interface. + ########################################################################### + +
[docs] def get_object_instance(self, state, name, context): + """ Creates an object using the specified state information. """ + + obj = None + + if isinstance(state, Reference): + if state.class_name == "File" and len(state.addresses) > 0: + obj = File(state.addresses[0].content) + + return obj
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/naming/pyfs_state_factory.html b/5.0/_modules/apptools/naming/pyfs_state_factory.html new file mode 100644 index 000000000..b942c237e --- /dev/null +++ b/5.0/_modules/apptools/naming/pyfs_state_factory.html @@ -0,0 +1,172 @@ + + + + + + + apptools.naming.pyfs_state_factory — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.naming.pyfs_state_factory

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" State factory for Python File System contexts. """
+
+
+# Enthought library imports.
+from apptools.io.api import File
+
+# Local imports.
+from .address import Address
+from .reference import Reference
+from .state_factory import StateFactory
+
+
+
[docs]class PyFSStateFactory(StateFactory): + """ State factory for Python File System contexts. """ + + ########################################################################### + # 'StateFactory' interface. + ########################################################################### + +
[docs] def get_state_to_bind(self, obj, name, context): + """ Returns the state of an object for binding. """ + + state = None + + if isinstance(obj, File): + # If the file is not actually in the directory represented by the + # context then we create and bind a reference to it. + if obj.parent.path != context.path: + state = Reference( + class_name=obj.__class__.__name__, + addresses=[Address(type="file", content=obj.path)], + ) + + return state
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/naming/reference.html b/5.0/_modules/apptools/naming/reference.html new file mode 100644 index 000000000..4e9f94dde --- /dev/null +++ b/5.0/_modules/apptools/naming/reference.html @@ -0,0 +1,173 @@ + + + + + + + apptools.naming.reference — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.naming.reference

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" A reference to an object that lives outside of the naming system. """
+
+
+# Enthought library imports.
+from traits.api import HasPrivateTraits, List, Str
+
+# Local imports.
+from .address import Address
+
+
+
[docs]class Reference(HasPrivateTraits): + """A reference to an object that lives outside of the naming system. + + References provide a way to store the address(s) of objects that live + outside of the naming system. A reference consists of a list of + addresses that represent a communications endpoint for the object being + referenced. + + A reference also contains information to assist in the creation of an + instance of the object to which it refers. It contains the name of + the class that will be created and the class name and location of a + factory that will be used to do the actual instance creation. + + """ + + #### 'Reference' interface ################################################ + + # The list of addresses that can be used to 'contact' the object. + addresses = List(Address) + + # The class name of the object that this reference refers to. + class_name = Str + + # The class name of the object factory. + factory_class_name = Str
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/naming/referenceable.html b/5.0/_modules/apptools/naming/referenceable.html new file mode 100644 index 000000000..4989cb547 --- /dev/null +++ b/5.0/_modules/apptools/naming/referenceable.html @@ -0,0 +1,155 @@ + + + + + + + apptools.naming.referenceable — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.naming.referenceable

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" Base class for classes that can produce a reference to themselves. """
+
+
+# Enthought library imports.
+from traits.api import HasPrivateTraits, Instance
+
+# Local imports.
+from .reference import Reference
+
+
+
[docs]class Referenceable(HasPrivateTraits): + """ Base class for classes that can produce a reference to themselves. """ + + #### 'Referenceable' interface ############################################ + + # The object's reference suitable for binding in a naming context. + reference = Instance(Reference)
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/naming/referenceable_state_factory.html b/5.0/_modules/apptools/naming/referenceable_state_factory.html new file mode 100644 index 000000000..6735d1441 --- /dev/null +++ b/5.0/_modules/apptools/naming/referenceable_state_factory.html @@ -0,0 +1,164 @@ + + + + + + + apptools.naming.referenceable_state_factory — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.naming.referenceable_state_factory

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" State factory for referenceable objects. """
+
+
+# Local imports.
+from .referenceable import Referenceable
+from .state_factory import StateFactory
+
+
+
[docs]class ReferenceableStateFactory(StateFactory): + """ State factory for referenceable objects. """ + + ########################################################################### + # 'StateFactory' interface. + ########################################################################### + +
[docs] def get_state_to_bind(self, obj, name, context): + """ Returns the state of an object for binding. """ + + state = None + + # If the object knows how to create a reference to it then let it + # do so. + if isinstance(obj, Referenceable): + state = obj.reference + + return state
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/naming/state_factory.html b/5.0/_modules/apptools/naming/state_factory.html new file mode 100644 index 000000000..6e858ad08 --- /dev/null +++ b/5.0/_modules/apptools/naming/state_factory.html @@ -0,0 +1,166 @@ + + + + + + + apptools.naming.state_factory — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.naming.state_factory

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" The base class for all state factories. """
+
+
+# Enthought library imports.
+from traits.api import HasPrivateTraits
+
+
+
[docs]class StateFactory(HasPrivateTraits): + """The base class for all state factories. + + A state factory accepts an object and returns some data representing the + object that is suitable for storing in a particular context. + + """ + + ########################################################################### + # 'StateFactory' interface. + ########################################################################### + +
[docs] def get_state_to_bind(self, obj, name, context): + """Returns the state of an object for binding. + + Returns None if the factory cannot create the state (ie. it does not + recognise the object passed to it). + + """ + + raise NotImplementedError
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/naming/trait_defs/naming_traits.html b/5.0/_modules/apptools/naming/trait_defs/naming_traits.html new file mode 100644 index 000000000..ad3cc1734 --- /dev/null +++ b/5.0/_modules/apptools/naming/trait_defs/naming_traits.html @@ -0,0 +1,300 @@ + + + + + + + apptools.naming.trait_defs.naming_traits — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.naming.trait_defs.naming_traits

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+
+# -------------------------------------------------------------------------------
+#  Imports:
+# -------------------------------------------------------------------------------
+
+import sys
+
+from traits.api import Trait, TraitHandler, TraitFactory
+
+from traits.trait_base import class_of, get_module_name
+
+from apptools.naming.api import Binding
+
+
+# -------------------------------------------------------------------------------
+#  'NamingInstance' trait factory:
+# -------------------------------------------------------------------------------
+
+
+def NamingInstance(klass=None, value="", allow_none=False, **metadata):
+    metadata.setdefault("copy", "deep")
+    return Trait(
+        value,
+        NamingTraitHandler(
+            klass, or_none=allow_none, module=get_module_name()
+        ),
+        **metadata
+    )
+
+
+NamingInstance = TraitFactory(NamingInstance)
+
+# -------------------------------------------------------------------------------
+#  'NamingTraitHandler' class:
+# -------------------------------------------------------------------------------
+
+
+
[docs]class NamingTraitHandler(TraitHandler): + + # --------------------------------------------------------------------------- + # Initializes the object: + # --------------------------------------------------------------------------- + + def __init__(self, aClass, or_none, module): + """Initializes the object.""" + self.or_none = or_none is not False + self.module = module + self.aClass = aClass + if (aClass is not None) and ( + not isinstance(aClass, (str, type)) + ): + self.aClass = aClass.__class__ + +
[docs] def validate(self, object, name, value): + if isinstance(value, str): + if value == "": + if self.or_none: + return "" + else: + self.validate_failed(object, name, value) + try: + value = self._get_binding_for(value) + except: # noqa: E722 + self.validate_failed(object, name, value) + + if isinstance(self.aClass, str): + self.resolve_class(object, name, value) + + if isinstance(value, Binding) and ( + (self.aClass is None) or isinstance(value.obj, self.aClass) + ): + return value.namespace_name + self.validate_failed(object, name, value)
+ +
[docs] def info(self): + aClass = self.aClass + if aClass is None: + result = "path" + else: + if type(aClass) is not str: + aClass = aClass.__name__ + result = "path to an instance of " + class_of(aClass) + if self.or_none is None: + return result + " or an empty string" + return result
+ +
[docs] def validate_failed(self, object, name, value): + if not isinstance(value, type): + msg = "class %s" % value.__class__.__name__ + else: + msg = "%s (i.e. %s)" % (str(type(value))[1:-1], repr(value)) + self.error(object, name, msg)
+ +
[docs] def get_editor(self, trait): + if self.editor is None: + from traitsui.api import DropEditor + + self.editor = DropEditor( + klass=self.aClass, binding=True, readonly=False + ) + return self.editor
+ +
[docs] def post_setattr(self, object, name, value): + other = None + if value != "": + other = self._get_binding_for(value).obj + object.__dict__[name + "_"] = other
+ + def _get_binding_for(self, value): + + result = None + + # FIXME: The following code makes this whole component have a + # dependency on envisage, and worse, assumes the use of a particular + # project plugin! This is horrible and should be refactored out, + # possibly to a custom sub-class of whoever needs this behavior. + try: + from envisage import get_application + + workspace = get_application().service_registry.get_service( + "envisage.project.IWorkspace" + ) + result = workspace.lookup_binding(value) + except ImportError: + pass + + return result + +
[docs] def resolve_class(self, object, name, value): + aClass = self.find_class() + if aClass is None: + self.validate_failed(object, name, value) + self.aClass = aClass + + # fixme: The following is quite ugly, because it wants to try and fix + # the trait referencing this handler to use the 'fast path' now that + # the actual class has been resolved. The problem is finding the trait, + # especially in the case of List(Instance('foo')), where the + # object.base_trait(...) value is the List trait, not the Instance + # trait, so we need to check for this and pull out the List + # 'item_trait'. Obviously this does not extend well to other traits + # containing nested trait references (Dict?)... + trait = object.base_trait(name) + handler = trait.handler + if (handler is not self) and hasattr(handler, "item_trait"): + trait = handler.item_trait + trait.validate(self.fast_validate)
+ +
[docs] def find_class(self): + module = self.module + aClass = self.aClass + col = aClass.rfind(".") + if col >= 0: + module = aClass[:col] + aClass = aClass[col + 1:] + theClass = getattr(sys.modules.get(module), aClass, None) + if (theClass is None) and (col >= 0): + try: + theClass = getattr(__import__(module), aClass, None) + except Exception: + pass + return theClass
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/naming/unique_name.html b/5.0/_modules/apptools/naming/unique_name.html new file mode 100644 index 000000000..e33f1657a --- /dev/null +++ b/5.0/_modules/apptools/naming/unique_name.html @@ -0,0 +1,164 @@ + + + + + + + apptools.naming.unique_name — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.naming.unique_name

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+
+
+"""
+A re-usable method for calculating a unique name given a list of existing
+names.
+
+"""
+
+
+
[docs]def make_unique_name(base, existing=[], format="%s_%s"): + """ + Return a name, unique within a context, based on the specified name. + + base: the desired base name of the generated unique name. + existing: a sequence of the existing names to avoid returning. + format: a formatting specification for how the name is made unique. + + """ + + count = 2 + name = base + while name in existing: + name = format % (base, count) + count += 1 + + return name
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/persistence/file_path.html b/5.0/_modules/apptools/persistence/file_path.html new file mode 100644 index 000000000..2391cf819 --- /dev/null +++ b/5.0/_modules/apptools/persistence/file_path.html @@ -0,0 +1,218 @@ + + + + + + + apptools.persistence.file_path — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.persistence.file_path

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+"""Simple class to support file path objects that work well in the
+context of persistent storage with the state_pickler.
+
+"""
+
+# Standard library imports.
+import os
+from os.path import abspath, normpath, dirname, join
+
+
+
[docs]class FilePath(object): + """This class stores two paths to the file. A relative path and + an absolute one. The absolute path is used by the end user. When + this object is pickled the state_pickler sets the relative path + relative to the file that is being generated. When unpickled, the + stored relative path is used to set the absolute path correctly + based on the path of the saved file. + """ + + def __init__(self, value=""): + self.set(value) + + def __str__(self): + return self.abs_pth + + def __repr__(self): + return self.abs_pth.__repr__() + +
[docs] def get(self): + """Get the path.""" + return self.abs_pth
+ +
[docs] def set(self, value): + """Sets the value of the path.""" + self.rel_pth = value + if value: + self.abs_pth = normpath(abspath(value)) + else: + self.abs_pth = ""
+ +
[docs] def set_relative(self, base_f_name): + """Sets the path relative to `base_f_name`. Note that + `base_f_name` and self.rel_pth should be valid file names + correct on the current os. The set name is a file name that + has a POSIX path. + """ + + # Get normalized paths. + _src = abspath(base_f_name) + _dst = self.abs_pth + + # Now strip out any common prefix between the two paths. + for part in _src.split(os.sep): + if _dst.startswith(part + os.sep): + length = len(part) + 1 + _src = _src[length:] + _dst = _dst[length:] + else: + break + + # For each directory in the source, we need to add a reference to + # the parent directory to the destination. + ret = (_src.count(os.sep) * (".." + os.sep)) + _dst + + # Make it posix style. + if os.sep != "/": + ret.replace(os.sep, "/") + + # Store it. + self.rel_pth = ret
+ +
[docs] def set_absolute(self, base_f_name): + """Sets the absolute file name for the current relative file + name with respect to the given `base_f_name`. + """ + base_f_name = normpath(abspath(base_f_name)) + rel_file_name = normpath(self.rel_pth) + file_name = join(dirname(base_f_name), rel_file_name) + file_name = os.path.normpath(file_name) + self.abs_pth = file_name
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/persistence/project_loader.html b/5.0/_modules/apptools/persistence/project_loader.html new file mode 100644 index 000000000..75559d5eb --- /dev/null +++ b/5.0/_modules/apptools/persistence/project_loader.html @@ -0,0 +1,262 @@ + + + + + + + apptools.persistence.project_loader — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.persistence.project_loader

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+# Standard library imports
+import sys
+import pickle
+import logging
+
+# Enthought library imports
+from apptools.persistence.versioned_unpickler import VersionedUnpickler
+
+
+logger = logging.getLogger(__name__)
+
+
+
[docs]def load_project( + pickle_filename, updater_path, application_version, protocol, max_pass=-1 +): + """Reads a project from a pickle file and if necessary will update it to + the latest version of the application. + """ + + latest_file = pickle_filename + + # Read the pickled project's metadata. + f = open(latest_file, "rb") + metadata = VersionedUnpickler(f).load(max_pass) + f.close() + project_version = metadata.get("version", False) + + if not project_version: + raise ValueError("Could not read version number from the project file") + + logger.debug( + "Project version: %d, Application version: %d" + % (project_version, application_version) + ) + + # here you can temporarily force an upgrade each time for testing .... + # project_version = 0 + latest_file = upgrade_project( + pickle_filename, + updater_path, + project_version, + application_version, + protocol, + max_pass, + ) + + # Finally we can import the project ... + logger.info("loading %s" % latest_file) + i_f = open(latest_file, "rb") + project = VersionedUnpickler(i_f).load(max_pass) + i_f.close() + + return project
+ + +
[docs]def upgrade_project( + pickle_filename, + updater_path, + project_version, + application_version, + protocol, + max_pass=-1, +): + """Repeatedly read and write the project to disk updating it one version + at a time. + + Example the p5.project is at version 0 + The application is at version 3 + + p5.project --- Update1 ---> p5.project.v1 + p5.project.v1 --- Update2 ---> p5.project.v2 + p5.project.v2 --- Update3 ---> p5.project.v3 + p5.project.v3 ---> loaded into app + + The user then has the option to save the updated project as p5.project + """ + first_time = True + latest_file = pickle_filename + + # update the project until it's version matches the application's + while project_version < application_version: + + next_version = project_version + 1 + + if first_time: + i_f = open(pickle_filename, "rb") + data = i_f.read() + open("%s.bak" % pickle_filename, "wb").write(data) + i_f.seek(0) # rewind the file to the start + else: + name = "%s.v%d" % (pickle_filename, project_version) + i_f = open(name, "rb") + latest_file = name + + logger.info("converting %s" % latest_file) + + # find this version's updater ... + updater_name = "%s.update%d" % (updater_path, next_version) + __import__(updater_name) + mod = sys.modules[updater_name] + klass = getattr(mod, "Update%d" % next_version) + updater = klass() + + # load and update this version of the project + project = VersionedUnpickler(i_f, updater).load(max_pass) + i_f.close() + + # set the project version to be the same as the updater we just + # ran on the unpickled files ... + project.metadata["version"] = next_version + + # Persist the updated project ... + name = "%s.v%d" % (pickle_filename, next_version) + latest_file = name + o_f = open(name, "wb") + pickle.dump(project.metadata, o_f, protocol=protocol) + pickle.dump(project, o_f, protocol=protocol) + o_f.close() + + # Bump up the version number of the pickled project... + project_version += 1 + first_time = False + + return latest_file
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/persistence/state_pickler.html b/5.0/_modules/apptools/persistence/state_pickler.html new file mode 100644 index 000000000..21a0effe1 --- /dev/null +++ b/5.0/_modules/apptools/persistence/state_pickler.html @@ -0,0 +1,1168 @@ + + + + + + + apptools.persistence.state_pickler — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.persistence.state_pickler

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+"""This module provides code that allows one to pickle the state of a
+Python object to a dictionary.
+
+The motivation for this is simple.  The standard Python
+pickler/unpickler is best used to pickle simple objects and does not
+work too well for complex code.  Specifically, there are two major
+problems (1) the pickle file format is not easy to edit with a text
+editor and (2) when a pickle is unpickled, it creates all the
+necessary objects and sets the state of these objects.
+
+Issue (2) might not appear to be a problem.  However, often, the
+determination of the entire 'state' of an application requires the
+knowledge of the state of many objects that are not really in the
+users concern.  The user would ideally like to pickle just what he
+thinks is relevant.  Now, given that the user is not going to save the
+entire state of the application, the use of pickle is insufficient
+since the state is no longer completely known (or worth knowing).  The
+default `Unpickler` recreates the objects and the typical
+implementation of `__setstate__` is usually to simply update the
+object's `__dict__` attribute.  This is inadequate because the pickled
+information is taken out of the real context when it was saved.
+
+The `StatePickler` basically pickles the 'state' of an object into a
+large dictionary.  This pickled data may be easily unpickled and
+modified on the interpreter or edited with a text editor
+(`pprint.saferepr` is a friend).  The second problem is also
+eliminated.  When this state is unpickled using `StateUnpickler`, what
+you get is a special dictionary (a `State` instance).  This allows one
+to navigate the state just like the original object.  Its up to the
+user to create any new objects and set their states using this
+information.  This allows for a lot of flexibility while allowing one
+to save and set the state of (almost) any Python object.
+
+The `StateSetter` class helps set the state of a known instance.  When
+setting the state of an instance it checks to see if there is a
+`__set_pure_state__` method that in turn calls `StateSetter.set`
+appropriately.
+
+Additionally, there is support for versioning.  The class' version is
+obtain from the `__version__` class attribute.  This version along
+with the versions of the bases of a class is embedded into the
+metadata of the state and stored.  By using `version_registry.py` a
+user may register a handler for a particular class and module.  When
+the state of an object is set using `StateSetter.set_state`, then
+these handlers are called in reverse order of their MRO.  This gives
+the handler an opportunity to upgrade the state depending on its
+version.  Builtin classes are not scanned for versions.  If a class
+has no version, then by default it is assumed to be -1.
+
+
+Example::
+
+  >>> class A:
+  ...    def __init__(self):
+  ...        self.a = 'a'
+  ...
+  >>> a = A()
+  >>> a.a = 100
+  >>> import state_pickler
+  >>> s = state_pickler.dumps(a)               # Dump the state of `a`.
+  >>> state = state_pickler.loads_state(s)     # Get the state back.
+  >>> b = state_pickler.create_instance(state) # Create the object.
+  >>> state_pickler.set_state(b, state)        # Set the object's state.
+  >>> assert b.a == 100
+
+
+Features
+--------
+
+ - The output is a plain old dictionary so is easy to parse, edit etc.
+ - Handles references to avoid duplication.
+ - Gzips Numeric arrays when dumping them.
+ - Support for versioning.
+
+
+Caveats
+-------
+
+ - Does not pickle a whole bunch of stuff including code objects and
+   functions.
+ - The output is a pure dictionary and does not contain instances.  So
+   using this *as it is* in `__setstate__` will not work.  Instead
+   define a `__set_pure_state__` and use the `StateSetter` class or
+   the `set_state` function provided by this module.
+
+
+Notes
+-----
+
+  Browsing the code from XMarshaL_ and pickle.py proved useful for
+  ideas.  None of the code is taken from there though.
+
+.. _XMarshaL:  http://www.dezentral.de/soft/XMarshaL
+
+"""
+# Author: Prabhu Ramachandran <prabhu_r@users.sf.net>
+# Copyright (c) 2005-2015, Enthought, Inc.
+# License: BSD Style.
+
+# Standard library imports.
+import base64
+import sys
+import pickle
+import gzip
+from io import BytesIO, StringIO
+
+import numpy
+
+# Local imports.
+from . import version_registry
+from .file_path import FilePath
+
+NumpyArrayType = type(numpy.array([]))
+
+
+
[docs]def gzip_string(data): + """Given a string (`data`) this gzips the string and returns it.""" + s = BytesIO() + writer = gzip.GzipFile(mode="wb", fileobj=s) + writer.write(data) + writer.close() + s.seek(0) + return s.read()
+ + +
[docs]def gunzip_string(data): + """Given a gzipped string (`data`) this unzips the string and + returns it. + """ + if type(data) is bytes: + s = BytesIO(data) + else: + s = StringIO(data) + writer = gzip.GzipFile(mode="rb", fileobj=s) + data = writer.read() + writer.close() + return data
+ + +
[docs]class StatePicklerError(Exception): + pass
+ + +
[docs]class StateUnpicklerError(Exception): + pass
+ + +
[docs]class StateSetterError(Exception): + pass
+ + +###################################################################### +# `State` class +###################################################################### +
[docs]class State(dict): + """Used to encapsulate the state of an instance in a very + convenient form. The '__metadata__' attribute/key is a dictionary + that has class specific details like the class name, module name + etc. + """ + + def __init__(self, **kw): + dict.__init__(self, **kw) + self.__dict__ = self
+ + +###################################################################### +# `StateDict` class +###################################################################### +
[docs]class StateDict(dict): + """Used to encapsulate a dictionary stored in a `State` instance. + The has_instance attribute specifies if the dict has an instance + embedded in it. + """ + + def __init__(self, **kw): + dict.__init__(self, **kw) + self.has_instance = False
+ + +###################################################################### +# `StateList` class +###################################################################### +
[docs]class StateList(list): + """Used to encapsulate a list stored in a `State` instance. The + has_instance attribute specifies if the list has an instance + embedded in it. + """ + + def __init__(self, seq=None): + if seq: + list.__init__(self, seq) + else: + list.__init__(self) + self.has_instance = False
+ + +###################################################################### +# `StateTuple` class +###################################################################### +
[docs]class StateTuple(tuple): + """Used to encapsulate a tuple stored in a `State` instance. The + has_instance attribute specifies if the tuple has an instance + embedded in it. + """ + + def __new__(cls, seq=None): + if seq: + obj = super(StateTuple, cls).__new__(cls, tuple(seq)) + else: + obj = super(StateTuple, cls).__new__(cls) + obj.has_instance = False + return obj
+ + +###################################################################### +# `StatePickler` class +###################################################################### +
[docs]class StatePickler: + """Pickles the state of an object into a dictionary. The + dictionary is itself either saved as a pickled file (`dump`) or + pickled string (`dumps`). Alternatively, the `dump_state` method + will return the dictionary that is pickled. + + The format of the state dict is quite strightfoward. Basic types + (bool, int, long, float, complex, None, string) are + represented as they are. Everything else is stored as a + dictionary containing metadata information on the object's type + etc. and also the actual object in the 'data' key. For example:: + + >>> p = StatePickler() + >>> p.dump_state(1) + 1 + >>> l = [1,2.0, None, [1,2,3]] + >>> p.dump_state(l) + {'data': [1, 2.0, None, {'data': [1, 2, 3], 'type': 'list', 'id': 1}], + 'id': 0, + 'type': 'list'} + + Classes are also represented similarly. The state in this case is + obtained from the `__getstate__` method or from the `__dict__`. + Here is an example:: + + >>> class A: + ... __version__ = 1 # State version + ... def __init__(self): + ... self.attribute = 1 + ... + >>> a = A() + >>> p = StatePickler() + >>> p.dump_state(a) + {'class_name': 'A', + 'data': {'data': {'attribute': 1}, 'type': 'dict', 'id': 2}, + 'id': 0, + 'initargs': {'data': (), 'type': 'tuple', 'id': 1}, + 'module': '__main__', + 'type': 'instance', + 'version': [(('A', '__main__'), 1)]} + + When pickling data, references are taken care of. Numeric arrays + can be pickled and are stored as a gzipped base64 encoded string. + + """ + + def __init__(self): + self._clear() + type_map = { + bool: self._do_basic_type, + complex: self._do_basic_type, + float: self._do_basic_type, + int: self._do_basic_type, + type(None): self._do_basic_type, + str: self._do_basic_type, + bytes: self._do_basic_type, + tuple: self._do_tuple, + list: self._do_list, + dict: self._do_dict, + NumpyArrayType: self._do_numeric, + State: self._do_state, + } + self.type_map = type_map + +
[docs] def dump(self, value, file): + """Pickles the state of the object (`value`) into the passed + file. + """ + try: + # Store the file name we are writing to so we can munge + # file paths suitably. + self.file_name = file.name + except AttributeError: + pass + pickle.dump(self._do(value), file)
+ +
[docs] def dumps(self, value): + """Pickles the state of the object (`value`) and returns a + string. + """ + return pickle.dumps(self._do(value))
+ +
[docs] def dump_state(self, value): + """Returns a dictionary or a basic type representing the + complete state of the object (`value`). + + This value is pickled by the `dump` and `dumps` methods. + """ + return self._do(value)
+ + ###################################################################### + # Non-public methods + ###################################################################### + def _clear(self): + # Stores the file name of the file being used to dump the + # state. This is used to change any embedded paths relative + # to the saved file. + self.file_name = "" + # Caches id's to handle references. + self.obj_cache = {} + # Misc cache to cache things that are not persistent. For + # example, object.__getstate__()/__getinitargs__() usually + # returns a copy of a dict/tuple that could possibly be reused + # on another object's __getstate__. Caching these prevents + # some wierd problems with the `id` of the object. + self._misc_cache = [] + + def _flush_traits(self, obj): + """Checks if the object has traits and ensures that the traits + are set in the `__dict__` so we can pickle it. + """ + # Not needed with Traits3. + + def _do(self, obj): + obj_type = type(obj) + key = self._get_id(obj) + if key in self.obj_cache: + return self._do_reference(obj) + elif obj_type in self.type_map: + return self.type_map[obj_type](obj) + elif isinstance(obj, tuple): + # Takes care of StateTuples. + return self._do_tuple(obj) + elif isinstance(obj, list): + # Takes care of TraitListObjects. + return self._do_list(obj) + elif isinstance(obj, dict): + # Takes care of TraitDictObjects. + return self._do_dict(obj) + elif hasattr(obj, "__dict__"): + return self._do_instance(obj) + + def _get_id(self, value): + try: + key = hash(value) + except TypeError: + key = id(value) + return key + + def _register(self, value): + key = self._get_id(value) + cache = self.obj_cache + idx = len(cache) + cache[key] = idx + return idx + + def _do_basic_type(self, value): + return value + + def _do_reference(self, value): + key = self._get_id(value) + idx = self.obj_cache[key] + return dict(type="reference", id=idx, data=None) + + def _do_instance(self, value): + # Flush out the traits. + self._flush_traits(value) + + # Setup the relative paths of FilePaths before dumping. + if self.file_name and isinstance(value, FilePath): + value.set_relative(self.file_name) + + # Get the initargs. + args = () + if hasattr(value, "__getinitargs__") and value.__getinitargs__: + args = value.__getinitargs__() + + # Get the object state. + if hasattr(value, "__get_pure_state__"): + state = value.__get_pure_state__() + elif hasattr(value, "__getstate__"): + state = value.__getstate__() + else: + state = value.__dict__ + + state.pop("__traits_version__", None) + + # Cache the args and state since they are likely to be gc'd. + self._misc_cache.extend([args, state]) + # Register and process. + idx = self._register(value) + args_data = self._do(args) + data = self._do(state) + + # Get the version of the object. + version = version_registry.get_version(value) + module = value.__class__.__module__ + class_name = value.__class__.__name__ + + return dict( + type="instance", + module=module, + class_name=class_name, + version=version, + id=idx, + initargs=args_data, + data=data, + ) + + def _do_state(self, value): + metadata = value.__metadata__ + args = metadata.get("initargs") + state = dict(value) + state.pop("__metadata__") + + self._misc_cache.extend([args, state]) + + idx = self._register(value) + args_data = self._do(args) + data = self._do(state) + + return dict( + type="instance", + module=metadata["module"], + class_name=metadata["class_name"], + version=metadata["version"], + id=idx, + initargs=args_data, + data=data, + ) + + def _do_tuple(self, value): + idx = self._register(value) + data = tuple([self._do(x) for x in value]) + return dict(type="tuple", id=idx, data=data) + + def _do_list(self, value): + idx = self._register(value) + data = [self._do(x) for x in value] + return dict(type="list", id=idx, data=data) + + def _do_dict(self, value): + idx = self._register(value) + vals = [self._do(x) for x in value.values()] + data = dict(zip(value.keys(), vals)) + return dict(type="dict", id=idx, data=data) + + def _do_numeric(self, value): + idx = self._register(value) + data = base64.encodebytes(gzip_string(numpy.ndarray.dumps(value))) + return dict(type="numeric", id=idx, data=data)
+ + +###################################################################### +# `StateUnpickler` class +###################################################################### +
[docs]class StateUnpickler: + """Unpickles the state of an object saved using StatePickler. + + Please note that unlike the standard Unpickler, no instances of + any user class are created. The data for the state is obtained + from the file or string, reference objects are setup to refer to + the same state value and this state is returned in the form + usually in the form of a dictionary. For example:: + + >>> class A: + ... def __init__(self): + ... self.attribute = 1 + ... + >>> a = A() + >>> p = StatePickler() + >>> s = p.dumps(a) + >>> up = StateUnpickler() + >>> state = up.loads_state(s) + >>> state.__class__.__name__ + 'State' + >>> state.attribute + 1 + >>> state.__metadata__ + {'class_name': 'A', + 'has_instance': True, + 'id': 0, + 'initargs': (), + 'module': '__main__', + 'type': 'instance', + 'version': [(('A', '__main__'), -1)]} + + Note that the state is actually a `State` instance and is + navigable just like the original object. The details of the + instance are stored in the `__metadata__` attribute. This is + highly convenient since it is possible for someone to view and + modify the state very easily. + """ + + def __init__(self): + self._clear() + self.type_map = { + "reference": self._do_reference, + "instance": self._do_instance, + "tuple": self._do_tuple, + "list": self._do_list, + "dict": self._do_dict, + "numeric": self._do_numeric, + } + +
[docs] def load_state(self, file): + """Returns the state of an object loaded from the pickled data + in the given file. + """ + try: + self.file_name = file.name + except AttributeError: + pass + data = pickle.load(file) + result = self._process(data) + return result
+ +
[docs] def loads_state(self, string): + """Returns the state of an object loaded from the pickled data + in the given string. + """ + data = pickle.loads(string) + result = self._process(data) + return result
+ + ###################################################################### + # Non-public methods + ###################################################################### + def _clear(self): + # The file from which we are being loaded. + self.file_name = "" + # Cache of the objects. + self._obj_cache = {} + # Paths to the instances. + self._instances = [] + # Caches the references. + self._refs = {} + # Numeric arrays. + self._numeric = {} + + def _set_has_instance(self, obj, value): + if isinstance(obj, State): + obj.__metadata__["has_instance"] = value + elif isinstance(obj, (StateDict, StateList, StateTuple)): + obj.has_instance = value + + def _process(self, data): + result = self._do(data) + + # Setup all the Numeric arrays. Do this first since + # references use this. + for key, (path, val) in self._numeric.items(): + if isinstance(result, StateTuple): + result = list(result) + exec("result%s = val" % path) + result = StateTuple(result) + else: + exec("result%s = val" % path) + + # Setup the references so they really are references. + for key, paths in self._refs.items(): + for path in paths: + x = self._obj_cache[key] + if isinstance(result, StateTuple): + result = list(result) + exec("result%s = x" % path) + result = StateTuple(result) + else: + exec("result%s = x" % path) + # if the reference is to an instance append its path. + if isinstance(x, State): + self._instances.append(path) + + # Now setup the 'has_instance' attribute. If 'has_instance' + # is True then the object contains an instance somewhere + # inside it. + for path in self._instances: + pth = path + while pth: + ns = {"result": result} + exec("val = result%s" % pth, ns, ns) + self._set_has_instance(ns["val"], True) + end = pth.rfind("[") + pth = pth[:end] + # Now make sure that the first element also has_instance. + self._set_has_instance(result, True) + return result + + def _do(self, data, path=""): + if type(data) is dict: + return self.type_map[data["type"]](data, path) + else: + return data + + def _do_reference(self, value, path): + id = value["id"] + if id in self._refs: + self._refs[id].append(path) + else: + self._refs[id] = [path] + return State(__metadata__=value) + + def _handle_file_path(self, value): + if ( + (value["class_name"] == "FilePath") + and ("file_path" in value["module"]) + and self.file_name + ): + data = value["data"]["data"] + fp = FilePath(data["rel_pth"]) + fp.set_absolute(self.file_name) + data["abs_pth"] = fp.abs_pth + + def _do_instance(self, value, path): + self._instances.append(path) + initargs = self._do( + value["initargs"], path + '.__metadata__["initargs"]' + ) + # Handle FilePaths. + self._handle_file_path(value) + + d = self._do(value["data"], path) + md = dict( + type="instance", + module=value["module"], + class_name=value["class_name"], + version=value["version"], + id=value["id"], + initargs=initargs, + has_instance=True, + ) + result = State(**d) + result.__metadata__ = md + self._obj_cache[value["id"]] = result + return result + + def _do_tuple(self, value, path): + res = [] + for i, x in enumerate(value["data"]): + res.append(self._do(x, path + "[%d]" % i)) + result = StateTuple(res) + self._obj_cache[value["id"]] = result + return result + + def _do_list(self, value, path): + result = StateList() + for i, x in enumerate(value["data"]): + result.append(self._do(x, path + "[%d]" % i)) + self._obj_cache[value["id"]] = result + return result + + def _do_dict(self, value, path): + result = StateDict() + for key, val in value["data"].items(): + result[key] = self._do(val, path + '["%s"]' % key) + self._obj_cache[value["id"]] = result + return result + + def _do_numeric(self, value, path): + data = value["data"] + if isinstance(data, str): + data = value["data"].encode("utf-8") + junk = gunzip_string(base64.decodebytes(data)) + result = pickle.loads(junk, encoding="bytes") + self._numeric[value["id"]] = (path, result) + self._obj_cache[value["id"]] = result + return result
+ + +###################################################################### +# `StateSetter` class +###################################################################### +
[docs]class StateSetter: + """This is a convenience class that helps a user set the + attributes of an object given its saved state. For instances it + checks to see if a `__set_pure_state__` method exists and calls + that when it sets the state. + """ + + def __init__(self): + # Stores the ids of instances already done. + self._instance_ids = [] + self.type_map = { + State: self._do_instance, + StateTuple: self._do_tuple, + StateList: self._do_list, + StateDict: self._do_dict, + } + +
[docs] def set(self, obj, state, ignore=None, first=None, last=None): + """Sets the state of the object. + + This is to be used as a means to simplify loading the state of + an object from its `__setstate__` method using the dictionary + describing its state. Note that before the state is set, the + registered handlers for the particular class are called in + order to upgrade the version of the state to the latest + version. + + Parameters + ---------- + + - obj : `object` + + The object whose state is to be set. If this is `None` + (default) then the object is created. + + - state : `dict` + + The dictionary representing the state of the object. + + - ignore : `list(str)` + + The list of attributes specified in this list are ignored + and the state of these attributes are not set (this excludes + the ones specified in `first` and `last`). If one specifies + a '*' then all attributes are ignored except the ones + specified in `first` and `last`. + + - first : `list(str)` + + The list of attributes specified in this list are set first (in + order), before any other attributes are set. + + - last : `list(str)` + + The list of attributes specified in this list are set last (in + order), after all other attributes are set. + + """ + if (not isinstance(state, State)) and state.__metadata__[ + "type" + ] != "instance": + raise StateSetterError( + "Can only set the attributes of an instance." + ) + + # Upgrade the state to the latest using the registry. + self._update_and_check_state(obj, state) + + self._register(obj) + + # This wierdness is needed since the state's own `keys` might + # be set to something else. + state_keys = list(dict.keys(state)) + state_keys.remove("__metadata__") + + if first is None: + first = [] + if last is None: + last = [] + + # Remove all the ignored keys. + if ignore: + if "*" in ignore: + state_keys = first + last + else: + for name in ignore: + try: + state_keys.remove(name) + except KeyError: + pass + + # Do the `first` attributes. + for key in first: + state_keys.remove(key) + self._do(obj, key, state[key]) + + # Remove the `last` attributes. + for key in last: + state_keys.remove(key) + + # Set the remaining attributes. + for key in state_keys: + self._do(obj, key, state[key]) + + # Do the last ones in order. + for key in last: + self._do(obj, key, state[key])
+ + ###################################################################### + # Non-public methods. + ###################################################################### + def _register(self, obj): + idx = id(obj) + if idx not in self._instance_ids: + self._instance_ids.append(idx) + + def _is_registered(self, obj): + return id(obj) in self._instance_ids + + def _has_instance(self, value): + """Given something (`value`) that is part of the state this + returns if the value has an instance embedded in it or not. + """ + if isinstance(value, State): + return True + elif isinstance(value, (StateDict, StateList, StateTuple)): + return value.has_instance + return False + + def _get_pure(self, value): + """Returns the Python representation of the object (usually a + list, tuple or dict) that has no instances embedded within it. + """ + result = value + if self._has_instance(value): + raise StateSetterError("Value has an instance: %s" % value) + if isinstance(value, (StateList, StateTuple)): + result = [self._get_pure(x) for x in value] + if isinstance(value, StateTuple): + result = tuple(result) + elif isinstance(value, StateDict): + result = {} + for k, v in value.items(): + result[k] = self._get_pure(v) + return result + + def _update_and_check_state(self, obj, state): + """Updates the state from the registry and then checks if the + object and state have same class. + """ + # Upgrade this state object to the latest using the registry. + # This is done before testing because updating may change the + # class name/module. + version_registry.registry.update(state) + + # Make sure object and state have the same class and module names. + metadata = state.__metadata__ + cls = obj.__class__ + if metadata["class_name"] != cls.__name__: + raise StateSetterError( + "Instance (%s) and state (%s) do not have the same class" + " name!" % (cls.__name__, metadata["class_name"]) + ) + if metadata["module"] != cls.__module__: + raise StateSetterError( + "Instance (%s) and state (%s) do not have the same module" + " name!" % (cls.__module__, metadata["module"]) + ) + + def _do(self, obj, key, value): + try: + getattr(obj, key) + except AttributeError: + raise StateSetterError( + "Object %s does not have an attribute called: %s" % (obj, key) + ) + + if isinstance(value, (State, StateDict, StateList, StateTuple)): + # Special handlers are needed. + if not self._has_instance(value): + result = self._get_pure(value) + setattr(obj, key, result) + elif isinstance(value, StateTuple): + setattr(obj, key, self._do_tuple(getattr(obj, key), value)) + else: + self._do_object(getattr(obj, key), value) + else: + setattr(obj, key, value) + + def _do_object(self, obj, state): + self.type_map[state.__class__](obj, state) + + def _do_instance(self, obj, state): + if self._is_registered(obj): + return + else: + self._register(obj) + + metadata = state.__metadata__ + if hasattr(obj, "__set_pure_state__"): + self._update_and_check_state(obj, state) + obj.__set_pure_state__(state) + elif "tvtk_classes" in metadata["module"]: + self._update_and_check_state(obj, state) + tmp = self._get_pure(StateDict(**state)) + del tmp["__metadata__"] + obj.__setstate__(tmp) + else: + # No need to update or check since `set` does it for us. + self.set(obj, state) + + def _do_tuple(self, obj, state): + if not self._has_instance(state): + return self._get_pure(state) + else: + result = list(obj) + self._do_list(result, state) + return tuple(result) + + def _do_list(self, obj, state): + if len(obj) == len(state): + for i in range(len(obj)): + if not self._has_instance(state[i]): + obj[i] = self._get_pure(state[i]) + elif isinstance(state[i], tuple): + obj[i] = self._do_tuple(state[i]) + else: + self._do_object(obj[i], state[i]) + else: + raise StateSetterError( + "Cannot set state of list of incorrect size." + ) + + def _do_dict(self, obj, state): + for key, value in state.items(): + if not self._has_instance(value): + obj[key] = self._get_pure(value) + elif isinstance(value, tuple): + obj[key] = self._do_tuple(value) + else: + self._do_object(obj[key], value)
+ + +###################################################################### +# Internal Utility functions. +###################################################################### +def _get_file_read(f): + if hasattr(f, "read"): + return f + else: + return open(f, "rb") + + +def _get_file_write(f): + if hasattr(f, "write"): + return f + else: + return open(f, "wb") + + +###################################################################### +# Utility functions. +###################################################################### +
[docs]def dump(value, file): + """Pickles the state of the object (`value`) into the passed file + (or file name). + """ + f = _get_file_write(file) + try: + StatePickler().dump(value, f) + finally: + f.flush() + if f is not file: + f.close()
+ + +
[docs]def dumps(value): + """Pickles the state of the object (`value`) and returns a string.""" + return StatePickler().dumps(value)
+ + +
[docs]def load_state(file): + """Returns the state of an object loaded from the pickled data in + the given file (or file name). + """ + f = _get_file_read(file) + try: + state = StateUnpickler().load_state(f) + finally: + if f is not file: + f.close() + return state
+ + +
[docs]def loads_state(string): + """Returns the state of an object loaded from the pickled data + in the given string. + """ + return StateUnpickler().loads_state(string)
+ + +
[docs]def get_state(obj): + """Returns the state of the object (usually as a dictionary). The + returned state may be used directy to set the state of the object + via `set_state`. + """ + s = dumps(obj) + return loads_state(s)
+ + +
[docs]def set_state(obj, state, ignore=None, first=None, last=None): + StateSetter().set(obj, state, ignore, first, last)
+ + +set_state.__doc__ = StateSetter.set.__doc__ + + +
[docs]def update_state(state): + """Given the state of an object, this updates the state to the + latest version using the handlers given in the version registry. + The state is modified in-place. + """ + version_registry.registry.update(state)
+ + +
[docs]def create_instance(state): + """Create an instance from the state if possible.""" + if (not isinstance(state, State)) and ( + "class_name" not in state.__metadata__ + ): + raise StateSetterError("No class information in state") + metadata = state.__metadata__ + class_name = metadata.get("class_name") + mod_name = metadata.get("module") + if "tvtk_classes" in mod_name: + # FIXME: This sort of special-case is probably indicative of something + # that needs more thought, plus it makes it tought to decide whether + # this component depends on tvtk! + from tvtk.api import tvtk + + return getattr(tvtk, class_name)() + + initargs = metadata["initargs"] + if initargs.has_instance: + raise StateUnpicklerError("Cannot unpickle non-trivial initargs") + + __import__(mod_name, globals(), locals(), class_name) + mod = sys.modules[mod_name] + cls = getattr(mod, class_name) + return cls(*initargs)
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/persistence/updater.html b/5.0/_modules/apptools/persistence/updater.html new file mode 100644 index 000000000..8d32271d2 --- /dev/null +++ b/5.0/_modules/apptools/persistence/updater.html @@ -0,0 +1,174 @@ + + + + + + + apptools.persistence.updater — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.persistence.updater

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+
+
+def __replacement_setstate__(self, state):
+    """"""
+    state = self.__updater__(state)
+    self.__dict__.update(state)
+
+
+
[docs]class Updater: + + """An abstract class to provide functionality common to the updaters.""" + +
[docs] def get_latest(self, module, name): + """The refactorings dictionary contains mappings between old and new + module names. Since we only bump the version number one increment + there is only one possible answer. + """ + if hasattr(self, "refactorings"): + module = self.strip(module) + name = self.strip(name) + # returns the new module and name if it exists otherwise defaults + # to using the original module and name + module, name = self.refactorings.get( + (module, name), (module, name) + ) + + return module, name
+ +
[docs] def strip(self, string): + # Who would have thought that pickle would pass us + # names with \013 on the end? Is this after the files have + # manually edited? + if ord(string[-1:]) == 13: + return string[:-1] + + return string
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/persistence/version_registry.html b/5.0/_modules/apptools/persistence/version_registry.html new file mode 100644 index 000000000..040c4e8eb --- /dev/null +++ b/5.0/_modules/apptools/persistence/version_registry.html @@ -0,0 +1,239 @@ + + + + + + + apptools.persistence.version_registry — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.persistence.version_registry

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+"""A version registry that manages handlers for different state
+versions.
+"""
+
+
+# Standard library imports.
+import sys
+import inspect
+import logging
+
+
+logger = logging.getLogger(__name__)
+
+
+######################################################################
+# Utility functions.
+######################################################################
+
[docs]def get_version(obj): + """Walks the class hierarchy and obtains the versions of the + various classes and returns a list of tuples of the form + ((class_name, module), version) in reverse order of the MRO. + """ + res = [] + for cls in inspect.getmro(obj.__class__): + class_name, module = cls.__name__, cls.__module__ + if module in ["__builtin__"]: + # No point in versioning builtins. + continue + try: + version = cls.__version__ + except AttributeError: + version = -1 + res.append(((class_name, module), version)) + res.reverse() + return res
+ + +###################################################################### +# `HandlerRegistry` class. +###################################################################### +
[docs]class HandlerRegistry: + """A simple version conversion handler registry. Classes register + handlers in order to convert the state version to the latest + version. When an object's state is about to be set, the `update` + method of the registy is called. This in turn calls any handlers + registered for the class/module and this handler is then called + with the state and the version of the state. The state is + modified in-place by the handlers. + """ + + def __init__(self): + # The version conversion handlers. + # Key: (class_name, module), value: handler + self.handlers = {} + +
[docs] def register(self, class_name, module, handler): + """Register `handler` that handles versioning for class having + class name (`class_name`) and module name (`module`). The + handler function will be passed the state and its version to fix. + """ + key = (class_name, module) + if key in self.handlers: + msg = "Overwriting version handler for (%s, %s)" % (key[0], key[1]) + logger.warn(msg) + self.handlers[(class_name, module)] = handler
+ +
[docs] def unregister(self, class_name, module): + """Unregisters any handlers for a class and module.""" + self.handlers.pop((class_name, module))
+ +
[docs] def update(self, state): + """Updates the given state using the handlers. Note that the + state is modified in-place. + """ + if (not self.handlers) or (not hasattr(state, "__metadata__")): + return + versions = state.__metadata__["version"] + for ver in versions: + key = ver[0] + try: + self.handlers[key](state, ver[1]) + except KeyError: + pass
+ + +def _create_registry(): + """Creates a reload safe, singleton registry.""" + registry = None + for key in sys.modules.keys(): + if "version_registry" in key: + mod = sys.modules[key] + if hasattr(mod, "registry"): + registry = mod.registry + break + if not registry: + registry = HandlerRegistry() + return registry + + +# The singleton registry. +registry = _create_registry() +
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/persistence/versioned_unpickler.html b/5.0/_modules/apptools/persistence/versioned_unpickler.html new file mode 100644 index 000000000..667bf909c --- /dev/null +++ b/5.0/_modules/apptools/persistence/versioned_unpickler.html @@ -0,0 +1,342 @@ + + + + + + + apptools.persistence.versioned_unpickler — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.persistence.versioned_unpickler

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+# Standard library imports
+from pickle import _Unpickler as Unpickler
+from pickle import UnpicklingError, BUILD
+import logging
+from types import GeneratorType
+
+# Enthought library imports
+from apptools.persistence.updater import __replacement_setstate__
+
+
+logger = logging.getLogger(__name__)
+
+
+##############################################################################
+# class 'NewUnpickler'
+##############################################################################
+
[docs]class NewUnpickler(Unpickler): + """An unpickler that implements a two-stage pickling process to make it + possible to unpickle complicated Python object hierarchies where the + unserialized state of an object depends on the state of other objects in + the same pickle. + """ + +
[docs] def load(self, max_pass=-1): + """Read a pickled object representation from the open file. + + Return the reconstituted object hierarchy specified in the file. + """ + # List of objects to be unpickled. + self.objects = [] + + # We overload the load_build method. + dispatch = self.dispatch + dispatch[BUILD[0]] = NewUnpickler.load_build + + # call the super class' method. + ret = Unpickler.load(self) + self.initialize(max_pass) + self.objects = [] + + # Reset the Unpickler's dispatch table. + dispatch[BUILD[0]] = Unpickler.load_build + return ret
+ +
[docs] def initialize(self, max_pass): + # List of (object, generator) tuples that initialize objects. + generators = [] + + # Execute object's initialize to setup the generators. + for obj in self.objects: + if hasattr(obj, "__initialize__") and callable(obj.__initialize__): + ret = obj.__initialize__() + if isinstance(ret, GeneratorType): + generators.append((obj, ret)) + elif ret is not None: + raise UnpicklingError( + "Unexpected return value from " + "__initialize__. %s returned %s" % (obj, ret) + ) + + # Ensure a maximum number of passes + if max_pass < 0: + max_pass = len(generators) + + # Now run the generators. + count = 0 + while len(generators) > 0: + count += 1 + if count > max_pass: + not_done = [x[0] for x in generators] + msg = """Reached maximum pass count %s. You may have + a deadlock! The following objects are + uninitialized: %s""" % ( + max_pass, + not_done, + ) + raise UnpicklingError(msg) + for o, g in generators[:]: + try: + next(g) + except StopIteration: + generators.remove((o, g))
+ + # Make this a class method since dispatch is a class variable. + # Otherwise, supposing the initial VersionedUnpickler.load call (which + # would have overloaded the load_build method) makes a pickle.load call at + # some point, we would have the dispatch still pointing to + # NewPickler.load_build whereas the object being passed in will be an + # Unpickler instance, causing a TypeError. +
[docs] def load_build(cls, obj): + # Just save the instance in the list of objects. + if isinstance(obj, NewUnpickler): + obj.objects.append(obj.stack[-2]) + Unpickler.load_build(obj)
+ + load_build = classmethod(load_build)
+ + +
[docs]class VersionedUnpickler(NewUnpickler): + """This class reads in a pickled file created at revision version 'n' + and then applies the transforms specified in the updater class to + generate a new set of objects which are at revision version 'n+1'. + + I decided to keep the loading of the updater out of this generic class + because we will want updaters to be generated for each plugin's type + of project. + + This ensures that the VersionedUnpickler can remain ignorant about the + actual version numbers - all it needs to do is upgrade one release. + """ + + def __init__(self, file, updater=None): + Unpickler.__init__(self, file) + self.updater = updater + +
[docs] def find_class(self, module, name): + """Overridden method from Unpickler. + + NB __setstate__ is not called until later. + """ + + if self.updater: + # check to see if this class needs to be mapped to a new class + # or module name + original_module, original_name = module, name + module, name = self.updater.get_latest(module, name) + + # load the class... + klass = self.import_name(module, name) + + # add the updater.... TODO - why the old name? + self.add_updater(original_module, original_name, klass) + + else: + # there is no updater so we will be reading in an up to date + # version of the file... + try: + klass = Unpickler.find_class(self, module, name) + except Exception: + logger.error("Looking for [%s] [%s]" % (module, name)) + logger.exception( + "Problem using default unpickle functionality" + ) + + # restore the original __setstate__ if necessary + fn = getattr(klass, "__setstate_original__", False) + if fn: + setattr(klass, "__setstate__", fn) + + return klass
+ +
[docs] def add_updater(self, module, name, klass): + """If there is an updater defined for this class we will add it to the + class as the __setstate__ method. + """ + + fn = self.updater.setstates.get((module, name), False) + + if fn: + # move the existing __setstate__ out of the way + self.backup_setstate(module, klass) + + # add the updater into the class + setattr(klass, "__updater__", fn) + + # hook up our __setstate__ which updates self.__dict__ + setattr(klass, "__setstate__", __replacement_setstate__) + + else: + pass
+ +
[docs] def backup_setstate(self, module, klass): + """If the class has a user defined __setstate__ we back it up.""" + if getattr(klass, "__setstate__", False): + + if getattr(klass, "__setstate_original__", False): + # don't overwrite the original __setstate__ + name = "__setstate__%s" % self.updater.__class__ + else: + # backup the original __setstate__ which we will restore + # and run later when we have finished updating the class + name = "__setstate_original__" + + method = getattr(klass, "__setstate__") + setattr(klass, name, method) + + else: + # the class has no __setstate__ method so do nothing + pass
+ +
[docs] def import_name(self, module, name): + """ + If the class is needed for the latest version of the application then + it should presumably exist. + + If the class no longer exists then we should perhaps return + a proxy of the class. + + If the persisted file is at v1 say and the application is at v3 then + objects that are required for v1 and v2 do not have to exist they only + need to be placeholders for the state during an upgrade. + """ + module = __import__(module, globals(), locals(), [name]) + return vars(module)[name]
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/preferences/i_preferences.html b/5.0/_modules/apptools/preferences/i_preferences.html new file mode 100644 index 000000000..1b5559d48 --- /dev/null +++ b/5.0/_modules/apptools/preferences/i_preferences.html @@ -0,0 +1,310 @@ + + + + + + + apptools.preferences.i_preferences — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.preferences.i_preferences

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" The interface for a node in a preferences hierarchy. """
+
+
+# Enthought library imports.
+from traits.api import Instance, Interface, Str
+
+
+
[docs]class IPreferences(Interface): + """ The interface for a node in a preferences hierarchy. """ + + # The absolute path to this node from the root node (the empty string if + # this node *is* the root node). + path = Str + + # The parent node (None if this node *is* the root node). + parent = Instance("IPreferences") + + # The name of the node relative to its parent (the empty string if this + # node *is* the root node). + name = Str + + #### Methods where 'path' refers to a preference #### + +
[docs] def get(self, path, default=None, inherit=False): + """Get the value of the preference at the specified path. + + If no value exists for the path (or any part of the path does not + exist) then return the default value. + + Preference values are *always* returned as strings. + + e.g:: + + preferences.set('acme.ui.bgcolor', 'blue') + preferences.get('acme.ui.bgcolor') -> 'blue' + + preferences.set('acme.ui.width', 100) + preferences.get('acme.ui.width') -> '100' + + preferences.set('acme.ui.visible', True) + preferences.get('acme.ui.visible') -> 'True' + + If 'inherit' is True then we allow 'inherited' preference values. + + e.g. If we are looking up:: + + 'acme.ui.widget.bgcolor' + + and it does not exist then we will also try:: + + 'acme.ui.bgcolor' + 'acme.bgcolor' + 'bgcolor' + + Raise a 'ValueError' exception if the path is the empty string. + + """
+ +
[docs] def remove(self, path): + """Remove the preference at the specified path. + + Does nothing if no value exists for the path (or any part of the path + does not exist. + + Raise a 'ValueError' exception if the path is the empty string. + + e.g.:: + + preferences.remove('acme.ui.bgcolor') + + """
+ +
[docs] def set(self, path, value): + """Set the value of the preference at the specified path. + + Any missing nodes are created automatically. + + Primitive Python types can be set, but preferences are *always* + stored and returned as strings. + + e.g:: + + preferences.set('acme.ui.bgcolor', 'blue') + preferences.get('acme.ui.bgcolor') -> 'blue' + + preferences.set('acme.ui.width', 100) + preferences.get('acme.ui.width') -> '100' + + preferences.set('acme.ui.visible', True) + preferences.get('acme.ui.visible') -> 'True' + + Raise a 'ValueError' exception if the path is the empty string. + + """
+ + #### Methods where 'path' refers to a node #### + +
[docs] def clear(self, path=""): + """Remove all preference from the node at the specified path. + + If the path is the empty string (the default) then remove the + preferences in *this* node. + + This does not affect any of the node's children. + + e.g. To clear the preferences out of a node directly:: + + preferences.clear() + + Or to clear the preferences of a node at a given path:: + + preferences.clear('acme.ui') + + """
+ +
[docs] def keys(self, path=""): + """Return the preference keys of the node at the specified path. + + If the path is the empty string (the default) then return the + preference keys of *this* node. + + e.g:: + + keys = preferences.keys('acme.ui') + + """
+ +
[docs] def node(self, path=""): + """Return the node at the specified path. + + If the path is the empty string (the default) then return *this* node. + + Any missing nodes are created automatically. + + e.g:: + + node = preferences.node('acme.ui') + bgcolor = node.get('bgcolor') + + """
+ +
[docs] def node_exists(self, path=""): + """Return True if the node at the specified path exists + + If the path is the empty string (the default) then return True. + + e.g:: + + exists = preferences.exists('acme.ui') + + """
+ +
[docs] def node_names(self, path=""): + """Return the names of the children of the node at the specified path. + + If the path is the empty string (the default) then return the names of + the children of *this* node. + + e.g:: + + names = preferences.node_names('acme.ui') + + """
+ + #### Persistence methods #### + +
[docs] def flush(self): + """Force any changes in the node to the backing store. + + This includes any changes to the node's descendants. + + """
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/preferences/package_globals.html b/5.0/_modules/apptools/preferences/package_globals.html new file mode 100644 index 000000000..e477fc92e --- /dev/null +++ b/5.0/_modules/apptools/preferences/package_globals.html @@ -0,0 +1,167 @@ + + + + + + + apptools.preferences.package_globals — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.preferences.package_globals

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" Package-scope globals.
+
+The default preferences node is currently used by 'PreferencesHelper' and
+'PreferencesBinding' instances if no specific preferences node is set. This
+makes it easy for them to access the root node of an application-wide
+preferences hierarchy.
+
+"""
+
+
+# The default preferences node.
+_default_preferences = None
+
+
+
[docs]def get_default_preferences(): + """ Get the default preferences node. """ + + return _default_preferences
+ + +
[docs]def set_default_preferences(default_preferences): + """ Set the default preferences node. """ + + global _default_preferences + + _default_preferences = default_preferences + + # For convenience. + return _default_preferences
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/preferences/preference_binding.html b/5.0/_modules/apptools/preferences/preference_binding.html new file mode 100644 index 000000000..5cb1678c2 --- /dev/null +++ b/5.0/_modules/apptools/preferences/preference_binding.html @@ -0,0 +1,299 @@ + + + + + + + apptools.preferences.preference_binding — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.preferences.preference_binding

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" A binding between a trait on an object and a preference value. """
+
+
+# Enthought library imports.
+from traits.api import Any, HasTraits, Instance, Str, Undefined
+
+# Local imports.
+from .i_preferences import IPreferences
+from .package_globals import get_default_preferences
+
+
+
[docs]class PreferenceBinding(HasTraits): + """ A binding between a trait on an object and a preference value. """ + + #### 'PreferenceBinding' interface ######################################## + + # The object that we are binding the preference to. + obj = Any + + # The preferences node used by the binding. If this trait is not set then + # the package-global default preferences node is used (and if that is not + # set then the binding won't work ;^) + preferences = Instance(IPreferences) + + # The path to the preference value. + preference_path = Str + + # The name of the trait that we are binding the preference to. + trait_name = Str + + ########################################################################### + # 'object' interface. + ########################################################################### + + def __init__(self, **traits): + """ Constructor. """ + + super(PreferenceBinding, self).__init__(**traits) + + # Initialize the object's trait from the preference value. + self._set_trait(notify=False) + + # Wire-up trait change handlers etc. + self._initialize() + + ########################################################################### + # 'PreferenceBinding' interface. + ########################################################################### + + #### Trait initializers ################################################### + + def _preferences_default(self): + """ Trait initializer. """ + + return get_default_preferences() + + ########################################################################### + # Private interface. + ########################################################################### + + #### Trait change handlers ################################################ + + def _on_trait_changed(self, obj, trait_name, old, new): + """ Dynamic trait change handler. """ + + self.preferences.set(self.preference_path, new) + + #### Other observer pattern listeners ##################################### + + def _preferences_listener(self, node, key, old, new): + """ Listener called when a preference value is changed. """ + + components = self.preference_path.split(".") + if key == components[-1]: + self._set_trait() + + #### Methods ############################################################## + + # fixme: This method is mostly duplicated in 'PreferencesHelper' (the only + # difference is the line that gets the handler). + def _get_value(self, trait_name, value): + """Get the actual value to set. + + This method makes sure that any required work is done to convert the + preference value from a string. + + """ + + handler = self.obj.trait(trait_name).handler + + # If the trait type is 'Str' then we just take the raw value. + if type(handler) is Str: + pass + + # If the trait type is 'Str' then we convert the raw value. + elif type(handler) is Str: + value = str(value) + + # Otherwise, we eval it! + else: + try: + value = eval(value) + + # If the eval fails then there is probably a syntax error, but + # we will let the handler validation throw the exception. + except Exception: + pass + + return handler.validate(self, trait_name, value) + + def _initialize(self): + """ Wire-up trait change handlers etc. """ + + # Listen for the object's trait being changed. + self.obj.on_trait_change(self._on_trait_changed, self.trait_name) + + # Listen for the preference value being changed. + components = self.preference_path.split(".") + node = ".".join(components[:-1]) + + self.preferences.add_preferences_listener( + self._preferences_listener, node + ) + + def _set_trait(self, notify=True): + """ Set the object's trait to the value of the preference. """ + + value = self.preferences.get(self.preference_path, Undefined) + if value is not Undefined: + trait_value = self._get_value(self.trait_name, value) + traits = {self.trait_name: trait_value} + + self.obj.trait_set(trait_change_notify=notify, **traits)
+ + +# Factory function for creating bindings. +
[docs]def bind_preference(obj, trait_name, preference_path, preferences=None): + """ Create a new preference binding. """ + + # This may seem a bit wierd, but we manually build up a dictionary of + # the traits that need to be set at the time the 'PreferenceBinding' + # instance is created. + # + # This is because we only want to set the 'preferences' trait iff one + # is explicitly specified. If we passed it in with the default argument + # value of 'None' then it counts as 'setting' the trait which prevents + # the binding instance from defaulting to the package-global preferences. + # Also, if we try to set the 'preferences' trait *after* construction time + # then it is too late as the binding initialization is done in the + # constructor (we could of course split that out, which may be the 'right' + # way to do it ;^). + traits = { + "obj": obj, + "trait_name": trait_name, + "preference_path": preference_path, + } + + if preferences is not None: + traits["preferences"] = preferences + + return PreferenceBinding(**traits)
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/preferences/preferences.html b/5.0/_modules/apptools/preferences/preferences.html new file mode 100644 index 000000000..380b6bf59 --- /dev/null +++ b/5.0/_modules/apptools/preferences/preferences.html @@ -0,0 +1,687 @@ + + + + + + + apptools.preferences.preferences — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.preferences.preferences

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" The default implementation of a node in a preferences hierarchy. """
+
+# Standard library imports.
+import logging
+import threading
+
+# Enthought library imports.
+from traits.api import Any, Callable, Dict, HasTraits, Instance, List
+from traits.api import Property, Str, Undefined, provides
+
+# Local imports.
+from .i_preferences import IPreferences
+
+
+# Logging.
+logger = logging.getLogger(__name__)
+
+
+
[docs]@provides(IPreferences) +class Preferences(HasTraits): + """ The default implementation of a node in a preferences hierarchy. """ + + #### 'IPreferences' interface ############################################# + + # The absolute path to this node from the root node (the empty string if + # this node *is* the root node). + path = Property(Str) + + # The parent node (None if this node *is* the root node). + parent = Instance(IPreferences) + + # The name of the node relative to its parent (the empty string if this + # node *is* the root node). + name = Str + + #### 'Preferences' interface ############################################## + + # The default name of the file used to persist the preferences (if no + # filename is passed in to the 'load' and 'save' methods, then this is + # used instead). + filename = Str + + #### Protected 'Preferences' interface #################################### + + # A lock to make access to the node thread-safe. + # + # fixme: There *should* be no need to declare this as a trait, but if we + # don't then we have problems using nodes in the preferences manager UI. + # It is something to do with 'cloning' the node for use in a 'modal' traits + # UI... Hmmm... + _lk = Any + + # The node's children. + _children = Dict(Str, IPreferences) + + # The node's preferences. + _preferences = Dict(Str, Any) + + # Listeners for changes to the node's preferences. + # + # The callable must take 4 arguments, e.g:: + # + # listener(node, key, old, new) + _preferences_listeners = List(Callable) + + ########################################################################### + # 'object' interface. + ########################################################################### + + def __init__(self, **traits): + """ Constructor. """ + + # A lock to make access to the '_children', '_preferences' and + # '_preferences_listeners' traits thread-safe. + self._lk = threading.Lock() + + # Base class constructor. + super(Preferences, self).__init__(**traits) + + # If a filename has been specified then load the preferences from it. + if len(self.filename) > 0: + self.load() + + ########################################################################### + # 'IPreferences' interface. + ########################################################################### + + #### Trait properties ##################################################### + + def _get_path(self): + """ Property getter. """ + + names = [] + + node = self + while node.parent is not None: + names.append(node.name) + node = node.parent + + names.reverse() + + return ".".join(names) + + #### Methods ############################################################## + + #### Methods where 'path' refers to a preference #### + +
[docs] def get(self, path, default=None, inherit=False): + """ Get the value of the preference at the specified path. """ + + if len(path) == 0: + raise ValueError("empty path") + + components = path.split(".") + + # If there is only one component in the path then the operation takes + # place in this node. + if len(components) == 1: + value = self._get(path, Undefined) + + # Otherwise, find the next node and pass the rest of the path to that. + else: + node = self._get_child(components[0]) + if node is not None: + value = node.get(".".join(components[1:]), Undefined) + + else: + value = Undefined + + # If inherited values are allowed then try those as well. + # + # e.g. 'acme.ui.widget.bgcolor' + # 'acme.ui.bgcolor' + # 'acme.bgcolor' + # 'bgcolor' + while inherit and value is Undefined and len(components) > 1: + # Remove the penultimate component... + # + # e.g. 'acme.ui.widget.bgcolor' -> 'acme.ui.bgcolor' + del components[-2] + + # ... and try that. + value = self.get(".".join(components), default=Undefined) + + if value is Undefined: + value = default + + return value
+ +
[docs] def remove(self, path): + """ Remove the preference at the specified path. """ + + if len(path) == 0: + raise ValueError("empty path") + + components = path.split(".") + + # If there is only one component in the path then the operation takes + # place in this node. + if len(components) == 1: + self._remove(path) + + # Otherwise, find the next node and pass the rest of the path to that. + else: + node = self._get_child(components[0]) + if node is not None: + node.remove(".".join(components[1:]))
+ +
[docs] def set(self, path, value): + """ Set the value of the preference at the specified path. """ + + if len(path) == 0: + raise ValueError("empty path") + + components = path.split(".") + + # If there is only one component in the path then the operation takes + # place in this node. + if len(components) == 1: + self._set(path, value) + + # Otherwise, find the next node (creating it if it doesn't exist) + # and pass the rest of the path to that. + else: + node = self._node(components[0]) + node.set(".".join(components[1:]), value)
+ + #### Methods where 'path' refers to a node #### + +
[docs] def clear(self, path=""): + """ Remove all preferences from the node at the specified path. """ + + # If the path is empty then the operation takes place in this node. + if len(path) == 0: + self._clear() + + # Otherwise, find the next node and pass the rest of the path to that. + else: + components = path.split(".") + + node = self._get_child(components[0]) + if node is not None: + node.clear(".".join(components[1:]))
+ +
[docs] def keys(self, path=""): + """ Return the preference keys of the node at the specified path. """ + + # If the path is empty then the operation takes place in this node. + if len(path) == 0: + keys = self._keys() + + # Otherwise, find the next node and pass the rest of the path to that. + else: + components = path.split(".") + + node = self._get_child(components[0]) + if node is not None: + keys = node.keys(".".join(components[1:])) + + else: + keys = [] + + return keys
+ +
[docs] def node(self, path=""): + """ Return the node at the specified path. """ + + # If the path is empty then the operation takes place in this node. + if len(path) == 0: + node = self + + # Otherwise, find the next node and pass the rest of the path to that. + else: + components = path.split(".") + + node = self._node(components[0]) + node = node.node(".".join(components[1:])) + + return node
+ +
[docs] def node_exists(self, path=""): + """ Return True if the node at the specified path exists. """ + + # If the path is empty then the operation takes place in this node. + if len(path) == 0: + exists = True + + # Otherwise, find the next node and pass the rest of the path to that. + else: + components = path.split(".") + + node = self._get_child(components[0]) + if node is not None: + exists = node.node_exists(".".join(components[1:])) + + else: + exists = False + + return exists
+ +
[docs] def node_names(self, path=""): + """Return the names of the children of the node at the specified path. + """ + + # If the path is empty then the operation takes place in this node. + if len(path) == 0: + names = self._node_names() + + # Otherwise, find the next node and pass the rest of the path to that. + else: + components = path.split(".") + + node = self._get_child(components[0]) + if node is not None: + names = node.node_names(".".join(components[1:])) + + else: + names = [] + + return names
+ + #### Persistence methods #### + +
[docs] def flush(self): + """Force any changes in the node to the backing store. + + This includes any changes to the node's descendants. + + """ + + self.save()
+ + ########################################################################### + # 'Preferences' interface. + ########################################################################### + + #### Listener methods #### + +
[docs] def add_preferences_listener(self, listener, path=""): + """ Add a listener for changes to a node's preferences. """ + + # If the path is empty then the operation takes place in this node. + if len(path) == 0: + self._add_preferences_listener(listener) + + # Otherwise, find the next node and pass the rest of the path to that. + else: + components = path.split(".") + + node = self._node(components[0]) + node.add_preferences_listener(listener, ".".join(components[1:]))
+ +
[docs] def remove_preferences_listener(self, listener, path=""): + """ Remove a listener for changes to a node's preferences. """ + + # If the path is empty then the operation takes place in this node. + if len(path) == 0: + self._remove_preferences_listener(listener) + + # Otherwise, find the next node and pass the rest of the path to that. + else: + components = path.split(".") + + node = self._node(components[0]) + node.remove_preferences_listener( + listener, ".".join(components[1:]) + )
+ + #### Persistence methods #### + +
[docs] def load(self, file_or_filename=None): + """Load preferences from a file. + + This is a *merge* operation i.e. the contents of the file are added to + the node. + + This implementation uses 'ConfigObj' files. + + """ + + if file_or_filename is None: + file_or_filename = self.filename + + logger.debug("loading preferences from <%s>", file_or_filename) + + # Do the import here so that we don't make 'ConfigObj' a requirement + # if preferences aren't ever persisted (or a derived class chooses to + # use a different persistence mechanism). + from configobj import ConfigObj + + config_obj = ConfigObj(file_or_filename, encoding="utf-8") + + # 'name' is the section name, 'value' is a dictionary containing the + # name/value pairs in the section (the actual preferences ;^). + for name, value in config_obj.items(): + # Create/get the node from the section name. + components = name.split(".") + + node = self + for component in components: + node = node._node(component) + + # Add the contents of the section to the node. + self._add_dictionary_to_node(node, value)
+ +
[docs] def save(self, file_or_filename=None): + """Save the node's preferences to a file. + + This implementation uses 'ConfigObj' files. + + """ + + if file_or_filename is None: + file_or_filename = self.filename + + # If no file or filename is specified then don't save the preferences! + if len(file_or_filename) > 0: + # Do the import here so that we don't make 'ConfigObj' a + # requirement if preferences aren't ever persisted (or a derived + # class chooses to use a different persistence mechanism). + from configobj import ConfigObj + + logger.debug("saving preferences to <%s>", file_or_filename) + + config_obj = ConfigObj(file_or_filename, encoding="utf-8") + self._add_node_to_dictionary(self, config_obj) + config_obj.write()
+ + ########################################################################### + # Protected 'Preferences' interface. + # + # These are the only methods that should access the protected '_children' + # and '_preferences' traits. This helps make it easy to subclass this class + # to create other implementations (all the subclass has to do is to + # implement these protected methods). + # + ########################################################################### + + def _add_dictionary_to_node(self, node, dictionary): + """ Add the contents of a dictionary to a node's preferences. """ + + self._lk.acquire() + node._preferences.update(dictionary) + self._lk.release() + + def _add_node_to_dictionary(self, node, dictionary): + """ Add a node's preferences to a dictionary. """ + + # This method never manipulates the '_preferences' trait directly. + # Instead it does eveything via the other protected methods and hence + # doesn't need to grab the lock. + if len(node._keys()) > 0: + dictionary[node.path] = {} + for key in node._keys(): + dictionary[node.path][key] = node._get(key) + + for name in node._node_names(): + self._add_node_to_dictionary(node._get_child(name), dictionary) + + def _add_preferences_listener(self, listener): + """ Add a listener for changes to thisnode's preferences. """ + + self._lk.acquire() + self._preferences_listeners.append(listener) + self._lk.release() + + def _clear(self): + """ Remove all preferences from this node. """ + + self._lk.acquire() + self._preferences.clear() + self._lk.release() + + def _create_child(self, name): + """ Create a child of this node with the specified name. """ + + self._lk.acquire() + child = self._children[name] = Preferences(name=name, parent=self) + self._lk.release() + + return child + + def _get(self, key, default=None): + """ Get the value of a preference in this node. """ + + self._lk.acquire() + value = self._preferences.get(key, default) + self._lk.release() + + return value + + def _get_child(self, name): + """Return the child of this node with the specified name. + + Return None if no such child exists. + + """ + + self._lk.acquire() + child = self._children.get(name) + self._lk.release() + + return child + + def _keys(self): + """ Return the preference keys of this node. """ + + self._lk.acquire() + keys = list(self._preferences.keys()) + self._lk.release() + + return keys + + def _node(self, name): + """Return the child of this node with the specified name. + + Create the child node if it does not exist. + + """ + + node = self._get_child(name) + if node is None: + node = self._create_child(name) + + return node + + def _node_names(self): + """ Return the names of the children of this node. """ + + self._lk.acquire() + node_names = list(self._children.keys()) + self._lk.release() + + return node_names + + def _remove(self, name): + """ Remove a preference value from this node. """ + + self._lk.acquire() + if name in self._preferences: + del self._preferences[name] + self._lk.release() + + def _remove_preferences_listener(self, listener): + """ Remove a listener for changes to the node's preferences. """ + + self._lk.acquire() + if listener in self._preferences_listeners: + self._preferences_listeners.remove(listener) + self._lk.release() + + def _set(self, key, value): + """ Set the value of a preference in this node. """ + + # everything must be unicode encoded so that ConfigObj configuration + # can properly serialize the data. Python str are supposed to be ASCII + # encoded. + value = str(value) + + self._lk.acquire() + old = self._preferences.get(key) + self._preferences[key] = value + + # If the value is unchanged then don't call the listeners! + if old == value: + listeners = [] + + else: + listeners = self._preferences_listeners[:] + self._lk.release() + + for listener in listeners: + listener(self, key, old, value) + + ########################################################################### + # Debugging interface. + ########################################################################### + +
[docs] def dump(self, indent=""): + """ Dump the preferences hierarchy to stdout. """ + + if indent == "": + print() + + print(indent, "Node(%s)" % self.name, self._preferences) + indent += " " + + for child in self._children.values(): + child.dump(indent)
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/preferences/preferences_helper.html b/5.0/_modules/apptools/preferences/preferences_helper.html new file mode 100644 index 000000000..982d2dd8c --- /dev/null +++ b/5.0/_modules/apptools/preferences/preferences_helper.html @@ -0,0 +1,336 @@ + + + + + + + apptools.preferences.preferences_helper — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.preferences.preferences_helper

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" An object that can be initialized from a preferences node. """
+
+
+# Standard library imports.
+import logging
+
+# Enthought library imports.
+from traits.api import HasTraits, Instance, Str
+
+# Local imports.
+from .i_preferences import IPreferences
+from .package_globals import get_default_preferences
+
+
+# Logging.
+logger = logging.getLogger(__name__)
+
+
+
[docs]class PreferencesHelper(HasTraits): + """ A base class for objects that can be initialized from a preferences + node. + + Additional traits defined on subclasses will be listened to. Changes + are then synchronized with the preferences. Note that mutations on nested + containers e.g. List(List(Str)) cannot be synchronized and should be + avoided. + """ + + #### 'PreferencesHelper' interface ######################################## + + # The preferences node used by the helper. If this trait is not set then + # the package-global default preferences node is used. + # + # fixme: This introduces a 'sneaky' global reference to the preferences + # node! + preferences = Instance(IPreferences) + + # The path to the preference node that contains the preferences that we + # use to initialize instances of this class. + preferences_path = Str + + ########################################################################### + # 'object' interface. + ########################################################################### + + def __init__(self, **traits): + """ Constructor. """ + + super(PreferencesHelper, self).__init__(**traits) + + # Initialize the object's traits from the preferences node. + if self.preferences: + self._initialize(self.preferences) + + ########################################################################### + # Private interface. + ########################################################################### + + #### Trait initializers ################################################### + + def _preferences_default(self): + """ Trait initializer. """ + + # If no specific preferences node is set then we use the package-wide + # global node. + return get_default_preferences() + + #### Trait change handlers ################################################ + + def _anytrait_changed(self, trait_name, old, new): + """ Static trait change handler. """ + + if self.preferences is None: + return + + if self._is_preference_trait(trait_name): + self.preferences.set("%s.%s" % (self._get_path(), trait_name), new) + + # If the trait was a list or dict '_items' trait then just treat it as + # if the entire list or dict was changed. + elif trait_name.endswith('_items'): + trait_name = trait_name[:-6] + if self._is_preference_trait(trait_name): + self.preferences.set( + '%s.%s' % (self._get_path(), trait_name), + getattr(self, trait_name) + ) + + # If the change refers to a trait defined on this class, then + # the trait is not a preference trait and we do nothing. + + def _preferences_changed(self, old, new): + """ Static trait change handler. """ + + # Stop listening to the old preferences node. + if old is not None: + old.remove_preferences_listener( + self._preferences_changed_listener, self._get_path() + ) + + if new is not None: + # Initialize with the new preferences node (this also adds a + # listener for preferences being changed in the new node). + self._initialize(new, notify=True) + + #### Other observer pattern listeners ##################################### + + def _preferences_changed_listener(self, node, key, old, new): + """ Listener called when a preference value is changed. """ + + if key in self.trait_names(): + setattr(self, key, self._get_value(key, new)) + + #### Methods ############################################################## + + def _get_path(self): + """ Return the path to our preferences node. """ + + if len(self.preferences_path) > 0: + path = self.preferences_path + + else: + path = getattr(self, "PREFERENCES_PATH", None) + if path is None: + raise SystemError("no preferences path, %s" % self) + + else: + logger.warn('DEPRECATED: use "preferences_path" %s' % self) + + return path + + def _get_value(self, trait_name, value): + """Get the actual value to set. + + This method makes sure that any required work is done to convert the + preference value from a string. Str traits or those with the metadata + 'is_str=True' will just be passed the string itself. + + """ + + trait = self.trait(trait_name) + handler = trait.handler + + # If the trait type is 'Str' then we just take the raw value. + if isinstance(handler, Str) or trait.is_str: + pass + + # Otherwise, we eval it! + else: + try: + value = eval(value) + + # If the eval fails then there is probably a syntax error, but + # we will let the handler validation throw the exception. + except Exception: + pass + + if handler.validate is not None: + # Any traits have a validator of None. + validated = handler.validate(self, trait_name, value) + else: + validated = value + + return validated + + def _initialize(self, preferences, notify=False): + """ Initialize the object's traits from the preferences node. """ + + path = self._get_path() + keys = preferences.keys(path) + + traits_to_set = {} + for trait_name in self.trait_names(): + if trait_name in keys: + key = "%s.%s" % (path, trait_name) + value = self._get_value(trait_name, preferences.get(key)) + traits_to_set[trait_name] = value + + self.trait_set(trait_change_notify=notify, **traits_to_set) + + # Listen for changes to the node's preferences. + preferences.add_preferences_listener( + self._preferences_changed_listener, path + ) + + # fixme: Pretty much duplicated in 'PreferencesPage' (except for the + # class name of course!). + def _is_preference_trait(self, trait_name): + """ Return True if a trait represents a preference value. """ + + if ( + trait_name.startswith("_") + or trait_name.endswith("_") + or trait_name in PreferencesHelper.class_traits() + ): + return False + + return trait_name in self.editable_traits()
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/preferences/scoped_preferences.html b/5.0/_modules/apptools/preferences/scoped_preferences.html new file mode 100644 index 000000000..e2937244e --- /dev/null +++ b/5.0/_modules/apptools/preferences/scoped_preferences.html @@ -0,0 +1,582 @@ + + + + + + + apptools.preferences.scoped_preferences — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.preferences.scoped_preferences

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" A preferences node that adds the notion of preferences scopes. """
+
+# Standard library imports.
+from os.path import join
+
+# Enthought library imports.
+from traits.etsconfig.api import ETSConfig
+from traits.api import List, Str, Undefined
+
+# Local imports.
+from .i_preferences import IPreferences
+from .preferences import Preferences
+
+
+
[docs]class ScopedPreferences(Preferences): + """A preferences node that adds the notion of preferences scopes. + + Scopes provide a way to access preferences in a precedence order, usually + depending on where they came from, for example from the command-line, + or set by the user in a preferences file, or the defaults (set by the + developer). + + By default, this class provides two scopes - 'application' which is + persistent and 'default' which is not. + + Path names passed to 'ScopedPreferences' nodes can be either:: + + a) a preference path as used in a standard 'Preferences' node, e.g:: + + 'acme.widget.bgcolor'. + + In this case the operation either takes place in the primary scope + (for operations such as 'set' etc), or on all scopes in precedence + order (for operations such as 'get' etc). + + or + + b) a preference path that refers to a specific scope e.g:: + + 'default/acme.widget.bgcolor' + + In this case the operation takes place *only* in the specified scope. + + There is one drawback to this scheme. If you want to access a scope node + itself via the 'clear', 'keys', 'node', 'node_exists' or 'node_names' + methods then you have to append a trailing '/' to the path. Without that, + the node would try to perform the operation in the primary scope. + + e.g. To get the names of the children of the 'application' scope, use:: + + scoped.node_names('application/') + + If you did this:: + + scoped.node_names('application') + + Then the node would get the primary scope and try to find its child node + called 'application'. + + Of course you can just get the scope via:: + + application_scope = scoped.get_scope('application') + + and then call whatever methods you like on it - which is definitely more + intentional and is highly recommended:: + + application_scope.node_names() + + """ + + #### 'ScopedPreferences' interface ######################################## + + # The file that the application scope preferences are stored in. + # + # Defaults to:- + # + # os.path.join(ETSConfig.application_home, 'preferences.ini') + application_preferences_filename = Str + + # The scopes (in the order that they should be searched when looking up + # preferences). + # + # By default, this class provides two scopes - 'application' which is + # persistent and 'default' which is not. + scopes = List(IPreferences) + + # The name of the 'primary' scope. + # + # This is the scope that operations take place in if no scope is specified + # in a given path (for the 'get' operation, if no scope is specified the + # operation takes place in *all* scopes in order of precedence). If this is + # the empty string (the default) then the primary scope is the first scope + # in the 'scopes' list. + primary_scope_name = Str + + ########################################################################### + # 'IPreferences' protocol. + ########################################################################### + + #### Methods where 'path' refers to a preference #### + +
[docs] def get(self, path, default=None, inherit=False): + """ Get the value of the preference at the specified path. """ + + if len(path) == 0: + raise ValueError("empty path") + + # If the path contains a specific scope then lookup the preference in + # just that scope. + if self._path_contains_scope(path): + scope_name, path = self._parse_path(path) + nodes = [self._get_scope(scope_name)] + + # Otherwise, try each scope in turn (i.e. in order of precedence). + else: + nodes = self.scopes + + # Try all nodes first (without inheritance even if specified). + value = self._get(path, Undefined, nodes, inherit=False) + if value is Undefined: + if inherit: + value = self._get(path, default, nodes, inherit=True) + + else: + value = default + + return value
+ +
[docs] def remove(self, path): + """ Remove the preference at the specified path. """ + + if len(path) == 0: + raise ValueError("empty path") + + # If the path contains a specific scope then remove the preference from + # just that scope. + if self._path_contains_scope(path): + scope_name, path = self._parse_path(path) + node = self._get_scope(scope_name) + + # Otherwise, remove the preference from the primary scope. + else: + node = self._get_primary_scope() + + node.remove(path)
+ +
[docs] def set(self, path, value): + """ Set the value of the preference at the specified path. """ + + if len(path) == 0: + raise ValueError("empty path") + + # If the path contains a specific scope then set the value in that + # scope. + if self._path_contains_scope(path): + scope_name, path = self._parse_path(path) + node = self._get_scope(scope_name) + + # Otherwise, set the value in the primary scope. + else: + node = self._get_primary_scope() + + node.set(path, value)
+ + #### Methods where 'path' refers to a node #### + +
[docs] def clear(self, path=""): + """ Remove all preference from the node at the specified path. """ + + # If the path contains a specific scope then remove the preferences + # from a node in that scope. + if self._path_contains_scope(path): + scope_name, path = self._parse_path(path) + node = self._get_scope(scope_name) + + # Otherwise, remove the preferences from a node in the primary scope. + else: + node = self._get_primary_scope() + + return node.clear(path)
+ +
[docs] def keys(self, path=""): + """ Return the preference keys of the node at the specified path. """ + + # If the path contains a specific scope then get the keys of the node + # in that scope. + if self._path_contains_scope(path): + scope_name, path = self._parse_path(path) + nodes = [self._get_scope(scope_name)] + + # Otherwise, merge the keys of the node in all scopes. + else: + nodes = self.scopes + + keys = set() + for node in nodes: + keys.update(node.node(path).keys()) + + return list(keys)
+ +
[docs] def node(self, path=""): + """ Return the node at the specified path. """ + + if len(path) == 0: + node = self + + else: + # If the path contains a specific scope then we get the node that + # scope. + if self._path_contains_scope(path): + scope_name, path = self._parse_path(path) + node = self._get_scope(scope_name) + + # Otherwise, get the node from the primary scope. + else: + node = self._get_primary_scope() + + node = node.node(path) + + return node
+ +
[docs] def node_exists(self, path=""): + """ Return True if the node at the specified path exists. """ + + # If the path contains a specific scope then look for the node in that + # scope. + if self._path_contains_scope(path): + scope_name, path = self._parse_path(path) + node = self._get_scope(scope_name) + + # Otherwise, look for the node in the primary scope. + else: + node = self._get_primary_scope() + + return node.node_exists(path)
+ +
[docs] def node_names(self, path=""): + """Return the names of the children of the node at the specified path. + """ + + # If the path contains a specific scope then get the names of the + # children of the node in that scope. + if self._path_contains_scope(path): + scope_name, path = self._parse_path(path) + nodes = [self._get_scope(scope_name)] + + # Otherwise, merge the names of the children of the node in all scopes. + else: + nodes = self.scopes + + names = set() + for node in nodes: + names.update(node.node(path).node_names()) + + return list(names)
+ + ########################################################################### + # 'Preferences' protocol. + ########################################################################### + + #### Listener methods #### + +
[docs] def add_preferences_listener(self, listener, path=""): + """ Add a listener for changes to a node's preferences. """ + + # If the path contains a specific scope then add a preferences listener + # to the node in that scope. + if self._path_contains_scope(path): + scope_name, path = self._parse_path(path) + nodes = [self._get_scope(scope_name)] + + # Otherwise, add a preferences listener to the node in all scopes. + else: + nodes = self.scopes + + for node in nodes: + node.add_preferences_listener(listener, path)
+ +
[docs] def remove_preferences_listener(self, listener, path=""): + """ Remove a listener for changes to a node's preferences. """ + + # If the path contains a specific scope then remove a preferences + # listener from the node in that scope. + if self._path_contains_scope(path): + scope_name, path = self._parse_path(path) + nodes = [self._get_scope(scope_name)] + + # Otherwise, remove a preferences listener from the node in all scopes. + else: + nodes = self.scopes + + for node in nodes: + node.remove_preferences_listener(listener, path)
+ + #### Persistence methods #### + +
[docs] def load(self, file_or_filename=None): + """Load preferences from a file. + + This loads the preferences into the primary scope. + + fixme: I'm not sure it is worth providing an implentation here. I + think it would be better to encourage people to explicitly reference + a particular scope. + + """ + + if file_or_filename is None and len(self.filename) > 0: + file_or_filename = self.filename + + node = self._get_primary_scope() + node.load(file_or_filename)
+ +
[docs] def save(self, file_or_filename=None): + """Save the node's preferences to a file. + + This asks each scope in turn to save its preferences. + + If a file or filename is specified then it is only passed to the + primary scope. + + """ + + if file_or_filename is None and len(self.filename) > 0: + file_or_filename = self.filename + + self._get_primary_scope().save(file_or_filename) + for scope in self.scopes: + if scope is not self._get_primary_scope(): + scope.save()
+ + ########################################################################### + # 'ScopedPreferences' protocol. + ########################################################################### + + def _application_preferences_filename_default(self): + """ Trait initializer. """ + + return join(ETSConfig.application_home, "preferences.ini") + + # fixme: In hindsight, I don't think this class should have provided + # default scopes. This should have been an 'abstract' class that could + # be subclassed by classes providing specific scopes. + def _scopes_default(self): + """ Trait initializer. """ + + scopes = [ + Preferences( + name="application", + filename=self.application_preferences_filename, + ), + Preferences(name="default"), + ] + + return scopes + +
[docs] def get_scope(self, scope_name): + """Return the scope with the specified name. + + Return None if no such scope exists. + + """ + + for scope in self.scopes: + if scope_name == scope.name: + break + + else: + scope = None + + return scope
+ + ########################################################################### + # Private protocol. + ########################################################################### + + def _get(self, path, default, nodes, inherit): + """ Get a preference from a list of nodes. """ + + for node in nodes: + value = node.get(path, Undefined, inherit) + if value is not Undefined: + break + + else: + value = default + + return value + + def _get_scope(self, scope_name): + """Return the scope with the specified name. + + Raise a 'ValueError' is no such scope exists. + + """ + + scope = self.get_scope(scope_name) + if scope is None: + raise ValueError("no such scope %s" % scope_name) + + return scope + + def _get_primary_scope(self): + """Return the primary scope. + + By default, this is the first scope. + + """ + + if len(self.primary_scope_name) > 0: + scope = self._get_scope(self.primary_scope_name) + + else: + scope = self.scopes[0] + + return scope + + def _path_contains_scope(self, path): + """ Return True if the path contains a scope component. """ + + return "/" in path + + def _parse_path(self, path): + """ 'Parse' the path into two parts, the scope name and the rest! """ + + components = path.split("/") + + return components[0], "/".join(components[1:]) + + ########################################################################### + # Debugging interface. + ########################################################################### + +
[docs] def dump(self, indent=""): + """ Dump the preferences hierarchy to stdout. """ + + if indent == "": + print() + + print(indent, "Node(%s)" % self.name, self._preferences) + indent += " " + + for child in self.scopes: + child.dump(indent)
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/preferences/ui/i_preferences_page.html b/5.0/_modules/apptools/preferences/ui/i_preferences_page.html new file mode 100644 index 000000000..bada9bfa3 --- /dev/null +++ b/5.0/_modules/apptools/preferences/ui/i_preferences_page.html @@ -0,0 +1,162 @@ + + + + + + + apptools.preferences.ui.i_preferences_page — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.preferences.ui.i_preferences_page

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" The interface for pages in a preferences dialog. """
+
+
+# Enthought library imports.
+from traits.api import Interface, Str
+
+
+
[docs]class IPreferencesPage(Interface): + """ The interface for pages in a preferences dialog. """ + + # The page's category (e.g. 'General/Appearence'). The empty string means + # that this is a top-level page. + category = Str + + # The page's help identifier (optional). If a help Id *is* provided then + # there will be a 'Help' button shown on the preference page. + help_id = Str + + # The page name (this is what is shown in the preferences dialog). + name = Str + +
[docs] def apply(self): + """ Apply the page's preferences. """ + pass
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/preferences/ui/preferences_manager.html b/5.0/_modules/apptools/preferences/ui/preferences_manager.html new file mode 100644 index 000000000..c084f3ff2 --- /dev/null +++ b/5.0/_modules/apptools/preferences/ui/preferences_manager.html @@ -0,0 +1,418 @@ + + + + + + + apptools.preferences.ui.preferences_manager — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.preferences.ui.preferences_manager

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" The preferences manager. """
+
+
+# Enthought library imports.
+from traits.api import HasTraits, Instance, List, Property, Bool
+from traitsui.api import Handler, HSplit, Item, TreeEditor
+from traitsui.api import TreeNode, View, HTMLEditor
+from traitsui.menu import Action
+
+# Local imports.
+from .preferences_node import PreferencesNode
+from .preferences_page import PreferencesPage
+
+
+# A tree editor for preferences nodes.
+tree_editor = TreeEditor(
+    nodes=[
+        TreeNode(
+            node_for=[PreferencesNode],
+            auto_open=False,
+            children="children",
+            label="name",
+            rename=False,
+            copy=False,
+            delete=False,
+            insert=False,
+            menu=None,
+        ),
+    ],
+    editable=False,
+    hide_root=True,
+    selected="selected_node",
+    show_icons=False,
+)
+
+
+
[docs]class PreferencesHelpWindow(HasTraits): + """ Container class to present a view with string info. """ + +
[docs] def traits_view(self): + """ Default view to show for this class. """ + args = [] + kw_args = { + "title": "Preferences Page Help", + "buttons": ["OK"], + "width": 800, + "height": 800, + "resizable": True, + "id": "apptools.preferences.ui.preferences_manager.help", + } + to_show = {} + + for name, trait_obj in self.traits().items(): + if name != "trait_added" and name != "trait_modified": + to_show[name] = trait_obj.help + for name in to_show: + args.append(Item(name, style="readonly", editor=HTMLEditor())) + + view = View(*args, **kw_args) + return view
+ + +
[docs]class PreferencesManagerHandler(Handler): + """ The traits UI handler for the preferences manager. """ + + model = Instance(HasTraits) + + ########################################################################### + # 'Handler' interface. + ########################################################################### + +
[docs] def apply(self, info): + """ Handle the **Apply** button being clicked. """ + + info.object.apply()
+ +
[docs] def init(self, info): + """ Initialize the controls of a user interface. """ + + # Select the first node in the tree (if there is one). + self._select_first_node(info) + + return super(PreferencesManagerHandler, self).init(info)
+ +
[docs] def close(self, info, is_ok): + """ Close a dialog-based user interface. """ + + if is_ok: + info.object.apply() + + return super(PreferencesManagerHandler, self).close(info, is_ok)
+ +
[docs] def preferences_help(self, info): + """ Custom preferences help panel. The Traits help doesn't work.""" + current_page = self.model.selected_page + to_show = {} + for trait_name, trait_obj in current_page.traits().items(): + if hasattr(trait_obj, "show_help") and trait_obj.show_help: + to_show[trait_name] = trait_obj.help + + help_obj = PreferencesHelpWindow(**to_show) + help_obj.edit_traits(kind="livemodal")
+ + ########################################################################### + # Private interface. + ########################################################################### + + def _select_first_node(self, info): + """ Select the first node in the tree (if there is one). """ + + root = info.object.root + + if len(root.children) > 0: + node = root.children[0] + info.object.selected_page = node.page
+ + +
[docs]class PreferencesManager(HasTraits): + """ The preferences manager. """ + + # All of the preferences pages known to the manager. + pages = List(PreferencesPage) + + # The root of the preferences node tree. + root = Property(Instance(PreferencesNode)) + + # The preferences node currently selected in the tree. + selected_node = Instance(PreferencesNode) + + # The preferences associated with the currently selected preferences node. + selected_page = Instance(PreferencesPage) + + # Should the custom Info button be shown? If this is True, then an + # Info button is shown that pops up a trait view with an HTML entry + # for each trait of the *selected_page* with the metadata 'show_help' + # set to True. + show_help = Bool(False) + + # Should the Apply button be shown? + show_apply = Bool(False) + + #### Traits UI views ###################################################### + +
[docs] def traits_view(self): + """ Default traits view for this class. """ + + help_action = Action(name="Info", action="preferences_help") + + buttons = ["OK", "Cancel"] + + if self.show_apply: + buttons = ["Apply"] + buttons + if self.show_help: + buttons = [help_action] + buttons + + # A tree editor for preferences nodes. + tree_editor = TreeEditor( + nodes=[ + TreeNode( + node_for=[PreferencesNode], + auto_open=False, + children="children", + label="name", + rename=False, + copy=False, + delete=False, + insert=False, + menu=None, + ), + ], + on_select=self._selection_changed, + editable=False, + hide_root=True, + selected="selected_node", + show_icons=False, + ) + + view = View( + HSplit( + Item( + name="root", + editor=tree_editor, + show_label=False, + width=250, + ), + Item( + name="selected_page", + # editor = WidgetEditor(), + show_label=False, + width=450, + style="custom", + ), + ), + buttons=buttons, + handler=PreferencesManagerHandler(model=self), + resizable=True, + title="Preferences", + width=0.3, + height=0.3, + kind="modal", + ) + self.selected_page = self.pages[0] + return view
+ + ########################################################################### + # 'PreferencesManager' interface. + ########################################################################### + + #### Trait properties ##################################################### + + def _get_root(self): + """ Property getter. """ + + # Sort the pages by the length of their category path. This makes it + # easy for us to create the preference hierarchy as we know that all of + # a node's ancestors will have already been created. + def sort_key(a): + # We have the guard because if the category is the empty string + # then split will still return a list containing one item (and not + # the empty list). + if len(a.category) == 0: + len_a = 0 + + else: + len_a = len(a.category.split("/")) + + return len_a + + self.pages.sort(key=sort_key) + + # Create a corresponding preference node hierarchy (the root of the + # hierachy is NOT displayed in the preference dialog). + # + # fixme: Currently we have to create a dummy page for the root node + # event though the root does not get shown in the tree! + root_page = PreferencesPage(name="Root", preferences_path="root") + root = PreferencesNode(page=root_page) + + for page in self.pages: + # Get the page's parent node. + parent = self._get_parent(root, page) + + # Add a child node representing the page. + parent.append(PreferencesNode(page=page)) + + return root + + #### Trait change handlers ################################################ + + def _selection_changed(self, new_selection): + self.selected_node = new_selection + + def _selected_node_changed(self, new): + """ Static trait change handler. """ + if self.selected_node: + self.selected_page = self.selected_node.page + + #### Methods ############################################################## + +
[docs] def apply(self): + """ Apply all changes made in the manager. """ + + for page in self.pages: + page.apply()
+ + ########################################################################### + # Private interface. + ########################################################################### + + def _get_parent(self, root, page): + """ Return the page's parent preference node. """ + + parent = root + + if len(page.category) > 0: + components = page.category.split("/") + for component in components: + parent = parent.lookup(component) + + return parent
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/preferences/ui/preferences_node.html b/5.0/_modules/apptools/preferences/ui/preferences_node.html new file mode 100644 index 000000000..2aed6f3a0 --- /dev/null +++ b/5.0/_modules/apptools/preferences/ui/preferences_node.html @@ -0,0 +1,221 @@ + + + + + + + apptools.preferences.ui.preferences_node — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.preferences.ui.preferences_node

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" Abstract base class for a node in a preferences dialog. """
+
+# Enthought library imports.
+from traits.api import Delegate, Instance
+
+# Local imports.
+from .i_preferences_page import IPreferencesPage
+from .tree_item import TreeItem
+
+
+
[docs]class PreferencesNode(TreeItem): + """Abstract base class for a node in a preferences dialog. + + A preferences node has a name and an image which are used to represent the + node in a preferences dialog (usually in the form of a tree). + + """ + + #### 'PreferenceNode' interface ########################################### + + # The page's help identifier (optional). If a help Id *is* provided then + # there will be a 'Help' button shown on the preference page. + help_id = Delegate("page") + + # The page name (this is what is shown in the preferences dialog. + name = Delegate("page") + + # The page that we are a node for. + page = Instance(IPreferencesPage) + + ########################################################################### + # 'object' interface. + ########################################################################### + + def __str__(self): + """ Returns the string representation of the item. """ + + if self.page is None: + s = "root" + + else: + s = self.page.name + + return s + + __repr__ = __str__ + + ########################################################################### + # 'PreferencesNode' interface. + ########################################################################### + +
[docs] def create_page(self, parent): + """ Creates the preference page for this node. """ + + return self.page.create_control(parent)
+ +
[docs] def lookup(self, name): + """Returns the child of this node with the specified Id. + + Returns None if no such child exists. + + """ + + for node in self.children: + if node.name == name: + break + + else: + node = None + + return node
+ + ########################################################################### + # Debugging interface. + ########################################################################### + +
[docs] def dump(self, indent=""): + """ Pretty-print the node to stdout. """ + + print(indent, "Node", str(self)) + + for child in self.children: + child.dump(indent + " ")
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/preferences/ui/preferences_page.html b/5.0/_modules/apptools/preferences/ui/preferences_page.html new file mode 100644 index 000000000..b9aa66f87 --- /dev/null +++ b/5.0/_modules/apptools/preferences/ui/preferences_page.html @@ -0,0 +1,233 @@ + + + + + + + apptools.preferences.ui.preferences_page — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.preferences.ui.preferences_page

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" A page in a preferences dialog. """
+
+
+# Enthought library imports.
+from apptools.preferences.api import PreferencesHelper
+from traits.api import Any, Dict, Str, provides
+
+# Local imports.
+from .i_preferences_page import IPreferencesPage
+
+
+
[docs]@provides(IPreferencesPage) +class PreferencesPage(PreferencesHelper): + """ A page in a preferences dialog. """ + + #### 'IPreferencesPage' interface ######################################### + + # The page's category (e.g. 'General/Appearance'). The empty string means + # that this is a top-level page. + category = Str + + # DEPRECATED: The help_id was never fully implemented, and it's been + # over two years (now 4/2009). The original goal was for the the Help + # button to automatically appear and connect to a help page with a + # help_id. Not removing the trait right now to avoid breaking code + # that may be checking for this. + # + # Use PreferencesManager.show_help and trait show_help metadata instead. + help_id = Str + + # The page name (this is what is shown in the preferences dialog. + name = Str + + #### Private interface #################################################### + + # The traits UI that represents the page. + _ui = Any + + # A dictionary containing the traits that have been changed since the + # last call to 'apply'. + _changed = Dict + + ########################################################################### + # 'IPreferencesPage' interface. + ########################################################################### + +
[docs] def apply(self): + """ Apply the page's preferences. """ + + path = self._get_path() + + for trait_name, value in self._changed.items(): + if self._is_preference_trait(trait_name): + self.preferences.set("%s.%s" % (path, trait_name), value) + + self._changed.clear()
+ + ########################################################################### + # Private interface. + ########################################################################### + + #### Trait change handlers ################################################ + + def _anytrait_changed(self, trait_name, old, new): + """Static trait change handler. + + This is an important override! In the base-class when a trait is + changed the preferences node is updated too. Here, we stop that from + happening and just make a note of what changes have been made. The + preferences node gets updated when the 'apply' method is called. + + """ + + if self._is_preference_trait(trait_name): + self._changed[trait_name] = new + elif trait_name.endswith("_items"): + # If the trait was a list or dict '_items' trait then just treat it + # as if the entire list or dict was changed. + trait_name = trait_name[:-6] + if self._is_preference_trait(trait_name): + self._changed[trait_name] = getattr(self, trait_name) + + # fixme: Pretty much duplicated in 'PreferencesHelper' (except for the + # class name of course!). + def _is_preference_trait(self, trait_name): + """ Return True if a trait represents a preference value. """ + + if ( + trait_name.startswith("_") + or trait_name.endswith("_") + or trait_name in PreferencesPage.class_traits() + ): + return False + + return trait_name in self.editable_traits()
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/preferences/ui/tree_item.html b/5.0/_modules/apptools/preferences/ui/tree_item.html new file mode 100644 index 000000000..ffcc109ca --- /dev/null +++ b/5.0/_modules/apptools/preferences/ui/tree_item.html @@ -0,0 +1,269 @@ + + + + + + + apptools.preferences.ui.tree_item — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.preferences.ui.tree_item

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" A generic base-class for items in a tree data structure.
+
+An example:-
+
+root = TreeItem(data='Root')
+
+fruit = TreeItem(data='Fruit')
+fruit.append(TreeItem(data='Apple', allows_children=False))
+fruit.append(TreeItem(data='Orange', allows_children=False))
+fruit.append(TreeItem(data='Pear', allows_children=False))
+root.append(fruit)
+
+veg = TreeItem(data='Veg')
+veg.append(TreeItem(data='Carrot', allows_children=False))
+veg.append(TreeItem(data='Cauliflower', allows_children=False))
+veg.append(TreeItem(data='Sprout', allows_children=False))
+root.append(veg)
+
+"""
+
+
+# Enthought library imports.
+from traits.api import Any, Bool, HasTraits, Instance, List, Property
+
+
+
[docs]class TreeItem(HasTraits): + """ A generic base-class for items in a tree data structure. """ + + #### 'TreeItem' interface ################################################# + + # Does this item allow children? + allows_children = Bool(True) + + # The item's children. + children = List(Instance("TreeItem")) + + # Arbitrary data associated with the item. + data = Any + + # Does the item have any children? + has_children = Property(Bool) + + # The item's parent. + parent = Instance("TreeItem") + + ########################################################################### + # 'object' interface. + ########################################################################### + + def __str__(self): + """ Returns the informal string representation of the object. """ + + if self.data is None: + s = "" + + else: + s = str(self.data) + + return s + + ########################################################################### + # 'TreeItem' interface. + ########################################################################### + + #### Properties ########################################################### + + # has_children + def _get_has_children(self): + """ True iff the item has children. """ + + return len(self.children) != 0 + + #### Methods ############################################################## + +
[docs] def append(self, child): + """Appends a child to this item. + + This removes the child from its current parent (if it has one). + + """ + + return self.insert(len(self.children), child)
+ +
[docs] def insert(self, index, child): + """Inserts a child into this item at the specified index. + + This removes the child from its current parent (if it has one). + + """ + + if child.parent is not None: + child.parent.remove(child) + + child.parent = self + self.children.insert(index, child) + + return child
+ +
[docs] def remove(self, child): + """ Removes a child from this item. """ + + child.parent = None + self.children.remove(child) + + return child
+ +
[docs] def insert_before(self, before, child): + """Inserts a child into this item before the specified item. + + This removes the child from its current parent (if it has one). + + """ + + index = self.children.index(before) + + self.insert(index, child) + + return (index, child)
+ +
[docs] def insert_after(self, after, child): + """Inserts a child into this item after the specified item. + + This removes the child from its current parent (if it has one). + + """ + + index = self.children.index(after) + + self.insert(index + 1, child) + + return (index, child)
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/preferences/ui/widget_editor.html b/5.0/_modules/apptools/preferences/ui/widget_editor.html new file mode 100644 index 000000000..431d64f87 --- /dev/null +++ b/5.0/_modules/apptools/preferences/ui/widget_editor.html @@ -0,0 +1,232 @@ + + + + + + + apptools.preferences.ui.widget_editor — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.preferences.ui.widget_editor

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" An instance editor that allows total control over widget creation. """
+
+
+# Enthought library imports.
+from traits.api import Any
+from traitsui.api import EditorFactory
+from traitsui.toolkit import toolkit_object
+Editor = toolkit_object('editor:Editor')
+
+
+class _WidgetEditor(Editor):
+    """ An instance editor that allows total control over widget creation. """
+
+    #### '_WidgetEditor' interface ############################################
+
+    # The toolkit-specific parent of the editor.
+    parent = Any
+
+    ###########################################################################
+    # '_WidgetEditor' interface.
+    ###########################################################################
+
+    def init(self, parent):
+        """ Initialize the editor. """
+
+        self.parent = parent
+
+        # fixme: What if there are no pages?!?
+        page = self.object.pages[0]
+
+        # Create the editor's control.
+        self.control = page.create_control(parent)
+
+        # Listen for the page being changed.
+        self.object.on_trait_change(self._on_page_changed, "selected_page")
+
+    def dispose(self):
+        """ Dispose of the editor. """
+
+        page = self.object.selected_page
+        page.destroy_control()
+
+    def update_editor(self):
+        """ Update the editor. """
+
+        pass
+
+    ###########################################################################
+    # Private interface.
+    ###########################################################################
+
+    def _on_page_changed(self, obj, trait_name, old, new):
+        """ Dynamic trait change handler. """
+
+        if old is not None:
+            old.destroy_control()
+
+        if new is not None:
+            self.control = new.create_control(self.parent)
+
+
+
[docs]class WidgetEditor(EditorFactory): + """ A factory widget editors. """ + + ########################################################################### + # 'object' interface. + ########################################################################### + + def __call__(self, *args, **traits): + """ Call the object. """ + + return self.trait_set(**traits) + + ########################################################################### + # 'EditorFactory' interface. + ########################################################################### + +
[docs] def simple_editor(self, ui, object, name, description, parent): + """ Create a simple editor. """ + + editor = _WidgetEditor( + parent, + factory=self, + ui=ui, + object=object, + name=name, + description=description, + ) + + return editor
+ + custom_editor = simple_editor + text_editor = simple_editor + readonly_editor = simple_editor
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/scripting/package_globals.html b/5.0/_modules/apptools/scripting/package_globals.html new file mode 100644 index 000000000..34096d085 --- /dev/null +++ b/5.0/_modules/apptools/scripting/package_globals.html @@ -0,0 +1,159 @@ + + + + + + + apptools.scripting.package_globals — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.scripting.package_globals

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+"""
+Globals for the scripting package.
+"""
+
+
+# The global recorder.
+_recorder = None
+
+
+
[docs]def get_recorder(): + """Return the global recorder. Does not create a new one if none + exists. + """ + global _recorder + return _recorder
+ + +
[docs]def set_recorder(rec): + """Set the global recorder instance.""" + global _recorder + _recorder = rec
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/scripting/recordable.html b/5.0/_modules/apptools/scripting/recordable.html new file mode 100644 index 000000000..324ccca9d --- /dev/null +++ b/5.0/_modules/apptools/scripting/recordable.html @@ -0,0 +1,186 @@ + + + + + + + apptools.scripting.recordable — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.scripting.recordable

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+"""
+Decorator to mark functions and methods as recordable.
+"""
+
+from .package_globals import get_recorder
+
+
+# Guard to ensure that only the outermost recordable call is recorded
+# and nested calls ignored.
+_outermost_call = True
+
+
+
[docs]def recordable(func): + """A decorator that wraps a function into one that is recordable. + + This will record the function only if the global recorder has been + set via a `set_recorder` function call. + """ + + def _wrapper(*args, **kw): + """A wrapper returned to replace the decorated function.""" + global _outermost_call + + # Boolean to specify if the method was recorded or not. + record = False + if _outermost_call: + # Get the recorder. + rec = get_recorder() + if rec is not None: + _outermost_call = False + # Record the method if recorder is available. + record = True + try: + result = rec.record_function(func, args, kw) + finally: + _outermost_call = True + if not record: + # If the method was not recorded, just call it. + result = func(*args, **kw) + + return result + + # Mimic the actual function. + _wrapper.__name__ = func.__name__ + _wrapper.__doc__ = func.__doc__ + _wrapper.__dict__.update(func.__dict__) + + return _wrapper
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/scripting/recorder.html b/5.0/_modules/apptools/scripting/recorder.html new file mode 100644 index 000000000..2c84d2f66 --- /dev/null +++ b/5.0/_modules/apptools/scripting/recorder.html @@ -0,0 +1,874 @@ + + + + + + + apptools.scripting.recorder — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.scripting.recorder

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+"""
+Code to support recording to a readable and executable Python script.
+
+FIXME:
+    - Support for dictionaries?
+"""
+
+import builtins
+import warnings
+
+from traits.api import (
+    HasTraits,
+    List,
+    Str,
+    Dict,
+    Bool,
+    Property,
+    Int,
+    Instance,
+)
+from traits.util.camel_case import camel_case_to_python
+
+
+###############################################################################
+# `_RegistryData` class.
+###############################################################################
+class _RegistryData(HasTraits):
+    # Object's script ID
+    script_id = Property(Str)
+
+    # Path to object in object hierarchy.
+    path = Property(Str)
+
+    # Parent data for this object if any.
+    parent_data = Instance("_RegistryData", allow_none=True)
+
+    # The name of the trait on the parent which is this object.
+    trait_name_on_parent = Str("")
+
+    # List of traits we are listening for on this object.
+    names = List(Str)
+
+    # Nested recordable instances on the object.
+    sub_recordables = List(Str)
+
+    # List of traits that are lists.
+    list_names = List(Str)
+
+    _script_id = Str("")
+
+    ###########################################################################
+    # Non-public interface.
+    ###########################################################################
+    def _get_path(self):
+        pdata = self.parent_data
+        path = ""
+        if pdata is not None:
+            pid = pdata.script_id
+            ppath = pdata.path
+            tnop = self.trait_name_on_parent
+            if "[" in tnop:
+                # If the object is a nested object through an iterator,
+                # we instantiate it and don't refer to it through the
+                # path, this makes scripting convenient.
+                if len(ppath) == 0:
+                    path = pid + "." + tnop
+                else:
+                    path = ppath + "." + tnop
+            else:
+                path = ppath + "." + tnop
+
+        return path
+
+    def _get_script_id(self):
+        sid = self._script_id
+        if len(sid) == 0:
+            pdata = self.parent_data
+            sid = pdata.script_id + "." + self.trait_name_on_parent
+        return sid
+
+    def _set_script_id(self, id):
+        self._script_id = id
+
+
+###############################################################################
+# `RecorderError` class.
+###############################################################################
+
[docs]class RecorderError(Exception): + pass
+ + +############################################################################### +# `Recorder` class. +############################################################################### +
[docs]class Recorder(HasTraits): + + # The lines of code recorded. + lines = List(Str) + + # Are we recording or not? + recording = Bool(False, desc="if script recording is enabled or not") + + # The Python script we have recorded so far. This is just a + # convenience trait for the `get_code()` method. + script = Property(Str) + + ######################################## + # Private traits. + + # Dict used to store information on objects registered. It stores a + # unique name for the object and its path in the object hierarchy + # traversed. + _registry = Dict + + # Reverse registry with keys as script_id and object as value. + _reverse_registry = Dict + + # A mapping to generate unique names for objects. The key is the + # name used (which is something derived from the class name of the + # object) and the value is an integer describing the number of times + # that variable name has been used earlier. + _name_map = Dict(Str, Int) + + # A list of special reserved script IDs. This is handy when you + # want a particular object to have an easy to read script ID and not + # the default one based on its class name. This leads to slightly + # easier to read scripts. + _special_ids = List + + # What are the known names in the script? By known names we mean + # names which are actually bound to objects. + _known_ids = List(Str) + + # The known types in the namespace. + _known_types = List(Str) + + # A guard to check if we are currently in a recorded function call, + # in which case we don't want to do any recording. + _in_function = Bool(False) + + ########################################################################### + # `Recorder` interface. + ########################################################################### +
[docs] def record(self, code): + """Record a string to be stored to the output file. + + Parameters + ---------- + + code : str + A string of text. + """ + if self.recording and not self._in_function: + lines = self.lines + # Analyze the code and add extra code if needed. + self._analyze_code(code) + # Add the code. + lines.append(code)
+ +
[docs] def register( + self, + object, + parent=None, + trait_name_on_parent="", + ignore=None, + known=False, + script_id=None, + ): + """Register an object with the recorder. This sets up the + object for recording. + + By default all traits (except those starting and ending with + '_') are recorded. For attributes that are themselves + recordable, one may mark traits with a 'record' metadata as + follows: + + - If metadata `record=False` is set, the nested object will not be + recorded. + + - If `record=True`, then that object is also recorded if it is + not `None`. + + If the object is a list or dict that is marked with + `record=True`, the list is itself not listened to for changes + but all its contents are registered. + + If the `object` has a trait named `recorder` then this recorder + instance will be set to it if possible. + + Parameters + ---------- + + object : Instance(HasTraits) + The object to register in the registry. + + parent : Instance(HasTraits) + An optional parent object in which `object` is contained + + trait_name_on_parent : str + An optional trait name of the `object` in the `parent`. + + ignore : list(str) + An optional list of trait names on the `object` to be + ignored. + + known : bool + Optional specification if the `object` id is known on the + interpreter. This is needed if you are manually injecting + code to define/create an object. + + script_id : str + Optionally specify a script_id to use for this object. It + is not guaranteed that this ID will be used since it may + already be in use. + """ + registry = self._registry + + # Do nothing if the object is already registered. + if object in registry: + return + + # When parent is specified the trait_name_on_parent must also be. + if parent is not None: + assert len(trait_name_on_parent) > 0 + + if ignore is None: + ignore = [] + + if isinstance(object, HasTraits): + # Always ignore these. + ignore.extend(["trait_added", "trait_modified"]) + + sub_recordables = list(object.traits(record=True).keys()) + # Find all the trait names we must ignore. + ignore.extend(object.traits(record=False).keys()) + # The traits to listen for. + tnames = [ + t + for t in object.trait_names() + if not t.startswith("_") + and not t.endswith("_") + and t not in ignore + ] + # Find all list traits. + trts = object.traits() + list_names = [] + for t in tnames: + tt = trts[t].trait_type + if ( + hasattr(tt, "default_value_type") + and tt.default_value_type == 5 + ): + list_names.append(t) + else: + # No traits, so we can't do much. + sub_recordables = [] + tnames = [] + list_names = [] + + # Setup the registry data. + + # If a script id is supplied try and use it. + sid = "" + if script_id is not None: + r_registry = self._reverse_registry + while script_id in r_registry: + script_id = "%s1" % script_id + sid = script_id + # Add the chosen id to special_id list. + self._special_ids.append(sid) + + if parent is None: + pdata = None + if len(sid) == 0: + sid = self._get_unique_name(object) + else: + pdata = self._get_registry_data(parent) + tnop = trait_name_on_parent + if "[" in tnop: + # If the object is a nested object through an iterator, + # we instantiate it and don't refer to it through the + # path, this makes scripting convenient. + sid = self._get_unique_name(object) + + # Register the object with the data. + data = _RegistryData( + script_id=sid, + parent_data=pdata, + trait_name_on_parent=trait_name_on_parent, + names=tnames, + sub_recordables=sub_recordables, + list_names=list_names, + ) + registry[object] = data + + # Now get the script id of the object -- note that if sid is '' + # above then the script_id is computed from that of the parent. + sid = data.script_id + # Setup reverse registry so we can get the object from the + # script_id. + self._reverse_registry[sid] = object + + # Record the script_id if the known argument is explicitly set to + # True. + if known: + self._known_ids.append(sid) + + # Try and set the recorder attribute if necessary. + if hasattr(object, "recorder"): + try: + object.recorder = self + except Exception as e: + msg = "Cannot set 'recorder' trait of object %r: " "%s" % ( + object, + e, + ) + warnings.warn(msg, warnings.RuntimeWarning) + + if isinstance(object, HasTraits): + # Add handler for lists. + for name in list_names: + object.on_trait_change( + self._list_items_listner, "%s_items" % name + ) + + # Register all sub-recordables. + for name in sub_recordables: + obj = getattr(object, name) + if isinstance(obj, list): + # Don't register the object itself but register its + # children. + for i, child in enumerate(obj): + attr = "%s[%d]" % (name, i) + self.register( + child, parent=object, trait_name_on_parent=attr + ) + elif obj is not None: + self.register( + obj, parent=object, trait_name_on_parent=name + ) + # Listen for changes to the trait itself so the newly + # assigned object can also be listened to. + object.on_trait_change(self._object_changed_handler, name) + # Now add listner for the object itself. + object.on_trait_change(self._listner, tnames)
+ +
[docs] def unregister(self, object): + """Unregister the given object from the recorder. This inverts + the logic of the `register(...)` method. + """ + registry = self._registry + # Do nothing if the object isn't registered. + if object not in registry: + return + + data = registry[object] + + # Try and unset the recorder attribute if necessary. + if hasattr(object, "recorder"): + try: + object.recorder = None + except Exception as e: + msg = "Cannot unset 'recorder' trait of object %r:" "%s" % ( + object, + e, + ) + warnings.warn(msg, warnings.RuntimeWarning) + + if isinstance(object, HasTraits): + # Remove all list_items handlers. + for name in data.list_names: + object.on_trait_change( + self._list_items_listner, "%s_items" % name, remove=True + ) + + # Unregister all sub-recordables. + for name in data.sub_recordables: + obj = getattr(object, name) + if isinstance(obj, list): + # Unregister the children. + for i, child in enumerate(obj): + self.unregister(child) + elif obj is not None: + self.unregister(obj) + # Remove the trait handler for trait assignments. + object.on_trait_change( + self._object_changed_handler, name, remove=True + ) + # Now remove listner for the object itself. + object.on_trait_change(self._listner, data.names, remove=True) + + # Remove the object data from the registry etc. + if data.script_id in self._known_ids: + self._known_ids.remove(data.script_id) + del self._reverse_registry[data.script_id] + del registry[object]
+ +
[docs] def save(self, file): + """Save the recorded lines to the given file. It does not close + the file. + """ + file.write(self.get_code()) + file.flush()
+ +
[docs] def record_function(self, func, args, kw): + """Record a function call given the function and its + arguments.""" + if self.recording and not self._in_function: + # Record the function name and arguments. + call_str = self._function_as_string(func, args, kw) + # Call the function. + try: + self._in_function = True + result = func(*args, **kw) + finally: + self._in_function = False + + # Register the result if it is not None. + if func.__name__ == "__init__": + f_self = args[0] + code = self._import_class_string(f_self.__class__) + self.lines.append(code) + return_str = self._registry.get(f_self).script_id + else: + return_str = self._return_as_string(result) + if len(return_str) > 0: + self.lines.append("%s = %s" % (return_str, call_str)) + else: + self.lines.append("%s" % (call_str)) + else: + result = func(*args, **kw) + return result
+ +
[docs] def ui_save(self): + """Save recording to file, pop up a UI dialog to find out where + and close the file when done. + """ + from pyface.api import FileDialog, OK + + wildcard = "Python files (*.py)|*.py|" + FileDialog.WILDCARD_ALL + dialog = FileDialog( + title="Save Script", action="save as", wildcard=wildcard + ) + if dialog.open() == OK: + fname = dialog.path + f = open(fname, "w") + self.save(f) + f.close()
+ +
[docs] def clear(self): + """Clears all previous recorded state and unregisters all + registered objects.""" + # First unregister any registered objects. + registry = self._registry + while len(registry) > 0: + self.unregister(list(registry.keys())[0]) + # Clear the various lists. + self.lines[:] = [] + self._registry.clear() + self._known_ids[:] = [] + self._name_map.clear() + self._reverse_registry.clear() + self._known_types[:] = [] + self._special_ids[:] = []
+ +
[docs] def get_code(self): + """Returns the recorded lines as a string of printable code.""" + return "\n".join(self.lines) + "\n"
+ +
[docs] def is_registered(self, object): + """Returns True if the given object is registered with the + recorder.""" + return object in self._registry
+ +
[docs] def get_script_id(self, object): + """Returns the script_id of a registered object. Useful when + you want to manually add a record statement.""" + return self._get_registry_data(object).script_id
+ +
[docs] def get_object_path(self, object): + """Returns the path in the object hierarchy of a registered + object. Useful for debugging.""" + return self._get_registry_data(object).path
+ +
[docs] def write_script_id_in_namespace(self, script_id): + """If a script_id is not known in the current script's namespace, + this sets it using the path of the object or actually + instantiating it. If this is not possible (since the script_id + matches no existing object), nothing is recorded but the + framework is notified that the particular script_id is available + in the namespace. This is useful when you want to inject code + in the namespace to create a particular object. + """ + if not self.recording: + return + known_ids = self._known_ids + if script_id not in known_ids: + obj = self._reverse_registry.get(script_id) + # Add the ID to the known_ids. + known_ids.append(script_id) + if obj is not None: + data = self._registry.get(obj) + result = "" + if len(data.path) > 0: + # Record code for instantiation of object. + result = "%s = %s" % (script_id, data.path) + else: + # This is not the best thing to do but better than + # nothing. + result = self._import_class_string(obj.__class__) + cls = obj.__class__.__name__ + result += "\n%s = %s()" % (script_id, cls) + + if len(result) > 0: + self.lines.extend(result.split("\n"))
+ + ########################################################################### + # Non-public interface. + ########################################################################### + def _get_unique_name(self, obj): + """Return a unique object name (a string). Note that this does + not cache the object, so if called with the same object 3 times + you'll get three different names. + """ + cname = obj.__class__.__name__ + nm = self._name_map + result = "" + builtin = False + if cname in builtins.__dict__: + builtin = True + if hasattr(obj, "__name__"): + cname = obj.__name__ + else: + cname = camel_case_to_python(cname) + + special_ids = self._special_ids + while len(result) == 0 or result in special_ids: + if cname in nm: + id = nm[cname] + 1 + nm[cname] = id + result = "%s%d" % (cname, id) + else: + nm[cname] = 0 + # The first id doesn't need a number if it isn't builtin. + if builtin: + result = "%s0" % (cname) + else: + result = cname + return result + + def _get_registry_data(self, object): + """Get the data for an object from registry.""" + data = self._registry.get(object) + if data is None: + msg = ( + "Recorder: Can't get script_id since object %s not registered" + ) + raise RecorderError(msg % (object)) + return data + + def _listner(self, object, name, old, new): + """The listner for trait changes on an object. + + This is called by child listners or when any of the recordable + object's traits change when recording to a script is enabled. + + Parameters: + ----------- + + object : Object which has changed. + + name : extended name of attribute that changed. + + old : Old value. + + new : New value. + + """ + if self.recording and not self._in_function: + new_repr = repr(new) + sid = self._get_registry_data(object).script_id + if len(sid) == 0: + msg = "%s = %r" % (name, new) + else: + msg = "%s.%s = %r" % (sid, name, new) + if new_repr.startswith("<") and new_repr.endswith(">"): + self.record("# " + msg) + else: + self.record(msg) + + def _list_items_listner(self, object, name, old, event): + """The listner for *_items on list traits of the object.""" + # Set the path of registered objects in the modified list and + # all their children. This is done by unregistering the object + # and re-registering them. This is slow but. + registry = self._registry + sid = registry.get(object).script_id + trait_name = name[:-6] + items = getattr(object, trait_name) + for (i, item) in enumerate(items): + if item in registry: + data = registry.get(item) + tnop = data.trait_name_on_parent + if len(tnop) > 0: + data.trait_name_on_parent = "%s[%d]" % (trait_name, i) + + # Record the change. + if self.recording and not self._in_function: + index = event.index + removed = event.removed + added = event.added + nr = len(removed) + slice = "[%d:%d]" % (index, index + nr) + rhs = [self._object_as_string(item) for item in added] + rhs = ", ".join(rhs) + obj = "%s.%s" % (sid, name[:-6]) + msg = "%s%s = [%s]" % (obj, slice, rhs) + self.record(msg) + + def _object_changed_handler(self, object, name, old, new): + """Called when a child recordable object has been reassigned.""" + registry = self._registry + if old is not None: + if old in registry: + self.unregister(old) + if new is not None: + if new not in registry: + self.register(new, parent=object, trait_name_on_parent=name) + + def _get_script(self): + return self.get_code() + + def _analyze_code(self, code): + """Analyze the code and return extra code if needed.""" + lhs = "" + try: + lhs = code.split()[0] + except IndexError: + pass + + if "." in lhs: + ob_name = lhs.split(".")[0] + self.write_script_id_in_namespace(ob_name) + + def _function_as_string(self, func, args, kw): + """Return a string representing the function call.""" + func_name = func.__name__ + func_code = func.__code__ + # Even if func is really a decorated method it never shows up as + # a bound or unbound method here, so we have to inspect the + # argument names to figure out if this is a method or function. + if func_code.co_argcount > 0 and func_code.co_varnames[0] == "self": + # This is a method, the first argument is bound to self. + f_self = args[0] + # Convert the remaining arguments to strings. + argl = [self._object_as_string(arg) for arg in args[1:]] + + # If this is __init__ we special case it. + if func_name == "__init__": + # Register the object. + self.register(f_self, known=True) + func_name = f_self.__class__.__name__ + else: + sid = self._object_as_string(f_self) + func_name = "%s.%s" % (sid, func_name) + else: + argl = [self._object_as_string(arg) for arg in args] + + # Convert the keyword args. + kwl = [ + "%s=%s" % (key, self._object_as_string(value)) + for key, value in kw.items() + ] + argl.extend(kwl) + + # Make a string representation of the args, kw. + argstr = ", ".join(argl) + return "%s(%s)" % (func_name, argstr) + + def _is_arbitrary_object(self, object): + """Return True if the object is an arbitrary non-primitive object. + + We assume that if the hex id of the object is in its string + representation then it is an arbitrary object. + """ + ob_id = id(object) + orepr = repr(object) + hex_id = "%x" % ob_id + return hex_id.upper() in orepr.upper() + + def _object_as_string(self, object): + """Return a string representing the object.""" + registry = self._registry + if object in registry: + # Return script id if the object is known; create the script + # id on the namespace if needed before that. + sid = registry.get(object).script_id + base_id = sid.split(".")[0] + self.write_script_id_in_namespace(base_id) + return sid + else: + if not self._is_arbitrary_object(object): + return repr(object) + + # If we get here, we just register the object and call ourselves + # again to do the needful. + self.register(object) + return self._object_as_string(object) + + def _return_as_string(self, object): + """Return a string given a returned object from a function.""" + result = "" + ignore = (float, complex, bool, int, str) + if object is not None and type(object) not in ignore: + # If object is not know, register it. + registry = self._registry + if object not in registry: + self.register(object) + result = registry.get(object).script_id + # Since this is returned it is known on the namespace. + known_ids = self._known_ids + if result not in known_ids: + known_ids.append(result) + return result + + def _import_class_string(self, cls): + """Import a class if needed.""" + cname = cls.__name__ + result = "" + if cname not in builtins.__dict__: + mod = cls.__module__ + typename = "%s.%s" % (mod, cname) + if typename not in self._known_types: + result = "from %s import %s" % (mod, cname) + self._known_types.append(typename) + return result
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/scripting/recorder_with_ui.html b/5.0/_modules/apptools/scripting/recorder_with_ui.html new file mode 100644 index 000000000..8323559a9 --- /dev/null +++ b/5.0/_modules/apptools/scripting/recorder_with_ui.html @@ -0,0 +1,226 @@ + + + + + + + apptools.scripting.recorder_with_ui — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.scripting.recorder_with_ui

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+"""
+A Recorder subclass that presents a simple user interface.
+"""
+
+from traits.api import Code, Button, Int, on_trait_change, Any
+from traitsui.api import View, Item, Group, HGroup, CodeEditor, spring, Handler
+
+from .recorder import Recorder
+
+
+###############################################################################
+# `CloseHandler` class.
+###############################################################################
+
[docs]class CloseHandler(Handler): + """This class cleans up after the UI for the recorder is closed.""" + +
[docs] def close(self, info, is_ok): + """This method is invoked when the user closes the UI.""" + recorder = info.object + recorder.on_ui_close() + return True
+ + +############################################################################### +# `RecorderWithUI` class. +############################################################################### +
[docs]class RecorderWithUI(Recorder): + """ + This class represents a Recorder but with a simple user interface. + """ + + # The code to display + code = Code(editor=CodeEditor(line="current_line")) + + # Button to save script to file. + save_script = Button("Save Script") + + # The current line to show, used by the editor. + current_line = Int + + # The root object which is being recorded. + root = Any + + ######################################## + # Traits View. + view = View( + Group( + HGroup( + Item("recording", show_label=True), + spring, + Item("save_script", show_label=False), + ), + Group(Item("code", show_label=False)), + ), + width=600, + height=360, + id="apptools.scripting.recorder_with_ui", + buttons=["Cancel"], + resizable=True, + handler=CloseHandler(), + ) + + ###################################################################### + # RecorderWithUI interface. + ###################################################################### +
[docs] def on_ui_close(self): + """Called from the CloseHandler when the UI is closed. This + method basically stops the recording. + """ + from .util import stop_recording + from .package_globals import get_recorder + + if get_recorder() is self: + stop_recording(self.root, save=False) + else: + self.recording = False + self.unregister(self.root)
+ + ###################################################################### + # Non-public interface. + ###################################################################### + @on_trait_change("lines[]") + def _update_code(self): + self.code = self.get_code() + self.current_line = len(self.lines) + 1 + + def _save_script_fired(self): + self.ui_save()
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/scripting/util.html b/5.0/_modules/apptools/scripting/util.html new file mode 100644 index 000000000..39a0ea9ef --- /dev/null +++ b/5.0/_modules/apptools/scripting/util.html @@ -0,0 +1,186 @@ + + + + + + + apptools.scripting.util — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.scripting.util

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+"""Simple utility functions provided by the scripting API.
+"""
+
+from .recorder import Recorder
+from .recorder_with_ui import RecorderWithUI
+from .package_globals import get_recorder, set_recorder
+
+
+###############################################################################
+# Utility functions.
+###############################################################################
+
[docs]def start_recording(object, ui=True, **kw): + """Convenience function to start recording. Returns the recorder. + + Parameters + ---------- + + object : object to record. + + ui : bool specifying if a UI is to be shown or not + + kw : Keyword arguments to pass to the register function of the + recorder. + """ + if ui: + r = RecorderWithUI(root=object) + r.edit_traits(kind="live") + else: + r = Recorder() + # Set the global recorder. + set_recorder(r) + r.recording = True + r.register(object, **kw) + return r
+ + +
[docs]def stop_recording(object, save=True): + """Stop recording the object. If `save` is `True`, this will pop up + a UI to ask where to save the script. + """ + recorder = get_recorder() + recorder.unregister(object) + recorder.recording = False + # Set the global recorder back to None + set_recorder(None) + # Save the script. + if save: + recorder.ui_save()
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/selection/errors.html b/5.0/_modules/apptools/selection/errors.html new file mode 100644 index 000000000..441880612 --- /dev/null +++ b/5.0/_modules/apptools/selection/errors.html @@ -0,0 +1,172 @@ + + + + + + + apptools.selection.errors — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.selection.errors

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+
+
+
[docs]class ProviderNotRegisteredError(Exception): + """ Raised when a provider is requested by ID and not found. """ + + def __init__(self, provider_id): + self.provider_id = provider_id + + def __str__(self): + msg = "Selection provider with ID '{id}' not found." + return msg.format(id=self.provider_id)
+ + +
[docs]class IDConflictError(Exception): + """ Raised when a provider is added and its ID is already registered. """ + + def __init__(self, provider_id): + self.provider_id = provider_id + + def __str__(self): + msg = "A selection provider with ID '{id}' is already registered." + return msg.format(id=self.provider_id)
+ + +
[docs]class ListenerNotConnectedError(Exception): + """ Raised when a listener that was never connected is disconnected. """ + + def __init__(self, provider_id, listener): + self.provider_id = provider_id + self.listener = listener + + def __str__(self): + msg = "Selection listener {lr} is not connected to provider '{id}'." + return msg.format(lr=self.listener, id=self.provider_id)
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/selection/i_selection.html b/5.0/_modules/apptools/selection/i_selection.html new file mode 100644 index 000000000..bcb99d64a --- /dev/null +++ b/5.0/_modules/apptools/selection/i_selection.html @@ -0,0 +1,159 @@ + + + + + + + apptools.selection.i_selection — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.selection.i_selection

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+from traits.api import Interface, List, Str
+
+
+
[docs]class ISelection(Interface): + """ Collection of selected items. """ + + #: ID of the selection provider that created this selection object. + provider_id = Str + +
[docs] def is_empty(self): + """ Is the selection empty? """
+ + +
[docs]class IListSelection(ISelection): + """ Selection for ordered sequences of items. """ + + #: Selected objects. + items = List + + #: Indices of the selected objects in the selection provider. + indices = List
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/selection/i_selection_provider.html b/5.0/_modules/apptools/selection/i_selection_provider.html new file mode 100644 index 000000000..d2ffa29bc --- /dev/null +++ b/5.0/_modules/apptools/selection/i_selection_provider.html @@ -0,0 +1,176 @@ + + + + + + + apptools.selection.i_selection_provider — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.selection.i_selection_provider

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+from traits.api import Event, Interface, Str
+
+
+
[docs]class ISelectionProvider(Interface): + """ Source of selections. """ + + #: Unique ID identifying the provider. + provider_id = Str() + + #: Event triggered when the selection changes. + #: The content of the event is an :class:`~.ISelection` instance. + selection = Event + +
[docs] def get_selection(self): + """Return the current selection. + + Returns: + selection -- ISelection + Object representing the current selection. + """
+ +
[docs] def set_selection(self, items, ignore_missing=False): + """Set the current selection to the given items. + + If ``ignore_missing`` is ``True``, items that are not available in the + selection provider are silently ignored. If it is ``False`` (default), + an :class:`~.ValueError` should be raised. + + Arguments: + items -- list + List of items to be selected. + + ignore_missing -- bool + If ``False`` (default), the provider raises an exception if any + of the items in ``items`` is not available to be selected. + Otherwise, missing elements are silently ignored, and the rest + is selected. + """
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/selection/list_selection.html b/5.0/_modules/apptools/selection/list_selection.html new file mode 100644 index 000000000..cd640f662 --- /dev/null +++ b/5.0/_modules/apptools/selection/list_selection.html @@ -0,0 +1,196 @@ + + + + + + + apptools.selection.list_selection — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.selection.list_selection

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+from traits.api import HasTraits, List, provides, Str
+
+from apptools.selection.i_selection import IListSelection
+
+
+
[docs]@provides(IListSelection) +class ListSelection(HasTraits): + """Selection for ordered sequences of items. + + This is the default implementation of the :class:`~.IListSelection` + interface. + """ + + #### 'ISelection' protocol ################################################ + + #: ID of the selection provider that created this selection object. + provider_id = Str + +
[docs] def is_empty(self): + """ Is the selection empty? """ + return len(self.items) == 0
+ + #### 'IListSelection' protocol ############################################ + + #: Selected objects. + items = List + + #: Indices of the selected objects in the selection provider. + indices = List + + #### 'ListSelection' class protocol ####################################### + +
[docs] @classmethod + def from_available_items(cls, provider_id, selected, all_items): + """Create a list selection given a list of all available items. + + Fills in the required information (in particular, the indices) based + on a list of selected items and a list of all available items. + + .. note:: + - The list of available items must not contain any duplicate items. + - It is expected that ``selected`` is populated by items in + ``all_items``. + + """ + number_of_items = len(all_items) + indices = [] + + for item in selected: + for index in range(number_of_items): + if all_items[index] is item: + indices.append(index) + break + else: + msg = "Selected item: {!r}, could not be found" + raise ValueError(msg.format(item)) + + return cls(provider_id=provider_id, items=selected, indices=indices)
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/selection/selection_service.html b/5.0/_modules/apptools/selection/selection_service.html new file mode 100644 index 000000000..53ff56ffa --- /dev/null +++ b/5.0/_modules/apptools/selection/selection_service.html @@ -0,0 +1,325 @@ + + + + + + + apptools.selection.selection_service — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.selection.selection_service

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+from traits.api import Dict, HasTraits
+
+from apptools.selection.errors import (
+    ProviderNotRegisteredError,
+    IDConflictError,
+    ListenerNotConnectedError,
+)
+
+
+
[docs]class SelectionService(HasTraits): + """The selection service connects selection providers and listeners. + + The selection service is a register of selection providers, i.e., objects + that publish their current selection. + + Selections can be requested actively, by explicitly requesting the current + selection in a provider (:meth:`get_selection(id)`), or passively by + connecting selection listeners. + """ + + #### 'SelectionService' protocol ########################################## + +
[docs] def add_selection_provider(self, provider): + """Add a selection provider. + + The provider is identified by its ID. If a provider with the same + ID has been already registered, an :class:`~.IDConflictError` + is raised. + + Arguments: + provider -- ISelectionProvider + The selection provider added to the internal registry. + + """ + provider_id = provider.provider_id + if self.has_selection_provider(provider_id): + raise IDConflictError(provider_id=provider_id) + + self._providers[provider_id] = provider + + if provider_id in self._listeners: + self._connect_all_listeners(provider_id)
+ +
[docs] def has_selection_provider(self, provider_id): + """ Has a provider with the given ID been registered? """ + return provider_id in self._providers
+ +
[docs] def remove_selection_provider(self, provider): + """Remove a selection provider. + + If the provider has not been registered, a + :class:`~.ProviderNotRegisteredError` is raised. + + Arguments: + provider -- ISelectionProvider + The selection provider added to the internal registry. + """ + provider_id = provider.provider_id + self._raise_if_not_registered(provider_id) + + if provider_id in self._listeners: + self._disconnect_all_listeners(provider_id) + + del self._providers[provider_id]
+ +
[docs] def get_selection(self, provider_id): + """Return the current selection of the provider with the given ID. + + If a provider with that ID has not been registered, a + :class:`~.ProviderNotRegisteredError` is raised. + + Arguments: + provider_id -- str + The selection provider ID. + + Returns: + selection -- ISelection + The current selection of the provider. + """ + self._raise_if_not_registered(provider_id) + provider = self._providers[provider_id] + return provider.get_selection()
+ +
[docs] def set_selection(self, provider_id, items, ignore_missing=False): + """Set the current selection in a provider to the given items. + + If a provider with the given ID has not been registered, a + :class:`~.ProviderNotRegisteredError` is raised. + + If ``ignore_missing`` is ``True``, items that are not available in the + selection provider are silently ignored. If it is ``False`` (default), + a :class:`ValueError` should be raised. + + Arguments: + provider_id -- str + The selection provider ID. + + items -- list + List of items to be selected. + + ignore_missing -- bool + If ``False`` (default), the provider raises an exception if any + of the items in ``items`` is not available to be selected. + Otherwise, missing elements are silently ignored, and the rest + is selected. + """ + self._raise_if_not_registered(provider_id) + provider = self._providers[provider_id] + return provider.set_selection(items, ignore_missing=ignore_missing)
+ +
[docs] def connect_selection_listener(self, provider_id, func): + """Connect a listener to selection events from a specific provider. + + The signature if the listener callback is ``func(i_selection)``. + The listener is called: + + 1) When a provider with the given ID is registered, with its initial + selection as argument, or + + 2) whenever the provider fires a selection event. + + It is perfectly valid to connect a listener before a provider with the + given ID is registered. The listener will remain connected even if + the provider is repeatedly connected and disconnected. + + Arguments: + provider_id -- str + The selection provider ID. + func -- callable(i_selection) + A callable object that is notified when the selection changes. + """ + self._listeners.setdefault(provider_id, []) + self._listeners[provider_id].append(func) + + if self.has_selection_provider(provider_id): + self._toggle_listener(provider_id, func, remove=False)
+ +
[docs] def disconnect_selection_listener(self, provider_id, func): + """Disconnect a listener from a specific provider. + + Arguments: + provider_id -- str + The selection provider ID. + func -- callable(provider_id, i_selection) + A callable object that is notified when the selection changes. + """ + + if self.has_selection_provider(provider_id): + self._toggle_listener(provider_id, func, remove=True) + + try: + self._listeners[provider_id].remove(func) + except (ValueError, KeyError): + raise ListenerNotConnectedError( + provider_id=provider_id, listener=func + )
+ + #### Private protocol ##################################################### + + _listeners = Dict() + + _providers = Dict() + + def _toggle_listener(self, provider_id, func, remove): + provider = self._providers[provider_id] + provider.on_trait_change(func, "selection", remove=remove) + + def _connect_all_listeners(self, provider_id): + """Connect all listeners connected to a provider. + + As soon as they are connected, they receive the initial selection. + """ + provider = self._providers[provider_id] + selection = provider.get_selection() + for func in self._listeners[provider_id]: + self._toggle_listener(provider_id, func, remove=False) + # FIXME: make this robust to notifications that raise exceptions. + # Can we send the error to the traits exception hook? + func(selection) + + def _disconnect_all_listeners(self, provider_id): + for func in self._listeners[provider_id]: + self._toggle_listener(provider_id, func, remove=True) + + def _raise_if_not_registered(self, provider_id): + if not self.has_selection_provider(provider_id): + raise ProviderNotRegisteredError(provider_id=provider_id)
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/type_registry/type_registry.html b/5.0/_modules/apptools/type_registry/type_registry.html new file mode 100644 index 000000000..6a0642e6b --- /dev/null +++ b/5.0/_modules/apptools/type_registry/type_registry.html @@ -0,0 +1,403 @@ + + + + + + + apptools.type_registry.type_registry — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.type_registry.type_registry

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+
+
+
[docs]def get_mro(obj_class): + """Get a reasonable method resolution order of a class and its + superclasses for both old-style and new-style classes. + """ + if not hasattr(obj_class, "__mro__"): + # Old-style class. Mix in object to make a fake new-style class. + try: + obj_class = type(obj_class.__name__, (obj_class, object), {}) + except TypeError: + # Old-style extension type that does not descend from object. + mro = [obj_class] + else: + mro = obj_class.__mro__[1:-1] + else: + mro = obj_class.__mro__ + return mro
+ + +def _mod_name_key(typ): + """Return a '__module__:__name__' key for a type.""" + module = getattr(typ, "__module__", None) + name = getattr(typ, "__name__", None) + key = "{0}:{1}".format(module, name) + return key + + +
[docs]class TypeRegistry(object): + """Register objects for types. + + Each type maintains a stack of registered objects that can be pushed and + popped. + """ + + def __init__(self): + # Map types to lists of registered objects. The last is the most + # current and will be the one that is returned on lookup. + self.type_map = {} + + # Map '__module__:__name__' strings to lists of registered objects. + self.name_map = {} + + # Map abstract base classes to lists of registered objects. + self.abc_map = {} + + #### TypeRegistry public interface ######################################## + +
[docs] def push(self, typ, obj): + """Push an object onto the stack for the given type. + + Parameters + ---------- + typ : type or '__module__:__name__' string for a type + obj : object + The object to register. + """ + if isinstance(typ, str): + # Check the cached types. + for cls in self.type_map: + if _mod_name_key(cls) == typ: + self.type_map[cls].append(obj) + break + else: + if typ not in self.name_map: + self.name_map[typ] = [] + self.name_map[typ].append(obj) + else: + if typ not in self.type_map: + self.type_map[typ] = [] + self.type_map[typ].append(obj)
+ +
[docs] def push_abc(self, typ, obj): + """Push an object onto the stack for the given ABC. + + Parameters + ---------- + typ : abc.ABCMeta + obj : object + """ + if typ not in self.abc_map: + self.abc_map[typ] = [] + self.abc_map[typ].append(obj)
+ +
[docs] def pop(self, typ): + """Pop a registered object for the given type. + + Parameters + ---------- + typ : type or '__module__:__name__' string for a type + + Returns + ------- + obj : object + The last registered object for the type. + + Raises + ------ + KeyError if the type is not registered. + """ + if isinstance(typ, str): + if typ not in self.name_map: + # We may have it cached in the type map. We will have to + # iterate over all of the types to check. + for cls in self.type_map: + if _mod_name_key(cls) == typ: + old = self._pop_value(self.type_map, cls) + break + else: + raise KeyError("No registered value for {0!r}".format(typ)) + else: + old = self._pop_value(self.name_map, typ) + else: + if typ in self.type_map: + old = self._pop_value(self.type_map, typ) + elif typ in self.abc_map: + old = self._pop_value(self.abc_map, typ) + else: + old = self._pop_value(self.name_map, _mod_name_key(typ)) + return old
+ +
[docs] def lookup(self, instance): + """Look up the registered object for the given instance. + + Parameters + ---------- + instance : object + An instance of a possibly registered type. + + Returns + ------- + obj : object + The registered object for the type of the instance, one of the + type's superclasses, or else one of the ABCs the type implements. + + Raises + ------ + KeyError if the instance's type has not been registered. + """ + return self.lookup_by_type(type(instance))
+ +
[docs] def lookup_by_type(self, typ): + """Look up the registered object for a type. + + Parameters + ---------- + typ : type + + Returns + ------- + obj : object + The registered object for the type, one of its superclasses, or + else one of the ABCs it implements. + + Raises + ------ + KeyError if the type has not been registered. + """ + return self.lookup_all_by_type(typ)[-1]
+ +
[docs] def lookup_all(self, instance): + """Look up all the registered objects for the given instance. + + Parameters + ---------- + instance : object + An instance of a possibly registered type. + + Returns + ------- + objs : list of objects + The list of registered objects for the instance. If the given + instance is not registered, its superclasses are searched. If none + of the superclasses are registered, search the possible ABCs. + + Raises + ------ + KeyError if the instance's type has not been registered. + """ + return self.lookup_all_by_type(type(instance))
+ +
[docs] def lookup_all_by_type(self, typ): + """Look up all the registered objects for a type. + + Parameters + ---------- + typ : type + + Returns + ------- + objs : list of objects + The list of registered objects for the type. If the given type is + not registered, its superclasses are searched. If none of the + superclasses are registered, search the possible ABCs. + + Raises + ------ + KeyError if the type has not been registered. + """ + # If a concrete superclass is registered use it. + for cls in get_mro(typ): + if cls in self.type_map or self._in_name_map(cls): + objs = self.type_map[cls] + if objs: + return objs + + # None of the concrete superclasses. Check the ABCs. + for abstract, objs in self.abc_map.items(): + if issubclass(typ, abstract) and objs: + return objs + + # If we have reached here, the lookup failed. + raise KeyError("No registered value for {0!r}".format(typ))
+ + #### Private implementation ############################################### + + def _pop_value(self, mapping, key): + """Pop a value from a keyed stack in a mapping, taking care to remove + the key if the stack is depleted. + """ + objs = mapping[key] + old = objs.pop() + if not objs: + del mapping[key] + return old + + def _in_name_map(self, typ): + """Check if the given type is specified in the name map. + + Parameters + ---------- + typ : type + + Returns + ------- + is_in_name_map : bool + If True, the registered value will be moved over to the type map + for future lookups. + """ + key = _mod_name_key(typ) + if key in self.name_map: + self.type_map[typ] = self.name_map.pop(key) + return True + else: + return False
+ + +
[docs]class LazyRegistry(TypeRegistry): + """A type registry that will lazily import the registered objects. + + Register '__module__:__name__' strings for the lazily imported objects. + These will only be imported when the matching type is looked up. The module + name must be a fully-qualified absolute name with all of the parent + packages specified. + """ + +
[docs] def lookup_by_type(self, typ): + """Look up the registered object for a type.""" + mod_name = TypeRegistry.lookup_by_type(self, typ) + return self._import_object(mod_name)
+ + def _import_object(self, mod_object): + module, name = mod_object.split(":") + mod = __import__(module, {}, {}, [name], 0) + return getattr(mod, name)
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/undo/abstract_command.html b/5.0/_modules/apptools/undo/abstract_command.html new file mode 100644 index 000000000..48a26016b --- /dev/null +++ b/5.0/_modules/apptools/undo/abstract_command.html @@ -0,0 +1,205 @@ + + + + + + + apptools.undo.abstract_command — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.undo.abstract_command

+# ------------------------------------------------------------------------------
+# Copyright (c) 2007, Riverbank Computing Limited
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in enthought/LICENSE.txt and may be redistributed only
+# under the conditions described in the aforementioned license.  The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+# Thanks for using Enthought open source!
+#
+# Author: Riverbank Computing Limited
+# Description: <Enthought undo package component>
+# ------------------------------------------------------------------------------
+
+# Enthought library imports.
+from traits.api import Any, HasTraits, Str, provides
+
+# Local imports.
+from .i_command import ICommand
+
+
+
[docs]@provides(ICommand) +class AbstractCommand(HasTraits): + """The AbstractCommand class is an abstract base class that implements the + ICommand interface. + """ + + #### 'ICommand' interface ################################################# + + # This is the data on which the command operates. + data = Any + + # This is the name of the command as it will appear in any GUI element. It + # may include '&' which will be automatically removed whenever it is + # inappropriate. + name = Str + + ########################################################################### + # 'ICommand' interface. + ########################################################################### + +
[docs] def do(self): + """This is called by the command stack to do the command and to return + any value. The command must save any state necessary for the 'redo()' + and 'undo()' methods to work. The class's __init__() must also ensure + that deep copies of any arguments are made if appropriate. It is + guaranteed that this will only ever be called once and that it will be + called before any call to 'redo()' or 'undo()'. + """ + + raise NotImplementedError
+ +
[docs] def merge(self, other): + """This is called by the command stack to try and merge another + command with this one. True is returned if the commands were merged. + 'other' is the command that is about to be executed. If the commands + are merged then 'other' will discarded and not placed on the command + stack. A subsequent undo or redo of this modified command must have + the same effect as the two original commands. + """ + + # By default merges never happen. + return False
+ +
[docs] def redo(self): + """This is called by the command stack to redo the command. Any + returned value will replace the value that the command stack references + from the original call to 'do()' or previous call to 'redo()'. + """ + + raise NotImplementedError
+ +
[docs] def undo(self): + """ This is called by the command stack to undo the command. """ + + raise NotImplementedError
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/undo/action/abstract_command_stack_action.html b/5.0/_modules/apptools/undo/action/abstract_command_stack_action.html new file mode 100644 index 000000000..eef451dd3 --- /dev/null +++ b/5.0/_modules/apptools/undo/action/abstract_command_stack_action.html @@ -0,0 +1,212 @@ + + + + + + + apptools.undo.action.abstract_command_stack_action — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.undo.action.abstract_command_stack_action

+# ------------------------------------------------------------------------------
+# Copyright (c) 2008, Riverbank Computing Limited
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in enthought/LICENSE.txt and may be redistributed only
+# under the conditions described in the aforementioned license.  The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+# Thanks for using Enthought open source!
+#
+# Author: Riverbank Computing Limited
+# Description: <Enthought undo package component>
+# ------------------------------------------------------------------------------
+
+# Enthought library imports.
+from pyface.action.api import Action
+from traits.api import Instance
+
+# Local library imports
+from ..i_undo_manager import IUndoManager
+
+
+
[docs]class AbstractCommandStackAction(Action): + """The abstract base class for all actions that operate on a command + stack. + """ + + #### 'AbstractCommandStackAction' interface ############################### + + # The undo manager. + undo_manager = Instance(IUndoManager) + + ########################################################################### + # 'object' interface. + ########################################################################### + + def __init__(self, **traits): + """ Initialise the instance. """ + + super(AbstractCommandStackAction, self).__init__(**traits) + + self.undo_manager.on_trait_event( + self._on_stack_updated, "stack_updated" + ) + + # Update the action to initialise it. + self._update_action() + + ########################################################################### + # 'Action' interface. + ########################################################################### + +
[docs] def destroy(self): + """Called when the action is no longer required. + + By default this method does nothing, but this would be a great place to + unhook trait listeners etc. + + """ + + self.undo_manager.on_trait_event( + self._on_stack_updated, "stack_updated", remove=True + )
+ + ########################################################################### + # Protected interface. + ########################################################################### + + def _update_action(self): + """ Update the state of the action. """ + + raise NotImplementedError + + ########################################################################### + # Private interface. + ########################################################################### + + def _on_stack_updated(self, stack): + """ Handle changes to the state of a command stack. """ + + # Ignore unless it is the active stack. + if stack is self.undo_manager.active_stack: + self._update_action()
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/undo/action/command_action.html b/5.0/_modules/apptools/undo/action/command_action.html new file mode 100644 index 000000000..151e51aaa --- /dev/null +++ b/5.0/_modules/apptools/undo/action/command_action.html @@ -0,0 +1,187 @@ + + + + + + + apptools.undo.action.command_action — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.undo.action.command_action

+# ------------------------------------------------------------------------------
+# Copyright (c) 2007, Riverbank Computing Limited
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in enthought/LICENSE.txt and may be redistributed only
+# under the conditions described in the aforementioned license.  The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+# Thanks for using Enthought open source!
+#
+# Author: Riverbank Computing Limited
+# Description: <Enthought undo package component>
+# ------------------------------------------------------------------------------
+
+# Enthought library imports.
+from pyface.action.api import Action
+from traits.api import Any, Callable, Instance
+from ..i_command_stack import ICommandStack
+
+
+
[docs]class CommandAction(Action): + """The CommandAction class is an Action class that wraps undo/redo + commands. It is only useful for commands that do not take any arguments or + return any result. + """ + + #### 'CommandAction' interface ############################################ + + # The command to create when the action is performed. + command = Callable + + # The command stack onto which the command will be pushed when the action + # is performed. + command_stack = Instance(ICommandStack) + + # This is the data on which the command operates. + data = Any + + ########################################################################### + # 'Action' interface. + ########################################################################### + +
[docs] def perform(self, event): + """This is reimplemented to push a new command instance onto the + command stack. + """ + + self.command_stack.push(self.command(data=self.data))
+ + def _name_default(self): + """ This gets the action name from the command. """ + + if self.command: + name = self.command().name + else: + name = "" + + return name
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/undo/action/redo_action.html b/5.0/_modules/apptools/undo/action/redo_action.html new file mode 100644 index 000000000..34960ff83 --- /dev/null +++ b/5.0/_modules/apptools/undo/action/redo_action.html @@ -0,0 +1,178 @@ + + + + + + + apptools.undo.action.redo_action — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.undo.action.redo_action

+# ------------------------------------------------------------------------------
+# Copyright (c) 2007, Riverbank Computing Limited
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in enthought/LICENSE.txt and may be redistributed only
+# under the conditions described in the aforementioned license.  The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+# Thanks for using Enthought open source!
+#
+# Author: Riverbank Computing Limited
+# Description: <Enthought undo package component>
+# ------------------------------------------------------------------------------
+
+# Local imports.
+from .abstract_command_stack_action import AbstractCommandStackAction
+
+
+
[docs]class RedoAction(AbstractCommandStackAction): + """An action that redos the last command undone of the active command + stack. + """ + + ########################################################################### + # 'Action' interface. + ########################################################################### + +
[docs] def perform(self, event): + """ Perform the action. """ + + self.undo_manager.redo()
+ + ########################################################################### + # 'AbstractUndoAction' interface. + ########################################################################### + + def _update_action(self): + """ Update the state of the action. """ + + name = self.undo_manager.redo_name + + if name: + name = "&Redo " + name + self.enabled = True + else: + name = "&Redo" + self.enabled = False + + self.name = name
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/undo/action/undo_action.html b/5.0/_modules/apptools/undo/action/undo_action.html new file mode 100644 index 000000000..c5910362b --- /dev/null +++ b/5.0/_modules/apptools/undo/action/undo_action.html @@ -0,0 +1,176 @@ + + + + + + + apptools.undo.action.undo_action — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.undo.action.undo_action

+# ------------------------------------------------------------------------------
+# Copyright (c) 2007, Riverbank Computing Limited
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in enthought/LICENSE.txt and may be redistributed only
+# under the conditions described in the aforementioned license.  The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+# Thanks for using Enthought open source!
+#
+# Author: Riverbank Computing Limited
+# Description: <Enthought undo package component>
+# ------------------------------------------------------------------------------
+
+# Local imports.
+from .abstract_command_stack_action import AbstractCommandStackAction
+
+
+
[docs]class UndoAction(AbstractCommandStackAction): + """ An action that undos the last command of the active command stack. """ + + ########################################################################### + # 'Action' interface. + ########################################################################### + +
[docs] def perform(self, event): + """ Perform the action. """ + + self.undo_manager.undo()
+ + ########################################################################### + # 'AbstractUndoAction' interface. + ########################################################################### + + def _update_action(self): + """ Update the state of the action. """ + + name = self.undo_manager.undo_name + + if name: + name = "&Undo " + name + self.enabled = True + else: + name = "&Undo" + self.enabled = False + + self.name = name
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/undo/command_stack.html b/5.0/_modules/apptools/undo/command_stack.html new file mode 100644 index 000000000..bba612ffb --- /dev/null +++ b/5.0/_modules/apptools/undo/command_stack.html @@ -0,0 +1,448 @@ + + + + + + + apptools.undo.command_stack — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.undo.command_stack

+# ------------------------------------------------------------------------------
+# Copyright (c) 2008, Riverbank Computing Limited
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in enthought/LICENSE.txt and may be redistributed only
+# under the conditions described in the aforementioned license.  The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+# Thanks for using Enthought open source!
+#
+# Author: Riverbank Computing Limited
+# Description: <Enthought undo package component>
+# ------------------------------------------------------------------------------
+
+# Enthought library imports.
+from traits.api import (
+    Bool,
+    HasTraits,
+    Instance,
+    Int,
+    List,
+    Property,
+    Str,
+    provides,
+)
+
+# Local imports.
+from .abstract_command import AbstractCommand
+from .i_command import ICommand
+from .i_command_stack import ICommandStack
+from .i_undo_manager import IUndoManager
+
+
+class _StackEntry(HasTraits):
+    """ The _StackEntry class is a single entry on a command stack. """
+
+    #### '_StackEntry' interface ##############################################
+
+    # Set if the entry corresponds to a clean point on the stack.
+    clean = Bool(False)
+
+    # The command instance.
+    command = Instance(ICommand)
+
+    # The sequence number of the entry.
+    sequence_nr = Int
+
+
+class _MacroCommand(AbstractCommand):
+    """ The _MacroCommand class is an internal command that handles macros. """
+
+    #### '_MacroCommand' interface ############################################
+
+    # The commands that make up this macro.
+    macro_commands = List(Instance(ICommand))
+
+    ###########################################################################
+    # 'ICommand' interface.
+    ###########################################################################
+
+    def do(self):
+        """ Invoke the command. """
+
+        # This is a dummy.
+        return None
+
+    def merge(self, other):
+        """ Try and merge a command. """
+
+        if len(self.macro_commands) == 0:
+            merged = False
+        else:
+            merged = self.macro_commands[-1].merge(other)
+
+        return merged
+
+    def redo(self):
+        """ Redo the sub-commands. """
+
+        for cmd in self.macro_commands:
+            cmd.redo()
+
+        # Macros cannot return values.
+        return None
+
+    def undo(self):
+        """ Undo the sub-commands. """
+
+        for cmd in self.macro_commands:
+            cmd.undo()
+
+
+
[docs]@provides(ICommandStack) +class CommandStack(HasTraits): + """The CommandStack class is the default implementation of the + ICommandStack interface. + """ + + #### 'ICommandStack' interface ############################################ + + # This is the clean state of the stack. Its value changes as commands are + # undone and redone. It can also be explicity set to mark the current + # stack position as being clean (when the data is saved to disk for + # example). + clean = Property(Bool) + + # This is the name of the command that can be redone. It will be empty if + # there is no command that can be redone. It is maintained by the undo + # stack. + redo_name = Property(Str) + + # This is the undo manager that manages this stack. + undo_manager = Instance(IUndoManager) + + # This is the name of the command that can be undone. It will be empty if + # there is no command that can be undone. It is maintained by the undo + # stack. + undo_name = Property(Str) + + #### Private interface #################################################### + + # The current index into the stack (ie. the last command that was done). + _index = Int(-1) + + # The current macro stack. + _macro_stack = List(Instance(_MacroCommand)) + + # The stack itself. + _stack = List(Instance(_StackEntry)) + + ########################################################################### + # 'ICommandStack' interface. + ########################################################################### + +
[docs] def begin_macro(self, name): + """This begins a macro by creating an empty command with the given + 'name'. All subsequent calls to 'push()' create commands that will be + children of the empty command until the next call to 'end_macro()'. + Macros may be nested. The stack is disabled (ie. nothing can be undone + or redone) while a macro is being created (ie. while there is an + outstanding 'end_macro()' call). + """ + + command = _MacroCommand(name=name) + self.push(command) + self._macro_stack.append(command)
+ +
[docs] def clear(self): + """This clears the stack, without undoing or redoing any commands, and + leaves the stack in a clean state. It is typically used when all + changes to the data have been abandoned. + """ + + self._index = -1 + self._stack = [] + self._macro_stack = [] + + self.undo_manager.stack_updated = self
+ +
[docs] def end_macro(self): + """ This ends a macro. """ + + try: + self._macro_stack.pop() + except IndexError: + pass
+ +
[docs] def push(self, command): + """This executes a command and saves it on the command stack so that + it can be subsequently undone and redone. 'command' is an instance + that implements the ICommand interface. Its 'do()' method is called + to execute the command. If any value is returned by 'do()' then it is + returned by 'push()'. + """ + + # See if the command can be merged with the previous one. + if len(self._macro_stack) == 0: + if self._index >= 0: + merged = self._stack[self._index].command.merge(command) + else: + merged = False + else: + merged = self._macro_stack[-1].merge(command) + + # Increment the global sequence number. + if not merged: + self.undo_manager.sequence_nr += 1 + + # Execute the command. + result = command.do() + + # Do nothing more if the command was merged. + if merged: + return result + + # Only update the command stack if there is no current macro. + if len(self._macro_stack) == 0: + # Remove everything on the stack after the last command that was + # done. + self._index += 1 + del self._stack[self._index:] + + # Create a new stack entry and add it to the stack. + entry = _StackEntry( + command=command, sequence_nr=self.undo_manager.sequence_nr + ) + + self._stack.append(entry) + self.undo_manager.stack_updated = self + else: + # Add the command to the parent macro command. + self._macro_stack[-1].macro_commands.append(command) + + return result
+ +
[docs] def redo(self, sequence_nr=0): + """If 'sequence_nr' is 0 then the last command that was undone is + redone and any result returned. Otherwise commands are redone up to + and including the given 'sequence_nr' and any result of the last of + these is returned. + """ + + # Make sure a redo is valid in the current context. + if self.redo_name == "": + return None + + if sequence_nr == 0: + result = self._redo_one() + else: + result = None + + while self._index + 1 < len(self._stack): + if self._stack[self._index + 1].sequence_nr > sequence_nr: + break + + result = self._redo_one() + + self.undo_manager.stack_updated = self + + return result
+ +
[docs] def undo(self, sequence_nr=0): + """If 'sequence_nr' is 0 then the last command is undone. Otherwise + commands are undone up to and including the given 'sequence_nr'. + """ + + # Make sure an undo is valid in the current context. + if self.undo_name == "": + return + + if sequence_nr == 0: + self._undo_one() + else: + while self._index >= 0: + if self._stack[self._index].sequence_nr <= sequence_nr: + break + + self._undo_one() + + self.undo_manager.stack_updated = self
+ + ########################################################################### + # Private interface. + ########################################################################### + + def _redo_one(self): + """ Redo the command at the current index and return the result. """ + + self._index += 1 + entry = self._stack[self._index] + + return entry.command.redo() + + def _undo_one(self): + """ Undo the command at the current index. """ + + entry = self._stack[self._index] + self._index -= 1 + + entry.command.undo() + + def _get_clean(self): + """ Get the clean state of the stack. """ + + if self._index >= 0: + clean = self._stack[self._index].clean + else: + clean = True + + return clean + + def _set_clean(self, clean): + """ Set the clean state of the stack. """ + + if self._index >= 0: + self._stack[self._index].clean = clean + + def _get_redo_name(self): + """ Get the name of the redo command, if any. """ + + redo_name = "" + + if len(self._macro_stack) == 0 and self._index + 1 < len(self._stack): + redo_name = self._stack[self._index + 1].command.name.replace( + "&", "" + ) + + return redo_name + + def _get_undo_name(self): + """ Get the name of the undo command, if any. """ + + undo_name = "" + + if len(self._macro_stack) == 0 and self._index >= 0: + command = self._stack[self._index].command + undo_name = command.name.replace("&", "") + + return undo_name
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/undo/i_command.html b/5.0/_modules/apptools/undo/i_command.html new file mode 100644 index 000000000..2be484b01 --- /dev/null +++ b/5.0/_modules/apptools/undo/i_command.html @@ -0,0 +1,194 @@ + + + + + + + apptools.undo.i_command — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.undo.i_command

+# ------------------------------------------------------------------------------
+# Copyright (c) 2007, Riverbank Computing Limited
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in enthought/LICENSE.txt and may be redistributed only
+# under the conditions described in the aforementioned license.  The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+# Thanks for using Enthought open source!
+#
+# Author: Riverbank Computing Limited
+# Description: <Enthought undo package component>
+# ------------------------------------------------------------------------------
+
+
+# Enthought library imports.
+from traits.api import Any, Interface, Str
+
+
+
[docs]class ICommand(Interface): + """The command interface. The state of the data can be changed by passing + an instance that implements this interface to the 'push()' method of a + command stack along with any arguments. + """ + + #### 'ICommand' interface ################################################# + + # This is the data on which the command operates. + data = Any + + # This is the name of the command as it will appear in any GUI element. It + # may include '&' which will be automatically removed whenever it is + # inappropriate. + name = Str + + ########################################################################### + # 'ICommand' interface. + ########################################################################### + +
[docs] def do(self): + """This is called by the command stack to do the command and to return + any value. The command must save any state necessary for the 'redo()' + and 'undo()' methods to work. The class's __init__() must also ensure + that deep copies of any arguments are made if appropriate. It is + guaranteed that this will only ever be called once and that it will be + called before any call to 'redo()' or 'undo()'. + """
+ +
[docs] def merge(self, other): + """This is called by the command stack to try and merge another + command with this one. True is returned if the commands were merged. + 'other' is the command that is about to be executed. If the commands + are merged then 'other' will discarded and not placed on the command + stack. A subsequent undo or redo of this modified command must have + the same effect as the two original commands. + """
+ +
[docs] def redo(self): + """This is called by the command stack to redo the command. Any + returned value will replace the value that the command stack references + from the original call to 'do()' or previous call to 'redo()'. + """
+ +
[docs] def undo(self): + """ This is called by the command stack to undo the command. """
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/undo/i_command_stack.html b/5.0/_modules/apptools/undo/i_command_stack.html new file mode 100644 index 000000000..38cfbb57b --- /dev/null +++ b/5.0/_modules/apptools/undo/i_command_stack.html @@ -0,0 +1,220 @@ + + + + + + + apptools.undo.i_command_stack — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.undo.i_command_stack

+# ------------------------------------------------------------------------------
+# Copyright (c) 2007, Riverbank Computing Limited
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in enthought/LICENSE.txt and may be redistributed only
+# under the conditions described in the aforementioned license.  The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+# Thanks for using Enthought open source!
+#
+# Author: Riverbank Computing Limited
+# Description: <Enthought undo package component>
+# ------------------------------------------------------------------------------
+
+# Enthought library imports.
+from traits.api import Bool, Instance, Interface, Str
+
+# Local imports.
+from .i_undo_manager import IUndoManager
+
+
+
[docs]class ICommandStack(Interface): + """The command stack interface. A command stack is responsible for + managing the changes to a data model and recording those changes so that + they can be undone or redone. + """ + + #### 'ICommandStack' interface ############################################ + + # This is the clean state of the stack. Its value changes as commands are + # undone and redone. It can also be explicity set to mark the current + # stack position as being clean (when the data is saved to disk for + # example). + clean = Bool + + # This is the name of the command that can be redone. It will be empty if + # there is no command that can be redone. It is maintained by the undo + # stack. + redo_name = Str + + # This is the undo manager that manages this stack. + undo_manager = Instance(IUndoManager) + + # This is the name of the command that can be undone. It will be empty if + # there is no command that can be undone. It is maintained by the undo + # stack. + undo_name = Str + + ########################################################################### + # 'ICommandStack' interface. + ########################################################################### + +
[docs] def begin_macro(self, name): + """This begins a macro by creating an empty command with the given + 'name'. The commands passed to all subsequent calls to 'push()' will + be contained in the macro until the next call to 'end_macro()'. Macros + may be nested. The stack is disabled (ie. nothing can be undone or + redone) while a macro is being created (ie. while there is an + outstanding 'end_macro()' call). + """
+ +
[docs] def clear(self): + """This clears the stack, without undoing or redoing any commands, and + leaves the stack in a clean state. It is typically used when all + changes to the data have been abandoned. + """
+ +
[docs] def end_macro(self): + """ This ends a macro. """
+ +
[docs] def push(self, command): + """This executes a command and saves it on the command stack so that + it can be subsequently undone and redone. 'command' is an instance + that implements the ICommand interface. Its 'do()' method is called + to execute the command. If any value is returned by 'do()' then it is + returned by 'push()'. The command stack will keep a reference to the + result so that it can recognise it as an argument to a subsequent + command (which allows a script to properly save a result needed later). + """
+ +
[docs] def redo(self, sequence_nr=0): + """If 'sequence_nr' is 0 then the last command that was undone is + redone and any result returned. Otherwise commands are redone up to + and including the given 'sequence_nr' and any result of the last of + these is returned. + """
+ +
[docs] def undo(self, sequence_nr=0): + """If 'sequence_nr' is 0 then the last command is undone. Otherwise + commands are undone up to and including the given 'sequence_nr'. + """
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/undo/i_undo_manager.html b/5.0/_modules/apptools/undo/i_undo_manager.html new file mode 100644 index 000000000..cefd9792d --- /dev/null +++ b/5.0/_modules/apptools/undo/i_undo_manager.html @@ -0,0 +1,192 @@ + + + + + + + apptools.undo.i_undo_manager — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.undo.i_undo_manager

+# ------------------------------------------------------------------------------
+# Copyright (c) 2008, Riverbank Computing Limited
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in enthought/LICENSE.txt and may be redistributed only
+# under the conditions described in the aforementioned license.  The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+# Thanks for using Enthought open source!
+#
+# Author: Riverbank Computing Limited
+# Description: <Enthought undo package component>
+# ------------------------------------------------------------------------------
+
+# Enthought library imports.
+from traits.api import Bool, Event, Instance, Int, Interface, Str
+
+
+
[docs]class IUndoManager(Interface): + """The undo manager interface. An undo manager is responsible for one or + more command stacks. Typically an application would have a single undo + manager. + """ + + #### 'IUndoManager' interface ############################################# + + # This is the currently active command stack and may be None. Typically it + # is set when some sort of editor becomes active. + active_stack = Instance("apptools.undo.api.ICommandStack") + + # This reflects the clean state of the currently active command stack. It + # is intended to support a "document modified" indicator in the GUI. It is + # maintained by the undo manager. + active_stack_clean = Bool + + # This is the name of the command that can be redone. It will be empty if + # there is no command that can be redone. It is maintained by the undo + # manager. + redo_name = Str + + # This is the sequence number of the next command to be performed. It is + # incremented immediately before a command is invoked (by its 'do()' + # method). + sequence_nr = Int + + # This event is fired when the index of a command stack changes. Note that + # it may not be the active stack. + stack_updated = Event(Instance("apptools.undo.api.ICommandStack")) + + # This is the name of the command that can be undone. It will be empty if + # there is no command that can be undone. It is maintained by the undo + # manager. + undo_name = Str + + ########################################################################### + # 'IUndoManager' interface. + ########################################################################### + +
[docs] def redo(self): + """ Redo the last undone command of the active command stack. """
+ +
[docs] def undo(self): + """ Undo the last command of the active command stack. """
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/apptools/undo/undo_manager.html b/5.0/_modules/apptools/undo/undo_manager.html new file mode 100644 index 000000000..0707f8edf --- /dev/null +++ b/5.0/_modules/apptools/undo/undo_manager.html @@ -0,0 +1,251 @@ + + + + + + + apptools.undo.undo_manager — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.undo.undo_manager

+# ------------------------------------------------------------------------------
+# Copyright (c) 2008, Riverbank Computing Limited
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in enthought/LICENSE.txt and may be redistributed only
+# under the conditions described in the aforementioned license.  The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+# Thanks for using Enthought open source!
+#
+# Author: Riverbank Computing Limited
+# Description: <Enthought undo package component>
+# ------------------------------------------------------------------------------
+
+# Enthought library imports.
+from traits.api import (
+    Bool,
+    Event,
+    HasTraits,
+    Instance,
+    Int,
+    Property,
+    Str,
+    provides,
+)
+
+# Local imports.
+from .i_undo_manager import IUndoManager
+
+
+
[docs]@provides(IUndoManager) +class UndoManager(HasTraits): + """The UndoManager class is the default implementation of the + IUndoManager interface. + """ + + #### 'IUndoManager' interface ############################################# + + # This is the currently active command stack and may be None. Typically it + # is set when some sort of editor becomes active. + active_stack = Instance("apptools.undo.api.ICommandStack") + + # This reflects the clean state of the currently active command stack. It + # is intended to support a "document modified" indicator in the GUI. It is + # maintained by the undo manager. + active_stack_clean = Property(Bool) + + # This is the name of the command that can be redone. It will be empty if + # there is no command that can be redone. It is maintained by the undo + # manager. + redo_name = Property(Str) + + # This is the sequence number of the next command to be performed. It is + # incremented immediately before a command is invoked (by its 'do()' + # method). + sequence_nr = Int + + # This event is fired when the index of a command stack changes. The value + # of the event is the stack that has changed. Note that it may not be the + # active stack. + stack_updated = Event + + # This is the name of the command that can be undone. It will be empty if + # there is no command that can be undone. It is maintained by the undo + # manager. + undo_name = Property(Str) + + ########################################################################### + # 'IUndoManager' interface. + ########################################################################### + +
[docs] def redo(self): + """ Redo the last undone command of the active command stack. """ + + if self.active_stack is not None: + self.active_stack.redo()
+ +
[docs] def undo(self): + """ Undo the last command of the active command stack. """ + + if self.active_stack is not None: + self.active_stack.undo()
+ + ########################################################################### + # Private interface. + ########################################################################### + + def _active_stack_changed(self, new): + """ Handle a different stack becoming active. """ + + # Pretend that the stack contents have changed. + self.stack_updated = new + + def _get_active_stack_clean(self): + """ Get the current clean state. """ + + if self.active_stack is None: + active_stack_clean = True + else: + active_stack_clean = self.active_stack.clean + + return active_stack_clean + + def _get_redo_name(self): + """ Get the current redo name. """ + + if self.active_stack is None: + redo_name = "" + else: + redo_name = self.active_stack.redo_name + + return redo_name + + def _get_undo_name(self): + """ Get the current undo name. """ + + if self.active_stack is None: + undo_name = "" + else: + undo_name = self.active_stack.undo_name + + return undo_name
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/index.html b/5.0/_modules/index.html new file mode 100644 index 000000000..53b122600 --- /dev/null +++ b/5.0/_modules/index.html @@ -0,0 +1,215 @@ + + + + + + + Overview: module code — Apptools Documentation + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

All modules for which code is available

+ + +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/traits/trait_types.html b/5.0/_modules/traits/trait_types.html new file mode 100644 index 000000000..aee7cb0dd --- /dev/null +++ b/5.0/_modules/traits/trait_types.html @@ -0,0 +1,4427 @@ + + + + + + + traits.trait_types — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for traits.trait_types

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+
+""" Core Trait definitions.
+"""
+
+import collections.abc
+import datetime
+from importlib import import_module
+import operator
+import re
+import sys
+try:
+    from os import fspath
+except ImportError:
+    fspath = None
+from os.path import isfile, isdir
+from types import FunctionType, MethodType, ModuleType
+import uuid
+
+from .constants import DefaultValue, TraitKind, ValidateTrait
+from .trait_base import (
+    strx,
+    get_module_name,
+    HandleWeakRef,
+    class_of,
+    RangeTypes,
+    safe_contains,
+    SequenceTypes,
+    TypeTypes,
+    Undefined,
+    TraitsCache,
+    xgetattr,
+)
+from .trait_converters import trait_from, trait_cast
+from .trait_dict_object import TraitDictEvent, TraitDictObject
+from .trait_errors import TraitError
+from .trait_list_object import TraitListEvent, TraitListObject
+from .trait_set_object import TraitSetEvent, TraitSetObject
+from .trait_type import TraitType, _infer_default_value_type
+from .traits import (
+    Trait,
+    _TraitMaker,
+    _InstanceArgs,
+)
+from .util.import_symbol import import_symbol
+
+# TraitsUI integration imports
+from .editor_factories import (
+    code_editor,
+    html_editor,
+    password_editor,
+    shell_editor,
+    date_editor,
+    datetime_editor,
+    time_editor,
+    list_editor,
+)
+
+
+# Constants
+
+SetTypes = SequenceTypes + (set,)
+
+# Numeric type fast validator definitions
+
+# A few words about the next block of code:
+
+# The coerce validator is a generic validator for possibly coercible types
+# (see validate_trait_coerce_type in ctraits.c).
+#
+# The tuples below are of the form
+# (ValidateTrait.coerce, type1, [type2, type3, ...],
+#     [None, ctype1, [ctype2, ...]])
+#
+# 'type1' corresponds to the main type for the trait
+# 'None' acts as the separator between 'types' and 'ctypes' (coercible types)
+#
+# The validation passes if:
+# 1) The trait value type is (a subtype of) one of 'type1', 'type2',  ...
+#    in which case the value is returned as-is
+# or
+# 2) The trait value type is (a subtype of) one of 'ctype1', 'ctype2', ...
+#    in which case the value is returned coerced to trait type using
+#    'return type1(value')
+
+try:
+    # The numpy enhanced definitions:
+    from numpy import integer, floating, complexfloating, bool_
+
+    int_fast_validate = (ValidateTrait.coerce, int, integer)
+    float_fast_validate = (
+        ValidateTrait.coerce,
+        float,
+        floating,
+        None,
+        int,
+        integer,
+    )
+    complex_fast_validate = (
+        ValidateTrait.coerce,
+        complex,
+        complexfloating,
+        None,
+        float,
+        floating,
+        int,
+        integer,
+    )
+    bool_fast_validate = (ValidateTrait.coerce, bool, None, bool_)
+    # Tuple or single type suitable for an isinstance check.
+    _BOOL_TYPES = (bool, bool_)
+except ImportError:
+    # The standard python definitions (without numpy):
+    int_fast_validate = (ValidateTrait.coerce, int)
+    float_fast_validate = (ValidateTrait.coerce, float, None, int)
+    complex_fast_validate = (ValidateTrait.coerce, complex, None, float, int)
+    bool_fast_validate = (ValidateTrait.coerce, bool)
+    # Tuple or single type suitable for an isinstance check.
+    _BOOL_TYPES = bool
+
+
+def default_text_editor(trait, type=None):
+    """ Return a default text editor for a trait.
+
+    Parameters
+    ----------
+    trait : TraitType
+        The trait we are constructing the editor for.
+    type : callable, optional
+        A callable (usually a Python type) to use to evaluate the text content
+        of the editor and return the correct type of value for the trait.
+
+    Returns
+    -------
+    TextEditor
+        A TraitsUI TextEditor instance for the trait.
+    """
+    auto_set = trait.auto_set
+    if auto_set is None:
+        auto_set = True
+
+    enter_set = trait.enter_set or False
+
+    from traitsui.api import TextEditor
+
+    if type is None:
+        return TextEditor(auto_set=auto_set, enter_set=enter_set)
+
+    return TextEditor(auto_set=auto_set, enter_set=enter_set, evaluate=type)
+
+
+# Generic validators
+
+def _validate_int(value):
+    """ Convert an integer-like Python object to an int, or raise TypeError.
+    """
+    if type(value) is int:
+        return value
+    else:
+        return int(operator.index(value))
+
+
+def _validate_float(value):
+    """ Convert an arbitrary Python object to a float, or raise TypeError.
+    """
+    if type(value) is float:  # fast path for common case
+        return value
+    try:
+        nb_float = type(value).__float__
+    except AttributeError:
+        raise TypeError(
+            "Object of type {!r} not convertible to float".format(type(value))
+        )
+    return nb_float(value)
+
+
+# Trait Types
+
+class Any(TraitType):
+    """ A trait type whose value can be anything.
+    """
+
+    #: The default value for the trait:
+    default_value = None
+
+    #: A description of the type of value this trait accepts:
+    info_text = "any value"
+
+
+class BaseInt(TraitType):
+    """ A trait type whose value must be an int.
+
+    Values which support the Python index protocol will validate and will be
+    converted to the corresponding int value.
+    """
+
+    #: The function to use for evaluating strings to this type:
+    evaluate = int
+
+    #: The default value for the trait:
+    default_value = 0
+
+    #: A description of the type of value this trait accepts:
+    info_text = "an integer"
+
+    def validate(self, object, name, value):
+        """ Validates that a specified value is valid for this trait.
+        """
+        try:
+            return _validate_int(value)
+        except TypeError:
+            self.error(object, name, value)
+
+    def create_editor(self):
+        """ Returns the default traits UI editor for this type of trait.
+        """
+        return default_text_editor(self, int)
+
+
+class Int(BaseInt):
+    """ A fast-validating trait type whose value must be an integer.
+
+    Values which support the Python index protocol will validate and will be
+    converted to the corresponding int value.
+    """
+
+    #: The C-level fast validator to use:
+    fast_validate = (ValidateTrait.int,)
+
+
+class BaseFloat(TraitType):
+    """ A trait type whose value must be a float.
+
+    Values which support automatic conversion to floats via the Python
+    __float__ method will validate and will be converted to the corresponding
+    float value.
+    """
+
+    #: The function to use for evaluating strings to this type:
+    evaluate = float
+
+    #: The default value for the trait:
+    default_value = 0.0
+
+    #: A description of the type of value this trait accepts:
+    info_text = "a float"
+
+    def validate(self, object, name, value):
+        """ Validates that a specified value is valid for this trait.
+
+        Note: The 'fast validator' version performs this check in C.
+        """
+        try:
+            return _validate_float(value)
+        except TypeError:
+            self.error(object, name, value)
+
+    def create_editor(self):
+        """ Returns the default traits UI editor for this type of trait.
+        """
+        return default_text_editor(self, float)
+
+
+class Float(BaseFloat):
+    """ A fast-validating trait type whose value must be a float.
+
+    Values which support automatic conversion to floats via the Python
+    __float__ method will validate and will be converted to the corresponding
+    float value.
+    """
+
+    #: The C-level fast validator to use:
+    fast_validate = (ValidateTrait.float,)
+
+
+class BaseComplex(TraitType):
+    """ A trait type whose value must be a complex number.
+
+    Integers and floating-point numbers will be converted to the
+    corresponding complex value.
+    """
+
+    #: The function to use for evaluating strings to this type:
+    evaluate = complex
+
+    #: The default value for the trait:
+    default_value = 0.0 + 0.0j
+
+    #: A description of the type of value this trait accepts:
+    info_text = "a complex number"
+
+    def validate(self, object, name, value):
+        """ Validates that a specified value is valid for this trait.
+
+        Note: The 'fast validator' version performs this check in C.
+        """
+        if isinstance(value, complex):
+            return value
+
+        if isinstance(value, (float, int)):
+            return complex(value)
+
+        self.error(object, name, value)
+
+    def create_editor(self):
+        """ Returns the default traits UI editor for this type of trait.
+        """
+        return default_text_editor(self, complex)
+
+
+class Complex(BaseComplex):
+    """ A fast-validating trait type whose value must be a complex number.
+
+    Integers and floating-point numbers will be converted to the
+    corresponding complex value.
+    """
+
+    #: The C-level fast validator to use:
+    fast_validate = complex_fast_validate
+
+
+class BaseStr(TraitType):
+    """ A trait type whose value must be a string.
+    """
+
+    #: The default value for the trait:
+    default_value = ""
+
+    #: A description of the type of value this trait accepts:
+    info_text = "a string"
+
+    def validate(self, object, name, value):
+        """ Validates that a specified value is valid for this trait.
+
+        Note: The 'fast validator' version performs this check in C.
+        """
+        if isinstance(value, str):
+            return value
+
+        self.error(object, name, value)
+
+    def create_editor(self):
+        """ Returns the default traits UI editor for this type of trait.
+        """
+        from .editor_factories import multi_line_text_editor
+
+        auto_set = self.auto_set
+        if auto_set is None:
+            auto_set = True
+        enter_set = self.enter_set or False
+
+        return multi_line_text_editor(auto_set, enter_set)
+
+
+class Str(BaseStr):
+    """ A fast-validating trait type whose value must be a complex number.
+    """
+
+    #: The C-level fast validator to use:
+    fast_validate = (ValidateTrait.coerce, str)
+
+
+class Title(Str):
+    """ A Str trait which by default uses a TraitsUI TitleEditor.
+    """
+
+    def create_editor(self):
+        """ Returns the default traits UI editor to use for a trait.
+        """
+        from traitsui.api import TitleEditor
+
+        if hasattr(self, "allow_selection"):
+            return TitleEditor(allow_selection=self.allow_selection)
+        else:
+            return TitleEditor()
+
+
+class BaseBytes(TraitType):
+    """ A trait type whose value must be a bytestring.
+    """
+
+    #: The default value for the trait:
+    default_value = b""
+
+    #: A description of the type of value this trait accepts:
+    info_text = "a bytes string"
+
+    #: An encoding to use with TraitsUI editors
+    encoding = None
+
+    def validate(self, object, name, value):
+        """ Validates that a specified value is valid for this trait.
+
+        Note: The 'fast validator' version performs this check in C.
+        """
+        if isinstance(value, bytes):
+            return value
+
+        self.error(object, name, value)
+
+    def create_editor(self):
+        """ Returns the default traits UI editor for this type of trait.
+        """
+        from .traits import bytes_editor
+
+        auto_set = self.auto_set
+        if auto_set is None:
+            auto_set = True
+        enter_set = self.enter_set or False
+
+        return bytes_editor(auto_set, enter_set, self.encoding)
+
+
+class Bytes(BaseBytes):
+    """ A fast-validating trait type whose value must be a bytestring.
+    """
+
+    #: The C-level fast validator to use:
+    fast_validate = (ValidateTrait.coerce, bytes)
+
+
+class BaseBool(TraitType):
+    """ A trait type whose value must be a bool.
+    """
+
+    #: The function to use for evaluating strings to this type:
+    evaluate = bool
+
+    #: The default value for the trait:
+    default_value = False
+
+    #: A description of the type of value this trait accepts:
+    info_text = "a boolean"
+
+    def validate(self, object, name, value):
+        """ Validates that a specified value is valid for this trait.
+
+        Note: The 'fast validator' version performs this check in C.
+        """
+        if isinstance(value, _BOOL_TYPES):
+            return bool(value)
+
+        self.error(object, name, value)
+
+    def create_editor(self):
+        """ Returns the default traits UI editor for this type of trait.
+        """
+        from traitsui.api import BooleanEditor
+
+        return BooleanEditor()
+
+
+class Bool(BaseBool):
+    """ A fast-validating trait type whose value must be a bool.
+    """
+
+    #: The C-level fast validator to use:
+    fast_validate = bool_fast_validate
+
+
+class BaseCInt(BaseInt):
+    """ A coercing trait type whose value is an integer.
+    """
+
+    #: The function to use for evaluating strings to this type:
+    evaluate = int
+
+    def validate(self, object, name, value):
+        """ Validates that a specified value is valid for this trait.
+
+        Note: The 'fast validator' version performs this check in C.
+        """
+        try:
+            return int(value)
+        except (ValueError, TypeError):
+            self.error(object, name, value)
+
+
+class CInt(BaseCInt):
+    """ A fast-validating, coercing trait type whose value is an int.
+    """
+
+    #: The C-level fast validator to use:
+    fast_validate = (ValidateTrait.cast, int)
+
+
+class BaseCFloat(BaseFloat):
+    """ A coercing trait type whose value is a float.
+    """
+
+    #: The function to use for evaluating strings to this type:
+    evaluate = float
+
+    def validate(self, object, name, value):
+        """ Validates that a specified value is valid for this trait.
+
+        Note: The 'fast validator' version performs this check in C.
+        """
+        try:
+            return float(value)
+        except (ValueError, TypeError):
+            self.error(object, name, value)
+
+
+class CFloat(BaseCFloat):
+    """ A fast-validating, coercing trait type whose value is a float.
+    """
+
+    #: The C-level fast validator to use:
+    fast_validate = (ValidateTrait.cast, float)
+
+
+class BaseCComplex(BaseComplex):
+    """ A coercing trait type whose value is a complex number.
+    """
+
+    #: The function to use for evaluating strings to this type:
+    evaluate = complex
+
+    def validate(self, object, name, value):
+        """ Validates that a specified value is valid for this trait.
+
+        Note: The 'fast validator' version performs this check in C.
+        """
+        try:
+            return complex(value)
+        except (ValueError, TypeError):
+            self.error(object, name, value)
+
+
+class CComplex(BaseCComplex):
+    """ A fast-validating, coercing trait type whose value is a complex number.
+    """
+
+    #: The C-level fast validator to use:
+    fast_validate = (ValidateTrait.cast, complex)
+
+
+class BaseCStr(BaseStr):
+    """ A coercing trait type whose value is a string.
+    """
+
+    def validate(self, object, name, value):
+        """ Validates that a specified value is valid for this trait.
+
+        Note: The 'fast validator' version performs this check in C.
+        """
+        try:
+            return str(value)
+        except:
+            self.error(object, name, value)
+
+
+class CStr(BaseCStr):
+    """ A fast-validating, coercing trait type whose value is a string.
+    """
+
+    #: The C-level fast validator to use:
+    fast_validate = (ValidateTrait.cast, str)
+
+
+class BaseCBytes(BaseBytes):
+    """ A coercing trait type whose value is a bytestring.
+    """
+
+    def validate(self, object, name, value):
+        """ Validates that a specified value is valid for this trait.
+
+        Note: The 'fast validator' version performs this check in C.
+        """
+        try:
+            return bytes(value)
+        except:
+            self.error(object, name, value)
+
+
+class CBytes(BaseCBytes):
+    """ A fast-validating, coercing trait type whose value is a bytestring.
+    """
+
+    #: The C-level fast validator to use:
+    fast_validate = (ValidateTrait.cast, bytes)
+
+
+class BaseCBool(BaseBool):
+    """ A coercing trait type whose value is a bool.
+    """
+
+    #: The function to use for evaluating strings to this type:
+    evaluate = bool
+
+    def validate(self, object, name, value):
+        """ Validates that a specified value is valid for this trait.
+
+        Note: The 'fast validator' version performs this check in C.
+        """
+        try:
+            return bool(value)
+        except:
+            self.error(object, name, value)
+
+
+class CBool(BaseCBool):
+    """ A fast-validating, coercing trait type whose value is a bool.
+    """
+
+    #: The C-level fast validator to use:
+    fast_validate = (ValidateTrait.cast, bool)
+
+
+class String(TraitType):
+    """ A trait type whose value must be a string with optional constraints.
+
+    The value is a string whose length is in a specified range, and which
+    optionally matches a specified regular expression.
+
+    Parameters
+    ----------
+    value : str
+        The default value for the string.
+    minlen : integer
+        The minimum length allowed for the string.
+    maxlen : integer
+        The maximum length allowed for the string.
+    regex : str
+        A Python regular expression that the string must match.
+    **metadata
+        The trait metadata for the trait.
+
+    Attributes
+    ----------
+    minlen : integer
+        The minimum length allowed for the string.
+    maxlen : integer
+        The maximum length allowed for the string.
+    regex : str
+        A Python regular expression that the string must match.
+    """
+
+    def __init__(
+        self, value="", minlen=0, maxlen=sys.maxsize, regex="", **metadata
+    ):
+        super(String, self).__init__(value, **metadata)
+        self.minlen = max(0, minlen)
+        self.maxlen = max(self.minlen, maxlen)
+        self.regex = regex
+        self._init()
+
+    def _init(self):
+        """ Completes initialization of the trait at construction or unpickling
+        time.
+        """
+        self._validate = "validate_all"
+        if self.regex != "":
+            self.match = re.compile(self.regex).match
+            if (self.minlen == 0) and (self.maxlen == sys.maxsize):
+                self._validate = "validate_regex"
+        elif (self.minlen == 0) and (self.maxlen == sys.maxsize):
+            self._validate = "validate_str"
+        else:
+            self._validate = "validate_len"
+
+    def validate(self, object, name, value):
+        """ Validates that the value is a valid string.
+        """
+        return getattr(self, self._validate)(object, name, value)
+
+    def validate_all(self, object, name, value):
+        """ Validates that the value is a valid string in the specified length
+            range which matches the specified regular expression.
+        """
+        try:
+            value = strx(value)
+            if (self.minlen <= len(value) <= self.maxlen) and (
+                self.match(value) is not None
+            ):
+                return value
+        except:
+            pass
+
+        self.error(object, name, value)
+
+    def validate_str(self, object, name, value):
+        """ Validates that the value is a valid string.
+        """
+        try:
+            return strx(value)
+        except:
+            pass
+
+        self.error(object, name, value)
+
+    def validate_len(self, object, name, value):
+        """ Validates that the value is a valid string in the specified length
+        range.
+        """
+        try:
+            value = strx(value)
+            if self.minlen <= len(value) <= self.maxlen:
+                return value
+        except:
+            pass
+
+        self.error(object, name, value)
+
+    def validate_regex(self, object, name, value):
+        """ Validates that the value is a valid string which matches the
+        specified regular expression.
+        """
+        try:
+            value = strx(value)
+            if self.match(value) is not None:
+                return value
+        except:
+            pass
+
+        self.error(object, name, value)
+
+    def info(self):
+        """ Returns a description of the trait.
+        """
+        msg = ""
+        if (self.minlen != 0) and (self.maxlen != sys.maxsize):
+            msg = " between %d and %d characters long" % (
+                self.minlen,
+                self.maxlen,
+            )
+        elif self.maxlen != sys.maxsize:
+            msg = " <= %d characters long" % self.maxlen
+        elif self.minlen != 0:
+            msg = " >= %d characters long" % self.minlen
+        if self.regex != "":
+            if msg != "":
+                msg += " and"
+            msg += " matching the pattern '%s'" % self.regex
+        return "a string" + msg
+
+    def create_editor(self):
+        """ Returns the default traits UI editor for this type of trait.
+        """
+        return default_text_editor(self)
+
+    def __getstate__(self):
+        """ Returns the current state of the trait.
+        """
+        result = self.__dict__.copy()
+        for name in ["validate", "match"]:
+            if name in result:
+                del result[name]
+
+        return result
+
+    def __setstate__(self, state):
+        """ Sets the current state of the trait.
+        """
+        self.__dict__.update(state)
+        self._init()
+
+
+class Regex(String):
+    """ A trait type whose value must match a regular expression.
+
+    Parameters
+    ----------
+    value : str
+        The default value of the trait.
+    regex : str
+        The regular expression that the trait value must match.
+    **metadata
+        Trait metadata.
+    """
+
+    def __init__(self, value="", regex=".*", **metadata):
+        super(Regex, self).__init__(value=value, regex=regex, **metadata)
+
+
+class Code(String):
+    """ A trait type whose value holds a string of source code.
+
+    Validation does not perform any sort of syntax checking. The default
+    TraitsUI editor is a CodeEditor.
+    """
+
+    #: The standard metadata for the trait:
+    metadata = {"editor": code_editor}
+
+
+class HTML(String):
+    """ A trait type whose value holds an HTML string.
+
+    The validation of the value does not enforce HTML syntax.  The default
+    TraitsUI editor is an HTMLEditor.
+    """
+
+    #: The standard metadata for the trait:
+    metadata = {"editor": html_editor}
+
+
+class Password(String):
+    """ A trait type whose value holds a password string.
+
+    The default TraitsUI editor is an PasswordEditor, which obscures text
+    entered by the user.
+    """
+
+    #: The standard metadata for the trait:
+    metadata = {"editor": password_editor}
+
+
+class BaseCallable(TraitType):
+    """ A trait type whose value must be a Python callable.
+    """
+
+    #: The standard metadata for the trait:
+    metadata = {"copy": "ref"}
+
+    #: The default value for the trait:
+    default_value = None
+
+    #: A description of the type of value this trait accepts:
+    info_text = "a callable value"
+
+    def validate(self, object, name, value):
+        """ Validates that the value is a Python callable.
+        """
+        if (value is None) or callable(value):
+            return value
+
+        self.error(object, name, value)
+
+
+class Callable(BaseCallable):
+    """ A fast-validating trait type whose value must be a Python callable.
+    """
+    def __init__(self, value=None, allow_none=True, **metadata):
+
+        self.fast_validate = (ValidateTrait.callable, allow_none)
+
+        default_value = metadata.pop("default_value", value)
+
+        super().__init__(default_value, **metadata)
+
+
+class BaseType(TraitType):
+    """ A trait type whose value must be an instance of a Python type.
+
+    This is an abstract class and should not be directly instantiated.
+    """
+
+    def validate(self, object, name, value):
+        """ Validates that the value is a Python callable.
+        """
+        if isinstance(value, self.fast_validate[1:]):
+            return value
+
+        self.error(object, name, value)
+
+
+class This(BaseType):
+    """ A trait type whose value must be an instance of the defining class.
+    """
+
+    #: The C-level fast validator to use:
+    fast_validate = (ValidateTrait.self_type,)
+
+    #: A description of the type of value this trait accepts:
+    info_text = "an instance of the same type as the receiver"
+
+    def __init__(self, value=None, allow_none=True, **metadata):
+        super(This, self).__init__(value, **metadata)
+
+        if allow_none:
+            self.fast_validate = (ValidateTrait.self_type, None)
+            self.validate = self.validate_none
+            self.info = self.info_none
+
+    def validate(self, object, name, value):
+        if isinstance(value, object.__class__):
+            return value
+
+        self.error(object, name, value)
+
+    def validate_none(self, object, name, value):
+        if isinstance(value, object.__class__) or (value is None):
+            return value
+
+        self.error(object, name, value)
+
+    def info(self):
+        return "an instance of the same type as the receiver"
+
+    def info_none(self):
+        return "an instance of the same type as the receiver or None"
+
+
+class self(This):
+    """ A trait type whose default value is the object containing the trait.
+
+    The trait can be assigned to, but any new value must be an instance of
+    the defining class.
+    """
+
+    #: The default value type to use (i.e. 'self'):
+    default_value_type = DefaultValue.object
+
+
+class Function(TraitType):
+    """ A trait type whose value must be a function.
+    """
+
+    #: The C-level fast validator to use:
+    fast_validate = (ValidateTrait.coerce, FunctionType)
+
+    #: A description of the type of value this trait accepts:
+    info_text = "a function"
+
+
+class Method(TraitType):
+    """ A trait type whose value must be a method.
+    """
+
+    #: The C-level fast validator to use:
+    fast_validate = (ValidateTrait.coerce, MethodType)
+
+    #: A description of the type of value this trait accepts:
+    info_text = "a method"
+
+
+class Module(TraitType):
+    """ A trait type whose value must be a module.
+    """
+
+    #: The C-level fast validator to use:
+    fast_validate = (ValidateTrait.coerce, ModuleType)
+
+    #: A description of the type of value this trait accepts:
+    info_text = "a module"
+
+
+class Python(TraitType):
+    """ A trait type that behaves as a standard Python attribute.
+
+    This trait type allows any value to be assigned, and raises an
+    ValueError if an attempt is made to get the value before one has been
+    assigned. It has no default value. This trait is most often used in
+    conjunction with wildcard naming. See the *Traits User Manual* for
+    details on wildcards.
+    """
+
+    #: The standard metadata for the trait:
+    metadata = {"type": "python"}
+
+    #: The default value for the trait:
+    default_value = Undefined
+
+
+class ReadOnly(TraitType):
+    """ A trait type that is write-once, and then read-only.
+
+    The initial value of the attribute is the special, singleton object
+    Undefined. The trait allows any value to be assigned to the attribute
+    if the current value is the Undefined object. Once any other value is
+    assigned, no further assignment is allowed. Normally, the initial
+    assignment to the attribute is performed in the class constructor,
+    based on information passed to the constructor. If the read-only value
+    is known in advance of run time, use Constant instead of ReadOnly to
+    define the trait.
+    """
+
+    # Defines the CTrait type to use for this trait:
+    ctrait_type = TraitKind.read_only
+
+    #: The default value for the trait:
+    default_value = Undefined
+
+
+# Create a singleton instance as the trait:
+ReadOnly = ReadOnly()
+
+
+class Disallow(TraitType):
+    """ A trait that prevents any value from being assigned or read.
+
+    Any attempt to get or set the value of the trait attribute raises an
+    exception. This trait is most often used in conjunction with wildcard
+    naming, for example, to catch spelling mistakes in attribute names.
+
+    See the *Traits User Manual* for details on wildcards.
+    """
+
+    #: Defines the CTrait type to use for this trait:
+    ctrait_type = TraitKind.disallow
+
+
+# Create a singleton instance as the trait:
+Disallow = Disallow()
+
+
+class Constant(TraitType):
+    """ A trait type whose value is a constant.
+
+    Traits of this type are very space efficient (and fast) because
+    *value* is not stored in each instance using the trait, but only in
+    the trait object itself.
+
+    Parameters
+    ----------
+    value : any
+        The constant value for the trait.
+    **metadata
+        Trait metadata for the trait.
+    """
+
+    #: Defines the CTrait type to use for this trait:
+    ctrait_type = TraitKind.constant
+
+    #: The standard metadata for the trait:
+    metadata = {"type": "constant", "transient": True}
+
+
+class Delegate(TraitType):
+    """ A trait type whose value is delegated to a trait on another object.
+
+    This is a base class that shouldn't be used directly, rather use one of
+    the subclasses DelegatesTo or PrototypesFrom, depending on desired
+    behaviour.
+
+    An object containing a delegator trait attribute must contain a
+    second attribute that references the object containing the delegate
+    trait attribute. The name of this second attribute is passed as the
+    *delegate* argument.
+
+    The following rules govern the application of the prefix parameter:
+
+    * If *prefix* is empty or omitted, the delegation is to an attribute
+      of the delegate object with the same name as the delegator
+      attribute.
+    * If *prefix* is a valid Python attribute name, then the delegation
+      is to an attribute whose name is the value of *prefix*.
+    * If *prefix* ends with an asterisk ('*') and is longer than one
+      character, then the delegation is to an attribute whose name is
+      the value of *prefix*, minus the trailing asterisk, prepended to
+      the delegator attribute name.
+    * If *prefix* is equal to a single asterisk, the delegation is to an
+      attribute whose name is the value of the delegator object's
+      __prefix__ attribute prepended to delegator attribute name.
+
+    Parameters
+    ----------
+    delegate : str
+        The name of the trait that holds the HasTraits instance that the
+        value is delegated to.
+    prefix : str
+        The name of the trait on the delegate that holds the delegated
+        value.  If empty, then the name of this trait will be used.
+    modify : bool
+        Whether modifications of this trait are applied to the delegated
+        object.  This differentiates the behaviour of DelegatesTo and
+        PrototypedFrom.
+    listenable : bool
+        Whether changes to the delegated trait will fire listeners to
+        this trait.
+
+    Attributes
+    ----------
+    delegate : str
+        The name of the trait that holds the HasTraits instance that the
+        value is delegated to.
+    prefix : str
+        The name of the trait on the delegate that holds the delegated
+        value.  If empty, then the name of this trait will be used.
+    prefix_type : int
+        An integer giving the type of prefix being used.
+    modify : bool
+        Whether modifications of this trait are applied to the delegated
+        object.  This differentiates the behaviour of DelegatesTo and
+        PrototypedFrom.
+    """
+
+    #: Defines the CTrait type to use for this trait:
+    ctrait_type = TraitKind.delegate
+
+    #: The standard metadata for the trait:
+    metadata = {"type": "delegate", "transient": False}
+
+    def __init__(
+        self, delegate, prefix="", modify=False, listenable=True, **metadata
+    ):
+        """ Creates a Delegate trait.
+        """
+        if prefix == "":
+            prefix_type = 0
+        elif prefix[-1:] != "*":
+            prefix_type = 1
+        else:
+            prefix = prefix[:-1]
+            if prefix != "":
+                prefix_type = 2
+            else:
+                prefix_type = 3
+
+        metadata["_delegate"] = delegate
+        metadata["_prefix"] = prefix
+        metadata["_listenable"] = listenable
+
+        super(Delegate, self).__init__(**metadata)
+
+        self.delegate = delegate
+        self.prefix = prefix
+        self.prefix_type = prefix_type
+        self.modify = modify
+
+    def as_ctrait(self):
+        """ Returns a CTrait corresponding to the trait defined by this class.
+        """
+        trait = super(Delegate, self).as_ctrait()
+        trait.delegate(
+            self.delegate, self.prefix, self.prefix_type, self.modify
+        )
+
+        return trait
+
+
+class DelegatesTo(Delegate):
+    """ A trait type that matches the 'delegate' design pattern.
+
+    This defines a trait whose value and definition is "delegated" to
+    another trait on a different object.
+
+    An object containing a delegator trait attribute must contain a
+    second attribute that references the object containing the delegate
+    trait attribute. The name of this second attribute is passed as the
+    *delegate* argument to the DelegatesTo() function.
+
+    The following rules govern the application of the prefix parameter:
+
+    * If *prefix* is empty or omitted, the delegation is to an attribute
+      of the delegate object with the same name as the delegator
+      attribute.
+    * If *prefix* is a valid Python attribute name, then the delegation
+      is to an attribute whose name is the value of *prefix*.
+    * If *prefix* ends with an asterisk ('*') and is longer than one
+      character, then the delegation is to an attribute whose name is
+      the value of *prefix*, minus the trailing asterisk, prepended to
+      the delegator attribute name.
+    * If *prefix* is equal to a single asterisk, the delegation is to an
+      attribute whose name is the value of the delegator object's
+      __prefix__ attribute prepended to delegator attribute name.
+
+    Note that any changes to the delegator attribute are actually
+    applied to the corresponding attribute on the delegate object. The
+    original object containing the delegator trait is not modified.
+
+    Parameters
+    ----------
+    delegate : str
+        Name of the attribute on the current object which references
+        the object that is the trait's delegate.
+    prefix : str
+        A prefix or substitution applied to the original attribute when
+        looking up the delegated attribute.
+    listenable : bool
+        Indicates whether a listener can be attached to this attribute
+        such that changes to the delegated attribute will trigger it.
+    **metadata
+        Trait metadata for the trait.
+    """
+
+    def __init__(self, delegate, prefix="", listenable=True, **metadata):
+        super(DelegatesTo, self).__init__(
+            delegate,
+            prefix=prefix,
+            modify=True,
+            listenable=listenable,
+            **metadata
+        )
+
+
+class PrototypedFrom(Delegate):
+    """ A trait type that matches the 'prototype' design pattern.
+
+    This defines a trait whose default value and definition is "prototyped"
+    from another trait on a different object.
+
+    An object containing a prototyped trait attribute must contain a
+    second attribute that references the object containing the prototype
+    trait attribute. The name of this second attribute is passed as the
+    *prototype* argument to the PrototypedFrom() function.
+
+    The following rules govern the application of the prefix parameter:
+
+    * If *prefix* is empty or omitted, the prototype delegation is to an
+      attribute of the prototype object with the same name as the
+      prototyped attribute.
+    * If *prefix* is a valid Python attribute name, then the prototype
+      delegation is to an attribute whose name is the value of *prefix*.
+    * If *prefix* ends with an asterisk ('*') and is longer than one
+      character, then the prototype delegation is to an attribute whose
+      name is the value of *prefix*, minus the trailing asterisk,
+      prepended to the prototyped attribute name.
+    * If *prefix* is equal to a single asterisk, the prototype
+      delegation is to an attribute whose name is the value of the
+      prototype object's __prefix__ attribute prepended to the
+      prototyped attribute name.
+
+    Note that any changes to the prototyped attribute are made to the
+    original object, not the prototype object. The prototype object is
+    only used to define to trait type and default value.
+
+    Parameters
+    ----------
+    prototype : str
+        Name of the attribute on the current object which references the
+        object that is the trait's prototype.
+    prefix : str
+        A prefix or substitution applied to the original attribute when
+        looking up the prototyped attribute.
+    listenable : bool
+        Indicates whether a listener can be attached to this attribute
+        such that changes to the corresponding attribute on the
+        prototype object will trigger it.
+    **metadata
+        Trait metadata for the trait.
+    """
+
+    def __init__(self, prototype, prefix="", listenable=True, **metadata):
+        super(PrototypedFrom, self).__init__(
+            prototype,
+            prefix=prefix,
+            modify=False,
+            listenable=listenable,
+            **metadata
+        )
+
+
+class Expression(TraitType):
+    """ A trait type whose value must be a valid Python expression.
+
+    The compiled form of a valid expression is stored as the mapped value of
+    the trait.
+    """
+
+    #: The default value for the trait:
+    default_value = "0"
+
+    #: A description of the type of value this trait accepts:
+    info_text = "a valid Python expression"
+
+    #: Indicate that this is a mapped trait:
+    is_mapped = True
+
+    def validate(self, object, name, value):
+        """ Validates that a specified value is valid for this trait.
+        """
+        try:
+            return compile(value, "<string>", "eval")
+        except:
+            self.error(object, name, value)
+
+    def post_setattr(self, object, name, value):
+        """ Performs additional post-assignment processing.
+        """
+        object.__dict__[name + "_"] = value
+
+    def mapped_value(self, value):
+        """ Returns the 'mapped' value for the specified **value**.
+        """
+        return compile(value, "<string>", "eval")
+
+    def as_ctrait(self):
+        """ Returns a CTrait corresponding to the trait defined by this class.
+        """
+        # Tell the C code that 'setattr' should store the original, unadapted
+        # value passed to it:
+        ctrait = super(Expression, self).as_ctrait()
+        ctrait.setattr_original_value = True
+        return ctrait
+
+
+class PythonValue(Any):
+    """ A trait type whose value can be of any type.
+
+    The default editor is a ShellEditor.
+    """
+
+    #: The standard metadata for the trait:
+    metadata = {"editor": shell_editor}
+
+
+class BaseFile(BaseStr):
+    """ A trait type whose value must be a file path string.
+
+    For Python 3.6 and later this will accept os.pathlib Path objects,
+    converting them to the corresponding string value.
+
+    Parameters
+    ----------
+    value : str
+        The default value for the trait.
+    filter : str
+        A wildcard string to filter filenames in the file dialog box used by
+        the attribute trait editor.
+    auto_set : bool
+        Indicates whether the file editor updates the trait value after
+        every key stroke.
+    entries : int
+        A hint to the TraitsUI editor about how many values to display in
+        the editor.
+    exists : bool
+        Indicates whether the trait value must be an existing file or
+        not.
+
+    Attributes
+    ----------
+    filter : str
+        A wildcard string to filter filenames in the file dialog box used by
+        the attribute trait editor.
+    auto_set : bool
+        Indicates whether the file editor updates the trait value after
+        every key stroke.
+    entries : int
+        A hint to the TraitsUI editor about how many values to display in
+        the editor.
+    exists : bool
+        Indicates whether the trait value must be an existing file or
+        not.
+    """
+
+    #: A description of the type of value this trait accepts:
+    info_text = "a filename or object implementing the os.PathLike interface"
+
+    def __init__(
+        self,
+        value="",
+        filter=None,
+        auto_set=False,
+        entries=0,
+        exists=False,
+        **metadata
+    ):
+        self.filter = filter
+        self.auto_set = auto_set
+        self.entries = entries
+        self.exists = exists
+
+        super(BaseFile, self).__init__(value, **metadata)
+
+    def validate(self, object, name, value):
+        """ Validates that a specified value is valid for this trait.
+
+            Note: The 'fast validator' version performs this check in C.
+        """
+        if fspath is not None:
+            # Python 3.5 does not implement __fspath__
+            try:
+                # If value is of type os.PathLike, get the path representation
+                # The path representation could be either a str or bytes type
+                # If fspath returns bytes, further validation will fail.
+                value = fspath(value)
+            except TypeError:
+                pass
+
+        validated_value = super(BaseFile, self).validate(object, name, value)
+        if not self.exists:
+            return validated_value
+        elif isfile(value):
+            return validated_value
+
+        self.error(object, name, value)
+
+    def create_editor(self):
+        from traitsui.editors.file_editor import FileEditor
+
+        editor = FileEditor(
+            filter=self.filter or [],
+            auto_set=self.auto_set,
+            entries=self.entries,
+            dialog_style="open" if self.exists else "save",
+        )
+        return editor
+
+
+class File(BaseFile):
+    """ A fast-validating trait type whose value must be a file path string.
+
+    For Python 3.6 and later this will accept os.pathlib Path objects,
+    converting them to the corresponding string value.
+
+    Parameters
+    ----------
+    value : str
+        The default value for the trait.
+    filter : str
+        A wildcard string to filter filenames in the file dialog box used by
+        the attribute trait editor.
+    auto_set : bool
+        Indicates whether the file editor updates the trait value after
+        every key stroke.
+    entries : int
+        A hint to the TraitsUI editor about how many values to display in
+        the editor.
+    exists : bool
+        Indicates whether the trait value must be an existing file or
+        not.
+
+    Attributes
+    ----------
+    filter : str
+        A wildcard string to filter filenames in the file dialog box used by
+        the attribute trait editor.
+    auto_set : bool
+        Indicates whether the file editor updates the trait value after
+        every key stroke.
+    entries : int
+        A hint to the TraitsUI editor about how many values to display in
+        the editor.
+    exists : bool
+        Indicates whether the trait value must be an existing file or
+        not.
+    """
+
+    def __init__(
+        self,
+        value="",
+        filter=None,
+        auto_set=False,
+        entries=0,
+        exists=False,
+        **metadata
+    ):
+        super(File, self).__init__(
+            value, filter, auto_set, entries, exists, **metadata
+        )
+
+
+class BaseDirectory(BaseStr):
+    """ A trait type whose value must be a directory path string.
+
+    For Python 3.6 and greater, it also accepts objects implementing
+    the :class:`os.PathLike` interface, converting them to the corresponding
+    string.
+
+    Parameters
+    ----------
+    value : str
+        The default value for the trait.
+    auto_set : bool
+        Indicates whether the directory editor updates the trait value
+        after every key stroke.
+    entries : int
+        A hint to the TraitsUI editor about how many values to display in
+        the editor.
+    exists : bool
+        Indicates whether the trait value must be an existing directory or
+        not.
+
+    Attributes
+    ----------
+    auto_set : bool
+        Indicates whether the directory editor updates the trait value
+        after every key stroke.
+    entries : int
+        A hint to the TraitsUI editor about how many values to display in
+        the editor.
+    exists : bool
+        Indicates whether the trait value must be an existing directory or
+        not.
+    """
+
+    #: A description of the type of value this trait accepts:
+    info_text = ("a directory name or an object implementing "
+                 "the os.PathLike interface")
+
+    def __init__(
+        self, value="", auto_set=False, entries=0, exists=False, **metadata
+    ):
+        self.entries = entries
+        self.auto_set = auto_set
+        self.exists = exists
+
+        super(BaseDirectory, self).__init__(value, **metadata)
+
+    def validate(self, object, name, value):
+        """ Validates that a specified value is valid for this trait.
+
+        Note: The 'fast validator' version performs this check in C.
+        """
+        if fspath is not None:
+            # Python 3.5 does not implement __fspath__
+            try:
+                value = fspath(value)
+            except TypeError:
+                pass
+
+        validated_value = super(BaseDirectory, self).validate(
+            object, name, value
+        )
+        if not self.exists:
+            return validated_value
+        elif isdir(value):
+            return validated_value
+
+        self.error(object, name, value)
+
+    def create_editor(self):
+        from traitsui.editors.directory_editor import DirectoryEditor
+
+        editor = DirectoryEditor(auto_set=self.auto_set, entries=self.entries)
+        return editor
+
+
+class Directory(BaseDirectory):
+    """ A fast-validating trait type whose value is a directory path string.
+
+    For Python 3.6 and greater, it also accepts objects implementing
+    the :class:`os.PathLike` interface, converting them to the corresponding
+    string.
+
+    Parameters
+    ----------
+    value : str
+        The default value for the trait.
+    auto_set : bool
+        Indicates whether the directory editor updates the trait value
+        after every key stroke.
+    entries : int
+        A hint to the TraitsUI editor about how many values to display in
+        the editor.
+    exists : bool
+        Indicates whether the trait value must be an existing directory or
+        not.
+
+    Attributes
+    ----------
+    auto_set : bool
+        Indicates whether the directory editor updates the trait value
+        after every key stroke.
+    entries : int
+        A hint to the TraitsUI editor about how many values to display in
+        the editor.
+    exists : bool
+        Indicates whether the trait value must be an existing directory or
+        not.
+    """
+
+    def __init__(
+        self, value="", auto_set=False, entries=0, exists=False, **metadata
+    ):
+        # Fast validation is disabled (Github issue #877).
+        super(Directory, self).__init__(
+            value, auto_set, entries, exists, **metadata
+        )
+
+
+class BaseRange(TraitType):
+    """ A trait type whose numeric value lies inside a range.
+
+    The value held will be either an integer or a float, which type is
+    determined by whether the *low*, *high* and *value* arguments are
+    integers or floats.
+
+    The *low*, *high*, and *value* arguments must be of the same type
+    (integer or float), except in the case where either *low* or *high* is
+    a string (i.e. extended trait name).
+
+    If *value* is None or omitted, the default value is *low*, unless *low*
+    is None or omitted, in which case the default value is *high*.
+
+    Parameters
+    ----------
+    low : integer, float or string (i.e. extended trait name)
+        The low end of the range.
+    high : integer, float or string (i.e. extended trait name)
+        The high end of the range.
+    value : integer, float or string (i.e. extended trait name)
+        The default value of the trait.
+    exclude_low : bool
+        Indicates whether the low end of the range is exclusive.
+    exclude_high : bool
+        Indicates whether the high end of the range is exclusive.
+    """
+
+    def __init__(
+        self,
+        low=None,
+        high=None,
+        value=None,
+        exclude_low=False,
+        exclude_high=False,
+        **metadata
+    ):
+        if value is None:
+            if low is not None:
+                value = low
+            else:
+                value = high
+
+        super(BaseRange, self).__init__(value, **metadata)
+
+        vtype = type(high)
+        if (low is not None) and (
+            not issubclass(vtype, (float, str))
+        ):
+            vtype = type(low)
+
+        is_static = not issubclass(vtype, str)
+        if is_static and (vtype not in RangeTypes):
+            raise TraitError(
+                "Range can only be use for int or float "
+                "values, but a value of type %s was specified." % vtype
+            )
+
+        self._low_name = self._high_name = ""
+        self._vtype = Undefined
+
+        kind = None
+
+        if vtype is float:
+            self._validate = "float_validate"
+            kind = ValidateTrait.float_range
+            self._type_desc = "a floating point number"
+            if low is not None:
+                low = float(low)
+
+            if high is not None:
+                high = float(high)
+
+        elif vtype is int:
+            self._validate = "int_validate"
+            self._type_desc = "an integer"
+            if low is not None:
+                low = int(low)
+
+            if high is not None:
+                high = int(high)
+        else:
+            self.get, self.set, self.validate = self._get, self._set, None
+            self._vtype = None
+            self._type_desc = "a number"
+
+            if isinstance(high, str):
+                self._high_name = high = "object." + high
+            else:
+                self._vtype = type(high)
+            high = compile(str(high), "<string>", "eval")
+
+            if isinstance(low, str):
+                self._low_name = low = "object." + low
+            else:
+                self._vtype = type(low)
+            low = compile(str(low), "<string>", "eval")
+
+            if isinstance(value, str):
+                value = "object." + value
+            self._value = compile(str(value), "<string>", "eval")
+
+            self.default_value_type = DefaultValue.callable
+            self.default_value = self._get_default_value
+
+        exclude_mask = 0
+        if exclude_low:
+            exclude_mask |= 1
+
+        if exclude_high:
+            exclude_mask |= 2
+
+        if is_static and kind is not None:
+            self.init_fast_validate(kind, low, high, exclude_mask)
+
+        #: Assign type-corrected arguments to handler attributes:
+        self._low = low
+        self._high = high
+        self._exclude_low = exclude_low
+        self._exclude_high = exclude_high
+
+    def init_fast_validate(self, *args):
+        """ Does nothing for the BaseRange class. Used in the Range class to
+        set up the fast validator.
+        """
+        pass
+
+    def validate(self, object, name, value):
+        """ Validate that the value is in the specified range.
+        """
+        return getattr(self, self._validate)(object, name, value)
+
+    def float_validate(self, object, name, value):
+        """ Validate that the value is a float value in the specified range.
+        """
+        # Convert to exact type float, re-raising a TypeError as a TraitError
+        # and letting other errors propagate. Keep original value for
+        # error-reporting purposes.
+        original_value = value
+        try:
+            value = _validate_float(value)
+        except TypeError:
+            self.error(object, name, original_value)
+
+        if (
+            (
+                (self._low is None)
+                or (self._exclude_low and (self._low < value))
+                or ((not self._exclude_low) and (self._low <= value))
+            )
+            and (
+                (self._high is None)
+                or (self._exclude_high and (self._high > value))
+                or ((not self._exclude_high) and (self._high >= value))
+            )
+        ):
+            return value
+
+        self.error(object, name, original_value)
+
+    def int_validate(self, object, name, value):
+        """ Validate that the value is an int value in the specified range.
+        """
+        # Convert to exact type float, re-raising a TypeError as a TraitError
+        # and letting other errors propagate. Keep original value for
+        # error-reporting purposes.
+        original_value = value
+        try:
+            value = _validate_int(value)
+        except TypeError:
+            self.error(object, name, original_value)
+
+        if (
+            (
+                (self._low is None)
+                or (self._exclude_low and (self._low < value))
+                or ((not self._exclude_low) and (self._low <= value))
+            )
+            and (
+                (self._high is None)
+                or (self._exclude_high and (self._high > value))
+                or ((not self._exclude_high) and (self._high >= value))
+            )
+        ):
+            return value
+
+        self.error(object, name, original_value)
+
+    def _get_default_value(self, object):
+        """ Returns the default value of the range.
+        """
+        return eval(self._value)
+
+    def _get(self, object, name, trait):
+        """ Returns the current value of a dynamic range trait.
+        """
+        cname = "_traits_cache_" + name
+        value = object.__dict__.get(cname, Undefined)
+        if value is Undefined:
+            object.__dict__[cname] = value = eval(self._value)
+
+        low = eval(self._low)
+        high = eval(self._high)
+        if (low is not None) and (value < low):
+            value = low
+        elif (high is not None) and (value > high):
+            value = high
+
+        return self._typed_value(value, low, high)
+
+    def _set(self, object, name, value):
+        """ Sets the current value of a dynamic range trait.
+        """
+        if not isinstance(value, str):
+            try:
+                low = eval(self._low)
+                high = eval(self._high)
+                if (low is None) and (high is None):
+                    if isinstance(value, RangeTypes):
+                        self._set_value(object, name, value)
+                        return
+                else:
+                    new_value = self._typed_value(value, low, high)
+                    if (
+                        (low is None)
+                        or (self._exclude_low and (low < new_value))
+                        or ((not self._exclude_low) and (low <= new_value))
+                    ) and (
+                        (high is None)
+                        or (self._exclude_high and (high > new_value))
+                        or ((not self._exclude_high) and (high >= new_value))
+                    ):
+                        self._set_value(object, name, new_value)
+                        return
+            except:
+                pass
+
+        self.error(object, name, value)
+
+    def _typed_value(self, value, low, high):
+        """ Returns the specified value with the correct type for the current
+            dynamic range.
+        """
+        vtype = self._vtype
+        if vtype is None:
+            if low is not None:
+                vtype = type(low)
+            elif high is not None:
+                vtype = type(high)
+            else:
+                vtype = lambda x: x
+
+        return vtype(value)
+
+    def _set_value(self, object, name, value):
+        """ Sets the specified value as the value of the dynamic range.
+        """
+        cname = "_traits_cache_" + name
+        old = object.__dict__.get(cname, Undefined)
+        if old is Undefined:
+            old = eval(self._value)
+        object.__dict__[cname] = value
+        if value != old:
+            object.trait_property_changed(name, old, value)
+
+    def full_info(self, object, name, value):
+        """ Returns a description of the trait.
+        """
+        if self._vtype is not Undefined:
+            low = eval(self._low)
+            high = eval(self._high)
+            low, high = (
+                self._typed_value(low, low, high),
+                self._typed_value(high, low, high),
+            )
+        else:
+            low = self._low
+            high = self._high
+
+        if low is None:
+            if high is None:
+                return self._type_desc
+
+            return "%s <%s %s" % (
+                self._type_desc,
+                "="[self._exclude_high:],
+                high,
+            )
+
+        elif high is None:
+            return "%s >%s %s" % (
+                self._type_desc,
+                "="[self._exclude_low:],
+                low,
+            )
+
+        return "%s <%s %s <%s %s" % (
+            low,
+            "="[self._exclude_low:],
+            self._type_desc,
+            "="[self._exclude_high:],
+            high,
+        )
+
+    def create_editor(self):
+        """ Returns the default UI editor for the trait.
+        """
+        # fixme: Needs to support a dynamic range editor.
+
+        auto_set = self.auto_set
+        if auto_set is None:
+            auto_set = True
+
+        from traitsui.api import RangeEditor
+
+        return RangeEditor(
+            self,
+            mode=self.mode or "auto",
+            cols=self.cols or 3,
+            auto_set=auto_set,
+            enter_set=self.enter_set or False,
+            low_label=self.low or "",
+            high_label=self.high or "",
+            low_name=self._low_name,
+            high_name=self._high_name,
+        )
+
+
+class Range(BaseRange):
+    """ A fast-validating trait type whose numeric value lies inside a range.
+    """
+
+    def init_fast_validate(self, *args):
+        """ Set up the C-level fast validator.
+        """
+        self.fast_validate = args
+
+
+class BaseEnum(TraitType):
+    """ A trait type whose value is an element of a finite collection.
+
+    This trait type can be either *static*, with the collection of valid values
+    specified directly in the constructor, or *dynamic*, with the collection
+    provided by the value of another trait attribute.
+
+    For both static and dynamic enumerations, a default value can be provided
+    as a positional argument. If no default is provided, the default is the
+    first item (in iteration order) of the underlying collection.
+
+    Notes
+    -----
+
+    1. If the enumeration is based on an unordered collection like a
+       ``set``, and no explicit default is given, the default used will
+       effectively be arbitrary (the first element of the set in iteration
+       order). It's recommended that a default be given explicitly in this
+       case.
+
+    2. Instances of ``str``, ``bytes`` and ``bytearray`` are not treated
+       as collections for the purposes of this trait type, both for pragmatic
+       reasons (it's more likely that a user wants to use a string as an
+       element in a collection than as a collection in its own right), and
+       because the behavior of the ``in`` operator for those types does not
+       express the usual membership semantics (for example, ``"bc" in "abc"``
+       is ``True``).
+
+    Parameters
+    ----------
+    *args
+        The enumeration of all valid values for the trait. For a static
+        enumeration trait (where the *values* keyword argument is not given)
+        the supported signatures for ``args`` are as follows:
+
+        (collection,)
+            A nonempty collection of valid values. The default is the first
+            element of the collection, in iteration order.
+        (default, collection)
+            The default value, followed by a nonempty collection of valid
+            values. The default should be an element of the collection, but
+            this is not checked.
+        (item1, item2, ..., itemn)
+            One or more items giving the valid values for the collection.
+            The default is *item1*.
+
+        For a dynamic enumeration trait, where the *values* keyword argument
+        is given, the supported signatures for ``args`` are:
+
+        ()
+            No arguments given. In this case the default is the first item
+            of the collection, in iteration order.
+        (default,)
+            The default value for the collection.
+
+        For the static case, the ambiguity in the signatures is resolved
+        as follows: if ``args`` has length ``1`` or ``2``, ``args[-1]`` can be
+        iterated over, and ``args[-1]`` is not an instance of ``str``,
+        ``bytes`` or ``bytearray``, then ``args[-1]`` is assumed to give the
+        collection of values. Otherwise, all elements of ``args`` are assumed
+        to be items in the collection. Thus the first two signatures are safe
+        from ambiguity, and it's recommended to use one of these two signatures
+        in preference to the third form.
+    values : str, optional
+        The name of a trait holding the valid values. If given, this is
+        a dynamic enumeration, otherwise it's a static numeration.
+    **metadata
+        Metadata for the trait.
+
+    Attributes
+    ----------
+    values : tuple or None
+        For a static enumeration, this is a tuple holding the valid values.
+        For a dynamic enumeration, this is None.
+    name : str or None
+        For a dynamic enumeration, this is the name of a trait holding
+        the collection of valid values. For a static enumeration, this is
+        None.
+    """
+
+    def __init__(self, *args, values=None, **metadata):
+        self.name = values
+
+        nargs = len(args)
+        if self.name is not None:
+            # Dynamic enumeration
+            self.values = None
+            self.get, self.set, self.validate = self._get, self._set, None
+            if nargs == 0:
+                super(BaseEnum, self).__init__(**metadata)
+            elif nargs == 1:
+                default_value = args[0]
+                super(BaseEnum, self).__init__(default_value, **metadata)
+            else:
+                raise TraitError(
+                    "Incorrect number of arguments specified "
+                    "when using the 'values' keyword"
+                )
+        else:
+            # Static enumeration
+            if nargs == 0:
+                raise TraitError("Enum trait requires at least 1 argument")
+
+            # If we have either 1 or 2 arguments and the last argument is a
+            # collection, then that collection provides the values of the
+            # enumeration. Otherwise, args itself is the collection.
+            have_collection_arg = (
+                nargs <= 2
+                and not isinstance(args[-1], (str, bytes, bytearray))
+                and isinstance(args[-1], collections.abc.Iterable)
+            )
+            self.values = tuple(args[-1]) if have_collection_arg else args
+            if not self.values:
+                raise TraitError("Enum collection should be nonempty")
+
+            # In the two-argument collection case, the first argument is
+            # the default. Otherwise, we take the first element of self.values.
+            if have_collection_arg and nargs == 2:
+                default_value = args[0]
+            else:
+                default_value = self.values[0]
+
+            self.init_fast_validate(ValidateTrait.enum, self.values)
+
+            super(BaseEnum, self).__init__(default_value, **metadata)
+
+    def init_fast_validate(self, *args):
+        """ Does nothing for the BaseEnum class. Used in the Enum class to set
+            up the fast validator.
+        """
+        pass
+
+    def validate(self, object, name, value):
+        """ Validates that the value is one of the enumerated set of valid
+        values.
+        """
+        if value in self.values:
+            return value
+
+        self.error(object, name, value)
+
+    def full_info(self, object, name, value):
+        """ Returns a description of the trait.
+        """
+        if self.name is None:
+            values = self.values
+        else:
+            values = xgetattr(object, self.name)
+
+        return " or ".join([repr(x) for x in values])
+
+    def create_editor(self):
+        """ Returns the default UI editor for the trait.
+        """
+        from traitsui.api import EnumEditor
+
+        if self.name is None:
+            values = self.values
+            name = ""
+        else:
+            values = None
+            name = self.name
+
+        editor = EnumEditor(
+            name=name,
+            cols=self.cols or 3,
+            evaluate=self.evaluate,
+            format_func=self.format_func,
+            mode=self.mode if self.mode else "radio",
+        )
+        # Workaround enthought/traitsui#782
+        if values is not None:
+            editor.values = values
+        return editor
+
+    def _get(self, object, name, trait):
+        """ Returns the current value of a dynamic enum trait.
+        """
+        value = self.get_value(object, name, trait)
+        values = xgetattr(object, self.name)
+        if not safe_contains(value, values):
+            value = next(iter(values), None)
+        return value
+
+    def _set(self, object, name, value):
+        """ Sets the current value of a dynamic range trait.
+        """
+        if safe_contains(value, xgetattr(object, self.name)):
+            self.set_value(object, name, value)
+        else:
+            self.error(object, name, value)
+
+
+class Enum(BaseEnum):
+    """ A fast-validating trait type whose value is an element of a finite
+    collection.
+
+    This trait type can be either *static*, with the collection of valid values
+    specified directly in the constructor, or *dynamic*, with the collection
+    provided by the value of another trait attribute.
+
+    For both static and dynamic enumerations, a default value can be provided
+    as a positional argument. If no default is provided, the default is the
+    first item (in iteration order) of the underlying collection.
+
+    Notes
+    -----
+
+    1. If the enumeration is based on an unordered collection like a
+       ``set``, and no explicit default is given, the default used will
+       effectively be arbitrary (the first element of the set in iteration
+       order). It's recommended that a default be given explicitly in this
+       case.
+
+    2. Instances of ``str``, ``bytes`` and ``bytearray`` are not treated
+       as collections for the purposes of this trait type, both for pragmatic
+       reasons (it's more likely that a user wants to use a string as an
+       element in a collection than as a collection in its own right), and
+       because the behavior of the ``in`` operator for those types does not
+       express the usual membership semantics (for example, ``"bc" in "abc"``
+       is ``True``).
+
+    Parameters
+    ----------
+    *args
+        The enumeration of all valid values for the trait. For a static
+        enumeration trait (where the *values* keyword argument is not given)
+        the supported signatures for ``args`` are as follows:
+
+        (collection,)
+            A nonempty collection of valid values. The default is the first
+            element of the collection, in iteration order.
+        (default, collection)
+            The default value, followed by a nonempty collection of valid
+            values. The default should be an element of the collection, but
+            this is not checked.
+        (item1, item2, ..., itemn)
+            One or more items giving the valid values for the collection.
+            The default is *item1*.
+
+        For a dynamic enumeration trait, where the *values* keyword argument
+        is given, the supported signatures for ``args`` are:
+
+        ()
+            No arguments given. In this case the default is the first item
+            of the collection, in iteration order.
+        (default,)
+            The default value for the collection.
+
+        For the static case, the ambiguity in the signatures is resolved
+        as follows: if ``args`` has length ``1`` or ``2``, ``args[-1]`` can be
+        iterated over, and ``args[-1]`` is not an instance of ``str``,
+        ``bytes`` or ``bytearray``, then ``args[-1]`` is assumed to give the
+        collection of values. Otherwise, all elements of ``args`` are assumed
+        to be items in the collection. Thus the first two signatures are safe
+        from ambiguity, and it's recommended to use one of these two signatures
+        in preference to the third form.
+    values : str, optional
+        The name of a trait holding the valid values. If given, this is
+        a dynamic enumeration, otherwise it's a static numeration.
+    **metadata
+        Metadata for the trait.
+
+    Attributes
+    ----------
+    values : tuple or None
+        For a static enumeration, this is a tuple holding the valid values.
+        For a dynamic enumeration, this is None.
+    name : str or None
+        For a dynamic enumeration, this is the name of a trait holding
+        the collection of valid values. For a static enumeration, this is
+        None.
+    """
+
+    def init_fast_validate(self, *args):
+        """ Set up C-level fast validation. """
+        self.fast_validate = args
+
+
+class BaseTuple(TraitType):
+    """ A trait type holding a tuple with typed elements.
+
+    The default value is determined as follows:
+
+    1.  If no arguments are specified, the default value is ().
+    2.  If a tuple is specified as the first argument, it is the default
+        value.
+    3.  If a tuple is not specified as the first argument, the default
+        value is a tuple whose length is the length of the argument list,
+        and whose values are the default values for the corresponding trait
+        types.
+
+    Example for case #2::
+
+        mytuple = Tuple(('Fred', 'Betty', 5))
+
+    The trait's value must be a 3-element tuple whose first and second
+    elements are strings, and whose third element is an integer. The
+    default value is ``('Fred', 'Betty', 5)``.
+
+    Example for case #3::
+
+        mytuple = Tuple('Fred', 'Betty', 5)
+
+    The trait's value must be a 3-element tuple whose first and second
+    elements are strings, and whose third element is an integer. The
+    default value is ``('','',0)``.
+
+    Parameters
+    ----------
+    *types
+        Definition of the default and allowed tuples. If the first item of
+        *types* is a tuple, it is used as the default value.
+        The remaining argument list is used to form a tuple that constrains
+        the  values assigned to the returned trait. The trait's value must
+        be a tuple of the same length as the remaining argument list, whose
+        elements must match the types specified by the corresponding items
+        of the remaining argument list.
+    **metadata
+        Trait metadata for the trait.
+
+    Attributes
+    ----------
+    types : tuple
+        The tuple of traits specifying the type of each element in order.
+    no_type_check : bool
+        Flag to indicate whether validation should check the type of each
+        element.
+    """
+
+    def __init__(self, *types, **metadata):
+        if len(types) == 0:
+            self.init_fast_validate(ValidateTrait.coerce, tuple, None, list)
+
+            super(BaseTuple, self).__init__((), **metadata)
+
+            return
+
+        default_value = None
+
+        if isinstance(types[0], tuple):
+            default_value, types = types[0], types[1:]
+            if len(types) == 0:
+                types = [Trait(element) for element in default_value]
+
+        self.types = tuple([trait_from(type) for type in types])
+        self.init_fast_validate(ValidateTrait.tuple, self.types)
+
+        if default_value is None:
+            default_value = tuple(
+                [type.default_value()[1] for type in self.types]
+            )
+
+        super(BaseTuple, self).__init__(default_value, **metadata)
+
+    def init_fast_validate(self, *args):
+        """ Saves the validation parameters.
+        """
+        self.no_type_check = args[0] == ValidateTrait.coerce
+
+    def validate(self, object, name, value):
+        """ Validates that the value is a valid tuple.
+        """
+        if self.no_type_check:
+            if isinstance(value, tuple):
+                return value
+
+            if isinstance(value, list):
+                return tuple(value)
+
+            self.error(object, name, value)
+
+        try:
+            if isinstance(value, list):
+                value = tuple(value)
+
+            if isinstance(value, tuple):
+                types = self.types
+                if len(value) == len(types):
+                    values = []
+                    for i, type in enumerate(types):
+                        values.append(type.validate(object, name, value[i]))
+
+                    return tuple(values)
+        except:
+            pass
+
+        self.error(object, name, value)
+
+    def full_info(self, object, name, value):
+        """ Returns a description of the trait.
+        """
+        if self.no_type_check:
+            return "a tuple"
+
+        return "a tuple of the form: (%s)" % (
+            ", ".join(
+                [type.full_info(object, name, value) for type in self.types]
+            )
+        )
+
+    def create_editor(self):
+        """ Returns the default UI editor for the trait.
+        """
+        from traitsui.api import TupleEditor
+
+        auto_set = self.auto_set
+        if auto_set is None:
+            auto_set = True
+        enter_set = self.enter_set or False
+
+        return TupleEditor(
+            types=self.types,
+            labels=self.labels or [],
+            cols=self.cols or 1,
+            auto_set=auto_set,
+            enter_set=enter_set,
+        )
+
+
+class Tuple(BaseTuple):
+    """ A fast-validating trait type holding a tuple with typed elements.
+    """
+
+    def init_fast_validate(self, *args):
+        """ Set up the C-level fast validator.
+        """
+        super(Tuple, self).init_fast_validate(*args)
+
+        self.fast_validate = args
+
+
+class ValidatedTuple(BaseTuple):
+    """ A trait type holding a tuple with customized validation.
+
+    Parameters
+    ----------
+    *types
+        Definition of the default and allowed tuples. (see
+        :class:`~.BaseTuple` for more details)
+    fvalidate : callable, optional
+        A callable to provide the additional custom validation for the
+        tuple. The callable will be passed the tuple value and should
+        return True or False.
+    fvalidate_info : string, optional
+        A string describing the custom validation to use for the error
+        messages.
+    **metadata
+        Trait metadata for the trait.
+
+    Example
+    -------
+    The definition::
+
+        value_range = ValidatedTuple(
+            Int(0), Int(1), fvalidate=lambda x: x[0] < x[1])
+
+    will accept only tuples ``(a, b)`` containing two integers that
+    satisfy ``a < b``.
+    """
+
+    def __init__(self, *types, **metadata):
+        metadata.setdefault("fvalidate", None)
+        metadata.setdefault("fvalidate_info", "")
+        super(ValidatedTuple, self).__init__(*types, **metadata)
+
+    def validate(self, object, name, value):
+        """ Validates that the value is a valid tuple.
+        """
+        values = super(ValidatedTuple, self).validate(object, name, value)
+        # Exceptions in the fvalidate function will not result in a TraitError
+        # but will be allowed to propagate up the frame stacks.
+        if self.fvalidate is None or self.fvalidate(values):
+            return values
+        else:
+            self.error(object, name, value)
+
+    def full_info(self, object, name, value):
+        """ Returns a description of the trait.
+        """
+        message = "a tuple of the form: ({0}) that passes custom validation{1}"
+        types_info = ", ".join(
+            [type_.full_info(object, name, value) for type_ in self.types]
+        )
+        if self.fvalidate_info is not None:
+            fvalidate_info = ": {0}".format(self.fvalidate_info)
+        else:
+            fvalidate_info = ""
+        return message.format(types_info, fvalidate_info)
+
+
+class List(TraitType):
+    """ A trait type for a list of values of the specified type.
+
+    The length of the list assigned to the trait must be such that::
+
+        minlen <= len(list) <= maxlen
+
+    Parameters
+    ----------
+    trait : a trait or value that can be converted using trait_from()
+        The type of item that the list contains. If not specified, the list
+        can contain items of any type.
+    value : list
+        Default value for the list.
+    minlen : integer
+        The minimum length of a list that can be assigned to the trait.
+    maxlen : integer
+        The maximum length of a list that can be assigned to the trait.
+    items : bool
+        Whether there is a corresponding `<name>_items` trait.
+    **metadata
+        Trait metadata for the trait.
+
+    Attributes
+    ----------
+    item_trait : trait
+        The type of item that the list contains.
+    minlen : integer
+        The minimum length of a list that can be assigned to the trait.
+    maxlen : integer
+        The maximum length of a list that can be assigned to the trait.
+    has_items : bool
+        Whether there is a corresponding `<name>_items` trait.
+    """
+
+    info_trait = None
+    default_value_type = DefaultValue.trait_list_object
+    _items_event = None
+
+    def __init__(
+        self,
+        trait=None,
+        value=None,
+        minlen=0,
+        maxlen=sys.maxsize,
+        items=True,
+        **metadata
+    ):
+        metadata.setdefault("copy", "deep")
+
+        if isinstance(trait, SequenceTypes):
+            trait, value = value, list(trait)
+
+        if value is None:
+            value = []
+
+        self.item_trait = trait_from(trait)
+        self.minlen = max(0, minlen)
+        self.maxlen = max(minlen, maxlen)
+        self.has_items = items
+
+        if self.item_trait.instance_handler == "_instance_changed_handler":
+            metadata.setdefault("instance_handler", "_list_changed_handler")
+
+        super(List, self).__init__(value, **metadata)
+
+    def validate(self, object, name, value):
+        """ Validates that the values is a valid list.
+
+        .. note::
+
+            `object` can be None when validating a default value (see e.g.
+            :meth:`~traits.trait_handlers.TraitType.clone`)
+
+        """
+        if isinstance(value, list) and (
+            self.minlen <= len(value) <= self.maxlen
+        ):
+            if object is None:
+                return value
+
+            return TraitListObject(self, object, name, value)
+
+        self.error(object, name, value)
+
+    def full_info(self, object, name, value):
+        """ Returns a description of the trait.
+        """
+        if self.minlen == 0:
+            if self.maxlen == sys.maxsize:
+                size = "items"
+            else:
+                size = "at most %d items" % self.maxlen
+        else:
+            if self.maxlen == sys.maxsize:
+                size = "at least %d items" % self.minlen
+            else:
+                size = "from %s to %s items" % (self.minlen, self.maxlen)
+
+        return "a list of %s which are %s" % (
+            size,
+            self.item_trait.full_info(object, name, value),
+        )
+
+    def create_editor(self):
+        """ Returns the default UI editor for the trait.
+        """
+        return list_editor(self, self)
+
+    def inner_traits(self):
+        """ Returns the *inner trait* (or traits) for this trait.
+        """
+        return (self.item_trait,)
+
+    # -- Private Methods ------------------------------------------------------
+
+    def items_event(self):
+        cls = self.__class__
+        if cls._items_event is None:
+            cls._items_event = Event(
+                TraitListEvent, is_base=False
+            ).as_ctrait()
+
+        return cls._items_event
+
+
+class CList(List):
+    """ A coercing trait type for a list of values of the specified type.
+    """
+
+    def validate(self, object, name, value):
+        """ Validates that the values is a valid list.
+        """
+        if not isinstance(value, list):
+            try:
+                # Should work for all iterables as well as strings (which do
+                # not define an __iter__ method)
+                value = list(value)
+            except (ValueError, TypeError):
+                value = [value]
+
+        return super(CList, self).validate(object, name, value)
+
+    def full_info(self, object, name, value):
+        """ Returns a description of the trait.
+        """
+        return "%s or %s" % (
+            self.item_trait.full_info(object, name, value),
+            super(CList, self).full_info(object, name, value),
+        )
+
+
+class PrefixList(TraitType):
+    r"""Ensures that a value assigned to the attribute is a member of a list of
+     specified string values, or is a unique prefix of one of those values.
+
+    The values that can be assigned to a trait attribute of type PrefixList
+    type is the set of all strings supplied to the PrefixList constructor,
+    as well as any unique prefix of those strings. That is, if the set of
+    strings supplied to the constructor is described by
+    [*s*\ :sub:`1`\ , *s*\ :sub:`2`\ , ..., *s*\ :sub:`n`\ ], then the
+    string *v* is a valid value for the trait if *v* == *s*\ :sub:`i[:j]`
+    for one and only one pair of values (i, j). If *v* is a valid value,
+    then the actual value assigned to the trait attribute is the
+    corresponding *s*\ :sub:`i` value that *v* matched.
+
+    The legal values can be provided as an iterable of values.
+
+    Example
+    -------
+    ::
+        class Person(HasTraits):
+            married = PrefixList(['yes', 'no'])
+
+    The Person class has a **married** trait that accepts any of the
+    strings 'y', 'ye', 'yes', 'n', or 'no' as valid values. However, the
+    actual values assigned as the value of the trait attribute are limited
+    to either 'yes' or 'no'. That is, if the value 'y' is assigned to the
+    **married** attribute, the actual value assigned will be 'yes'.
+
+    Note that the algorithm used by PrefixList in determining whether
+    a string is a valid value is fairly efficient in terms of both time and
+    space, and is not based on a brute force set of comparisons.
+
+    Parameters
+    ----------
+    values
+        A single iterable of legal string values.
+
+    Attributes
+    ----------
+    values : tuple of strings
+        Enumeration of all legal values for a trait.
+    """
+
+    #: The default value for the trait:
+    default_value = None
+
+    #: The default value type to use (i.e. 'constant'):
+    default_value_type = DefaultValue.constant
+
+    def __init__(self, values, **metadata):
+        if isinstance(values, (str, bytes, bytearray)):
+            raise TypeError(
+                "Legal values should be provided via an iterable of strings, "
+                "got {!r}.".format(values)
+            )
+        self.values = list(values)
+        self.values_ = values_ = {}
+        for key in values:
+            values_[key] = key
+
+        default = self.default_value
+        if 'default_value' in metadata:
+            default = metadata.pop('default_value')
+            default = self.value_for(default)
+        elif self.values:
+            default = self.values[0]
+
+        super().__init__(default, **metadata)
+
+    def value_for(self, value):
+        if not isinstance(value, str):
+            raise TraitError(
+                "The value of a {} trait must be {}, but a value of {!r} {!r} "
+                "was specified.".format(
+                    self.__class__.__name__, self.info(), value, type(value))
+            )
+
+        if value in self.values_:
+            return self.values_[value]
+
+        matches = [key for key in self.values if key.startswith(value)]
+        if len(matches) == 1:
+            self.values_[value] = match = matches[0]
+            return match
+
+        raise TraitError(
+            "The value of a {} trait must be {}, but a value of {!r} {!r} was "
+            "specified.".format(
+                self.__class__.__name__, self.info(), value, type(value))
+        )
+
+    def info(self):
+        return (
+            " or ".join([repr(x) for x in self.values])
+            + " (or any unique prefix)"
+        )
+
+
+class Set(TraitType):
+    """ A trait type for a set of values of the specified type.
+
+    Parameters
+    ----------
+    trait : a trait or value that can be converted to a trait using Trait()
+        The type of item that the list contains. If not specified, the list
+        can contain items of any type.
+    value : set
+        Default value for the set.
+    items : bool
+        Whether there is a corresponding `<name>_items` trait.
+    **metadata
+        Trait metadata for the trait.
+
+    Attributes
+    ----------
+    item_trait : a trait or value that can be converted to a trait
+        The type of item that the list contains. If not specified, the list
+        can contain items of any type.
+    has_items : bool
+        Whether there is a corresponding `<name>_items` trait.
+    """
+
+    info_trait = None
+    default_value_type = DefaultValue.trait_set_object
+    _items_event = None
+
+    def __init__(self, trait=None, value=None, items=True, **metadata):
+        metadata.setdefault("copy", "deep")
+
+        if isinstance(trait, SetTypes):
+            trait, value = value, set(trait)
+
+        if value is None:
+            value = set()
+
+        self.item_trait = trait_from(trait)
+        self.has_items = items
+
+        super(Set, self).__init__(value, **metadata)
+
+    def validate(self, object, name, value):
+        """ Validates that the values is a valid set.
+
+        .. note::
+
+            `object` can be None when validating a default value (see e.g.
+            :meth:`~traits.trait_handlers.TraitType.clone`)
+
+        """
+        if isinstance(value, set):
+            if object is None:
+                return value
+
+            return TraitSetObject(self, object, name, value)
+
+        self.error(object, name, value)
+
+    def full_info(self, object, name, value):
+        """ Returns a description of the trait.
+        """
+        return "a set of %s" % self.item_trait.full_info(object, name, value)
+
+    def create_editor(self):
+        """ Returns the default UI editor for the trait.
+        """
+        from traitsui.api import TextEditor
+
+        return TextEditor(evaluate=eval)
+
+    def inner_traits(self):
+        """ Returns the *inner trait* (or traits) for this trait.
+        """
+        return (self.item_trait,)
+
+    # -- Private Methods ------------------------------------------------------
+
+    def items_event(self):
+        if self.__class__._items_event is None:
+            self.__class__._items_event = Event(
+                TraitSetEvent, is_base=False
+            ).as_ctrait()
+
+        return self.__class__._items_event
+
+
+class CSet(Set):
+    """ A coercing trait type for a set of values of the specified type.
+    """
+
+    def validate(self, object, name, value):
+        """ Validates that the values is a valid list.
+        """
+        if not isinstance(value, set):
+            try:
+                # Should work for all iterables as well as strings (which do
+                # not define an __iter__ method)
+                value = set(value)
+            except (ValueError, TypeError):
+                value = set([value])
+
+        return super(CSet, self).validate(object, name, value)
+
+    def full_info(self, object, name, value):
+        """ Returns a description of the trait.
+        """
+        return "%s or %s" % (
+            self.item_trait.full_info(object, name, value),
+            super(CSet, self).full_info(object, name, value),
+        )
+
+
+class Dict(TraitType):
+    """ A trait type for a dictionary with specified key and value types.
+
+
+    Parameters
+    ----------
+    key_trait : a trait or value that can be converted using trait_from()
+        The trait type for keys in the dictionary; if not specified, any
+        values can be used as keys.
+    value_trait : a trait or value that can be converted using trait_from()
+        The trait type for values in the dictionary; if not specified, any
+        values can be used as dictionary values.
+    value : dict
+        The default value for the returned trait.
+    items : bool
+        Indicates whether the value contains items.
+
+    Attributes
+    ----------
+    key_trait : a trait
+        The trait type for keys in the dictionary; if not specified, any
+        values can be used as keys.
+    value_trait : a trait
+        The trait type for values in the dictionary; if not specified, any
+        values can be used as dictionary values.
+    value_trait_handler : TraitHandler
+        The TraitHandler for the value_trait.
+    has_items : bool
+        Indicates whether the value contains items.
+    """
+
+    info_trait = None
+    default_value_type = DefaultValue.trait_dict_object
+    _items_event = None
+
+    def __init__(
+        self,
+        key_trait=None,
+        value_trait=None,
+        value=None,
+        items=True,
+        **metadata
+    ):
+        if isinstance(key_trait, dict):
+            key_trait, value_trait, value = value_trait, value, key_trait
+
+        if value is None:
+            value = {}
+
+        self.key_trait = trait_from(key_trait)
+        self.value_trait = trait_from(value_trait)
+        self.has_items = items
+
+        handler = self.value_trait.handler
+        if (handler is not None) and handler.has_items:
+            handler = handler.clone()
+            handler.has_items = False
+        self.value_handler = handler
+
+        super(Dict, self).__init__(value, **metadata)
+
+    def validate(self, object, name, value):
+        """ Validates that the value is a valid dictionary.
+
+        Note
+        ----
+        `object` can be None when validating a default value (see e.g.
+        :meth:`~traits.trait_handlers.TraitType.clone`)
+        """
+        if isinstance(value, dict):
+            if object is None:
+                return value
+            return TraitDictObject(self, object, name, value)
+
+        self.error(object, name, value)
+
+    def full_info(self, object, name, value):
+        """ Returns a description of the trait.
+        """
+        return (
+            "a dictionary with keys which are %s and with values which "
+            "are %s"
+        ) % (
+            self.key_trait.full_info(object, name, value),
+            self.value_trait.full_info(object, name, value),
+        )
+
+    def create_editor(self):
+        """ Returns the default UI editor for the trait.
+        """
+        from traitsui.api import TextEditor
+
+        return TextEditor(evaluate=eval)
+
+    def inner_traits(self):
+        """ Returns the *inner trait* (or traits) for this trait.
+        """
+        return (self.key_trait, self.value_trait)
+
+    # -- Private Methods ------------------------------------------------------
+
+    def items_event(self):
+        cls = self.__class__
+        if cls._items_event is None:
+            cls._items_event = Event(TraitDictEvent, is_base=False).as_ctrait()
+
+        return cls._items_event
+
+
+#: Allowed values and mappings for the 'adapt' keyword.
+#:
+#: - 'no': Adaptation is not allowed.
+#: - 'yes': Adaptation is allowed. If adaptation fails, an
+#:   exception should be raised.
+#: - 'default': Adaptation is allowed. If adaptation fails, the
+#:   default value for the trait should be used.
+AdaptMap = {"no": 0, "yes": 1, "default": 2}
+
+
+class Map(TraitType):
+    """ Checks that the value assigned to a trait attribute is a key of a
+        specified dictionary, and also assigns the dictionary value
+        corresponding to that key to a *shadow* attribute.
+
+        A trait attribute of type Map is called a *mapped* trait
+        attribute. In practice, this means that the resulting object actually
+        contains two attributes: one whose value is a key of the Map
+        dictionary, and the other whose value is the corresponding value of the
+        Map dictionary. The name of the shadow attribute is simply the base
+        attribute name with an underscore ('_') appended. Mapped trait
+        attributes can be used to allow a variety of user-friendly input values
+        to be mapped to a set of internal, program-friendly values.
+
+        Example
+        -------
+
+        The following example defines a ``Person`` class::
+
+            >>> class Person(HasTraits):
+            ...     married = Map({'yes': 1, 'no': 0 }, default_value="yes")
+            ...
+            >>> bob = Person()
+            >>> print(bob.married)
+            yes
+            >>> print(bob.married_)
+            1
+
+        In this example, the default value of the ``married`` attribute of the
+        Person class is 'yes'. Because this attribute is defined using
+        Map, instances of Person have another attribute,
+        ``married_``, whose default value is 1, the dictionary value
+        corresponding to the key 'yes'.
+
+        Parameters
+        ----------
+        map : dict
+            A dictionary whose keys are valid values for the trait attribute,
+            and whose corresponding values are the values for the shadow
+            trait attribute.
+        default_value : object, optional
+            The default value for the trait. If given, this should be a key
+            from the mapping. If not given, the first key from the mapping (in
+            normal dictionary iteration order) will be used as the default.
+
+        Attributes
+        ----------
+        map : dict
+            A dictionary whose keys are valid values for the trait attribute,
+            and whose corresponding values are the values for the shadow
+            trait attribute.
+    """
+
+    is_mapped = True
+
+    def __init__(self, map, **metadata):
+
+        self.map = map
+        self.fast_validate = (ValidateTrait.map, map)
+
+        try:
+            default_value = metadata.pop("default_value")
+        except KeyError:
+            default_value = next(iter(self.map))
+
+        super().__init__(default_value, **metadata)
+
+    def validate(self, object, name, value):
+        try:
+            if value in self.map:
+                return value
+        except TypeError:
+            pass
+
+        self.error(object, name, value)
+
+    def mapped_value(self, value):
+        """ Get the mapped value for a value. """
+        return self.map[value]
+
+    def post_setattr(self, object, name, value):
+        setattr(object, name + "_", self.mapped_value(value))
+
+    def info(self):
+        keys = sorted(repr(x) for x in self.map.keys())
+        return " or ".join(keys)
+
+    def get_editor(self, trait):
+        from traitsui.api import EnumEditor
+
+        return EnumEditor(values=self, cols=trait.cols or 3)
+
+
+class PrefixMap(TraitType):
+    """ A cross between the PrefixList and Map classes.
+
+    Like Map, PrefixMap is created using a dictionary, but in this
+    case, the keys of the dictionary must be strings. Like PrefixList,
+    a string *v* is a valid value for the trait attribute if it is a prefix of
+    one and only one key *k* in the dictionary. The actual values assigned to
+    the trait attribute is *k*, and its corresponding mapped attribute is
+    *map*[*k*].
+
+    Example
+    -------
+    ::
+
+        mapping = {'true': 1, 'yes': 1, 'false': 0, 'no': 0 }
+        boolean_map = PrefixMap(mapping)
+
+    This example defines a Boolean trait that accepts any prefix of 'true',
+    'yes', 'false', or 'no', and maps them to 1 or 0.
+
+    Parameters
+    ----------
+    map : dict
+        A dictionary whose keys are strings that are valid values for the
+        trait attribute, and whose corresponding values are the values for
+        the shadow trait attribute.
+    default_value : object, optional
+        The default value for the trait. If given, this should be either a key
+        from the mapping or a unique prefix of a key from the mapping. If not
+        given, the first key from the mapping (in normal dictionary iteration
+        order) will be used as the default.
+
+    Attributes
+    ----------
+    map : dict
+        A dictionary whose keys are strings that are valid values for the
+        trait attribute, and whose corresponding values are the values for
+        the shadow trait attribute.
+    """
+    is_mapped = True
+
+    def __init__(self, map, **metadata):
+        self.map = map
+        self._map = {}
+        for key in map.keys():
+            self._map[key] = key
+
+        try:
+            default_value = metadata.pop("default_value")
+        except KeyError:
+            default_value = next(iter(self.map))
+        else:
+            default_value = self.value_for(default_value)
+
+        super().__init__(default_value, **metadata)
+
+    def value_for(self, value):
+        if not isinstance(value, str):
+            raise TraitError(
+                "Value must be {}, but a value {!r} was specified.".format(
+                    self.info(), value)
+            )
+
+        if value in self._map:
+            return self._map[value]
+
+        matches = [key for key in self.map if key.startswith(value)]
+        if len(matches) == 1:
+            self._map[value] = match = matches[0]
+            return match
+
+        raise TraitError(
+            "Value must be {}, but a value {!r} was specified.".format(
+                self.info(), value)
+        )
+
+    def mapped_value(self, value):
+        """ Get the mapped value for a value. """
+        return self.map[value]
+
+    def post_setattr(self, object, name, value):
+        setattr(object, name + "_", self.mapped_value(value))
+
+    def info(self):
+        keys = sorted(repr(x) for x in self.map.keys())
+        return " or ".join(keys) + " (or any unique prefix)"
+
+    def get_editor(self, trait):
+        from traitsui.api import EnumEditor
+
+        return EnumEditor(values=self, cols=trait.cols or 3)
+
+
+class BaseClass(TraitType):
+    """ A base trait type for trait types which have an associated class.
+
+    Traits sometimes need to be able to access classes which have not
+    yet been defined, or which are from a module that we want to defer
+    importing from.  To support this, classes can be determined
+    dynamically by specifying a string name for the class (e.g.
+    ``'package1.package2.module.class'``).  This base class provides the
+    machinery for this sort of deferred access to classes.
+
+    Any subclass must define instances with 'klass' and 'module' attributes
+    that contain the string name of the class (or actual class object) and
+    the module name that contained the original trait definition (used for
+    resolving local class names (e.g. 'LocalClass')).
+
+    This is an abstract class that only provides helper methods used to
+    resolve the class name into an actual class object.
+
+    Attributes
+    ----------
+    klass : type or str
+        The class object or a string that refers to it.
+    module : str
+        The name of the module that contains the class.
+    """
+
+    def resolve_class(self, object, name, value):
+        """ Resolve the class object as part of validation.
+
+        This is called when the ``klass`` attribute is a string and sets the
+        ``klass`` attribute to the actual klass object as a side-effect.  If
+        the class cannot be resolved, it will call validate_failed().
+        """
+        klass = self.validate_class(self.find_class(self.klass))
+        if klass is None:
+            self.validate_failed(object, name, value)
+
+        self.klass = klass
+
+    def validate_class(self, klass):
+        """ Validate a class object. """
+        return klass
+
+    def find_class(self, klass):
+        """ Given a string describing a class, get the class object.
+        """
+        module = self.module
+        col = klass.rfind(".")
+        if col >= 0:
+            module = klass[:col]
+            klass = klass[col + 1:]
+
+        theClass = getattr(sys.modules.get(module), klass, None)
+        if (theClass is None) and (col >= 0):
+            try:
+                mod = import_module(module)
+                theClass = getattr(mod, klass, None)
+            except Exception:
+                pass
+
+        return theClass
+
+    def validate_failed(self, object, name, value):
+        """ Raise a TraitError if the class could not be resolved. """
+        self.error(object, name, value)
+
+
+class BaseInstance(BaseClass):
+    """ A trait type whose value is an instance of a class or its subclasses.
+
+    The default value is **None** if *klass* is an instance or if it is a
+    class and *args* and *kw* are not specified. Otherwise, the default value
+    is the instance obtained by calling ``klass(*args, **kw)``. Note that the
+    constructor call is performed each time a default value is assigned, so
+    each default value assigned is a unique instance.
+
+    Parameters
+    ----------
+    klass : class, str or instance
+        The object that forms the basis for the trait; if it is an
+        instance, then trait values must be instances of the same class or
+        a subclass. This object is not the default value, even if it is an
+        instance.  If the provided value is a string, it is expected to be
+        a reference to a class that will be resolved at run-time.
+    factory : callable
+        A callable, typically a class, that when called with *args* and
+        *kw*, returns the default value for the trait. If not specified,
+        or *None*, *klass* is used as the factory.
+    args : tuple
+        Positional arguments for generating the default value.
+    kw : dictionary
+        Keyword arguments for generating the default value.
+    allow_none : bool
+        Indicates whether None is allowed as a value.
+    adapt : str
+        A string specifying how adaptation should be applied. The possible
+        values are:
+
+        - 'no': Adaptation is not allowed.
+        - 'yes': Adaptation is allowed. If adaptation fails, an
+          exception should be raised.
+        - 'default': Adaptation is allowed. If adaptation fails, the
+          default value for the trait should be used.
+
+    Attributes
+    ----------
+    factory : callable
+        A callable, typically a class, that when called with *args* and
+        *kw*, returns the default value for the trait. If not specified,
+        or *None*, *klass* is used as the factory.
+    args : tuple
+        Positional arguments for generating the default value.
+    kw : dictionary
+        Keyword arguments for generating the default value.
+    allow_none : bool
+        Indicates whether None is allowed as a value.
+    adapt : str
+        A string specifying how adaptation should be applied. The possible
+        values are:
+
+        - 'no': Adaptation is not allowed.
+        - 'yes': Adaptation is allowed. If adaptation fails, an
+          exception should be raised.
+        - 'default': Adaptation is allowed. If adaptation fails, the
+          default value for the trait should be used.
+    """
+
+    #: Default adaptation behavior.
+    adapt_default = "no"
+
+    def __init__(
+        self,
+        klass=None,
+        factory=None,
+        args=None,
+        kw=None,
+        allow_none=True,
+        adapt=None,
+        module=None,
+        **metadata
+    ):
+        if klass is None:
+            raise TraitError(
+                "A %s trait must have a class specified."
+                % self.__class__.__name__
+            )
+
+        metadata.setdefault("copy", "deep")
+        metadata.setdefault("instance_handler", "_instance_changed_handler")
+
+        adapt = adapt or self.adapt_default
+        if adapt not in AdaptMap:
+            raise TraitError("'adapt' must be 'yes', 'no' or 'default'.")
+
+        if isinstance(factory, tuple):
+            if args is None:
+                args, factory = factory, klass
+            elif isinstance(args, dict):
+                factory, args, kw = klass, factory, args
+
+        elif (kw is None) and isinstance(factory, dict):
+            kw, factory = factory, klass
+
+        elif ((args is not None) or (kw is not None)) and (factory is None):
+            factory = klass
+
+        self._allow_none = allow_none
+        self.adapt = AdaptMap[adapt]
+        self.module = module or get_module_name()
+
+        if isinstance(klass, str):
+            self.klass = klass
+        else:
+            if not isinstance(klass, type):
+                klass = klass.__class__
+
+            self.klass = klass
+            self.init_fast_validate()
+
+        value = factory
+        if factory is not None:
+            if args is None:
+                args = ()
+
+            if kw is None:
+                if isinstance(args, dict):
+                    kw = args
+                    args = ()
+                else:
+                    kw = {}
+            elif not isinstance(kw, dict):
+                raise TraitError("The 'kw' argument must be a dictionary.")
+
+            if (not callable(factory)) and (
+                not isinstance(factory, str)
+            ):
+                if (len(args) > 0) or (len(kw) > 0):
+                    raise TraitError("'factory' must be callable")
+            else:
+                value = _InstanceArgs(factory, args, kw)
+
+        self.default_value = value
+
+        super(BaseInstance, self).__init__(value, **metadata)
+
+    def validate(self, object, name, value):
+        """ Validates that the value is a valid object instance.
+        """
+        from traits.adaptation.api import adapt
+
+        if value is None:
+            if self._allow_none:
+                return value
+
+            self.validate_failed(object, name, value)
+
+        if isinstance(self.klass, str):
+            self.resolve_class(object, name, value)
+
+        # Adaptation mode 0: do a simple isinstance check.
+        if self.adapt == 0:
+            if isinstance(value, self.klass):
+                return value
+            else:
+                self.validate_failed(object, name, value)
+
+        # Try adaptation; return adapted value on success.
+        result = adapt(value, self.klass, None)
+        if result is not None:
+            return result
+
+        # Adaptation failed. Move on to an isinstance check.
+        if isinstance(value, self.klass):
+            return value
+
+        # Adaptation and isinstance both failed. In mode 1, fail.
+        # Otherwise, return the default.
+        if self.adapt == 1:
+            self.validate_failed(object, name, value)
+        else:
+            result = self.default_value
+            if isinstance(result, _InstanceArgs):
+                return result[0](*result[1], **result[2])
+            else:
+                return result
+
+    def info(self):
+        """ Returns a description of the trait.
+        """
+        klass = self.klass
+        if not isinstance(klass, str):
+            klass = klass.__name__
+
+        if self.adapt == 0:
+            result = class_of(klass)
+        else:
+            result = (
+                "an implementor of, or can be adapted to implement, %s" % klass
+            )
+
+        if self._allow_none:
+            return result + " or None"
+
+        return result
+
+    def get_default_value(self):
+        """ Returns a tuple of the form: ( default_value_type, default_value )
+            which describes the default value for this trait.
+        """
+        dv = self.default_value
+        dvt = self.default_value_type
+        if dvt < 0:
+            if not isinstance(dv, _InstanceArgs):
+                return super(BaseInstance, self).get_default_value()
+
+            self.default_value_type = dvt = DefaultValue.callable_and_args
+            self.default_value = dv = (
+                self.create_default_value,
+                dv.args,
+                dv.kw,
+            )
+
+        return (dvt, dv)
+
+    def create_editor(self):
+        """ Returns the default traits UI editor for this type of trait.
+        """
+        from traitsui.api import InstanceEditor
+
+        return InstanceEditor(
+            label=self.label or "",
+            view=self.view or "",
+            kind=self.kind or "live",
+        )
+
+    # -- Private Methods ------------------------------------------------------
+
+    def create_default_value(self, *args, **kw):
+        klass = args[0]
+        if isinstance(klass, str):
+            klass = self.validate_class(self.find_class(klass))
+            if klass is None:
+                raise TraitError("Unable to locate class: " + args[0])
+
+        return klass(*args[1:], **kw)
+
+    #: fixme: Do we still need this method using the new style?...
+    def allow_none(self):
+        self._allow_none = True
+        self.init_fast_validate()
+
+    def init_fast_validate(self):
+        """ Does nothing for the BaseInstance' class. Used by the 'Instance',
+            'Supports' and 'AdaptsTo' classes to set up the C-level fast
+            validator.
+        """
+        pass
+
+    def resolve_class(self, object, name, value):
+        super(BaseInstance, self).resolve_class(object, name, value)
+
+        # fixme: The following is quite ugly, because it wants to try and fix
+        # the trait referencing this handler to use the 'fast path' now that
+        # the actual class has been resolved. The problem is finding the trait,
+        # especially in the case of List(Instance('foo')), where the
+        # object.base_trait(...) value is the List trait, not the Instance
+        # trait, so we need to check for this and pull out the List
+        # 'item_trait'. Obviously this does not extend well to other traits
+        # containing nested trait references (Dict?)...
+        self.init_fast_validate()
+        trait = object.base_trait(name)
+        handler = trait.handler
+        if handler is not self:
+            set_validate = getattr(handler, "set_validate", None)
+            if set_validate is not None:
+                # The outer trait is a TraitCompound. Recompute its
+                # fast_validate table now that we have updated ours.
+                # FIXME: there are probably still issues if the TraitCompound
+                # is further nested.
+                set_validate()
+            else:
+                item_trait = getattr(handler, "item_trait", None)
+                if item_trait is not None and item_trait.handler is self:
+                    # The outer trait is a List trait.
+                    trait = item_trait
+                    handler = self
+                else:
+                    return
+        if handler.fast_validate is not None:
+            trait.set_validate(handler.fast_validate)
+
+
+class Instance(BaseInstance):
+    """ A fast-validated trait type whose value is an instance of a class.
+    """
+
+    def init_fast_validate(self):
+        """ Sets up the C-level fast validator. """
+
+        if self.adapt == 0:
+            fast_validate = [ValidateTrait.instance, self.klass]
+            if self._allow_none:
+                fast_validate = [ValidateTrait.instance, None, self.klass]
+            else:
+                fast_validate = [ValidateTrait.instance, self.klass]
+
+            if self.klass in TypeTypes:
+                fast_validate[0] = ValidateTrait.type
+
+            self.fast_validate = tuple(fast_validate)
+        else:
+            self.fast_validate = (
+                ValidateTrait.adapt, self.klass, self.adapt, self._allow_none)
+
+
+class Supports(Instance):
+    """ A trait type whose value is adapted to a specified protocol.
+
+    In other words, the value of the trait directly provide, or can be adapted
+    to, the given protocol (Interface or type).
+
+    The value of the trait after assignment is the possibly adapted value
+    (i.e., it is the original assigned value if that provides the protocol,
+    or is an adapter otherwise).
+
+    The original, unadapted value is stored in a "shadow" attribute with
+    the same name followed by an underscore (e.g., ``foo`` and ``foo_``).
+    """
+
+    adapt_default = "yes"
+
+    def post_setattr(self, object, name, value):
+        """ Performs additional post-assignment processing.
+        """
+        # Save the original, unadapted value in the mapped trait:
+        object.__dict__[name + "_"] = value
+
+    def as_ctrait(self):
+        """ Returns a CTrait corresponding to the trait defined by this class.
+        """
+        return self.modify_ctrait(super(Supports, self).as_ctrait())
+
+    def modify_ctrait(self, ctrait):
+
+        # Tell the C code that the 'post_setattr' method wants the original,
+        # unadapted value passed to 'setattr':
+        ctrait.post_setattr_original_value = True
+        return ctrait
+
+
+class AdaptsTo(Supports):
+    """ A trait type whose value must support a specified protocol.
+
+    In other words, the value of the trait directly provide, or can be adapted
+    to, the given protocol (Interface or type).
+
+    The value of the trait after assignment is the original, unadapted value.
+
+    A possibly adapted value is stored in a "shadow" attribute with
+    the same name followed by an underscore (e.g., ``foo`` and ``foo_``).
+    """
+
+    def modify_ctrait(self, ctrait):
+        # Tell the C code that 'setattr' should store the original, unadapted
+        # value passed to it:
+        ctrait.setattr_original_value = True
+        return ctrait
+
+
+class Type(BaseClass):
+    """ A trait type whose value must be a subclass of a specified class.
+
+    Parameters
+    ----------
+    value : class or None
+        The default value of the trait.
+    klass : class, str or None
+        The class that trait values must be subclasses of.  If None, then
+        the default value is used instead.  If both are None, then the
+        ``object`` type is used.  If it is a string, the first time that
+        the validate method is called, the class will be imported and
+        the value replaced with the class object.
+    allow_none : bool
+        Indicates whether None is allowed as an assignable value. Even if
+        **False**, the default *value* may be **None**.
+    **metadata
+        Trait metadata for the trait.
+
+    Attributes
+    ----------
+    klass : class or str
+        The class that trait values must be subclasses of.  If this is a
+        string, the first time that the validate method is called, the
+        class will be imported and the value replaced with the class object.
+    module : str
+        The name of the module where local class names (ie. class names
+        with no module components) are presumed to be importable from.
+        This is the caller's caller's module, as determined by the
+        ``get_module_method``.
+    """
+
+    def __init__(self, value=None, klass=None, allow_none=True, **metadata):
+        if value is None:
+            if klass is None:
+                klass = object
+
+        elif klass is None:
+            klass = value
+
+        if isinstance(klass, str):
+            self.validate = self.resolve
+
+        elif not isinstance(klass, type):
+            raise TraitError("A Type trait must specify a class.")
+
+        self.klass = klass
+        self._allow_none = allow_none
+        self.module = get_module_name()
+
+        super(Type, self).__init__(value, **metadata)
+
+    def validate(self, object, name, value):
+        """ Validates that the value is a valid object instance.
+        """
+        try:
+            if issubclass(value, self.klass):
+                return value
+        except:
+            if (value is None) and (self._allow_none):
+                return value
+
+        self.error(object, name, value)
+
+    def resolve(self, object, name, value):
+        """ Resolves a class originally specified as a string into an actual
+            class, then resets the trait so that future calls will be handled
+            by the normal validate method.
+        """
+        if isinstance(self.klass, str):
+            self.resolve_class(object, name, value)
+            del self.validate
+
+        return self.validate(object, name, value)
+
+    def info(self):
+        """ Returns a description of the trait.
+        """
+        klass = self.klass
+        if not isinstance(klass, str):
+            klass = klass.__name__
+
+        result = "a subclass of " + klass
+
+        if self._allow_none:
+            return result + " or None"
+
+        return result
+
+    def get_default_value(self):
+        """ Returns a tuple of the form: ( default_value_type, default_value )
+        which describes the default value for this trait.
+        """
+        if not isinstance(self.default_value, str):
+            return super(Type, self).get_default_value()
+
+        return (
+            DefaultValue.callable_and_args,
+            (self.resolve_default_value, (), None),
+        )
+
+    def resolve_default_value(self):
+        """ Resolves a class name into a class so that it can be used to
+            return the class as the default value of the trait.
+        """
+        if isinstance(self.klass, str):
+            try:
+                self.resolve_class(None, None, None)
+                del self.validate
+            except:
+                raise TraitError(
+                    "Could not resolve %s into a valid class" % self.klass
+                )
+
+        return self.klass
+
+
+#: An alias for the Type trait
+Subclass = Type
+
+
+class Event(TraitType):
+    """ A trait type that holds no value but can be set and listened to.
+
+    Event traits are write-only traits.  They do not hold any value, but
+    they can be assigned to, and listeners to the trait will be notified
+    of the assignment.  Since no value is held, trait change functions that
+    ask for the ``old`` value of the trait will be given the Undefined
+    special value.
+
+    Event traits can be given an optional trait type that is used to validate
+    values assigned to the trait.  If the assigned value does not validate,
+    then a TraitError will occur.
+
+    Parameters
+    ----------
+    trait : a trait
+        The type of value that can be assigned to the event.
+    """
+
+    def __init__(self, trait=None, **metadata):
+        metadata["type"] = "event"
+        metadata["transient"] = True
+
+        super(Event, self).__init__(**metadata)
+
+        self.trait = None
+        if trait is not None:
+            self.trait = trait_from(trait)
+            validate = self.trait.get_validate()
+            if validate is not None:
+                self.fast_validate = validate
+
+    def full_info(self, object, name, value):
+        """ Returns a description of the trait.
+        """
+        trait = self.trait
+        if trait is None:
+            return "any value"
+
+        return trait.full_info(object, name, value)
+
+
+class Button(Event):
+    """ An Event trait type whose UI editor is a button.
+
+    Parameters
+    ----------
+    label : str
+        The label for the button.
+    image : pyface.ImageResource
+        An image to display on the button.
+    style : 'button', 'radio', 'toolbar' or 'checkbox'
+        The style of button to display.
+    values_trait : str
+        For a "button" or "toolbar" style, the name of an enum
+        trait whose values will populate a drop-down menu on the button.
+        The selected value will replace the label on the button.
+    orientation : 'horizontal' or 'vertical'
+        The orientation of the label relative to the image.
+    width_padding : integer between 0 and 31
+        Extra padding (in pixels) added to the left and right sides of
+        the button.
+    height_padding : integer between 0 and 31
+        Extra padding (in pixels) added to the top and bottom of the
+        button.
+    view : traitsui View, optional
+        An optional View to display when the button is clicked.
+    **metadata
+        Trait metadata for the trait.
+
+    Attributes
+    ----------
+    label : str
+        The label for the button.
+    image : pyface.ImageResource
+        An image to display on the button.
+    style : 'button', 'radio', 'toolbar' or 'checkbox'
+        The style of button to display.
+    values_trait : str
+        For a "button" or "toolbar" style, the name of an enum
+        trait whose values will populate a drop-down menu on the button.
+        The selected value will replace the label on the button.
+    orientation : 'horizontal' or 'vertical'
+        The orientation of the label relative to the image.
+    width_padding : integer between 0 and 31
+        Extra padding (in pixels) added to the left and right sides of
+        the button.
+    height_padding : integer between 0 and 31
+        Extra padding (in pixels) added to the top and bottom of the
+        button.
+    view : traitsui View, optional
+        An optional View to display when the button is clicked.
+    """
+
+    def __init__(
+        self,
+        label="",
+        image=None,
+        values_trait=None,
+        style="button",
+        orientation="vertical",
+        width_padding=7,
+        height_padding=5,
+        view=None,
+        **metadata
+    ):
+        self.label = label
+        self.values_trait = values_trait
+        self.image = image
+        self.style = style
+        self.orientation = orientation
+        self.width_padding = width_padding
+        self.height_padding = height_padding
+        self.view = view
+        super(Button, self).__init__(**metadata)
+
+    def create_editor(self):
+        from traitsui.api import ButtonEditor
+
+        editor = ButtonEditor(
+            label=self.label,
+            values_trait=self.values_trait,
+            image=self.image,
+            style=self.style,
+            orientation=self.orientation,
+            width_padding=self.width_padding,
+            height_padding=self.height_padding,
+            view=self.view,
+        )
+        return editor
+
+
+class ToolbarButton(Button):
+    """ A Button trait type whose UI editor is a toolbar button.
+
+    This is just a Button trait with different defaults to style it like
+    a toolbar button.
+
+    Parameters
+    ----------
+    label : str
+        The label for the button.
+    image : pyface.ImageResource
+        An image to display on the button.
+    style : 'button', 'radio', 'toolbar' or 'checkbox'
+        The style of button to display.
+    orientation : 'horizontal' or 'vertical'
+        The orientation of the label relative to the image.
+    width_padding : integer between 0 and 31
+        Extra padding (in pixels) added to the left and right sides of
+        the button.
+    height_padding : integer between 0 and 31
+        Extra padding (in pixels) added to the top and bottom of the
+        button.
+    **metadata
+        Trait metadata for the trait.
+
+    Attributes
+    ----------
+    label : str
+        The label for the button.
+    image : pyface.ImageResource
+        An image to display on the button.
+    style : 'button', 'radio', 'toolbar' or 'checkbox'
+        The style of button to display.
+    values_trait : str
+        For a "button" or "toolbar" style, the name of an enum
+        trait whose values will populate a drop-down menu on the button.
+        The selected value will replace the label on the button.
+    orientation : 'horizontal' or 'vertical'
+        The orientation of the label relative to the image.
+    width_padding : integer between 0 and 31
+        Extra padding (in pixels) added to the left and right sides of
+        the button.
+    height_padding : integer between 0 and 31
+        Extra padding (in pixels) added to the top and bottom of the
+        button.
+    view : traitsui View, optional
+        An optional View to display when the button is clicked.
+    """
+
+    def __init__(
+        self,
+        label="",
+        image=None,
+        style="toolbar",
+        orientation="vertical",
+        width_padding=2,
+        height_padding=2,
+        **metadata
+    ):
+        super(ToolbarButton, self).__init__(
+            label,
+            image=image,
+            style=style,
+            orientation=orientation,
+            width_padding=width_padding,
+            height_padding=height_padding,
+            **metadata
+        )
+
+
+class Either(TraitType):
+    """ A trait type whose value can be any of of a specified list of traits.
+
+    Parameters
+    ----------
+    *traits
+        Arguments that define allowable trait values.
+    **metadata
+        Trait metadata for the trait.
+
+    Attributes
+    ----------
+    trait_maker : TraitHandler
+        A TraitHandler generated by _TraitMaker from the arguments.
+    """
+
+    def __init__(self, *traits, **metadata):
+        self.trait_maker = _TraitMaker(
+            metadata.pop("default", None), *traits, **metadata
+        )
+
+    def as_ctrait(self):
+        """ Returns a CTrait corresponding to the trait defined by this class.
+        """
+        return self.trait_maker.as_ctrait()
+
+
+class _NoneTrait(TraitType):
+    """ Defines a trait that only accepts the None value
+
+    This is primarily used for supporting ``Union``.
+    """
+
+    info_text = "None"
+
+    default_value = None
+
+    default_value_type = DefaultValue.constant
+
+    def __init__(self, **metadata):
+        default_value = metadata.pop("default_value", None)
+        if default_value is not None:
+            raise ValueError("Cannot set default value {} "
+                             "for _NoneTrait".format(default_value))
+        super(_NoneTrait, self).__init__(**metadata)
+
+    def validate(self, obj, name, value):
+        if value is None:
+            return value
+
+        self.error(obj, name, value)
+
+
+class Union(TraitType):
+    """ Defines a trait whose value can be any of of a specified list of
+    trait types or list of trait type instances or None
+
+    If the default value is not defined on Union, the default value from the
+    first trait will be used.
+    """
+
+    def __init__(self, *traits, **metadata):
+        self.list_ctrait_instances = []
+
+        if not traits:
+            traits = (_NoneTrait,)
+
+        for trait in traits:
+            if trait is None:
+                trait = _NoneTrait
+            ctrait_instance = trait_cast(trait)
+            if ctrait_instance is None:
+                raise ValueError("Union trait declaration expects a trait "
+                                 "type or an instance of trait type or None,"
+                                 " but got {!r} instead".format(trait))
+
+            self.list_ctrait_instances.append(ctrait_instance)
+
+        # ``Either`` uses 'default' for defining static default values.
+        # Raise if 'default' is found in order to help code migrate to Union
+        if "default" in metadata:
+            raise ValueError(
+                "Union default value should be set via 'default_value', not "
+                "'default'."
+            )
+
+        default_value = None
+        if 'default_value' in metadata:
+            default_value = metadata.pop("default_value")
+        elif self.list_ctrait_instances:
+            default_value = self.list_ctrait_instances[0].default
+
+        self.default_value_type = _infer_default_value_type(default_value)
+        super().__init__(default_value, **metadata)
+
+    def validate(self, obj, name, value):
+        """ Return the value by the first trait in the list that can
+        validate the assigned value, raise an error if none of them can.
+        """
+        for trait_type_instance in self.list_ctrait_instances:
+            try:
+                return trait_type_instance.validate(obj, name, value)
+            except TraitError:
+                pass
+
+        self.error(obj, name, value)
+
+    def info(self):
+        return " or ".join([ctrait.info() for ctrait in
+                            self.list_ctrait_instances])
+
+    def inner_traits(self):
+        return tuple(self.list_ctrait_instances)
+
+    def get_editor(self, trait):
+        from traitsui.api import TextEditor, CompoundEditor
+
+        the_editors = [x.get_editor() for x in self.list_ctrait_instances]
+        text_editor = TextEditor()
+        count = 0
+        editors = []
+        for editor in the_editors:
+            if isinstance(text_editor, editor.__class__):
+                count += 1
+                if count > 1:
+                    continue
+            editors.append(editor)
+
+        return CompoundEditor(editors=editors)
+
+
+# -------------------------------------------------------------------------------
+#  'Symbol' trait:
+# -------------------------------------------------------------------------------
+class Symbol(TraitType):
+    """ A property trait type that refers to a Python object by name.
+
+    The value set to the trait must be a value of the form
+    ``'[package.package...package.]module[:symbol[([arg1,...,argn])]]'``
+    which is imported and evaluated to get underlying value.
+
+    The value returned by the trait is the actual object that this string
+    refers to.  The value is cached, so any calls are only evaluated once.
+    """
+
+    #: A description of the type of value this trait accepts:
+    info_text = (
+        "an object or a string of the form "
+        "'[package.package...package.]module[:symbol[([arg1,...,argn])]]' "
+        "specifying where to locate the object"
+    )
+
+    def get(self, object, name):
+        value = object.__dict__.get(name, Undefined)
+        if value is Undefined:
+            cache = TraitsCache + name
+            ref = object.__dict__.get(cache)
+            if ref is None:
+                object.__dict__[cache] = ref = object.trait(
+                    name
+                ).default_value_for(object, name)
+
+            if isinstance(ref, str):
+                object.__dict__[name] = value = self._resolve(ref)
+
+        return value
+
+    def set(self, object, name, value):
+        dict = object.__dict__
+        old = dict.get(name, Undefined)
+        if isinstance(value, str):
+            dict.pop(name, None)
+            dict[TraitsCache + name] = value
+            object.trait_property_changed(name, old)
+        else:
+            dict[name] = value
+            object.trait_property_changed(name, old, value)
+
+    def _resolve(self, ref):
+        try:
+            elements = ref.split("(", 1)
+            symbol = import_symbol(elements[0])
+            if len(elements) == 1:
+                return symbol
+
+            args = eval("(" + elements[1])
+            if not isinstance(args, tuple):
+                args = (args,)
+
+            return symbol(*args)
+        except Exception:
+            raise TraitError(
+                "Could not resolve '%s' into a valid symbol." % ref
+            )
+
+
+class UUID(TraitType):
+    """ A read-only trait type whose value is a globally unique UUID (type 4).
+
+    Parameters
+    ----------
+    can_init : bool
+        Whether the value can be set during object instantiation.  Otherwise
+        the UUID is generated automatically.
+
+    Example
+    -------
+
+    Passing `can_init=True` allows the UUID value to be set during
+    object instantiation, e.g.::
+
+        class A(HasTraits):
+            id = UUID
+
+        class B(HasTraits):
+            id = UUID(can_init=True)
+
+        # TraitError!
+        A(id=uuid.uuid4())
+
+        # Okay!
+        B(id=uuid.uuid4())
+
+    Note however that in both cases, the UUID trait is set automatically
+    to a `uuid.UUID` instance (assuming none is provided during initialization
+    in the latter case).
+    """
+
+    #: A description of the type of value this trait accepts:
+    info_text = "a read-only UUID"
+
+    def __init__(self, can_init=False, **metadata):
+        super(UUID, self).__init__(None, **metadata)
+        self.can_init = can_init
+
+    def validate(self, object, name, value):
+        """ Raises an error, since no values can be assigned to the trait.
+        """
+        if not self.can_init:
+            raise TraitError(
+                "The '%s' trait of %s instance is a read-only "
+                "UUID." % (name, class_of(object))
+            )
+
+        if object.traits_inited():
+            msg = ("Initializable UUID trait is read-only "
+                   "after initialization")
+            raise TraitError(msg)
+
+        if isinstance(value, uuid.UUID):
+            return value
+
+        try:
+            # Construct the UUID from a string
+            return uuid.UUID(value)
+        except ValueError:
+            msg = ("The '{}' trait of '{}' expects an RFC 4122-compatible "
+                   "UUID value, but '{}' was given")
+            raise TraitError(msg.format(name, type(object).__name__, value))
+
+    def get_default_value(self):
+        """ Return a Traits default value tuple for the trait.
+
+        This uses the _create_uuid method to generate the defualt value.
+        """
+        return (
+            DefaultValue.callable_and_args,
+            (self._create_uuid, (), None),
+        )
+
+    # -- Private Methods ---------------------------------------------------
+
+    def _create_uuid(self):
+        return uuid.uuid4()
+
+
+class WeakRef(Instance):
+    """ A trait type holding a weak reference to an instance of a class.
+
+    Only a weak reference is maintained to any object assigned to a WeakRef
+    trait. If no other references exist to the assigned value, the value
+    may be garbage collected, in which case the value of the trait becomes
+    None. In all other cases, the value returned by the trait is the
+    original object.
+
+    Parameters
+    ----------
+    klass : class, str or instance
+        The object that forms the basis for the trait. If *klass* is
+        omitted, then values must be an instance of HasTraits.  If a string,
+        the value will be resolved to a class object at runtime.
+    allow_none : boolean
+        Indicates whether None can be _assigned_.  The trait attribute may
+        give a None value if the object referred to has been garbage collected
+        even if allow_none is False.
+    adapt : str
+        How to use the adaptation infrastructure when setting the value.
+    """
+
+    def __init__(
+        self,
+        klass="traits.has_traits.HasTraits",
+        allow_none=False,
+        adapt="yes",
+        **metadata
+    ):
+        metadata.setdefault("copy", "ref")
+
+        super(WeakRef, self).__init__(
+            klass,
+            allow_none=allow_none,
+            adapt=adapt,
+            module=get_module_name(),
+            **metadata
+        )
+
+    def get(self, object, name):
+        value = getattr(object, name + "_", None)
+        if value is not None:
+            return value.value()
+
+        return None
+
+    def set(self, object, name, value):
+        old = self.get(object, name)
+
+        if value is None:
+            object.__dict__[name + "_"] = None
+        else:
+            object.__dict__[name + "_"] = HandleWeakRef(object, name, value)
+
+        if value is not old:
+            object.trait_property_changed(name, old, value)
+
+    def resolve_class(self, object, name, value):
+        # fixme: We have to override this method to prevent the 'fast validate'
+        # from being set up, since the trait using this is a 'property' style
+        # trait which is not currently compatible with the 'fast_validate'
+        # style (causes internal Python SystemError messages).
+        klass = self.find_class(self.klass)
+        if klass is None:
+            self.validate_failed(object, name, value)
+
+        self.klass = klass
+
+
+#: A trait type for datetime.date instances.
+Date = BaseInstance(datetime.date, editor=date_editor)
+
+
+#: A trait type for datetime.datetime instances.
+Datetime = BaseInstance(datetime.datetime, editor=datetime_editor)
+
+
+#: A trait type for datetime.time instances.
+Time = BaseInstance(datetime.time, editor=time_editor)
+
+
+# Predefined, reusable trait instances
+
+# Everything from this point onwards is deprecated, and has a simple
+# drop-in replacement.
+
+#: A trait whose value must support a specified protocol. This is
+#: an alias for :class:`Supports`. Use ``Supports`` instead.
+AdaptedTo = Supports
+
+#: A trait whose value must be a (Unicode) string. This is an alias for
+#: :class:`BaseStr`. Use ``BaseStr`` instead.
+BaseUnicode = BaseStr
+
+#: A trait whose value must be a (Unicode) string, using a C-level
+#: fast validator. This is an alias for :class:`Str`. Use ``Str`` instead.
+Unicode = Str
+
+#: A trait whose value must be a (Unicode) string and which supports
+#: coercions of non-string values to string. This is
+#: an alias for :class:`BaseCStr`. Use ``BaseCStr`` instead.
+BaseCUnicode = BaseCStr
+
+#: A trait whose value must be a (Unicode) string and which supports
+#: coercions of non-string values to string, using a C-level fast validator.
+#: This is an alias for :class:`CStr`. Use ``CStr`` instead.
+CUnicode = CStr
+
+#: A trait whose value must be an integer. This is an alias for
+#: :class:`BaseInt`. Use ``BaseInt`` instead.
+BaseLong = BaseInt
+
+#: A trait whose value must be an integer, using a C-level fast validator.
+#: This is an alias for :class:`Int`. Use ``Int`` instead.
+Long = Int
+
+#: A trait whose value must be an integer and which supports coercions
+#: of non-integer values to integer. This is an alias for
+#: :class:`BaseCInt`. Use ``BaseCInt`` instead.
+BaseCLong = BaseCInt
+
+#: A trait whose value must be an integer and which supports coercions
+#: of non-integer values to integer, using a C-level fast validator.
+#: This is an alias for :class:`CInt`. Use ``CInt`` instead.
+CLong = CInt
+
+#: Synonym for Bool; default value is ``False``. This trait type is
+#: deprecated. Use ``Bool(False)`` or ``Bool()`` instead.
+false = Bool
+
+#: Boolean values only; default value is ``True``. This trait type is
+#: deprecated. Use ``Bool(True)`` instead.
+true = Bool(True)
+
+#: Allows any value to be assigned; no type-checking is performed.
+#: Default value is ``Undefined``. This trait type is deprecated. Use
+#: ``Any(Undefined)`` instead.
+undefined = Any(Undefined)
+
+# -- List Traits --------------------------------------------------------------
+
+#: List of integer values; default value is ``[]``. This trait type is
+#: deprecated. Use ``List(Int)`` instead.
+ListInt = List(int)
+
+#: List of float values; default value is ``[]``. This trait type is
+#: deprecated. Use ``List(Float)`` instead.
+ListFloat = List(float)
+
+#: List of string values; default value is ``[]``. This trait type is
+#: deprecated. Use ``List(Str)`` instead.
+ListStr = List(str)
+
+#: List of string values; default value is ``[]``. This trait type is
+#: deprecated. Use ``List(Str)`` instead.
+ListUnicode = List(str)
+
+#: List of complex values; default value is ``[]``. This trait type is
+#: deprecated. Use ``List(Complex)`` instead.
+ListComplex = List(complex)
+
+#: List of Boolean values; default value is ``[]``. This trait type is
+#: deprecated. Use ``List(Bool)`` instead.
+ListBool = List(bool)
+
+#: List of function values; default value is ``[]``. This trait type is
+#: deprecated. Use ``List(Instance(types.FunctionType, allow_none=False))``
+#: instead.
+ListFunction = List(FunctionType)
+
+#: List of method values; default value is ``[]``. This trait type is
+#: deprecated. Use ``List(Instance(types.MethodType, allow_none=False))``
+#: instead.
+ListMethod = List(MethodType)
+
+#: List of container type values; default value is ``[]``. This trait type is
+#: deprecated. Use ``List(This(allow_none=False))`` instead.
+ListThis = List(This(allow_none=False))
+
+# -- Dictionary Traits --------------------------------------------------------
+
+#: Only a dictionary with strings as keys can be assigned; only string keys
+#: can be inserted. The default value is {}. This trait type is deprecated. Use
+#: ``Dict(Str, Any)`` instead.
+DictStrAny = Dict(str, Any)
+
+#: Only a dictionary mapping strings to strings can be assigned; only string
+#: keys with string values can be inserted. The default value is {}. This trait
+#: type is deprecated. Use ``Dict(Str, Str)`` instead.
+DictStrStr = Dict(str, str)
+
+#: Only a dictionary mapping strings to integers can be assigned; only string
+#: keys with integer values can be inserted. The default value is {}. This
+#: trait type is deprecated. Use ``Dict(Str, Int)`` instead.
+DictStrInt = Dict(str, int)
+
+#: Only a dictionary mapping strings to floats can be assigned; only string
+#: keys with float values can be inserted. The default value is {}. This trait
+#: type is deprecated. Use ``Dict(Str, Float)`` instead.
+DictStrFloat = Dict(str, float)
+
+#: Only a dictionary mapping strings to booleans can be assigned; only string
+#: keys with boolean values can be inserted. The default value is {}. This
+#: trait type is deprecated. Use ``Dict(Str, Bool)`` instead.
+DictStrBool = Dict(str, bool)
+
+#: Only a dictionary mapping strings to lists can be assigned; only string keys
+#: with list values can be inserted. The default value is {}. This trait type
+#: is deprecated. Use ``Dict(Str, List)`` instead.
+DictStrList = Dict(str, list)
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/traits/traits.html b/5.0/_modules/traits/traits.html new file mode 100644 index 000000000..4b1a7ea95 --- /dev/null +++ b/5.0/_modules/traits/traits.html @@ -0,0 +1,833 @@ + + + + + + + traits.traits — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for traits.traits

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+
+"""
+Defines the 'core' traits for the Traits package. A trait is a type definition
+that can be used for normal Python object attributes, giving the attributes
+some additional characteristics:
+
+Initialization:
+    Traits have predefined values that do not need to be explicitly
+    initialized in the class constructor or elsewhere.
+Validation:
+    Trait attributes have flexible, type-checked values.
+Delegation:
+    Trait attributes' values can be delegated to other objects.
+Notification:
+    Trait attributes can automatically notify interested parties when
+    their values change.
+Visualization:
+    Trait attributes can automatically construct (automatic or
+    programmer-defined) user interfaces that allow their values to be
+    edited or displayed)
+
+.. note:: 'trait' is a synonym for 'property', but is used instead of the
+    word 'property' to differentiate it from the Python language 'property'
+    feature.
+"""
+
+from types import FunctionType, MethodType
+import warnings
+
+from .constants import (
+    ComparisonMode,
+    DefaultValue,
+    TraitKind,
+)
+from .ctrait import CTrait
+from .trait_errors import TraitError
+from .trait_base import (
+    SequenceTypes,
+    TypeTypes,
+    add_article,
+)
+from .trait_converters import (
+    trait_cast,
+    check_trait as try_trait_cast,
+)
+
+from .trait_handler import TraitHandler
+from .trait_type import (
+    _infer_default_value_type,
+    _read_only,
+    _write_only,
+)
+from .trait_handlers import (
+    TraitInstance,
+    TraitFunction,
+    TraitCoerceType,
+    TraitCastType,
+    TraitEnum,
+    TraitCompound,
+    TraitMap,
+    _undefined_get,
+    _undefined_set,
+)
+from .trait_factory import (
+    TraitFactory,
+)
+from .util.deprecated import deprecated
+
+# Constants
+
+NoneType = type(None)  # Python 3's types does not include NoneType
+
+ConstantTypes = (NoneType, int, float, complex, str)
+
+PythonTypes = (
+    str,
+    int,
+    float,
+    complex,
+    list,
+    tuple,
+    dict,
+    FunctionType,
+    MethodType,
+    type,
+    NoneType,
+)
+
+CallableTypes = (FunctionType, MethodType)
+
+TraitTypes = (TraitHandler, CTrait)
+
+DefaultValues = {
+    str: "",
+    int: 0,
+    float: 0.0,
+    complex: 0j,
+    list: [],
+    tuple: (),
+    dict: {},
+    bool: False,
+}
+
+
+# This function is needed when unpickling historical pickles (pickles
+# created on versions of Traits prior to 6.0). It can be removed when
+# there's no longer any need to support pickles generated on older
+# versions of Traits.
+
+def __newobj__(cls, *args):
+    """ Unpickles new-style objects.
+    """
+    return cls.__new__(cls, *args)
+
+
+# --- 'instance' traits -------------------------------------------------------
+
+
+class _InstanceArgs(object):
+    def __init__(self, factory, args, kw):
+        self.args = (factory,) + args
+        self.kw = kw
+
+
+# --- 'creates a run-time default value' --------------------------------------
+
+
+class Default(object):
+    """ Generates a value the first time it is accessed.
+
+    A Default object can be used anywhere a default trait value would normally
+    be specified, to generate a default value dynamically.
+    """
+
+    def __init__(self, func=None, args=(), kw=None):
+        self.default_value = (func, args, kw)
+
+
+def Trait(*value_type, **metadata):
+    """ Creates a trait definition.
+
+    This function accepts a variety of forms of parameter lists:
+
+    +-------------------+---------------+-------------------------------------+
+    | Format            | Example       | Description                         |
+    +===================+===============+=====================================+
+    | Trait(*default*)  | Trait(150.0)  | The type of the trait is inferred   |
+    |                   |               | from the type of the default value, |
+    |                   |               | which must be in *ConstantTypes*.   |
+    +-------------------+---------------+-------------------------------------+
+    | Trait(*default*,  | Trait(None,   | The trait accepts any of the        |
+    | *other1*,         | 0, 1, 2,      | enumerated values, with the first   |
+    | *other2*, ...)    | 'many')       | value being the default value. The  |
+    |                   |               | values must be of types in          |
+    |                   |               | *ConstantTypes*, but they need not  |
+    |                   |               | be of the same type. The *default*  |
+    |                   |               | value is not valid for assignment   |
+    |                   |               | unless it is repeated later in the  |
+    |                   |               | list.                               |
+    +-------------------+---------------+-------------------------------------+
+    | Trait([*default*, | Trait([None,  | Similar to the previous format, but |
+    | *other1*,         | 0, 1, 2,      | takes an explicit list or a list    |
+    | *other2*, ...])   | 'many'])      | variable.                           |
+    +-------------------+---------------+-------------------------------------+
+    | Trait(*type*)     | Trait(Int)    | The *type* parameter must be a name |
+    |                   |               | of a Python type (see               |
+    |                   |               | *PythonTypes*). Assigned values     |
+    |                   |               | must be of exactly the specified    |
+    |                   |               | type; no casting or coercion is     |
+    |                   |               | performed. The default value is the |
+    |                   |               | appropriate form of zero, False,    |
+    |                   |               | or emtpy string, set or sequence.   |
+    +-------------------+---------------+-------------------------------------+
+    | Trait(*class*)    |::             | Values must be instances of *class* |
+    |                   |               | or of a subclass of *class*. The    |
+    |                   | class MyClass:| default value is None, but None     |
+    |                   |    pass       | cannot be assigned as a value.      |
+    |                   | foo = Trait(  |                                     |
+    |                   | MyClass)      |                                     |
+    +-------------------+---------------+-------------------------------------+
+    | Trait(None,       |::             | Similar to the previous format, but |
+    | *class*)          |               | None *can* be assigned as a value.  |
+    |                   | class MyClass:|                                     |
+    |                   |   pass        |                                     |
+    |                   | foo = Trait(  |                                     |
+    |                   | None, MyClass)|                                     |
+    +-------------------+---------------+-------------------------------------+
+    | Trait(*instance*) |::             | Values must be instances of the     |
+    |                   |               | same class as *instance*, or of a   |
+    |                   | class MyClass:| subclass of that class. The         |
+    |                   |    pass       | specified instance is the default   |
+    |                   | i = MyClass() | value.                              |
+    |                   | foo =         |                                     |
+    |                   |   Trait(i)    |                                     |
+    +-------------------+---------------+-------------------------------------+
+    | Trait(*handler*)  | Trait(        | Assignment to this trait is         |
+    |                   | TraitEnum )   | validated by an object derived from |
+    |                   |               | **traits.TraitHandler**.            |
+    +-------------------+---------------+-------------------------------------+
+    | Trait(*default*,  | Trait(0.0, 0.0| This is the most general form of    |
+    | { *type* |        | 'stuff',      | the function. The notation:         |
+    | *constant* |      | TupleType)    | ``{...|...|...}+`` means a list of  |
+    | *dict* | *class* ||               | one or more of any of the items     |
+    | *function* |      |               | listed between the braces. Thus, the|
+    | *handler* |       |               | most general form of the function   |
+    | *trait* }+ )      |               | consists of a default value,        |
+    |                   |               | followed by one or more of several  |
+    |                   |               | possible items. A trait defined by  |
+    |                   |               | multiple items is called a          |
+    |                   |               | "compound" trait.                   |
+    +-------------------+---------------+-------------------------------------+
+
+    All forms of the Trait function accept both predefined and arbitrary
+    keyword arguments. The value of each keyword argument becomes bound to the
+    resulting trait object as the value of an attribute having the same name
+    as the keyword. This feature lets you associate metadata with a trait.
+
+    The following predefined keywords are accepted:
+
+    desc : str
+        Describes the intended meaning of the trait. It is used in
+        exception messages and fly-over help in user interfaces.
+    label : str
+        Provides a human-readable name for the trait. It is used to label user
+        interface editors for traits.
+    editor : traits.api.Editor
+        Instance of a subclass Editor object to use when creating a user
+        interface editor for the trait. See the "Traits UI User Guide" for
+        more information on trait editors.
+    comparison_mode : int
+        Indicates when trait change notifications should be generated based
+        upon the result of comparing the old and new values of a trait
+        assignment. Possible values come from the ``ComparisonMode`` enum:
+
+        * 0 (none): The values are not compared and a trait change
+          notification is generated on each assignment.
+        * 1 (identity): A trait change notification is
+          generated if the old and new values are not the same object.
+        * 2 (equality): A trait change notification is generated if the
+          old and new values are not equal using Python's standard equality
+          testing. This is the default.
+
+    """
+    return _TraitMaker(*value_type, **metadata).as_ctrait()
+
+
+class _TraitMaker(object):
+
+    # Ctrait type map for special trait types:
+    type_map = {"event": TraitKind.event, "constant": TraitKind.constant}
+
+    def __init__(self, *value_type, **metadata):
+        metadata.setdefault("type", "trait")
+        self.define(*value_type, **metadata)
+
+    def define(self, *value_type, **metadata):
+        """ Define the trait. """
+        default_value_type = DefaultValue.unspecified
+        default_value = handler = clone = None
+
+        if len(value_type) > 0:
+            default_value = value_type[0]
+            value_type = value_type[1:]
+
+            if (len(value_type) == 0) and (
+                type(default_value) in SequenceTypes
+            ):
+                default_value, value_type = default_value[0], default_value
+
+            if len(value_type) == 0:
+                default_value = try_trait_cast(default_value)
+
+                if default_value in PythonTypes:
+                    handler = TraitCoerceType(default_value)
+                    default_value = DefaultValues.get(default_value)
+
+                elif isinstance(default_value, CTrait):
+                    clone = default_value
+                    default_value_type, default_value = clone.default_value()
+                    metadata["type"] = clone.type
+
+                elif isinstance(default_value, TraitHandler):
+                    handler = default_value
+                    default_value = None
+
+                else:
+                    typeValue = type(default_value)
+                    if typeValue in TypeTypes:
+                        handler = TraitCastType(typeValue)
+
+                    else:
+                        metadata.setdefault(
+                            "instance_handler", "_instance_changed_handler"
+                        )
+                        handler = TraitInstance(default_value)
+                        if default_value is handler.aClass:
+                            default_value = DefaultValues.get(default_value)
+            else:
+                enum = []
+                other = []
+                map = {}
+                self.do_list(value_type, enum, map, other)
+
+                if ((len(enum) == 1) and (enum[0] is None)) and (
+                    (len(other) == 1) and isinstance(other[0], TraitInstance)
+                ):
+                    enum = []
+                    other[0].allow_none()
+                    metadata.setdefault(
+                        "instance_handler", "_instance_changed_handler"
+                    )
+                if len(enum) > 0:
+                    if ((len(map) + len(other)) == 0) and (
+                        default_value not in enum
+                    ):
+                        enum.insert(0, default_value)
+
+                    other.append(TraitEnum(enum))
+
+                if len(map) > 0:
+                    other.append(TraitMap(map))
+
+                if len(other) == 0:
+                    handler = TraitHandler()
+
+                elif len(other) == 1:
+                    handler = other[0]
+                    if isinstance(handler, CTrait):
+                        clone, handler = handler, None
+                        metadata["type"] = clone.type
+
+                    elif isinstance(handler, TraitInstance):
+                        metadata.setdefault(
+                            "instance_handler", "_instance_changed_handler"
+                        )
+
+                        if default_value is None:
+                            handler.allow_none()
+
+                        elif isinstance(default_value, _InstanceArgs):
+                            default_value_type = (
+                                DefaultValue.callable_and_args
+                            )
+                            default_value = (
+                                handler.create_default_value,
+                                default_value.args,
+                                default_value.kw,
+                            )
+
+                        elif (len(enum) == 0) and (len(map) == 0):
+                            aClass = handler.aClass
+                            typeValue = type(default_value)
+
+                            if typeValue is dict:
+                                default_value_type = (
+                                    DefaultValue.callable_and_args
+                                )
+                                default_value = (aClass, (), default_value)
+                            elif not isinstance(default_value, aClass):
+                                if typeValue is not tuple:
+                                    default_value = (default_value,)
+                                default_value_type = (
+                                    DefaultValue.callable_and_args
+                                )
+                                default_value = (aClass, default_value, None)
+                else:
+                    for i, item in enumerate(other):
+                        if isinstance(item, CTrait):
+                            if item.type != "trait":
+                                raise TraitError(
+                                    "Cannot create a complex "
+                                    "trait containing %s trait."
+                                    % add_article(item.type)
+                                )
+                            handler = item.handler
+                            if handler is None:
+                                break
+                            other[i] = handler
+                    else:
+                        handler = TraitCompound(other)
+
+        # Save the results:
+        self.handler = handler
+        self.clone = clone
+
+        if default_value_type < 0:
+            if isinstance(default_value, Default):
+                default_value_type = DefaultValue.callable_and_args
+                default_value = default_value.default_value
+            else:
+                if (handler is None) and (clone is not None):
+                    handler = clone.handler
+
+                if handler is not None:
+                    default_value_type = handler.default_value_type
+                    if default_value_type < 0:
+                        try:
+                            default_value = handler.validate(
+                                None, "", default_value
+                            )
+                        except:
+                            pass
+
+                if default_value_type < 0:
+                    default_value_type = _infer_default_value_type(
+                        default_value
+                    )
+
+        self.default_value_type = default_value_type
+        self.default_value = default_value
+        self.metadata = metadata.copy()
+
+    def do_list(self, list, enum, map, other):
+        """ Determine the correct TraitHandler for each item in a list. """
+        for item in list:
+            if item in PythonTypes:
+                other.append(TraitCoerceType(item))
+            else:
+                item = try_trait_cast(item)
+                typeItem = type(item)
+
+                if typeItem in ConstantTypes:
+                    enum.append(item)
+
+                elif typeItem in SequenceTypes:
+                    self.do_list(item, enum, map, other)
+
+                elif typeItem is dict:
+                    map.update(item)
+
+                elif typeItem in CallableTypes:
+                    other.append(TraitFunction(item))
+
+                elif isinstance(item, TraitTypes):
+                    other.append(item)
+
+                else:
+                    other.append(TraitInstance(item))
+
+    def as_ctrait(self):
+        """ Return a properly initialized 'CTrait' instance. """
+        metadata = self.metadata
+        trait = CTrait(
+            self.type_map.get(metadata.get("type"), TraitKind.trait))
+        clone = self.clone
+        if clone is not None:
+            trait.clone(clone)
+            if clone.__dict__ is not None:
+                trait.__dict__ = clone.__dict__.copy()
+
+        trait.set_default_value(self.default_value_type, self.default_value)
+
+        handler = self.handler
+        if handler is not None:
+            trait.handler = handler
+            validate = getattr(handler, "fast_validate", None)
+            if validate is None:
+                validate = handler.validate
+            trait.set_validate(validate)
+
+            post_setattr = getattr(handler, "post_setattr", None)
+            if post_setattr is not None:
+                trait.post_setattr = post_setattr
+                trait.is_mapped = handler.is_mapped
+
+        rich_compare = metadata.get("rich_compare")
+        if rich_compare is not None:
+            # Ref: enthought/traits#602
+            warnings.warn(
+                "The 'rich_compare' metadata has been deprecated. Please "
+                "use the 'comparison_mode' metadata instead. In a future "
+                "release, rich_compare will have no effect.",
+                DeprecationWarning,
+                stacklevel=4,
+            )
+            if rich_compare:
+                trait.comparison_mode = ComparisonMode.equality
+            else:
+                trait.comparison_mode = ComparisonMode.identity
+
+        comparison_mode = metadata.pop("comparison_mode", None)
+        if comparison_mode is not None:
+            trait.comparison_mode = comparison_mode
+
+        if len(metadata) > 0:
+            if trait.__dict__ is None:
+                trait.__dict__ = metadata
+            else:
+                trait.__dict__.update(metadata)
+
+        return trait
+
+
+def Property(
+    fget=None,
+    fset=None,
+    fvalidate=None,
+    force=False,
+    handler=None,
+    trait=None,
+    **metadata
+):
+    """ Returns a trait whose value is a Python property.
+
+    If no getter, setter or validate functions are specified (and **force** is
+    not True), it is assumed that they are defined elsewhere on the class whose
+    attribute this trait is assigned to. For example::
+
+        class Bar(HasTraits):
+
+            # A float traits Property that should be always positive.
+            foo = Property(Float)
+
+            # Shadow trait attribute
+            _foo = Float
+
+            def _set_foo(self,x):
+                self._foo = x
+
+            def _validate_foo(self, x):
+                if x <= 0:
+                    raise TraitError(
+                        'foo property should be a positive number')
+                return x
+
+            def _get_foo(self):
+                return self._foo
+
+    You can use the **depends_on** metadata attribute to indicate that the
+    property depends on the value of another trait. The value of **depends_on**
+    is an extended name specifier for traits that the property depends on. The
+    property will a trait change notification if any of the traits specified
+    by **depends_on** change. For example::
+
+        class Wheel ( Part ):
+            axle     = Instanced( Axle )
+            position = Property( depends_on = 'axle.chassis.position' )
+
+    For details of the extended trait name syntax, refer to the
+    on_trait_change() method of the HasTraits class.
+
+    Parameters
+    ----------
+    fget : function
+        The "getter" function for the property.
+    fset : function
+        The "setter" function for the property.
+    fvalidate : function
+        The validation function for the property. The method should return the
+        value to set or raise TraitError if the new value is not valid.
+    force : bool
+        Indicates whether to use only the function definitions specified by
+        **fget** and **fset**, and not look elsewhere on the class.
+    handler : function
+        A trait handler function for the trait.
+    trait : Trait or value
+        A trait definition or a value that can be converted to a trait that
+        constrains the values of the property trait.
+    """
+    metadata["type"] = "property"
+
+    # If no parameters specified, must be a forward reference (if not forced):
+    if (not force) and (fset is None):
+        sum = (
+            (fget is not None) + (fvalidate is not None) + (trait is not None)
+        )
+        if sum <= 1:
+            if sum == 0:
+                return ForwardProperty(metadata)
+
+            handler = None
+            if fget is not None:
+                trait = fget
+
+            if trait is not None:
+                trait = trait_cast(trait)
+                if trait is not None:
+                    fvalidate = handler = trait.handler
+                    if fvalidate is not None:
+                        fvalidate = handler.validate
+
+            if (fvalidate is not None) or (trait is not None):
+                if "editor" not in metadata:
+                    if (trait is not None) and (trait.editor is not None):
+                        metadata["editor"] = trait.editor
+
+                return ForwardProperty(metadata, fvalidate, handler)
+
+    if fget is None:
+        metadata["transient"] = True
+        if fset is None:
+            fget = _undefined_get
+            fset = _undefined_set
+        else:
+            fget = _write_only
+
+    elif fset is None:
+        fset = _read_only
+        metadata["transient"] = True
+
+    if trait is not None:
+        trait = trait_cast(trait)
+        handler = trait.handler
+        if (fvalidate is None) and (handler is not None):
+            fvalidate = handler.validate
+
+        if ("editor" not in metadata) and (trait.editor is not None):
+            metadata["editor"] = trait.editor
+
+    metadata.setdefault("depends_on", getattr(fget, "depends_on", None))
+    if (metadata.get("depends_on") is not None) and getattr(
+        fget, "cached_property", False
+    ):
+        metadata.setdefault("cached", True)
+
+    trait = CTrait(TraitKind.property)
+    trait.__dict__ = metadata.copy()
+    trait.property_fields = (fget, fset, fvalidate)
+    trait.handler = handler
+
+    return trait
+
+
+Property = TraitFactory(Property)
+
+
+class ForwardProperty(object):
+    """ Used to implement Property traits where accessor functions are defined
+    implicitly on the class.
+    """
+
+    def __init__(self, metadata, validate=None, handler=None):
+        self.metadata = metadata.copy()
+        self.validate = validate
+        self.handler = handler
+
+
+# Predefined, reusable trait instances
+
+# Generic trait with 'object' behavior:
+generic_trait = CTrait(TraitKind.generic)
+
+
+# User interface related color and font traits
+
+@deprecated("'Color' in 'traits' package has been deprecated. "
+            "Use 'Color' from 'traitsui' package instead.")
+def Color(*args, **metadata):
+    """ Returns a trait whose value must be a GUI toolkit-specific color.
+
+    .. deprecated:: 6.1.0
+        ``Color`` trait in this package will be removed in the future. It is
+        replaced by ``Color`` trait in TraitsUI package.
+    """
+    from traitsui.toolkit_traits import ColorTrait
+
+    return ColorTrait(*args, **metadata)
+
+
+Color = TraitFactory(Color)
+
+
+@deprecated("'RGBColor' in 'traits' package has been deprecated. "
+            "Use 'RGBColor' from 'traitsui' package instead.")
+def RGBColor(*args, **metadata):
+    """ Returns a trait whose value must be a GUI toolkit-specific RGB-based
+    color.
+
+    .. deprecated:: 6.1.0
+        ``RGBColor`` trait in this package will be removed in the future. It is
+        replaced by ``RGBColor`` trait in TraitsUI package.
+    """
+    from traitsui.toolkit_traits import RGBColorTrait
+
+    return RGBColorTrait(*args, **metadata)
+
+
+RGBColor = TraitFactory(RGBColor)
+
+
+@deprecated("'Font' in 'traits' package has been deprecated. "
+            "Use 'Font' from 'traitsui' package instead.")
+def Font(*args, **metadata):
+    """ Returns a trait whose value must be a GUI toolkit-specific font.
+
+    .. deprecated:: 6.1.0
+        ``Font`` trait in this package will be removed in the future. It is
+        replaced by ``Font`` trait in TraitsUI package.
+    """
+    from traitsui.toolkit_traits import FontTrait
+
+    return FontTrait(*args, **metadata)
+
+
+Font = TraitFactory(Font)
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/traitsui/editors/code_editor.html b/5.0/_modules/traitsui/editors/code_editor.html new file mode 100644 index 000000000..ae28fc000 --- /dev/null +++ b/5.0/_modules/traitsui/editors/code_editor.html @@ -0,0 +1,243 @@ + + + + + + + traitsui.editors.code_editor — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for traitsui.editors.code_editor

+# ------------------------------------------------------------------------------
+#
+#  Copyright (c) 2008, Enthought, Inc.
+#  All rights reserved.
+#
+#  This software is provided without warranty under the terms of the BSD
+#  license included in LICENSE.txt and may be redistributed only
+#  under the conditions described in the aforementioned license.  The license
+#  is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+#  Thanks for using Enthought open source!
+#
+#  Author: David C. Morrill
+#  Date:   01/27/2006
+#
+# ------------------------------------------------------------------------------
+
+""" Defines the code editor factory for all traits toolkit backends,
+useful for tools such as debuggers.
+"""
+
+
+
+from traits.api import Instance, Str, Enum, Bool
+
+from ..editor_factory import EditorFactory
+from ..toolkit_traits import Color
+
+# -------------------------------------------------------------------------
+#  'ToolkitEditorFactory' class:
+# -------------------------------------------------------------------------
+
+
+class ToolkitEditorFactory(EditorFactory):
+    """ Editor factory for code editors.
+    """
+
+    # -------------------------------------------------------------------------
+    #  Trait definitions:
+    # -------------------------------------------------------------------------
+
+    #: Object trait containing list of line numbers to mark (optional)
+    mark_lines = Str()
+
+    #: Background color for marking lines
+    mark_color = Color(0xECE9D8)
+
+    #: Object trait containing the currently selected line (optional)
+    selected_line = Str()
+
+    #: Object trait containing the currently selected text (optional)
+    selected_text = Str()
+
+    #: Object trait containing the currently selected text start position
+    #: (optional)
+    selected_start_pos = Str()
+
+    #: Object trait containing the currently selected text end position
+    #: (optional)
+    selected_end_pos = Str()
+
+    #: Background color for selected lines
+    selected_color = Color(0xA4FFFF)
+
+    #: Where should the search toolbar be placed?
+    search = Enum("top", "bottom", "none")
+
+    #: Background color for lines that match the current search
+    search_color = Color(0xFFFF94)
+
+    #: Current line
+    line = Str()
+
+    #: Current column
+    column = Str()
+
+    #: Should code folding be enabled?
+    foldable = Bool(True)
+
+    #: Should line numbers be displayed in the margin?
+    show_line_numbers = Bool(True)
+
+    #: Is user input set on every change?
+    auto_set = Bool(True)
+
+    #: Should the editor auto-scroll when a new **selected_line** value is set?
+    auto_scroll = Bool(True)
+
+    #: Optional key bindings associated with the editor
+    key_bindings = Instance("traitsui.key_bindings.KeyBindings")
+
+    #: Calltip clicked event
+    calltip_clicked = Str()
+
+    #: The lexer to use. Default is 'python'; 'null' indicates no lexing.
+    lexer = Str("python")
+
+    #: Object trait containing the list of line numbers to dim (optional)
+    dim_lines = Str()
+
+    #: Object trait to dim lines to. Can be of form #rrggbb or a color spec. If
+    #: not specified, dark grey is used.
+    dim_color = Str()
+
+    #: Object trait containing the list of line numbers to put squiggles under
+    #: (optional)
+    squiggle_lines = Str()
+
+    #: Object trait for the color of squiggles. If not specified, red is used.
+    squiggle_color = Str()
+
+
+# Define the Code Editor class.
+CodeEditor = ToolkitEditorFactory
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/traitsui/editors/tabular_editor.html b/5.0/_modules/traitsui/editors/tabular_editor.html new file mode 100644 index 000000000..a6db5e448 --- /dev/null +++ b/5.0/_modules/traitsui/editors/tabular_editor.html @@ -0,0 +1,290 @@ + + + + + + + traitsui.editors.tabular_editor — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for traitsui.editors.tabular_editor

+# -------------------------------------------------------------------------
+#
+#  Copyright (c) 2007, Enthought, Inc.
+#  All rights reserved.
+#
+#  This software is provided without warranty under the terms of the BSD
+#  license included in LICENSE.txt and may be redistributed only
+#  under the conditions described in the aforementioned license.  The license
+#  is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+#  Thanks for using Enthought open source!
+#
+#  Author: David C. Morrill
+#  Date:   05/20/2007
+#
+# -------------------------------------------------------------------------
+
+""" A traits UI editor for editing tabular data (arrays, list of tuples, lists
+    of objects, etc).
+"""
+
+
+
+from pyface.ui_traits import Image
+from traits.api import Str, Bool, Property, List, Enum, Instance
+
+from ..basic_editor_factory import BasicEditorFactory
+
+from ..toolkit import toolkit_object
+
+
+class TabularEditor(BasicEditorFactory):
+    """ Editor factory for tabular editors.
+    """
+
+    # -- Trait Definitions ----------------------------------------------------
+
+    #: The editor class to be created:
+    klass = Property()
+
+    #: Should column headers (i.e. titles) be displayed?
+    show_titles = Bool(True)
+
+    #: Should row headers be displayed (Qt4 only)?
+    show_row_titles = Bool(False)
+
+    #: The optional extended name of the trait used to indicate that a complete
+    #: table update is needed:
+    update = Str()
+
+    #: The optional extended name of the trait used to indicate that the table
+    #: just needs to be repainted.
+    refresh = Str()
+
+    #: Should the table update automatically when the table item's contents
+    #: change? Note that in order for this feature to work correctly, the editor
+    #: trait should be a list of objects derived from HasTraits. Also,
+    #: performance can be affected when very long lists are used, since enabling
+    #: this feature adds and removed Traits listeners to each item in the list.
+    auto_update = Bool(False)
+
+    #: The optional extended name of the trait to synchronize the selection
+    #: values with:
+    selected = Str()
+
+    #: The optional extended name of the trait to synchronize the selection rows
+    #: with:
+    selected_row = Str()
+
+    #: Whether or not to allow selection.
+    selectable = Bool(True)
+
+    #: The optional extended name of the trait to synchronize the activated value
+    #: with:
+    activated = Str()
+
+    #: The optional extended name of the trait to synchronize the activated
+    #: value's row with:
+    activated_row = Str()
+
+    #: The optional extended name of the trait to synchronize left click data
+    #: with. The data is a TabularEditorEvent:
+    clicked = Str()
+
+    #: The optional extended name of the trait to synchronize left double click
+    #: data with. The data is a TabularEditorEvent:
+    dclicked = Str()
+
+    #: The optional extended name of the trait to synchronize right click data
+    #: with. The data is a TabularEditorEvent:
+    right_clicked = Str()
+
+    #: The optional extended name of the trait to synchronize right double
+    #: clicked data with. The data is a TabularEditorEvent:
+    right_dclicked = Str()
+
+    #: The optional extended name of the trait to synchronize column
+    #: clicked data with. The data is a TabularEditorEvent:
+    column_clicked = Str()
+
+    #: The optional extended name of the trait to synchronize column
+    #: right clicked data with. The data is a TabularEditorEvent:
+    column_right_clicked = Str()
+
+    #: The optional extended name of the Event trait that should be used to
+    #: trigger a scroll-to command. The data is an integer giving the row.
+    scroll_to_row = Str()
+
+    #: The optional extended name of the Event trait that should be used to
+    #: trigger a scroll-to command. The data is an integer giving the column.
+    scroll_to_column = Str()
+
+    #: Controls behavior of scroll to row
+    scroll_to_row_hint = Enum("center", "top", "bottom", "visible")
+
+    #: Can the user edit the values?
+    editable = Bool(True)
+
+    #: Can the user edit the labels (i.e. the first column)
+    editable_labels = Bool(False)
+
+    #: Are multiple selected items allowed?
+    multi_select = Bool(False)
+
+    #: Should horizontal lines be drawn between items?
+    horizontal_lines = Bool(True)
+
+    #: Should vertical lines be drawn between items?
+    vertical_lines = Bool(True)
+
+    #: Should the columns automatically resize? Don't allow this when the amount
+    #: of data is large.
+    auto_resize = Bool(False)
+
+    #: Should the rows automatically resize (Qt4 only)? Don't allow
+    #: this when the amount of data is large.
+    auto_resize_rows = Bool(False)
+
+    #: Whether to stretch the last column to fit the available space.
+    stretch_last_section = Bool(True)
+
+    #: The adapter from trait values to editor values:
+    adapter = Instance("traitsui.tabular_adapter.TabularAdapter", ())
+
+    #: What type of operations are allowed on the list:
+    operations = List(
+        Enum("delete", "insert", "append", "edit", "move"),
+        ["delete", "insert", "append", "edit", "move"],
+    )
+
+    #: Are 'drag_move' operations allowed (i.e. True), or should they always be
+    #: treated as 'drag_copy' operations (i.e. False):
+    drag_move = Bool(True)
+
+    #: The set of images that can be used:
+    images = List(Image)
+
+    def _get_klass(self):
+        """ Returns the toolkit-specific editor class to be instantiated.
+        """
+        return toolkit_object("tabular_editor:TabularEditor")
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_modules/traitsui/view.html b/5.0/_modules/traitsui/view.html new file mode 100644 index 000000000..9ef548fe1 --- /dev/null +++ b/5.0/_modules/traitsui/view.html @@ -0,0 +1,610 @@ + + + + + + + traitsui.view — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for traitsui.view

+# ------------------------------------------------------------------------------
+#
+#  Copyright (c) 2005, Enthought, Inc.
+#  All rights reserved.
+#
+#  This software is provided without warranty under the terms of the BSD
+#  license included in LICENSE.txt and may be redistributed only
+#  under the conditions described in the aforementioned license.  The license
+#  is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+#  Thanks for using Enthought open source!
+#
+#  Author: David C. Morrill
+#  Date:   10/07/2004
+#
+# ------------------------------------------------------------------------------
+
+""" Defines the View class used to represent the structural content of a
+    Traits-based user interface.
+"""
+
+
+
+from pyface.ui_traits import Image
+from traits.api import (
+    Any,
+    Bool,
+    Callable,
+    Enum,
+    Event,
+    Float,
+    Instance,
+    List,
+    Str,
+    Trait,
+)
+
+from .view_element import ViewElement, ViewSubElement
+
+from .ui import UI
+
+from .ui_traits import (
+    AButton,
+    AnObject,
+    Buttons,
+    DockStyle,
+    EditorStyle,
+    ExportType,
+    HelpId,
+    Image,
+    SequenceTypes,
+    ViewStatus,
+)
+
+from .handler import Handler, default_handler
+
+from .group import Group
+
+from .item import Item
+
+from .include import Include
+
+from .helper import PrefixList
+
+# -------------------------------------------------------------------------
+#  Trait definitions:
+# -------------------------------------------------------------------------
+
+# Name of the view trait:
+AnId = Str(desc="the name of the view")
+
+# Contents of the view trait (i.e., a single Group object):
+Content = Instance(Group, desc="the content of the view")
+
+# An optional model/view factory for converting the model into a viewable
+# 'model_view' object
+AModelView = Callable(
+    desc="the factory function for converting a model "
+    "into a model/view object"
+)
+
+# Reference to a Handler object trait:
+AHandler = Any(desc="the handler for the view")
+
+# Dialog window title trait:
+ATitle = Str(desc="the window title for the view")
+
+# User interface 'kind' trait. The values have the following meanings:
+#
+# * 'panel': An embeddable panel. This type of window is intended to be used as
+#   part of a larger interface.
+# * 'subpanel': An embeddable panel that does not display command buttons,
+#   even if the View specifies them.
+# * 'modal': A modal dialog box that operates on a clone of the object until
+#   the user commits the change.
+# * 'nonmodal':  A nonmodal dialog box that operates on a clone of the object
+#   until the user commits the change
+# * 'live': A nonmodal dialog box that immediately updates the object.
+# * 'livemodal': A modal dialog box that immediately updates the object.
+# * 'popup': A temporary, frameless popup dialog that immediately updates the
+#   object and is active only while the mouse pointer is in the dialog.
+# * 'info': A temporary, frameless popup dialog that immediately updates the
+#   object and is active only while the dialog is still over the invoking
+#   control.
+# * 'wizard': A wizard modal dialog box. A wizard contains a sequence of
+#   pages, which can be accessed by clicking **Next** and **Back** buttons.
+#   Changes to attribute values are applied only when the user clicks the
+#   **Finish** button on the last page.
+AKind = PrefixList(
+    (
+        "panel",
+        "subpanel",
+        "modal",
+        "nonmodal",
+        "livemodal",
+        "live",
+        "popup",
+        "popover",
+        "info",
+        "wizard",
+    ),
+    default_value='live',
+    desc="the kind of view window to create",
+    cols=4,
+)
+
+# Apply changes handler:
+OnApply = Callable(
+    desc="the routine to call when modal changes are applied " "or reverted"
+)
+
+# Is the dialog window resizable?
+IsResizable = Bool(False, desc="whether dialog can be resized or not")
+
+# Is the view scrollable?
+IsScrollable = Bool(False, desc="whether view should be scrollable or not")
+
+# The valid categories of imported elements that can be dragged into the view:
+ImportTypes = List(
+    Str, desc="the categories of elements that can be " "dragged into the view"
+)
+
+# The view position and size traits:
+Width = Float(-1e6, desc="the width of the view window")
+Height = Float(-1e6, desc="the height of the view window")
+XCoordinate = Float(-1e6, desc="the x coordinate of the view window")
+YCoordinate = Float(-1e6, desc="the y coordinate of the view window")
+
+# The result that should be returned if the user clicks the window or dialog
+# close button or icon
+CloseResult = Enum(
+    None,
+    True,
+    False,
+    desc="the result to return when the user clicks the "
+    "window or dialog close button or icon",
+)
+
+# The KeyBindings trait:
+AKeyBindings = Instance(
+    "traitsui.key_bindings.KeyBindings",
+    desc="the global key bindings for the view",
+)
+
+
+class View(ViewElement):
+    """ A Traits-based user interface for one or more objects.
+
+        The attributes of the View object determine the contents and layout of
+        an attribute-editing window. A View object contains a set of Group,
+        Item, and Include objects. A View object can be an attribute of an
+        object derived from HasTraits, or it can be a standalone object.
+    """
+
+    # -------------------------------------------------------------------------
+    #  Trait definitions:
+    # -------------------------------------------------------------------------
+
+    #: A unique identifier for the view:
+    id = AnId
+
+    #: The top-level Group object for the view:
+    content = Content
+
+    #: The menu bar for the view. Usually requires a custom **handler**:
+    menubar = Any  # Instance( pyface.action.MenuBarManager )
+
+    #: The toolbar for the view. Usually requires a custom **handler**:
+    toolbar = Any  # Instance( pyface.action.ToolBarManager )
+
+    #: Status bar items to add to the view's status bar. The value can be:
+    #:
+    #:   - **None**: No status bar for the view (the default).
+    #:   - string: Same as [ StatusItem( name = string ) ].
+    #:   - StatusItem: Same as [ StatusItem ].
+    #:   - [ [StatusItem|string], ... ]: Create a status bar with one field for
+    #:     each StatusItem in the list (or tuple). The status bar fields are
+    #:     defined from left to right in the order specified. A string value is
+    #:     converted to: StatusItem( name = string ):
+    statusbar = ViewStatus
+
+    #: List of button actions to add to the view. The **traitsui.menu**
+    #: module defines standard buttons, such as **OKButton**, and standard sets
+    #: of buttons, such as **ModalButtons**, which can be used to define a value
+    #: for this attribute. This value can also be a list of button name strings,
+    #: such as ``['OK', 'Cancel', 'Help']``. If set to the empty list, the
+    #: view contains a default set of buttons (equivalent to **LiveButtons**:
+    #: Undo/Redo, Revert, OK, Cancel, Help). To suppress buttons in the view,
+    #: use the **NoButtons** variable, defined in **traitsui.menu**.
+    buttons = Buttons
+
+    #: The default button to activate when Enter is pressed. If not specified,
+    #: pressing Enter will not activate any button.
+    default_button = AButton
+
+    #: The set of global key bindings for the view. Each time a key is pressed
+    #: while the view has keyboard focus, the key is checked to see if it is one
+    #: of the keys recognized by the KeyBindings object. If it is, the matching
+    #: KeyBinding's method name is checked to see if it is defined on any of the
+    #: object's in the view's context. If it is, the method is invoked. If the
+    #: result of the method is **False**, then the search continues with the
+    #: next object in the context. If any invoked method returns a non-False
+    #: value, processing stops and the key is marked as having been handled. If
+    #: all invoked methods return **False**, or no matching KeyBinding object is
+    #: found, the key is processed normally. If the view has a non-empty *id*
+    #: trait, the contents of the **KeyBindings** object will be saved as part
+    #: of the view's persistent data:
+    key_bindings = AKeyBindings
+
+    #: The Handler object that provides GUI logic for handling events in the
+    #: window. Set this attribute only if you are using a custom Handler. If
+    #: not set, the default Traits UI Handler is used.
+    handler = AHandler
+
+    #: The factory function for converting a model into a model/view object:
+    model_view = AModelView
+
+    #: Title for the view, displayed in the title bar when the view appears as a
+    #: secondary window (i.e., dialog or wizard). If not specified, "Edit
+    #: properties" is used as the title.
+    title = ATitle
+
+    #: The name of the icon to display in the dialog window title bar:
+    icon = Image
+
+    #: The kind of user interface to create:
+    kind = AKind
+
+    #: The default object being edited:
+    object = AnObject
+
+    #: The default editor style of elements in the view:
+    style = EditorStyle
+
+    #: The default docking style to use for sub-groups of the view. The following
+    #: values are possible:
+    #:
+    #: * 'fixed': No rearrangement of sub-groups is allowed.
+    #: * 'horizontal': Moveable elements have a visual "handle" to the left by
+    #:   which the element can be dragged.
+    #: * 'vertical': Moveable elements have a visual "handle" above them by
+    #:   which the element can be dragged.
+    #: * 'tabbed': Moveable elements appear as tabbed pages, which can be
+    #:   arranged within the window or "stacked" so that only one appears at
+    #:   at a time.
+    dock = DockStyle
+
+    #: The image to display on notebook tabs:
+    image = Image
+
+    #: Called when modal changes are applied or reverted:
+    on_apply = OnApply
+
+    #: Can the user resize the window?
+    resizable = IsResizable
+
+    #: Can the user scroll the view? If set to True, window-level scroll bars
+    #: appear whenever the window is too small to show all of its contents at
+    #: one time. If set to False, the window does not scroll, but individual
+    #: widgets might still contain scroll bars.
+    scrollable = IsScrollable
+
+    #: The category of exported elements:
+    export = ExportType
+
+    #: The valid categories of imported elements:
+    imports = ImportTypes
+
+    #: External help context identifier, which can be used by a custom help
+    #: handler. This attribute is ignored by the default help handler.
+    help_id = HelpId
+
+    #: Requested x-coordinate (horizontal position) for the view window. This
+    #: attribute can be specified in the following ways:
+    #:
+    #: * A positive integer: indicates the number of pixels from the left edge
+    #:   of the screen to the left edge of the window.
+    #: * A negative integer: indicates the number of pixels from the right edge
+    #:   of the screen to the right edge of the window.
+    #: * A floating point value between 0 and 1: indicates the fraction of the
+    #:   total screen width between the left edge of the screen and the left edge
+    #:   of the window.
+    #: * A floating point value between -1 and 0: indicates the fraction of the
+    #:   total screen width between the right edge of the screen and the right
+    #:   edge of the window.
+    x = XCoordinate
+
+    #: Requested y-coordinate (vertical position) for the view window. This
+    #: attribute behaves exactly like the **x** attribute, except that its value
+    #: indicates the position of the top or bottom of the view window relative
+    #: to the top or bottom of the screen.
+    y = YCoordinate
+
+    #: Requested width for the view window, as an (integer) number of pixels, or
+    #: as a (floating point) fraction of the screen width.
+    width = Width
+
+    #: Requested height for the view window, as an (integer) number of pixels, or
+    #: as a (floating point) fraction of the screen height.
+    height = Height
+
+    #: Class of dropped objects that can be added:
+    drop_class = Any()
+
+    #: Event when the view has been updated:
+    updated = Event()
+
+    #: What result should be returned if the user clicks the window or dialog
+    #: close button or icon?
+    close_result = CloseResult
+
+    #: Note: Group objects delegate their 'object' and 'style' traits to the
+    #: View
+
+    # -- Deprecated Traits (DO NOT USE) ---------------------------------------
+
+    ok = Bool(False)
+    cancel = Bool(False)
+    undo = Bool(False)
+    redo = Bool(False)
+    apply = Bool(False)
+    revert = Bool(False)
+    help = Bool(False)
+
+    def __init__(self, *values, **traits):
+        """ Initializes the object.
+        """
+        ViewElement.__init__(self, **traits)
+        self.set_content(*values)
+
+    def set_content(self, *values):
+        """ Sets the content of a view.
+        """
+        content = []
+        accum = []
+        for value in values:
+            if isinstance(value, ViewSubElement):
+                content.append(value)
+            elif type(value) in SequenceTypes:
+                content.append(Group(*value))
+            elif (
+                isinstance(value, str)
+                and (value[:1] == "<")
+                and (value[-1:] == ">")
+            ):
+                # Convert string to an Include value:
+                content.append(Include(value[1:-1].strip()))
+            else:
+                content.append(Item(value))
+
+        # If there are any 'Item' objects in the content, wrap the content in a
+        # Group:
+        for item in content:
+            if isinstance(item, Item):
+                content = [Group(*content)]
+                break
+
+        # Wrap all of the content up into a Group and save it as our content:
+        self.content = Group(container=self, *content)
+
+    def ui(
+        self,
+        context,
+        parent=None,
+        kind=None,
+        view_elements=None,
+        handler=None,
+        id="",
+        scrollable=None,
+        args=None,
+    ):
+        """ Creates a **UI** object, which generates the actual GUI window or
+        panel from a set of view elements.
+
+        Parameters
+        ----------
+        context : object or dictionary
+            A single object or a dictionary of string/object pairs, whose trait
+            attributes are to be edited. If not specified, the current object is
+            used.
+        parent : window component
+            The window parent of the View object's window
+        kind : string
+            The kind of window to create. See the **AKind** trait for details.
+            If *kind* is unspecified or None, the **kind** attribute of the
+            View object is used.
+        view_elements : ViewElements object
+            The set of Group, Item, and Include objects contained in the view.
+            Do not use this parameter when calling this method directly.
+        handler : Handler object
+            A handler object used for event handling in the dialog box. If
+            None, the default handler for Traits UI is used.
+        id : string
+            A unique ID for persisting preferences about this user interface,
+            such as size and position. If not specified, no user preferences
+            are saved.
+        scrollable : Boolean
+            Indicates whether the dialog box should be scrollable. When set to
+            True, scroll bars appear on the dialog box if it is not large enough
+            to display all of the items in the view at one time.
+
+        """
+        handler = handler or self.handler or default_handler()
+        if not isinstance(handler, Handler):
+            handler = handler()
+
+        if args is not None:
+            handler.trait_set(**args)
+
+        if not isinstance(context, dict):
+            context = context.trait_context()
+
+        context.setdefault("handler", handler)
+        handler = context["handler"]
+
+        if self.model_view is not None:
+            context["object"] = self.model_view(context["object"])
+
+        self_id = self.id
+        if self_id != "":
+            if id != "":
+                id = "%s:%s" % (self_id, id)
+            else:
+                id = self_id
+
+        if scrollable is None:
+            scrollable = self.scrollable
+
+        ui = UI(
+            view=self,
+            context=context,
+            handler=handler,
+            view_elements=view_elements,
+            title=self.title,
+            id=id,
+            scrollable=scrollable,
+        )
+
+        if kind is None:
+            kind = self.kind
+
+        ui.ui(parent, kind)
+
+        return ui
+
+    def replace_include(self, view_elements):
+        """ Replaces any items that have an ID with an Include object with
+            the same ID, and puts the object with the ID into the specified
+            ViewElements object.
+        """
+        if self.content is not None:
+            self.content.replace_include(view_elements)
+
+    def __repr__(self):
+        """ Returns a "pretty print" version of the View.
+        """
+        if self.content is None:
+            return "()"
+        return "( %s )" % ", ".join(
+            [item.__repr__() for item in self.content.content]
+        )
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/_sources/api.rst.txt b/5.0/_sources/api.rst.txt new file mode 100644 index 000000000..224ac6510 --- /dev/null +++ b/5.0/_sources/api.rst.txt @@ -0,0 +1,11 @@ +.. _api-documentation: + +API documentation +================= + +This section contains auto-generated API documentation for AppTools. + +.. toctree:: + :maxdepth: 2 + + api/modules diff --git a/5.0/_sources/api/apptools.io.h5.rst.txt b/5.0/_sources/api/apptools.io.h5.rst.txt new file mode 100644 index 000000000..dea85f58a --- /dev/null +++ b/5.0/_sources/api/apptools.io.h5.rst.txt @@ -0,0 +1,46 @@ +apptools.io.h5 package +====================== + +Submodules +---------- + +apptools.io.h5.dict\_node module +-------------------------------- + +.. automodule:: apptools.io.h5.dict_node + :members: + :undoc-members: + :show-inheritance: + +apptools.io.h5.file module +-------------------------- + +.. automodule:: apptools.io.h5.file + :members: + :undoc-members: + :show-inheritance: + +apptools.io.h5.table\_node module +--------------------------------- + +.. automodule:: apptools.io.h5.table_node + :members: + :undoc-members: + :show-inheritance: + +apptools.io.h5.utils module +--------------------------- + +.. automodule:: apptools.io.h5.utils + :members: + :undoc-members: + :show-inheritance: + + +Module contents +--------------- + +.. automodule:: apptools.io.h5 + :members: + :undoc-members: + :show-inheritance: diff --git a/5.0/_sources/api/apptools.io.rst.txt b/5.0/_sources/api/apptools.io.rst.txt new file mode 100644 index 000000000..0907ac23c --- /dev/null +++ b/5.0/_sources/api/apptools.io.rst.txt @@ -0,0 +1,37 @@ +apptools.io package +=================== + +Subpackages +----------- + +.. toctree:: + + apptools.io.h5 + +Submodules +---------- + +apptools.io.api module +---------------------- + +.. automodule:: apptools.io.api + :members: + :undoc-members: + :show-inheritance: + +apptools.io.file module +----------------------- + +.. automodule:: apptools.io.file + :members: + :undoc-members: + :show-inheritance: + + +Module contents +--------------- + +.. automodule:: apptools.io + :members: + :undoc-members: + :show-inheritance: diff --git a/5.0/_sources/api/apptools.logger.agent.rst.txt b/5.0/_sources/api/apptools.logger.agent.rst.txt new file mode 100644 index 000000000..c25c0dcfb --- /dev/null +++ b/5.0/_sources/api/apptools.logger.agent.rst.txt @@ -0,0 +1,38 @@ +apptools.logger.agent package +============================= + +Submodules +---------- + +apptools.logger.agent.attachments module +---------------------------------------- + +.. automodule:: apptools.logger.agent.attachments + :members: + :undoc-members: + :show-inheritance: + +apptools.logger.agent.quality\_agent\_mailer module +--------------------------------------------------- + +.. automodule:: apptools.logger.agent.quality_agent_mailer + :members: + :undoc-members: + :show-inheritance: + +apptools.logger.agent.quality\_agent\_view module +------------------------------------------------- + +.. automodule:: apptools.logger.agent.quality_agent_view + :members: + :undoc-members: + :show-inheritance: + + +Module contents +--------------- + +.. automodule:: apptools.logger.agent + :members: + :undoc-members: + :show-inheritance: diff --git a/5.0/_sources/api/apptools.logger.plugin.rst.txt b/5.0/_sources/api/apptools.logger.plugin.rst.txt new file mode 100644 index 000000000..b607fdd4e --- /dev/null +++ b/5.0/_sources/api/apptools.logger.plugin.rst.txt @@ -0,0 +1,45 @@ +apptools.logger.plugin package +============================== + +Subpackages +----------- + +.. toctree:: + + apptools.logger.plugin.view + +Submodules +---------- + +apptools.logger.plugin.logger\_plugin module +-------------------------------------------- + +.. automodule:: apptools.logger.plugin.logger_plugin + :members: + :undoc-members: + :show-inheritance: + +apptools.logger.plugin.logger\_preferences module +------------------------------------------------- + +.. automodule:: apptools.logger.plugin.logger_preferences + :members: + :undoc-members: + :show-inheritance: + +apptools.logger.plugin.logger\_service module +--------------------------------------------- + +.. automodule:: apptools.logger.plugin.logger_service + :members: + :undoc-members: + :show-inheritance: + + +Module contents +--------------- + +.. automodule:: apptools.logger.plugin + :members: + :undoc-members: + :show-inheritance: diff --git a/5.0/_sources/api/apptools.logger.plugin.view.rst.txt b/5.0/_sources/api/apptools.logger.plugin.view.rst.txt new file mode 100644 index 000000000..1d8dc0b3d --- /dev/null +++ b/5.0/_sources/api/apptools.logger.plugin.view.rst.txt @@ -0,0 +1,30 @@ +apptools.logger.plugin.view package +=================================== + +Submodules +---------- + +apptools.logger.plugin.view.logger\_preferences\_page module +------------------------------------------------------------ + +.. automodule:: apptools.logger.plugin.view.logger_preferences_page + :members: + :undoc-members: + :show-inheritance: + +apptools.logger.plugin.view.logger\_view module +----------------------------------------------- + +.. automodule:: apptools.logger.plugin.view.logger_view + :members: + :undoc-members: + :show-inheritance: + + +Module contents +--------------- + +.. automodule:: apptools.logger.plugin.view + :members: + :undoc-members: + :show-inheritance: diff --git a/5.0/_sources/api/apptools.logger.rst.txt b/5.0/_sources/api/apptools.logger.rst.txt new file mode 100644 index 000000000..c49196f26 --- /dev/null +++ b/5.0/_sources/api/apptools.logger.rst.txt @@ -0,0 +1,70 @@ +apptools.logger package +======================= + +Subpackages +----------- + +.. toctree:: + + apptools.logger.agent + apptools.logger.plugin + +Submodules +---------- + +apptools.logger.api module +-------------------------- + +.. automodule:: apptools.logger.api + :members: + :undoc-members: + :show-inheritance: + +apptools.logger.custom\_excepthook module +----------------------------------------- + +.. automodule:: apptools.logger.custom_excepthook + :members: + :undoc-members: + :show-inheritance: + +apptools.logger.log\_point module +--------------------------------- + +.. automodule:: apptools.logger.log_point + :members: + :undoc-members: + :show-inheritance: + +apptools.logger.log\_queue\_handler module +------------------------------------------ + +.. automodule:: apptools.logger.log_queue_handler + :members: + :undoc-members: + :show-inheritance: + +apptools.logger.logger module +----------------------------- + +.. automodule:: apptools.logger.logger + :members: + :undoc-members: + :show-inheritance: + +apptools.logger.ring\_buffer module +----------------------------------- + +.. automodule:: apptools.logger.ring_buffer + :members: + :undoc-members: + :show-inheritance: + + +Module contents +--------------- + +.. automodule:: apptools.logger + :members: + :undoc-members: + :show-inheritance: diff --git a/5.0/_sources/api/apptools.naming.rst.txt b/5.0/_sources/api/apptools.naming.rst.txt new file mode 100644 index 000000000..b4645f693 --- /dev/null +++ b/5.0/_sources/api/apptools.naming.rst.txt @@ -0,0 +1,221 @@ +apptools.naming package +======================= + +Subpackages +----------- + +.. toctree:: + + apptools.naming.trait_defs + +Submodules +---------- + +apptools.naming.address module +------------------------------ + +.. automodule:: apptools.naming.address + :members: + :undoc-members: + :show-inheritance: + +apptools.naming.api module +-------------------------- + +.. automodule:: apptools.naming.api + :members: + :undoc-members: + :show-inheritance: + +apptools.naming.binding module +------------------------------ + +.. automodule:: apptools.naming.binding + :members: + :undoc-members: + :show-inheritance: + +apptools.naming.context module +------------------------------ + +.. automodule:: apptools.naming.context + :members: + :undoc-members: + :show-inheritance: + +apptools.naming.dir\_context module +----------------------------------- + +.. automodule:: apptools.naming.dir_context + :members: + :undoc-members: + :show-inheritance: + +apptools.naming.dynamic\_context module +--------------------------------------- + +.. automodule:: apptools.naming.dynamic_context + :members: + :undoc-members: + :show-inheritance: + +apptools.naming.exception module +-------------------------------- + +.. automodule:: apptools.naming.exception + :members: + :undoc-members: + :show-inheritance: + +apptools.naming.initial\_context module +--------------------------------------- + +.. automodule:: apptools.naming.initial_context + :members: + :undoc-members: + :show-inheritance: + +apptools.naming.initial\_context\_factory module +------------------------------------------------ + +.. automodule:: apptools.naming.initial_context_factory + :members: + :undoc-members: + :show-inheritance: + +apptools.naming.naming\_event module +------------------------------------ + +.. automodule:: apptools.naming.naming_event + :members: + :undoc-members: + :show-inheritance: + +apptools.naming.naming\_manager module +-------------------------------------- + +.. automodule:: apptools.naming.naming_manager + :members: + :undoc-members: + :show-inheritance: + +apptools.naming.object\_factory module +-------------------------------------- + +.. automodule:: apptools.naming.object_factory + :members: + :undoc-members: + :show-inheritance: + +apptools.naming.object\_serializer module +----------------------------------------- + +.. automodule:: apptools.naming.object_serializer + :members: + :undoc-members: + :show-inheritance: + +apptools.naming.py\_context module +---------------------------------- + +.. automodule:: apptools.naming.py_context + :members: + :undoc-members: + :show-inheritance: + +apptools.naming.py\_object\_factory module +------------------------------------------ + +.. automodule:: apptools.naming.py_object_factory + :members: + :undoc-members: + :show-inheritance: + +apptools.naming.pyfs\_context module +------------------------------------ + +.. automodule:: apptools.naming.pyfs_context + :members: + :undoc-members: + :show-inheritance: + +apptools.naming.pyfs\_context\_factory module +--------------------------------------------- + +.. automodule:: apptools.naming.pyfs_context_factory + :members: + :undoc-members: + :show-inheritance: + +apptools.naming.pyfs\_initial\_context\_factory module +------------------------------------------------------ + +.. automodule:: apptools.naming.pyfs_initial_context_factory + :members: + :undoc-members: + :show-inheritance: + +apptools.naming.pyfs\_object\_factory module +-------------------------------------------- + +.. automodule:: apptools.naming.pyfs_object_factory + :members: + :undoc-members: + :show-inheritance: + +apptools.naming.pyfs\_state\_factory module +------------------------------------------- + +.. automodule:: apptools.naming.pyfs_state_factory + :members: + :undoc-members: + :show-inheritance: + +apptools.naming.reference module +-------------------------------- + +.. automodule:: apptools.naming.reference + :members: + :undoc-members: + :show-inheritance: + +apptools.naming.referenceable module +------------------------------------ + +.. automodule:: apptools.naming.referenceable + :members: + :undoc-members: + :show-inheritance: + +apptools.naming.referenceable\_state\_factory module +---------------------------------------------------- + +.. automodule:: apptools.naming.referenceable_state_factory + :members: + :undoc-members: + :show-inheritance: + +apptools.naming.state\_factory module +------------------------------------- + +.. automodule:: apptools.naming.state_factory + :members: + :undoc-members: + :show-inheritance: + +apptools.naming.unique\_name module +----------------------------------- + +.. automodule:: apptools.naming.unique_name + :members: + :undoc-members: + :show-inheritance: + + +Module contents +--------------- + +.. automodule:: apptools.naming + :members: + :undoc-members: + :show-inheritance: diff --git a/5.0/_sources/api/apptools.naming.trait_defs.rst.txt b/5.0/_sources/api/apptools.naming.trait_defs.rst.txt new file mode 100644 index 000000000..e2281ea3d --- /dev/null +++ b/5.0/_sources/api/apptools.naming.trait_defs.rst.txt @@ -0,0 +1,30 @@ +apptools.naming.trait\_defs package +=================================== + +Submodules +---------- + +apptools.naming.trait\_defs.api module +-------------------------------------- + +.. automodule:: apptools.naming.trait_defs.api + :members: + :undoc-members: + :show-inheritance: + +apptools.naming.trait\_defs.naming\_traits module +------------------------------------------------- + +.. automodule:: apptools.naming.trait_defs.naming_traits + :members: + :undoc-members: + :show-inheritance: + + +Module contents +--------------- + +.. automodule:: apptools.naming.trait_defs + :members: + :undoc-members: + :show-inheritance: diff --git a/5.0/_sources/api/apptools.persistence.rst.txt b/5.0/_sources/api/apptools.persistence.rst.txt new file mode 100644 index 000000000..4cda6c227 --- /dev/null +++ b/5.0/_sources/api/apptools.persistence.rst.txt @@ -0,0 +1,62 @@ +apptools.persistence package +============================ + +Submodules +---------- + +apptools.persistence.file\_path module +-------------------------------------- + +.. automodule:: apptools.persistence.file_path + :members: + :undoc-members: + :show-inheritance: + +apptools.persistence.project\_loader module +------------------------------------------- + +.. automodule:: apptools.persistence.project_loader + :members: + :undoc-members: + :show-inheritance: + +apptools.persistence.state\_pickler module +------------------------------------------ + +.. automodule:: apptools.persistence.state_pickler + :members: + :undoc-members: + :show-inheritance: + +apptools.persistence.updater module +----------------------------------- + +.. automodule:: apptools.persistence.updater + :members: + :undoc-members: + :show-inheritance: + +apptools.persistence.version\_registry module +--------------------------------------------- + +.. automodule:: apptools.persistence.version_registry + :members: + :undoc-members: + :show-inheritance: + +apptools.persistence.versioned\_unpickler module +------------------------------------------------ + +.. automodule:: apptools.persistence.versioned_unpickler + :members: + :undoc-members: + :show-inheritance: + + +Module contents +--------------- + +.. automodule:: apptools.persistence + :members: + :undoc-members: + :show-inheritance: diff --git a/5.0/_sources/api/apptools.preferences.rst.txt b/5.0/_sources/api/apptools.preferences.rst.txt new file mode 100644 index 000000000..cba31951c --- /dev/null +++ b/5.0/_sources/api/apptools.preferences.rst.txt @@ -0,0 +1,77 @@ +apptools.preferences package +============================ + +Subpackages +----------- + +.. toctree:: + + apptools.preferences.ui + +Submodules +---------- + +apptools.preferences.api module +------------------------------- + +.. automodule:: apptools.preferences.api + :members: + :undoc-members: + :show-inheritance: + +apptools.preferences.i\_preferences module +------------------------------------------ + +.. automodule:: apptools.preferences.i_preferences + :members: + :undoc-members: + :show-inheritance: + +apptools.preferences.package\_globals module +-------------------------------------------- + +.. automodule:: apptools.preferences.package_globals + :members: + :undoc-members: + :show-inheritance: + +apptools.preferences.preference\_binding module +----------------------------------------------- + +.. automodule:: apptools.preferences.preference_binding + :members: + :undoc-members: + :show-inheritance: + +apptools.preferences.preferences module +--------------------------------------- + +.. automodule:: apptools.preferences.preferences + :members: + :undoc-members: + :show-inheritance: + +apptools.preferences.preferences\_helper module +----------------------------------------------- + +.. automodule:: apptools.preferences.preferences_helper + :members: + :undoc-members: + :show-inheritance: + +apptools.preferences.scoped\_preferences module +----------------------------------------------- + +.. automodule:: apptools.preferences.scoped_preferences + :members: + :undoc-members: + :show-inheritance: + + +Module contents +--------------- + +.. automodule:: apptools.preferences + :members: + :undoc-members: + :show-inheritance: diff --git a/5.0/_sources/api/apptools.preferences.ui.rst.txt b/5.0/_sources/api/apptools.preferences.ui.rst.txt new file mode 100644 index 000000000..59a451f0a --- /dev/null +++ b/5.0/_sources/api/apptools.preferences.ui.rst.txt @@ -0,0 +1,70 @@ +apptools.preferences.ui package +=============================== + +Submodules +---------- + +apptools.preferences.ui.api module +---------------------------------- + +.. automodule:: apptools.preferences.ui.api + :members: + :undoc-members: + :show-inheritance: + +apptools.preferences.ui.i\_preferences\_page module +--------------------------------------------------- + +.. automodule:: apptools.preferences.ui.i_preferences_page + :members: + :undoc-members: + :show-inheritance: + +apptools.preferences.ui.preferences\_manager module +--------------------------------------------------- + +.. automodule:: apptools.preferences.ui.preferences_manager + :members: + :undoc-members: + :show-inheritance: + +apptools.preferences.ui.preferences\_node module +------------------------------------------------ + +.. automodule:: apptools.preferences.ui.preferences_node + :members: + :undoc-members: + :show-inheritance: + +apptools.preferences.ui.preferences\_page module +------------------------------------------------ + +.. automodule:: apptools.preferences.ui.preferences_page + :members: + :undoc-members: + :show-inheritance: + +apptools.preferences.ui.tree\_item module +----------------------------------------- + +.. automodule:: apptools.preferences.ui.tree_item + :members: + :undoc-members: + :show-inheritance: + +apptools.preferences.ui.widget\_editor module +--------------------------------------------- + +.. automodule:: apptools.preferences.ui.widget_editor + :members: + :undoc-members: + :show-inheritance: + + +Module contents +--------------- + +.. automodule:: apptools.preferences.ui + :members: + :undoc-members: + :show-inheritance: diff --git a/5.0/_sources/api/apptools.rst.txt b/5.0/_sources/api/apptools.rst.txt new file mode 100644 index 000000000..d1e57ded9 --- /dev/null +++ b/5.0/_sources/api/apptools.rst.txt @@ -0,0 +1,25 @@ +apptools package +================ + +Subpackages +----------- + +.. toctree:: + + apptools.io + apptools.logger + apptools.naming + apptools.persistence + apptools.preferences + apptools.scripting + apptools.selection + apptools.type_registry + apptools.undo + +Module contents +--------------- + +.. automodule:: apptools + :members: + :undoc-members: + :show-inheritance: diff --git a/5.0/_sources/api/apptools.scripting.rst.txt b/5.0/_sources/api/apptools.scripting.rst.txt new file mode 100644 index 000000000..4a47c7054 --- /dev/null +++ b/5.0/_sources/api/apptools.scripting.rst.txt @@ -0,0 +1,62 @@ +apptools.scripting package +========================== + +Submodules +---------- + +apptools.scripting.api module +----------------------------- + +.. automodule:: apptools.scripting.api + :members: + :undoc-members: + :show-inheritance: + +apptools.scripting.package\_globals module +------------------------------------------ + +.. automodule:: apptools.scripting.package_globals + :members: + :undoc-members: + :show-inheritance: + +apptools.scripting.recordable module +------------------------------------ + +.. automodule:: apptools.scripting.recordable + :members: + :undoc-members: + :show-inheritance: + +apptools.scripting.recorder module +---------------------------------- + +.. automodule:: apptools.scripting.recorder + :members: + :undoc-members: + :show-inheritance: + +apptools.scripting.recorder\_with\_ui module +-------------------------------------------- + +.. automodule:: apptools.scripting.recorder_with_ui + :members: + :undoc-members: + :show-inheritance: + +apptools.scripting.util module +------------------------------ + +.. automodule:: apptools.scripting.util + :members: + :undoc-members: + :show-inheritance: + + +Module contents +--------------- + +.. automodule:: apptools.scripting + :members: + :undoc-members: + :show-inheritance: diff --git a/5.0/_sources/api/apptools.selection.rst.txt b/5.0/_sources/api/apptools.selection.rst.txt new file mode 100644 index 000000000..d64022443 --- /dev/null +++ b/5.0/_sources/api/apptools.selection.rst.txt @@ -0,0 +1,62 @@ +apptools.selection package +========================== + +Submodules +---------- + +apptools.selection.api module +----------------------------- + +.. automodule:: apptools.selection.api + :members: + :undoc-members: + :show-inheritance: + +apptools.selection.errors module +-------------------------------- + +.. automodule:: apptools.selection.errors + :members: + :undoc-members: + :show-inheritance: + +apptools.selection.i\_selection module +-------------------------------------- + +.. automodule:: apptools.selection.i_selection + :members: + :undoc-members: + :show-inheritance: + +apptools.selection.i\_selection\_provider module +------------------------------------------------ + +.. automodule:: apptools.selection.i_selection_provider + :members: + :undoc-members: + :show-inheritance: + +apptools.selection.list\_selection module +----------------------------------------- + +.. automodule:: apptools.selection.list_selection + :members: + :undoc-members: + :show-inheritance: + +apptools.selection.selection\_service module +-------------------------------------------- + +.. automodule:: apptools.selection.selection_service + :members: + :undoc-members: + :show-inheritance: + + +Module contents +--------------- + +.. automodule:: apptools.selection + :members: + :undoc-members: + :show-inheritance: diff --git a/5.0/_sources/api/apptools.type_registry.rst.txt b/5.0/_sources/api/apptools.type_registry.rst.txt new file mode 100644 index 000000000..161af8365 --- /dev/null +++ b/5.0/_sources/api/apptools.type_registry.rst.txt @@ -0,0 +1,30 @@ +apptools.type\_registry package +=============================== + +Submodules +---------- + +apptools.type\_registry.api module +---------------------------------- + +.. automodule:: apptools.type_registry.api + :members: + :undoc-members: + :show-inheritance: + +apptools.type\_registry.type\_registry module +--------------------------------------------- + +.. automodule:: apptools.type_registry.type_registry + :members: + :undoc-members: + :show-inheritance: + + +Module contents +--------------- + +.. automodule:: apptools.type_registry + :members: + :undoc-members: + :show-inheritance: diff --git a/5.0/_sources/api/apptools.undo.action.rst.txt b/5.0/_sources/api/apptools.undo.action.rst.txt new file mode 100644 index 000000000..13985a67f --- /dev/null +++ b/5.0/_sources/api/apptools.undo.action.rst.txt @@ -0,0 +1,54 @@ +apptools.undo.action package +============================ + +Submodules +---------- + +apptools.undo.action.abstract\_command\_stack\_action module +------------------------------------------------------------ + +.. automodule:: apptools.undo.action.abstract_command_stack_action + :members: + :undoc-members: + :show-inheritance: + +apptools.undo.action.api module +------------------------------- + +.. automodule:: apptools.undo.action.api + :members: + :undoc-members: + :show-inheritance: + +apptools.undo.action.command\_action module +------------------------------------------- + +.. automodule:: apptools.undo.action.command_action + :members: + :undoc-members: + :show-inheritance: + +apptools.undo.action.redo\_action module +---------------------------------------- + +.. automodule:: apptools.undo.action.redo_action + :members: + :undoc-members: + :show-inheritance: + +apptools.undo.action.undo\_action module +---------------------------------------- + +.. automodule:: apptools.undo.action.undo_action + :members: + :undoc-members: + :show-inheritance: + + +Module contents +--------------- + +.. automodule:: apptools.undo.action + :members: + :undoc-members: + :show-inheritance: diff --git a/5.0/_sources/api/apptools.undo.rst.txt b/5.0/_sources/api/apptools.undo.rst.txt new file mode 100644 index 000000000..477e1bf52 --- /dev/null +++ b/5.0/_sources/api/apptools.undo.rst.txt @@ -0,0 +1,77 @@ +apptools.undo package +===================== + +Subpackages +----------- + +.. toctree:: + + apptools.undo.action + +Submodules +---------- + +apptools.undo.abstract\_command module +-------------------------------------- + +.. automodule:: apptools.undo.abstract_command + :members: + :undoc-members: + :show-inheritance: + +apptools.undo.api module +------------------------ + +.. automodule:: apptools.undo.api + :members: + :undoc-members: + :show-inheritance: + +apptools.undo.command\_stack module +----------------------------------- + +.. automodule:: apptools.undo.command_stack + :members: + :undoc-members: + :show-inheritance: + +apptools.undo.i\_command module +------------------------------- + +.. automodule:: apptools.undo.i_command + :members: + :undoc-members: + :show-inheritance: + +apptools.undo.i\_command\_stack module +-------------------------------------- + +.. automodule:: apptools.undo.i_command_stack + :members: + :undoc-members: + :show-inheritance: + +apptools.undo.i\_undo\_manager module +------------------------------------- + +.. automodule:: apptools.undo.i_undo_manager + :members: + :undoc-members: + :show-inheritance: + +apptools.undo.undo\_manager module +---------------------------------- + +.. automodule:: apptools.undo.undo_manager + :members: + :undoc-members: + :show-inheritance: + + +Module contents +--------------- + +.. automodule:: apptools.undo + :members: + :undoc-members: + :show-inheritance: diff --git a/5.0/_sources/api/modules.rst.txt b/5.0/_sources/api/modules.rst.txt new file mode 100644 index 000000000..f51f30d15 --- /dev/null +++ b/5.0/_sources/api/modules.rst.txt @@ -0,0 +1,7 @@ +apptools +======== + +.. toctree:: + :maxdepth: 4 + + apptools diff --git a/5.0/_sources/index.rst.txt b/5.0/_sources/index.rst.txt new file mode 100644 index 000000000..1891a5802 --- /dev/null +++ b/5.0/_sources/index.rst.txt @@ -0,0 +1,16 @@ +AppTools Documentation +============================================== + +.. toctree:: + :maxdepth: 2 + :glob: + + preferences/* + scripting/* + undo/* + selection/* + naming/* + io/* + api + +* :ref:`search` diff --git a/5.0/_sources/io/introduction.rst.txt b/5.0/_sources/io/introduction.rst.txt new file mode 100644 index 000000000..6bdd04ef5 --- /dev/null +++ b/5.0/_sources/io/introduction.rst.txt @@ -0,0 +1,28 @@ +File I/O +======== + +The :mod:`apptools.io` package provides a traited |File| object provides +properties and methods for common file path manipulation operations. Much of +this functionality was implemented before Python 3 `pathlib`_ standard library +became available to provide similar support. For new code we encourage users +to investigate if `pathlib`_ can satisfy their use cases before they turn to +the `apptools.io` |File| object + +HDF5 File Support +----------------- + +The :mod:`apptools.io.h5` sub-package provides a wrapper around `PyTables`_ +with a dictionary-style mapping. + + +.. + external links + +.. _pathlib: https://docs.python.org/3/library/pathlib.html +.. _PyTables: https://www.pytables.org/ + + +.. + # substitutions + +.. |File| replace:: :class:`~apptools.io.file.File` diff --git a/5.0/_sources/naming/Introduction.rst.txt b/5.0/_sources/naming/Introduction.rst.txt new file mode 100644 index 000000000..736f4b92a --- /dev/null +++ b/5.0/_sources/naming/Introduction.rst.txt @@ -0,0 +1,7 @@ +Naming +====== + +:mod:`apptools.naming` package is a Python implementation of the Naming portion of the `Java +Naming and Directory Interface `_, +including specific implementations for a heirarchy of Python objects. You can +also find the Java JNDI tutorial `here `_. diff --git a/5.0/_sources/preferences/Preferences.rst.txt b/5.0/_sources/preferences/Preferences.rst.txt new file mode 100644 index 000000000..51ee93fd8 --- /dev/null +++ b/5.0/_sources/preferences/Preferences.rst.txt @@ -0,0 +1,329 @@ +Preferences +=========== + +The preferences package provides a simple API for managing application +preferences. The classes in the package are implemented using a layered +approach where the lowest layer provides access to the raw preferences +mechanism and each layer on top providing more convenient ways to get and set +preference values. + +The Basic Preferences Mechanism +------------------------------- + +Lets start by taking a look at the lowest layer which consists of the +|IPreferences| interface and its default implementation in the |Preferences| +class. This layer implements the basic preferences system which is a +hierarchical arrangement of preferences 'nodes' (where each node is simply an +object that implements the |IPreferences| interface). Nodes in the hierarchy can +contain preference settings and/or child nodes. This layer also provides a +default way to read and write preferences from the filesystem using the +excellent `ConfigObj`_ package. + +This all sounds a bit complicated but, believe me, it isn't! To prove it +(hopefully) lets look at an example. Say I have the following preferences in +a file 'example.ini':: + + [acme.ui] + bgcolor = blue + width = 50 + ratio = 1.0 + visible = True + + [acme.ui.splash_screen] + image = splash + fgcolor = red + +I can create a preferences hierarchy from this file by:: + + >>> from apptools.preferences.api import Preferences + >>> preferences = Preferences(filename='example.ini') + >>> preferences.dump() + + Node() {} + Node(acme) {} + Node(ui) {'bgcolor': 'blue', 'width': '50', 'ratio': '1.0', 'visible': 'True'} + Node(splash_screen) {'image': 'splash', 'fgcolor': 'red'} + +The 'dump' method (useful for debugging etc) simply 'pretty prints' a +preferences hierarchy. The dictionary next to each node contains the node's +actual preferences. In this case, the root node (the node with no name) is the +preferences object that we created. This node now has one child node 'acme', +which contains no preferences. The 'acme' node has one child, 'ui', which +contains some preferences (e.g. 'bgcolor') and also a child node +'splash_screen' which also contains preferences (e.g. 'image'). + +To look up a preference we use:: + + >>> preferences.get('acme.ui.bgcolor') + 'blue' + +If no such preferences exists then, by default, None is returned:: + + >>> preferences.get('acme.ui.bogus') is None + True + +You can also specify an explicit default value:: + + >>> preferences.get('acme.ui.bogus', 'fred') + 'fred' + +To set a preference we use:: + + >>> preferences.set('acme.ui.bgcolor', 'red') + >>> preferences.get('acme.ui.bgcolor') + 'red' + +And to make sure the preferences are saved back to disk:: + + >>> preferences.flush() + +To add a new preference value we simply set it:: + + >>> preferences.set('acme.ui.fgcolor', 'black') + >>> preferences.get('acme.ui.fgcolor') + 'black' + +Any missing nodes in a call to 'set' are created automatically, hence:: + + >>> preferences.set('acme.ui.button.fgcolor', 'white') + >>> preferences.get('acme.ui.button.fgcolor') + 'white' + +Preferences can also be 'inherited'. e.g. Notice that the 'splash_screen' +node does not contain a 'bgcolor' preference, and hence:: + + >>> preferences.get('acme.ui.splash_screen.bgcolor') is None + True + +But if we allow the 'inheritance' of preference values then:: + + >>> preferences.get('acme.ui.splash_screen.bgcolor', inherit=True) + 'red' + +By using 'inheritance' here the preferences system will try the following +preferences:: + + 'acme.ui.splash_screen.bgcolor' + 'acme.ui.bgcolor' + 'acme.bgcolor' + 'bgcolor' + +Strings, Glorious Strings +~~~~~~~~~~~~~~~~~~~~~~~~~ + +At this point it is worth mentioning that preferences are *always* stored and +returned as strings. This is because of the limitations of the traditional +'.ini' file format i.e. they don't contain any type information! Now before you +start panicking, this doesn't mean that all of your preferences have to be +strings! Currently the preferences system allows, strings(!), booleans, ints, +longs, floats and complex numbers. When you store a non-string value it gets +converted to a string for you, but you *always* get a string back:: + + >>> preferences.get('acme.ui.width') + '50' + >>> preferences.set('acme.ui.width', 100) + >>> preferences.get('acme.ui.width') + '100' + + >>> preferences.get('acme.ui.visible') + 'True' + >>> preferences.set('acme.ui.visible', False) + >>> preferences.get('acme.ui.visible') + 'False' + +This is obviously not terribly convenient, and so the following section +discusses how we associate type information with our preferences to make +getting and setting them more natural. + +Preferences and Types +--------------------- + +As mentioned previously, we would like to be able to get and set non-string +preferences in a more convenient way. This is where the |PreferencesHelper| +class comes in. + +Let's take another look at 'example.ini':: + + [acme.ui] + bgcolor = blue + width = 50 + ratio = 1.0 + visible = True + + [acme.ui.splash_screen] + image = splash + fgcolor = red + +Say, I am interested in the preferences in the 'acme.ui' section. I can use a +preferences helper as follows:: + + from apptools.preferences.api import PreferencesHelper + + class SplashScreenPreferences(PreferencesHelper): + """ A preferences helper for the splash screen. """ + + preferences_path = 'acme.ui' + + bgcolor = Str + width = Int + ratio = Float + visible = Bool + + >>> preferences = Preferences(filename='example.ini') + >>> helper = SplashScreenPreferences(preferences=preferences) + >>> helper.bgcolor + 'blue' + >>> helper.width + 50 + >>> helper.ratio + 1.0 + >>> helper.visible + True + +And, obviously, I can set the value of the preferences via the helper too:: + + >>> helper.ratio = 0.5 + +And if you want to prove to yourself it really did set the preference:: + + >>> preferences.get('acme.ui.ratio') + '0.5' + +Using a preferences helper you also get notified via the usual trait +mechanism when the preferences are changed (either via the helper or via the +preferences node directly:: + + def listener(obj, trait_name, old, new): + print(trait_name, old, new) + + >>> helper.on_trait_change(listener) + >>> helper.ratio = 0.75 + ratio 0.5 0.75 + >>> preferences.set('acme.ui.ratio', 0.33) + ratio 0.75 0.33 + +Scoped Preferences +------------------ + +In many applications the idea of preferences scopes is useful. In a scoped +system, an actual preference value can be stored in any scope and when a call +is made to the 'get' method the scopes are searched in order of precedence. + +The default implementation (in the |ScopedPreferences| class) provides two +scopes by default: + +1) The application scope + +This scope stores itself in the 'ETSConfig.application_home' directory. This +scope is generally used when *setting* any user preferences. + +2) The default scope + +This scope is transient (i.e. it does not store itself anywhere). This scope +is generally used to load any predefined default values into the preferences +system. + +If you are happy with the default arrangement, then using the scoped +preferences is just like using the plain old non-scoped version:: + + >>> from apptools.preferences.api import ScopedPreferences + >>> preferences = ScopedPreferences(filename='example.ini') + >>> preferences.load('example.ini') + >>> preferences.dump() + + Node() {} + Node(application) {} + Node(acme) {} + Node(ui) {'bgcolor': 'blue', 'width': '50', 'ratio': '1.0', 'visible': 'True'} + Node(splash_screen) {'image': 'splash', 'fgcolor': 'red'} + Node(default) {} + +Here you can see that the root node now has a child node representing each +scope. + +When we are getting and setting preferences using scopes we generally want the +following behaviour: + +a) When we get a preference we want to look it up in each scope in order. The +first scope that contains a value 'wins'. + +b) When we set a preference, we want to set it in the first scope. By default +this means that when we set a preference it will be set in the application +scope. This is exactly what we want as the application scope is the scope that +is persistent. + +So usually, we just use the scoped preferences as before:: + + >>> preferences.get('acme.ui.bgcolor') + 'blue' + >>> preferences.set('acme.ui.bgcolor', 'red') + >>> preferences.dump() + + Node() {} + Node(application) {} + Node(acme) {} + Node(ui) {'bgcolor': 'red', 'width': '50', 'ratio': '1.0', 'visible': 'True'} + Node(splash_screen) {'image': 'splash', 'fgcolor': 'red'} + Node(default) {} + +And, conveniently, preference helpers work just the same with scoped +preferences too:: + + >>> helper = SplashScreenPreferences(preferences=preferences) + >>> helper.bgcolor + 'red' + >>> helper.width + 50 + >>> helper.ratio + 1.0 + >>> helper.visible + True + +Accessing a particular scope +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Should you care about getting or setting a preference in a particular scope +then you use the following syntax:: + + >>> preferences.set('default/acme.ui.bgcolor', 'red') + >>> preferences.get('default/acme.ui.bgcolor') + 'red' + >>> preferences.dump() + + Node() {} + Node(application) {} + Node(acme) {} + Node(ui) {'bgcolor': 'red', 'width': '50', 'ratio': '1.0', 'visible': 'True'} + Node(splash_screen) {'image': 'splash', 'fgcolor': 'red'} + Node(default) {} + Node(acme) {} + Node(ui) {'bgcolor': 'red'} + +You can also get hold of a scope via:: + + >>> default = preferences.get_scope('default') + +And then perform any of the usual operations on it. + +Further Reading +--------------- + +So that's a quick tour around the basic useage of the preferences API. For more +information about what is provided take a look at the :ref:`api-documentation`. + +If you are using Envisage to build your applications then you might also be +interested in the |Preferences in Envisage| section. + +.. + external links + +.. _ConfigObj: https://configobj.readthedocs.io/en/latest + +.. + # substitutions + +.. |ScopedPreferences| replace:: :class:`~apptools.preferences.scoped_preferences.ScopedPreferences` +.. |IPreferences| replace:: :class:`~apptools.preferences.i_preferences.IPreferences` +.. |Preferences| replace:: :class:`~apptools.preferences.preferences.Preferences` +.. |PreferencesHelper| replace:: :class:`~apptools.preferences.preferences_helper.PreferencesHelper` +.. |Preferences in Envisage| replace:: :ref:`preferences-in-envisage` diff --git a/5.0/_sources/preferences/PreferencesInEnvisage.rst.txt b/5.0/_sources/preferences/PreferencesInEnvisage.rst.txt new file mode 100644 index 000000000..931897eb6 --- /dev/null +++ b/5.0/_sources/preferences/PreferencesInEnvisage.rst.txt @@ -0,0 +1,52 @@ +.. _preferences-in-envisage: + +Preferences in Envisage +======================= + +This section discusses how an Envisage application uses the preferences +mechanism. Envisage tries not to dictate too much, and so this describes the +default behaviour, but you are free to override it as desired. + +Envisage uses the default implementation of the |ScopedPreferences| class which +is made available via the application's 'preferences' trait:: + + >>> application = Application(id='myapplication') + >>> application.preferences.set('acme.ui.bgcolor', 'yellow') + >>> application.preferences.get('acme.ui.bgcolor') + 'yellow' + +Hence, you use the Envisage preferences just like you would any other scoped +preferences. + +It also registers itself as the default preferences node used by the +|PreferencesHelper| class. Hence you don't need to provide a preferences node +explicitly to your helper:: + + >>> helper = SplashScreenPreferences() + >>> helper.bgcolor + 'blue' + >>> helper.width + 100 + >>> helper.ratio + 1.0 + >>> helper.visible + True + +The only extra thing that Envisage does for you is to provide an extension +point that allows you to contribute any number of '.ini' files that are +loaded into the default scope when the application is started. + +e.g. To contribute a preference file for my plugin I might use:: + + class MyPlugin(Plugin): + ... + + @contributes_to('envisage.preferences') + def get_preferences(self, application): + return ['pkgfile://mypackage:preferences.ini'] + +.. + # substitutions + +.. |PreferencesHelper| replace:: :class:`~apptools.preferences.preferences_helper.PreferencesHelper` +.. |ScopedPreferences| replace:: :class:`~apptools.preferences.scoped_preferences.ScopedPreferences` diff --git a/5.0/_sources/scripting/introduction.rst.txt b/5.0/_sources/scripting/introduction.rst.txt new file mode 100644 index 000000000..724a2767d --- /dev/null +++ b/5.0/_sources/scripting/introduction.rst.txt @@ -0,0 +1,198 @@ +.. _automatic-script-recording: + +Automatic script recording +=========================== + +This package provides a very handy and powerful Python script recording +facility. This can be used to: + + - record all actions performed on a traits based UI into a *human + readable*, Python script that should be able to recreate your UI + actions. + + - easily learn the scripting API of an application. + +This package is not just a toy framework and is powerful enough to +provide full script recording to the Mayavi_ application. Mayavi is a +powerful 3D visualization tool that is part of ETS_. + +.. _Mayavi: https://docs.enthought.com/mayavi/mayavi/ +.. _ETS: https://docs.enthought.com/ets/ + +.. _scripting-api: + + +The scripting API +------------------ + +The scripting API primarily allows you to record UI actions for objects +that have Traits. Technically the framework listens to all trait +changes so will work outside a UI. We do not document the full API +here, the best place to look for that is the +``apptools.scripting.recorder`` module which is reasonably well +documented. We provide a high level overview of the library. + +The quickest way to get started is to look at a small example. + + +.. _scripting-api-example: + +A tour by example +~~~~~~~~~~~~~~~~~~~ + +The following example is taken from the test suite. Consider a set of +simple objects organized in a hierarchy:: + + from traits.api import (HasTraits, Float, Instance, + Str, List, Bool, HasStrictTraits, Tuple, PrefixMap, Range, + Trait) + from apptools.scripting.api import (Recorder, recordable, + set_recorder) + + class Property(HasStrictTraits): + color = Tuple(Range(0.0, 1.0), Range(0.0, 1.0), Range(0.0, 1.0)) + opacity = Range(0.0, 1.0, 1.0) + representation = PrefixMap( + {"surface": 2, "wireframe": 1, "points": 0}, + default_value="surface" + ) + + class Toy(HasTraits): + color = Str + type = Str + # Note the use of the trait metadata to ignore this trait. + ignore = Bool(False, record=False) + + class Child(HasTraits): + name = Str('child') + age = Float(10.0) + # The recorder walks through sub-instances if they are marked + # with record=True + property = Instance(Property, (), record=True) + toy = Instance(Toy, record=True) + friends = List(Str) + + # The decorator records the method. + @recordable + def grow(self, x): + """Increase age by x years.""" + self.age += x + + class Parent(HasTraits): + children = List(Child, record=True) + recorder = Instance(Recorder, record=False) + +Using these simple classes we first create a simple object hierarchy as +follows:: + + p = Parent() + c = Child() + t = Toy() + c.toy = t + p.children.append(c) + +Given this hierarchy, we'd like to be able to record a script. To do +this we setup the recording infrastructure:: + + from mayavi.core.recorder import Recorder, set_recorder + # Create a recorder. + r = Recorder() + # Set the global recorder so the decorator works. + set_recorder(r) + r.register(p) + r.recording = True + +The key method here is the ``r.register(p)`` call above. It looks at +the traits of ``p`` and finds all traits and nested objects that specify +a ``record=True`` in their trait metadata (all methods starting and +ending with ``_`` are ignored). All sub-objects are in turn registered +with the recorder and so on. Callbacks are attached to traits changes +and these are wired up to produce readable and executable code. The +``set_recorder(r)`` call is also very important and sets the global +recorder so the framework listens to any functions that are decorated +with the ``recordable`` decorator. + +Now lets test this out like so:: + + # The following will be recorded. + c.name = 'Shiva' + c.property.representation = 'w' + c.property.opacity = 0.4 + c.grow(1) + +To see what's been recorded do this:: + + print(r.script) + +This prints:: + + child = parent.children[0] + child.name = 'Shiva' + child.property.representation = 'wireframe' + child.property.opacity = 0.40000000000000002 + child.grow(1) + +The recorder internally maintains a mapping between objects and unique +names for each object. It also stores the information about the +location of a particular object in the object hierarchy. For example, +the path to the ``Toy`` instance in the hierarchy above is +``parent.children[0].toy``. Since scripting with lists this way can be +tedious, the recorder first instantiates the ``child``:: + + child = parent.children[0] + +Subsequent lines use the ``child`` attribute. The recorder always tries +to instantiate the object referred to using its path information in this +manner. + +To record a function or method call one must simply decorate the +function/method with the ``recordable`` decorator. Nested recordable +functions are not recorded and trait changes are also not recorded if +done inside a recordable function. + +.. note:: + + 1. It is very important to note that the global recorder must be set + via the ``set_recorder`` method. The ``recordable`` decorator + relies on this being set to work. + + 2. The ``recordable`` decorator will work with plain Python classes + and with functions too. + +To stop recording do this:: + + r.unregister(p) + r.recording = False + +The ``r.unregister(p)`` reverses the ``r.register(p)`` call and +unregisters all nested objects as well. + + +.. _recorder-advanced-uses: + +Advanced use cases +~~~~~~~~~~~~~~~~~~~~ + +Here are a few advanced use cases. + + - The API also provides a ``RecorderWithUI`` class that provides a + simple user interface that prints the recorded script and allows the + user to save the script. + + - Sometimes it is not enough to just record trait changes, one may want + to pass an arbitrary string or command when recording is occurring. + To allow for this, if one defines a ``recorder`` trait on the object, + it is set to the current recorder. One can then use this recorder to + do whatever one wants. This is very convenient. + + - To ignore specific traits one must specify either a ``record=False`` + metadata to the trait definition or specify a list of strings to the + ``register`` method in the ``ignore`` keyword argument. + + - If you want to use a specific name for an object on the script you + can pass the ``script_id`` parameter to the register function. + + +For more details on the recorder itself we suggest reading the module +source code. It is fairly well documented and with the above background +should be enough to get you going. diff --git a/5.0/_sources/selection/selection.rst.txt b/5.0/_sources/selection/selection.rst.txt new file mode 100644 index 000000000..a57d9179e --- /dev/null +++ b/5.0/_sources/selection/selection.rst.txt @@ -0,0 +1,144 @@ +.. _selection_service: + +The selection service +===================== + +It is quite common in GUI applications to have a UI element displaying a +collection of items that a user can select ("selection providers"), while +other parts of the application must react to changes in the selection +("selection listeners"). + +Ideally, the listeners would not have a direct dependency on the UI object. +This is especially important in extensible `envisage`_ applications, where +a plugin might need to react to a selection change, but we do not want to +expose the internal organization of the application to external developers. + +This package defines a selection service that manages the communication +between providers and listener. + + +The :class:`~.SelectionService` object +-------------------------------------- + +The :class:`~.SelectionService` object is the central manager that handles +the communication between selection providers and listener. + +:ref:`Selection providers ` are components that wish to +publish information about their current selection for public consumption. +They register to a selection +service instance when they first have a selection available (e.g., when the +UI showing a list of selectable items is initialized), and un-register as soon +as the selection is not available anymore (e.g., the UI is destroyed when the +windows is closed). + +:ref:`Selection listeners ` can query the selection +service to get the current selection published by a provider, using the +provider unique ID. + +The service acts as a broker between providers and listeners, making sure that +they are notified when the +:attr:`~apptools.selection.i_selection_provider.ISelectionProvider.selection` +event is fired. + +.. _selection_providers: + +Selection providers +------------------- + +Any object can become a selection provider by implementing the +:class:`~apptools.selection.i_selection_provider.ISelectionProvider` +interface, and registering to the selection service. + +Selection providers must provide a unique ID +:attr:`~apptools.selection.i_selection_provider.ISelectionProvider.provider_id`, +which is used by listeners to request its current selection. + +Whenever its selection changes, providers fire a +:attr:`~apptools.selection.i_selection_provider.ISelectionProvider.selection` +event. The content of the event is an instance implementing +:class:`~.ISelection` that contains information about the selected items. +For example, a :class:`~.ListSelection` object contains a list of selected +items, and their indices. + +Selection providers can also be queried directly about their current selection +using the +:attr:`~apptools.selection.i_selection_provider.ISelectionProvider.get_selection` +method, and can be requested to change their selection to a new one with the +:attr:`~apptools.selection.i_selection_provider.ISelectionProvider.set_selection` +method. + +Registration +~~~~~~~~~~~~ + +Selection providers publish their selection by registering to the selection +service using the +:attr:`~apptools.selection.selection_service.SelectionService.add_selection_provider` +method. When the selection is no longer available, selection providers +should un-register through +:attr:`~apptools.selection.selection_service.SelectionService.remove_selection_provider`. + +Typically, selection providers are UI objects showing a list or tree of items, +they register as soon as the UI component is initialized, and un-register +when the UI component disappears (e.g., because their window has been closed). +In more complex applications, the registration could be done by a controller +object instead. + +.. _selection_listeners: + +Selection listeners +------------------- + +Selection listeners request information regarding the current selection +of a selection provider given their provider ID. The :class:`~.SelectionService` +supports two distinct use cases: + + 1) Passively listening to selection changes: listener connect to a specific + provider and are notified when the provider's selection changes. + + 2) Actively querying a provider for its current selection: the selection + service can be used to query a provider using its unique ID. + +Passive listening +~~~~~~~~~~~~~~~~~ + +Listeners connect to the selection events for a given provider using the +:attr:`~apptools.selection.selection_service.SelectionService.connect_selection_listener` +method. They need to provide the unique ID of the provider, and a function +(or callable) that is called to send the event. This callback function takes +one argument, an implementation of the :class:`~.ISelection` that represents +the selection. + +It is possible for a listener to connect to a provider ID before it is +registered. As soon as the provider is registered, the listener will receive +a notification containing the provider's initial selection. + +To disconnect a listener use the methods +:attr:`~apptools.selection.selection_service.SelectionService.disconnect_selection_listener`. + +Active querying +~~~~~~~~~~~~~~~ + +In other instances, an element of the application only needs the current +selection at a specific time. For example, a toolbar button could open dialog +representing a user action based on what is currently selected in the active +editor. + +The +:attr:`~apptools.selection.selection_service.SelectionService.get_selection` +method calls the corresponding method on the provider with the given ID and +returns an :class:`~.ISelection` instance. + +Setting a selection +~~~~~~~~~~~~~~~~~~~ + +Finally, it is possible to request a provider to set its selection to a given +set of objects with +:attr:`~apptools.selection.selection_service.SelectionService.set_selection`. +The main use case for this method is multiple views of the same list of +objects, which need to keep their selection synchronized. + +If the items specified in the arguments are not available in the provider, +a :class:`~apptools.selection.errors.ProviderNotRegisteredError` is raised, +unless the optional keyword argument :attr:`ignore_missing` is set to ``True``. + +.. _envisage: http://docs.enthought.com/envisage/ diff --git a/5.0/_sources/undo/Introduction.rst.txt b/5.0/_sources/undo/Introduction.rst.txt new file mode 100644 index 000000000..89dee28d6 --- /dev/null +++ b/5.0/_sources/undo/Introduction.rst.txt @@ -0,0 +1,275 @@ +Undo Framework +============== + +The Undo Framework is a component of the Enthought Tool Suite that provides +developers with an API that implements the standard pattern for do/undo/redo +commands. + +The framework is completely configurable. Alternate implementations of all +major components can be provided if necessary. + + +Framework Concepts +------------------ + +The following are the concepts supported by the framework. + +- Command + + A command is an application defined operation that can be done (i.e. + executed), undone (i.e. reverted) and redone (i.e. repeated). + + A command operates on some data and maintains sufficient state to allow it to + revert or repeat a change to the data. + + Commands may be merged so that potentially long sequences of similar + commands (e.g. to add a character to some text) can be collapsed into a + single command (e.g. to add a word to some text). + +- Macro + + A macro is a sequence of commands that is treated as a single command when + being undone or redone. + +- Command Stack + + A command is done by pushing it onto a command stack. The last command can + be undone and redone by calling appropriate command stack methods. It is + also possible to move the stack's position to any point and the command stack + will ensure that commands are undone or redone as required. + + A command stack maintains a *clean* state which is updated as commands are + done and undone. It may be explicitly set, for example when the data being + manipulated by the commands is saved to disk. + + Canned PyFace actions are provided as wrappers around command stack methods + to implement common menu items. + +- Undo Manager + + An undo manager is responsible for one or more command stacks and maintains + a reference to the currently active stack. It provides convenience undo and + redo methods that operate on the currently active stack. + + An undo manager ensures that each command execution is allocated a unique + sequence number, irrespective of which command stack it is pushed to. Using + this it is possible to synchronise multiple command stacks and restore them + to a particular point in time. + + An undo manager will generate an event whenever the clean state of the active + stack changes. This can be used to maintain some sort of GUI status + indicator to tell the user that their data has been modified since it was + last saved. + +Typically an application will have one undo manager and one undo stack for +each data type that can be edited. However this is not a requirement: how the +command stack's in particular are organised and linked (with the user +manager's sequence number) can need careful thought so as not to confuse the +user - particularly in a plugin based application that may have many editors. + +To support this typical usage the PyFace ``Workbench`` class has an +``undo_manager`` trait and the PyFace ``Editor`` class has a ``command_stack`` +trait. Both are lazy loaded so can be completely ignored if they are not used. + + +API Overview +------------ + +This section gives a brief overview of the various classes implemented in the +framework. The complete API_ documentation is available as endo generated +HTML. + +The example_ application demonstrates all the major features of the framework. + + +UndoManager +........... + +The ``UndoManager`` class is the default implementation of the ``IUndoManager`` +interface. + +``active_stack`` + This trait is a reference to the currently active command stack and may be + None. Typically it is set when some sort of editor becomes active. + +``active_stack_clean`` + This boolean trait reflects the clean state of the currently active + command stack. It is intended to support a "document modified" indicator + in the GUI. It is maintained by the undo manager. + +``stack_updated`` + This event is fired when the index of a command stack is changed. A + reference to the stack is passed as an argument to the event and may not + be the currently active stack. + +``undo_name`` + This Str trait is the name of the command that can be undone, and will + be empty if there is no such command. It is maintained by the undo + manager. + +``redo_name`` + This Str trait is the name of the command that can be redone, and will + be empty if there is no such command. It is maintained by the undo + manager. + +``sequence_nr`` + This integer trait is the sequence number of the next command to be + executed. It is incremented immediately before a command's ``do()`` + method is called. A particular sequence number identifies the state of + all command stacks handled by the undo manager and allows those stacks to + be set to the point they were at at a particular point in time. In other + words, the sequence number allows otherwise independent command stacks to + be synchronised. + +``undo()`` + This method calls the ``undo()`` method of the last command on the active + command stack. + +``redo()`` + This method calls the ``redo()`` method of the last undone command on the + active command stack. + + +CommandStack +............ + +The ``CommandStack`` class is the default implementation of the +``ICommandStack`` interface. + +``clean`` + This boolean traits reflects the clean state of the command stack. Its + value changes as commands are executed, undone and redone. It may also be + explicitly set to mark the current stack position as being clean (when + data is saved to disk for example). + +``undo_name`` + This Str trait is the name of the command that can be undone, and will + be empty if there is no such command. It is maintained by the command + stack. + +``redo_name`` + This Str trait is the name of the command that can be redone, and will + be empty if there is no such command. It is maintained by the command + stack. + +``undo_manager`` + This trait is a reference to the undo manager that manages the command + stack. + +``push(command)`` + This method executes the given command by calling its ``do()`` method. + Any value returned by ``do()`` is returned by ``push()``. If the command + couldn't be merged with the previous one then it is saved on the command + stack. + +``undo(sequence_nr=0)`` + This method undoes the last command. If a sequence number is given then + all commands are undone up to an including the sequence number. + +``redo(sequence_nr=0)`` + This method redoes the last command and returns any result. If a sequence + number is given then all commands are redone up to an including the + sequence number and any result of the last of these is returned. + +``clear()`` + This method clears the command stack, without undoing or redoing any + commands, and leaves the stack in a clean state. It is typically used + when all changes to the data have been abandoned. + +``begin_macro(name)`` + This method begins a macro by creating an empty command with the given + name. The commands passed to all subsequent calls to ``push()`` will be + contained in the macro until the next call to ``end_macro()``. Macros may + be nested. The command stack is disabled (ie. nothing can be undone or + redone) while a macro is being created (ie. while there is an outstanding + ``end_macro()`` call). + +``end_macro()`` + This method ends the current macro. + + +ICommand +........ + +The ``ICommand`` interface defines the interface that must be implemented by +any undoable/redoable command. + +``data`` + This optional trait is a reference to the data object that the command + operates on. It is not used by the framework itself. + +``name`` + This Str trait is the name of the command as it will appear in any GUI + element (e.g. in the text of an undo and redo menu entry). It may include + ``&`` to indicate a keyboard shortcut which will be automatically removed + whenever it is inappropriate. + +``__init__(*args)`` + If the command takes arguments then the command must ensure that deep + copies should be made if appropriate. + +``do()`` + This method is called by a command stack to execute the command and to + return any result. The command must save any state necessary for the + ``undo()`` and ``redo()`` methods to work. It is guaranteed that this + will only ever be called once and that it will be called before any call + to ``undo()`` or ``redo()``. + +``undo()`` + This method is called by a command stack to undo the command. + +``redo()`` + This method is called by a command stack to redo the command and to return + any result. + +``merge(other)`` + This method is called by the command stack to try and merge the ``other`` + command with this one. True should be returned if the commands were + merged. If the commands are merged then ``other`` will not be placed on + the command stack. A subsequent undo or redo of this modified command + must have the same effect as the two original commands. + + +AbstractCommand +............... + +``AbstractCommand`` is an abstract base class that implements the ``ICommand`` +interface. It provides a default implementation of the ``merge()`` method. + + +CommandAction +............. + +The ``CommandAction`` class is a sub-class of the PyFace ``Action`` class that +is used to wrap commands. + +``command`` + This callable trait must be set to a factory that will return an object + that implements ``ICommand``. It will be called when the action is invoked + and the object created pushed onto the command stack. + +``command_stack`` + This instance trait must be set to the command stack that commands invoked + by the action are pushed to. + +``data`` + This optional trait is a reference to the data object that will be passed + to the ``command`` factory when it is called. + + +UndoAction +.......... + +The ``UndoAction`` class is a canned PyFace action that undoes the last +command of the active command stack. + + +RedoAction +.......... + +The ``RedoAction`` class is a canned PyFace action that redoes the last +command undone of the active command stack. + + +.. _API: api/index.html +.. _example: https://svn.enthought.com/enthought/browser/AppTools/trunk/examples/undo/ diff --git a/5.0/_static/basic.css b/5.0/_static/basic.css new file mode 100644 index 000000000..b04360d69 --- /dev/null +++ b/5.0/_static/basic.css @@ -0,0 +1,768 @@ +/* + * basic.css + * ~~~~~~~~~ + * + * Sphinx stylesheet -- basic theme. + * + * :copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +/* -- main layout ----------------------------------------------------------- */ + +div.clearer { + clear: both; +} + +/* -- relbar ---------------------------------------------------------------- */ + +div.related { + width: 100%; + font-size: 90%; +} + +div.related h3 { + display: none; +} + +div.related ul { + margin: 0; + padding: 0 0 0 10px; + list-style: none; +} + +div.related li { + display: inline; +} + +div.related li.right { + float: right; + margin-right: 5px; +} + +/* -- sidebar --------------------------------------------------------------- */ + +div.sphinxsidebarwrapper { + padding: 10px 5px 0 10px; +} + +div.sphinxsidebar { + float: left; + width: 230px; + margin-left: -100%; + font-size: 90%; + word-wrap: break-word; + overflow-wrap : break-word; +} + +div.sphinxsidebar ul { + list-style: none; +} + +div.sphinxsidebar ul ul, +div.sphinxsidebar ul.want-points { + margin-left: 20px; + list-style: square; +} + +div.sphinxsidebar ul ul { + margin-top: 0; + margin-bottom: 0; +} + +div.sphinxsidebar form { + margin-top: 10px; +} + +div.sphinxsidebar input { + border: 1px solid #98dbcc; + font-family: sans-serif; + font-size: 1em; +} + +div.sphinxsidebar #searchbox form.search { + overflow: hidden; +} + +div.sphinxsidebar #searchbox input[type="text"] { + float: left; + width: 80%; + padding: 0.25em; + box-sizing: border-box; +} + +div.sphinxsidebar #searchbox input[type="submit"] { + float: left; + width: 20%; + border-left: none; + padding: 0.25em; + box-sizing: border-box; +} + + +img { + border: 0; + max-width: 100%; +} + +/* -- search page ----------------------------------------------------------- */ + +ul.search { + margin: 10px 0 0 20px; + padding: 0; +} + +ul.search li { + padding: 5px 0 5px 20px; + background-image: url(file.png); + background-repeat: no-repeat; + background-position: 0 7px; +} + +ul.search li a { + font-weight: bold; +} + +ul.search li div.context { + color: #888; + margin: 2px 0 0 30px; + text-align: left; +} + +ul.keywordmatches li.goodmatch a { + font-weight: bold; +} + +/* -- index page ------------------------------------------------------------ */ + +table.contentstable { + width: 90%; + margin-left: auto; + margin-right: auto; +} + +table.contentstable p.biglink { + line-height: 150%; +} + +a.biglink { + font-size: 1.3em; +} + +span.linkdescr { + font-style: italic; + padding-top: 5px; + font-size: 90%; +} + +/* -- general index --------------------------------------------------------- */ + +table.indextable { + width: 100%; +} + +table.indextable td { + text-align: left; + vertical-align: top; +} + +table.indextable ul { + margin-top: 0; + margin-bottom: 0; + list-style-type: none; +} + +table.indextable > tbody > tr > td > ul { + padding-left: 0em; +} + +table.indextable tr.pcap { + height: 10px; +} + +table.indextable tr.cap { + margin-top: 10px; + background-color: #f2f2f2; +} + +img.toggler { + margin-right: 3px; + margin-top: 3px; + cursor: pointer; +} + +div.modindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +div.genindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +/* -- domain module index --------------------------------------------------- */ + +table.modindextable td { + padding: 2px; + border-collapse: collapse; +} + +/* -- general body styles --------------------------------------------------- */ + +div.body { + min-width: 450px; + max-width: 800px; +} + +div.body p, div.body dd, div.body li, div.body blockquote { + -moz-hyphens: auto; + -ms-hyphens: auto; + -webkit-hyphens: auto; + hyphens: auto; +} + +a.headerlink { + visibility: hidden; +} + +a.brackets:before, +span.brackets > a:before{ + content: "["; +} + +a.brackets:after, +span.brackets > a:after { + content: "]"; +} + +h1:hover > a.headerlink, +h2:hover > a.headerlink, +h3:hover > a.headerlink, +h4:hover > a.headerlink, +h5:hover > a.headerlink, +h6:hover > a.headerlink, +dt:hover > a.headerlink, +caption:hover > a.headerlink, +p.caption:hover > a.headerlink, +div.code-block-caption:hover > a.headerlink { + visibility: visible; +} + +div.body p.caption { + text-align: inherit; +} + +div.body td { + text-align: left; +} + +.first { + margin-top: 0 !important; +} + +p.rubric { + margin-top: 30px; + font-weight: bold; +} + +img.align-left, .figure.align-left, object.align-left { + clear: left; + float: left; + margin-right: 1em; +} + +img.align-right, .figure.align-right, object.align-right { + clear: right; + float: right; + margin-left: 1em; +} + +img.align-center, .figure.align-center, object.align-center { + display: block; + margin-left: auto; + margin-right: auto; +} + +img.align-default, .figure.align-default { + display: block; + margin-left: auto; + margin-right: auto; +} + +.align-left { + text-align: left; +} + +.align-center { + text-align: center; +} + +.align-default { + text-align: center; +} + +.align-right { + text-align: right; +} + +/* -- sidebars -------------------------------------------------------------- */ + +div.sidebar { + margin: 0 0 0.5em 1em; + border: 1px solid #ddb; + padding: 7px 7px 0 7px; + background-color: #ffe; + width: 40%; + float: right; +} + +p.sidebar-title { + font-weight: bold; +} + +/* -- topics ---------------------------------------------------------------- */ + +div.topic { + border: 1px solid #ccc; + padding: 7px 7px 0 7px; + margin: 10px 0 10px 0; +} + +p.topic-title { + font-size: 1.1em; + font-weight: bold; + margin-top: 10px; +} + +/* -- admonitions ----------------------------------------------------------- */ + +div.admonition { + margin-top: 10px; + margin-bottom: 10px; + padding: 7px; +} + +div.admonition dt { + font-weight: bold; +} + +div.admonition dl { + margin-bottom: 0; +} + +p.admonition-title { + margin: 0px 10px 5px 0px; + font-weight: bold; +} + +div.body p.centered { + text-align: center; + margin-top: 25px; +} + +/* -- tables ---------------------------------------------------------------- */ + +table.docutils { + border: 0; + border-collapse: collapse; +} + +table.align-center { + margin-left: auto; + margin-right: auto; +} + +table.align-default { + margin-left: auto; + margin-right: auto; +} + +table caption span.caption-number { + font-style: italic; +} + +table caption span.caption-text { +} + +table.docutils td, table.docutils th { + padding: 1px 8px 1px 5px; + border-top: 0; + border-left: 0; + border-right: 0; + border-bottom: 1px solid #aaa; +} + +table.footnote td, table.footnote th { + border: 0 !important; +} + +th { + text-align: left; + padding-right: 5px; +} + +table.citation { + border-left: solid 1px gray; + margin-left: 1px; +} + +table.citation td { + border-bottom: none; +} + +th > p:first-child, +td > p:first-child { + margin-top: 0px; +} + +th > p:last-child, +td > p:last-child { + margin-bottom: 0px; +} + +/* -- figures --------------------------------------------------------------- */ + +div.figure { + margin: 0.5em; + padding: 0.5em; +} + +div.figure p.caption { + padding: 0.3em; +} + +div.figure p.caption span.caption-number { + font-style: italic; +} + +div.figure p.caption span.caption-text { +} + +/* -- field list styles ----------------------------------------------------- */ + +table.field-list td, table.field-list th { + border: 0 !important; +} + +.field-list ul { + margin: 0; + padding-left: 1em; +} + +.field-list p { + margin: 0; +} + +.field-name { + -moz-hyphens: manual; + -ms-hyphens: manual; + -webkit-hyphens: manual; + hyphens: manual; +} + +/* -- hlist styles ---------------------------------------------------------- */ + +table.hlist td { + vertical-align: top; +} + + +/* -- other body styles ----------------------------------------------------- */ + +ol.arabic { + list-style: decimal; +} + +ol.loweralpha { + list-style: lower-alpha; +} + +ol.upperalpha { + list-style: upper-alpha; +} + +ol.lowerroman { + list-style: lower-roman; +} + +ol.upperroman { + list-style: upper-roman; +} + +li > p:first-child { + margin-top: 0px; +} + +li > p:last-child { + margin-bottom: 0px; +} + +dl.footnote > dt, +dl.citation > dt { + float: left; +} + +dl.footnote > dd, +dl.citation > dd { + margin-bottom: 0em; +} + +dl.footnote > dd:after, +dl.citation > dd:after { + content: ""; + clear: both; +} + +dl.field-list { + display: grid; + grid-template-columns: fit-content(30%) auto; +} + +dl.field-list > dt { + font-weight: bold; + word-break: break-word; + padding-left: 0.5em; + padding-right: 5px; +} + +dl.field-list > dt:after { + content: ":"; +} + +dl.field-list > dd { + padding-left: 0.5em; + margin-top: 0em; + margin-left: 0em; + margin-bottom: 0em; +} + +dl { + margin-bottom: 15px; +} + +dd > p:first-child { + margin-top: 0px; +} + +dd ul, dd table { + margin-bottom: 10px; +} + +dd { + margin-top: 3px; + margin-bottom: 10px; + margin-left: 30px; +} + +dt:target, span.highlighted { + background-color: #fbe54e; +} + +rect.highlighted { + fill: #fbe54e; +} + +dl.glossary dt { + font-weight: bold; + font-size: 1.1em; +} + +.optional { + font-size: 1.3em; +} + +.sig-paren { + font-size: larger; +} + +.versionmodified { + font-style: italic; +} + +.system-message { + background-color: #fda; + padding: 5px; + border: 3px solid red; +} + +.footnote:target { + background-color: #ffa; +} + +.line-block { + display: block; + margin-top: 1em; + margin-bottom: 1em; +} + +.line-block .line-block { + margin-top: 0; + margin-bottom: 0; + margin-left: 1.5em; +} + +.guilabel, .menuselection { + font-family: sans-serif; +} + +.accelerator { + text-decoration: underline; +} + +.classifier { + font-style: oblique; +} + +.classifier:before { + font-style: normal; + margin: 0.5em; + content: ":"; +} + +abbr, acronym { + border-bottom: dotted 1px; + cursor: help; +} + +/* -- code displays --------------------------------------------------------- */ + +pre { + overflow: auto; + overflow-y: hidden; /* fixes display issues on Chrome browsers */ +} + +span.pre { + -moz-hyphens: none; + -ms-hyphens: none; + -webkit-hyphens: none; + hyphens: none; +} + +td.linenos pre { + padding: 5px 0px; + border: 0; + background-color: transparent; + color: #aaa; +} + +table.highlighttable { + margin-left: 0.5em; +} + +table.highlighttable td { + padding: 0 0.5em 0 0.5em; +} + +div.code-block-caption { + padding: 2px 5px; + font-size: small; +} + +div.code-block-caption code { + background-color: transparent; +} + +div.code-block-caption + div > div.highlight > pre { + margin-top: 0; +} + +div.doctest > div.highlight span.gp { /* gp: Generic.Prompt */ + user-select: none; +} + +div.code-block-caption span.caption-number { + padding: 0.1em 0.3em; + font-style: italic; +} + +div.code-block-caption span.caption-text { +} + +div.literal-block-wrapper { + padding: 1em 1em 0; +} + +div.literal-block-wrapper div.highlight { + margin: 0; +} + +code.descname { + background-color: transparent; + font-weight: bold; + font-size: 1.2em; +} + +code.descclassname { + background-color: transparent; +} + +code.xref, a code { + background-color: transparent; + font-weight: bold; +} + +h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { + background-color: transparent; +} + +.viewcode-link { + float: right; +} + +.viewcode-back { + float: right; + font-family: sans-serif; +} + +div.viewcode-block:target { + margin: -1px -10px; + padding: 0 10px; +} + +/* -- math display ---------------------------------------------------------- */ + +img.math { + vertical-align: middle; +} + +div.body div.math p { + text-align: center; +} + +span.eqno { + float: right; +} + +span.eqno a.headerlink { + position: relative; + left: 0px; + z-index: 1; +} + +div.math:hover a.headerlink { + visibility: visible; +} + +/* -- printout stylesheet --------------------------------------------------- */ + +@media print { + div.document, + div.documentwrapper, + div.bodywrapper { + margin: 0 !important; + width: 100%; + } + + div.sphinxsidebar, + div.related, + div.footer, + #top-link { + display: none; + } +} \ No newline at end of file diff --git a/5.0/_static/css/pygments.css b/5.0/_static/css/pygments.css new file mode 100644 index 000000000..1c9c56a51 --- /dev/null +++ b/5.0/_static/css/pygments.css @@ -0,0 +1,87 @@ +/* Styling for the source code listings: (mostly from pygments)*/ + +.highlight pre{ + overflow: auto; + padding: 5px; + background-color: #ffffff; + color: #333333; + border: 1px solid #ac9; + border-left: none; + border-right: none; +} + +/* Styling for pre elements: from http://perishablepress.com/press/2009/11/09/perfect-pre-tags/ */ +/* no vertical scrollbars for IE 6 */ +* html pre { + padding-bottom:25px; + overflow-y:hidden; + overflow:visible; + overflow-x:auto +} +/* no vertical scrollbars for IE 7 */ +*:first-child+html pre { + padding-bottom:25px; + overflow-y:hidden; + overflow:visible; + overflow-x:auto +} + +div#spc-section-body td.linenos pre { + padding: 5px 0px; + border: 0; + background-color: transparent; + color: #aaa; +} +.highlight .hll { background-color: #ffffcc } +.highlight { background: #ffffff; } +.highlight .c { color: #008000 } /* Comment */ +.highlight .k { color: #000080; font-weight: bold } /* Keyword */ +.highlight .n { color: #000000 } /* Name */ +.highlight .o { color: #000000 } /* Operator */ +.highlight .cm { color: #008000 } /* Comment.Multiline */ +.highlight .cp { color: #008000 } /* Comment.Preproc */ +.highlight .c1 { color: #008000 } /* Comment.Single */ +.highlight .cs { color: #008000 } /* Comment.Special */ +.highlight .kc { color: #000080; font-weight: bold } /* Keyword.Constant */ +.highlight .kd { color: #000080; font-weight: bold } /* Keyword.Declaration */ +.highlight .kn { color: #000080; font-weight: bold } /* Keyword.Namespace */ +.highlight .kp { color: #000080; font-weight: bold } /* Keyword.Pseudo */ +.highlight .kr { color: #000080; font-weight: bold } /* Keyword.Reserved */ +.highlight .kt { color: #000080; font-weight: bold } /* Keyword.Type */ +.highlight .m { color: #008080 } /* Literal.Number */ +.highlight .s { color: #800080 } /* Literal.String */ +.highlight .na { color: #000000 } /* Name.Attribute */ +.highlight .nb { color: #407090 } /* Name.Builtin */ +.highlight .nc { color: #0000F0; font-weight: bold } /* Name.Class */ +.highlight .no { color: #000000 } /* Name.Constant */ +.highlight .nd { color: #000000 } /* Name.Decorator */ +.highlight .ni { color: #000000 } /* Name.Entity */ +.highlight .ne { color: #000000 } /* Name.Exception */ +.highlight .nf { color: #008080; font-weight: bold } /* Name.Function */ +.highlight .nl { color: #000000 } /* Name.Label */ +.highlight .nn { color: #000000 } /* Name.Namespace */ +.highlight .nx { color: #000000 } /* Name.Other */ +.highlight .py { color: #000000 } /* Name.Property */ +.highlight .nt { color: #000000 } /* Name.Tag */ +.highlight .nv { color: #000000 } /* Name.Variable */ +.highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ +.highlight .mf { color: #008080 } /* Literal.Number.Float */ +.highlight .mh { color: #008080 } /* Literal.Number.Hex */ +.highlight .mi { color: #008080 } /* Literal.Number.Integer */ +.highlight .mo { color: #008080 } /* Literal.Number.Oct */ +.highlight .sb { color: #800080 } /* Literal.String.Backtick */ +.highlight .sc { color: #800080 } /* Literal.String.Char */ +.highlight .sd { color: #800000 } /* Literal.String.Doc */ +.highlight .s2 { color: #800080 } /* Literal.String.Double */ +.highlight .se { color: #800080 } /* Literal.String.Escape */ +.highlight .sh { color: #800080 } /* Literal.String.Heredoc */ +.highlight .si { color: #800080 } /* Literal.String.Interpol */ +.highlight .sx { color: #800080 } /* Literal.String.Other */ +.highlight .sr { color: #800080 } /* Literal.String.Regex */ +.highlight .s1 { color: #800080 } /* Literal.String.Single */ +.highlight .ss { color: #800080 } /* Literal.String.Symbol */ +.highlight .bp { color: #407090 } /* Name.Builtin.Pseudo */ +.highlight .vc { color: #000000 } /* Name.Variable.Class */ +.highlight .vg { color: #000000 } /* Name.Variable.Global */ +.highlight .vi { color: #000000 } /* Name.Variable.Instance */ +.highlight .il { color: #008080 } /* Literal.Number.Integer.Long */ diff --git a/5.0/_static/css/spc-bootstrap.css b/5.0/_static/css/spc-bootstrap.css new file mode 100644 index 000000000..9895e61fe --- /dev/null +++ b/5.0/_static/css/spc-bootstrap.css @@ -0,0 +1,6288 @@ +/*! + * Bootstrap v2.3.1 + * + * Copyright 2012 Twitter, Inc + * Licensed under the Apache License v2.0 + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Designed and built with all the love in the world @twitter by @mdo and @fat. + */ +.clearfix { + *zoom: 1; +} +.clearfix:before, +.clearfix:after { + display: table; + content: ""; + line-height: 0; +} +.clearfix:after { + clear: both; +} +.hide-text { + font: 0/0 a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0; +} +.input-block-level { + display: block; + width: 100%; + min-height: 31px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +nav, +section { + display: block; +} +audio, +canvas, +video { + display: inline-block; + *display: inline; + *zoom: 1; +} +audio:not([controls]) { + display: none; +} +html { + font-size: 100%; + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; +} +a:focus { + outline: thin dotted #333; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +a:hover, +a:active { + outline: 0; +} +sub, +sup { + position: relative; + font-size: 75%; + line-height: 0; + vertical-align: baseline; +} +sup { + top: -0.5em; +} +sub { + bottom: -0.25em; +} +img { + /* Responsive images (ensure images don't scale beyond their parents) */ + max-width: 100%; + /* Part 1: Set a maxium relative to the parent */ + width: auto\9; + /* IE7-8 need help adjusting responsive images */ + height: auto; + /* Part 2: Scale the height according to the width, otherwise you get stretching */ + vertical-align: middle; + border: 0; + -ms-interpolation-mode: bicubic; +} +#map_canvas img, +.google-maps img { + max-width: none; +} +button, +input, +select, +textarea { + margin: 0; + font-size: 100%; + vertical-align: middle; +} +button, +input { + *overflow: visible; + line-height: normal; +} +button::-moz-focus-inner, +input::-moz-focus-inner { + padding: 0; + border: 0; +} +button, +html input[type="button"], +input[type="reset"], +input[type="submit"] { + -webkit-appearance: button; + cursor: pointer; +} +label, +select, +button, +input[type="button"], +input[type="reset"], +input[type="submit"], +input[type="radio"], +input[type="checkbox"] { + cursor: pointer; +} +input[type="search"] { + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; + -webkit-appearance: textfield; +} +input[type="search"]::-webkit-search-decoration, +input[type="search"]::-webkit-search-cancel-button { + -webkit-appearance: none; +} +textarea { + overflow: auto; + vertical-align: top; +} +@media print { + * { + text-shadow: none !important; + color: #000 !important; + background: transparent !important; + box-shadow: none !important; + } + a, + a:visited { + text-decoration: underline; + } + a[href]:after { + content: " (" attr(href) ")"; + } + abbr[title]:after { + content: " (" attr(title) ")"; + } + .ir a:after, + a[href^="javascript:"]:after, + a[href^="#"]:after { + content: ""; + } + pre, + blockquote { + border: 1px solid #999; + page-break-inside: avoid; + } + thead { + display: table-header-group; + } + tr, + img { + page-break-inside: avoid; + } + img { + max-width: 100% !important; + } + @page { + margin: 0.5cm; + } + p, + h2, + h3 { + orphans: 3; + widows: 3; + } + h2, + h3 { + page-break-after: avoid; + } +} +body { + margin: 0; + font-family: 'Source Sans Pro', sans-serif; + font-size: 16px; + line-height: 21px; + color: #333333; + background-color: #ffffff; +} +a { + color: #0088cc; + text-decoration: none; +} +a:hover, +a:focus { + color: #005580; + text-decoration: underline; +} +.img-rounded { + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; +} +.img-polaroid { + padding: 4px; + background-color: #fff; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, 0.2); + -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); + -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); +} +.img-circle { + -webkit-border-radius: 500px; + -moz-border-radius: 500px; + border-radius: 500px; +} +.row { + margin-left: -20px; + *zoom: 1; +} +.row:before, +.row:after { + display: table; + content: ""; + line-height: 0; +} +.row:after { + clear: both; +} +[class*="span"] { + float: left; + min-height: 1px; + margin-left: 20px; +} +.container, +.navbar-static-top .container, +.navbar-fixed-top .container, +.navbar-fixed-bottom .container { + width: 940px; +} +.span12 { + width: 940px; +} +.span11 { + width: 860px; +} +.span10 { + width: 780px; +} +.span9 { + width: 700px; +} +.span8 { + width: 620px; +} +.span7 { + width: 540px; +} +.span6 { + width: 460px; +} +.span5 { + width: 380px; +} +.span4 { + width: 300px; +} +.span3 { + width: 220px; +} +.span2 { + width: 140px; +} +.span1 { + width: 60px; +} +.offset12 { + margin-left: 980px; +} +.offset11 { + margin-left: 900px; +} +.offset10 { + margin-left: 820px; +} +.offset9 { + margin-left: 740px; +} +.offset8 { + margin-left: 660px; +} +.offset7 { + margin-left: 580px; +} +.offset6 { + margin-left: 500px; +} +.offset5 { + margin-left: 420px; +} +.offset4 { + margin-left: 340px; +} +.offset3 { + margin-left: 260px; +} +.offset2 { + margin-left: 180px; +} +.offset1 { + margin-left: 100px; +} +.row-fluid { + width: 100%; + *zoom: 1; +} +.row-fluid:before, +.row-fluid:after { + display: table; + content: ""; + line-height: 0; +} +.row-fluid:after { + clear: both; +} +.row-fluid [class*="span"] { + display: block; + width: 100%; + min-height: 31px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + float: left; + margin-left: 2.12765957%; + *margin-left: 2.07446809%; +} +.row-fluid [class*="span"]:first-child { + margin-left: 0; +} +.row-fluid .controls-row [class*="span"] + [class*="span"] { + margin-left: 2.12765957%; +} +.row-fluid .span12 { + width: 100%; + *width: 99.94680851%; +} +.row-fluid .span11 { + width: 91.4893617%; + *width: 91.43617021%; +} +.row-fluid .span10 { + width: 82.9787234%; + *width: 82.92553191%; +} +.row-fluid .span9 { + width: 74.46808511%; + *width: 74.41489362%; +} +.row-fluid .span8 { + width: 65.95744681%; + *width: 65.90425532%; +} +.row-fluid .span7 { + width: 57.44680851%; + *width: 57.39361702%; +} +.row-fluid .span6 { + width: 48.93617021%; + *width: 48.88297872%; +} +.row-fluid .span5 { + width: 40.42553191%; + *width: 40.37234043%; +} +.row-fluid .span4 { + width: 31.91489362%; + *width: 31.86170213%; +} +.row-fluid .span3 { + width: 23.40425532%; + *width: 23.35106383%; +} +.row-fluid .span2 { + width: 14.89361702%; + *width: 14.84042553%; +} +.row-fluid .span1 { + width: 6.38297872%; + *width: 6.32978723%; +} +.row-fluid .offset12 { + margin-left: 104.25531915%; + *margin-left: 104.14893617%; +} +.row-fluid .offset12:first-child { + margin-left: 102.12765957%; + *margin-left: 102.0212766%; +} +.row-fluid .offset11 { + margin-left: 95.74468085%; + *margin-left: 95.63829787%; +} +.row-fluid .offset11:first-child { + margin-left: 93.61702128%; + *margin-left: 93.5106383%; +} +.row-fluid .offset10 { + margin-left: 87.23404255%; + *margin-left: 87.12765957%; +} +.row-fluid .offset10:first-child { + margin-left: 85.10638298%; + *margin-left: 85%; +} +.row-fluid .offset9 { + margin-left: 78.72340426%; + *margin-left: 78.61702128%; +} +.row-fluid .offset9:first-child { + margin-left: 76.59574468%; + *margin-left: 76.4893617%; +} +.row-fluid .offset8 { + margin-left: 70.21276596%; + *margin-left: 70.10638298%; +} +.row-fluid .offset8:first-child { + margin-left: 68.08510638%; + *margin-left: 67.9787234%; +} +.row-fluid .offset7 { + margin-left: 61.70212766%; + *margin-left: 61.59574468%; +} +.row-fluid .offset7:first-child { + margin-left: 59.57446809%; + *margin-left: 59.46808511%; +} +.row-fluid .offset6 { + margin-left: 53.19148936%; + *margin-left: 53.08510638%; +} +.row-fluid .offset6:first-child { + margin-left: 51.06382979%; + *margin-left: 50.95744681%; +} +.row-fluid .offset5 { + margin-left: 44.68085106%; + *margin-left: 44.57446809%; +} +.row-fluid .offset5:first-child { + margin-left: 42.55319149%; + *margin-left: 42.44680851%; +} +.row-fluid .offset4 { + margin-left: 36.17021277%; + *margin-left: 36.06382979%; +} +.row-fluid .offset4:first-child { + margin-left: 34.04255319%; + *margin-left: 33.93617021%; +} +.row-fluid .offset3 { + margin-left: 27.65957447%; + *margin-left: 27.55319149%; +} +.row-fluid .offset3:first-child { + margin-left: 25.53191489%; + *margin-left: 25.42553191%; +} +.row-fluid .offset2 { + margin-left: 19.14893617%; + *margin-left: 19.04255319%; +} +.row-fluid .offset2:first-child { + margin-left: 17.0212766%; + *margin-left: 16.91489362%; +} +.row-fluid .offset1 { + margin-left: 10.63829787%; + *margin-left: 10.53191489%; +} +.row-fluid .offset1:first-child { + margin-left: 8.5106383%; + *margin-left: 8.40425532%; +} +[class*="span"].hide, +.row-fluid [class*="span"].hide { + display: none; +} +[class*="span"].pull-right, +.row-fluid [class*="span"].pull-right { + float: right; +} +.container { + margin-right: auto; + margin-left: auto; + *zoom: 1; +} +.container:before, +.container:after { + display: table; + content: ""; + line-height: 0; +} +.container:after { + clear: both; +} +.container-fluid { + padding-right: 20px; + padding-left: 20px; + *zoom: 1; +} +.container-fluid:before, +.container-fluid:after { + display: table; + content: ""; + line-height: 0; +} +.container-fluid:after { + clear: both; +} +p { + margin: 0 0 10.5px; +} +.lead { + margin-bottom: 21px; + font-size: 24px; + font-weight: 200; + line-height: 31.5px; +} +small { + font-size: 85%; +} +strong { + font-weight: bold; +} +em { + font-style: italic; +} +cite { + font-style: normal; +} +.muted { + color: #999999; +} +a.muted:hover, +a.muted:focus { + color: #808080; +} +.text-warning { + color: #c09853; +} +a.text-warning:hover, +a.text-warning:focus { + color: #a47e3c; +} +.text-error { + color: #b94a48; +} +a.text-error:hover, +a.text-error:focus { + color: #953b39; +} +.text-info { + color: #3a87ad; +} +a.text-info:hover, +a.text-info:focus { + color: #2d6987; +} +.text-success { + color: #468847; +} +a.text-success:hover, +a.text-success:focus { + color: #356635; +} +.text-left { + text-align: left; +} +.text-right { + text-align: right; +} +.text-center { + text-align: center; +} +h1, +h2, +h3, +h4, +h5, +h6 { + margin: 10.5px 0; + font-family: inherit; + font-weight: bold; + line-height: 21px; + color: inherit; + text-rendering: optimizelegibility; +} +h1 small, +h2 small, +h3 small, +h4 small, +h5 small, +h6 small { + font-weight: normal; + line-height: 1; + color: #999999; +} +h1, +h2, +h3 { + line-height: 42px; +} +h1 { + font-size: 44px; +} +h2 { + font-size: 36px; +} +h3 { + font-size: 28px; +} +h4 { + font-size: 20px; +} +h5 { + font-size: 16px; +} +h6 { + font-size: 13.6px; +} +h1 small { + font-size: 28px; +} +h2 small { + font-size: 20px; +} +h3 small { + font-size: 16px; +} +h4 small { + font-size: 16px; +} +.page-header { + padding-bottom: 9.5px; + margin: 21px 0 31.5px; + border-bottom: 1px solid #eeeeee; +} +ul, +ol { + padding: 0; + margin: 0 0 10.5px 25px; +} +ul ul, +ul ol, +ol ol, +ol ul { + margin-bottom: 0; +} +li { + line-height: 21px; +} +ul.unstyled, +ol.unstyled { + margin-left: 0; + list-style: none; +} +ul.inline, +ol.inline { + margin-left: 0; + list-style: none; +} +ul.inline > li, +ol.inline > li { + display: inline-block; + *display: inline; + /* IE7 inline-block hack */ + *zoom: 1; + padding-left: 5px; + padding-right: 5px; +} +dl { + margin-bottom: 21px; +} +dt, +dd { + line-height: 21px; +} +dt { + font-weight: bold; +} +dd { + margin-left: 10.5px; +} +.dl-horizontal { + *zoom: 1; +} +.dl-horizontal:before, +.dl-horizontal:after { + display: table; + content: ""; + line-height: 0; +} +.dl-horizontal:after { + clear: both; +} +.dl-horizontal dt { + float: left; + width: 160px; + clear: left; + text-align: right; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.dl-horizontal dd { + margin-left: 180px; +} +hr { + margin: 21px 0; + border: 0; + border-top: 1px solid #eeeeee; + border-bottom: 1px solid #ffffff; +} +abbr[title], +abbr[data-original-title] { + cursor: help; + border-bottom: 1px dotted #999999; +} +abbr.initialism { + font-size: 90%; + text-transform: uppercase; +} +blockquote { + padding: 0 0 0 15px; + margin: 0 0 21px; + border-left: 5px solid #eeeeee; +} +blockquote p { + margin-bottom: 0; + font-size: 20px; + font-weight: 300; + line-height: 1.25; +} +blockquote small { + display: block; + line-height: 21px; + color: #999999; +} +blockquote small:before { + content: '\2014 \00A0'; +} +blockquote.pull-right { + float: right; + padding-right: 15px; + padding-left: 0; + border-right: 5px solid #eeeeee; + border-left: 0; +} +blockquote.pull-right p, +blockquote.pull-right small { + text-align: right; +} +blockquote.pull-right small:before { + content: ''; +} +blockquote.pull-right small:after { + content: '\00A0 \2014'; +} +q:before, +q:after, +blockquote:before, +blockquote:after { + content: ""; +} +address { + display: block; + margin-bottom: 21px; + font-style: normal; + line-height: 21px; +} +code, +pre { + padding: 0 3px 2px; + font-family: 'Source Code Pro', monospace; + font-size: 14px; + color: #333333; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} +code { + padding: 2px 4px; + color: #d14; + background-color: #f7f7f9; + border: 1px solid #e1e1e8; + white-space: nowrap; +} +pre { + display: block; + padding: 10px; + margin: 0 0 10.5px; + font-size: 15px; + line-height: 21px; + word-break: break-all; + word-wrap: break-word; + white-space: pre; + white-space: pre-wrap; + background-color: #f5f5f5; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, 0.15); + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} +pre.prettyprint { + margin-bottom: 21px; +} +pre code { + padding: 0; + color: inherit; + white-space: pre; + white-space: pre-wrap; + background-color: transparent; + border: 0; +} +.pre-scrollable { + max-height: 340px; + overflow-y: scroll; +} +form { + margin: 0 0 21px; +} +fieldset { + padding: 0; + margin: 0; + border: 0; +} +legend { + display: block; + width: 100%; + padding: 0; + margin-bottom: 21px; + font-size: 24px; + line-height: 42px; + color: #333333; + border: 0; + border-bottom: 1px solid #e5e5e5; +} +legend small { + font-size: 15.75px; + color: #999999; +} +label, +input, +button, +select, +textarea { + font-size: 16px; + font-weight: normal; + line-height: 21px; +} +input, +button, +select, +textarea { + font-family: 'Source Sans Pro', sans-serif; +} +label { + display: block; + margin-bottom: 5px; +} +select, +textarea, +input[type="text"], +input[type="password"], +input[type="datetime"], +input[type="datetime-local"], +input[type="date"], +input[type="month"], +input[type="time"], +input[type="week"], +input[type="number"], +input[type="email"], +input[type="url"], +input[type="search"], +input[type="tel"], +input[type="color"], +.uneditable-input { + display: inline-block; + height: 21px; + padding: 4px 6px; + margin-bottom: 10.5px; + font-size: 16px; + line-height: 21px; + color: #555555; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + vertical-align: middle; +} +input, +textarea, +.uneditable-input { + width: 206px; +} +textarea { + height: auto; +} +textarea, +input[type="text"], +input[type="password"], +input[type="datetime"], +input[type="datetime-local"], +input[type="date"], +input[type="month"], +input[type="time"], +input[type="week"], +input[type="number"], +input[type="email"], +input[type="url"], +input[type="search"], +input[type="tel"], +input[type="color"], +.uneditable-input { + background-color: #ffffff; + border: 1px solid #cccccc; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -webkit-transition: border linear .2s, box-shadow linear .2s; + -moz-transition: border linear .2s, box-shadow linear .2s; + -o-transition: border linear .2s, box-shadow linear .2s; + transition: border linear .2s, box-shadow linear .2s; +} +textarea:focus, +input[type="text"]:focus, +input[type="password"]:focus, +input[type="datetime"]:focus, +input[type="datetime-local"]:focus, +input[type="date"]:focus, +input[type="month"]:focus, +input[type="time"]:focus, +input[type="week"]:focus, +input[type="number"]:focus, +input[type="email"]:focus, +input[type="url"]:focus, +input[type="search"]:focus, +input[type="tel"]:focus, +input[type="color"]:focus, +.uneditable-input:focus { + border-color: rgba(82, 168, 236, 0.8); + outline: 0; + outline: thin dotted \9; + /* IE6-9 */ + -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(82,168,236,.6); + -moz-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(82,168,236,.6); + box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(82,168,236,.6); +} +input[type="radio"], +input[type="checkbox"] { + margin: 4px 0 0; + *margin-top: 0; + /* IE7 */ + margin-top: 1px \9; + /* IE8-9 */ + line-height: normal; +} +input[type="file"], +input[type="image"], +input[type="submit"], +input[type="reset"], +input[type="button"], +input[type="radio"], +input[type="checkbox"] { + width: auto; +} +select, +input[type="file"] { + height: 31px; + /* In IE7, the height of the select element cannot be changed by height, only font-size */ + *margin-top: 4px; + /* For IE7, add top margin to align select with labels */ + line-height: 31px; +} +select { + width: 220px; + border: 1px solid #cccccc; + background-color: #ffffff; +} +select[multiple], +select[size] { + height: auto; +} +select:focus, +input[type="file"]:focus, +input[type="radio"]:focus, +input[type="checkbox"]:focus { + outline: thin dotted #333; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +.uneditable-input, +.uneditable-textarea { + color: #999999; + background-color: #fcfcfc; + border-color: #cccccc; + -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025); + -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025); + cursor: not-allowed; +} +.uneditable-input { + overflow: hidden; + white-space: nowrap; +} +.uneditable-textarea { + width: auto; + height: auto; +} +input:-moz-placeholder, +textarea:-moz-placeholder { + color: #999999; +} +input:-ms-input-placeholder, +textarea:-ms-input-placeholder { + color: #999999; +} +input::-webkit-input-placeholder, +textarea::-webkit-input-placeholder { + color: #999999; +} +.radio, +.checkbox { + min-height: 21px; + padding-left: 20px; +} +.radio input[type="radio"], +.checkbox input[type="checkbox"] { + float: left; + margin-left: -20px; +} +.controls > .radio:first-child, +.controls > .checkbox:first-child { + padding-top: 5px; +} +.radio.inline, +.checkbox.inline { + display: inline-block; + padding-top: 5px; + margin-bottom: 0; + vertical-align: middle; +} +.radio.inline + .radio.inline, +.checkbox.inline + .checkbox.inline { + margin-left: 10px; +} +.input-mini { + width: 60px; +} +.input-small { + width: 90px; +} +.input-medium { + width: 150px; +} +.input-large { + width: 210px; +} +.input-xlarge { + width: 270px; +} +.input-xxlarge { + width: 530px; +} +input[class*="span"], +select[class*="span"], +textarea[class*="span"], +.uneditable-input[class*="span"], +.row-fluid input[class*="span"], +.row-fluid select[class*="span"], +.row-fluid textarea[class*="span"], +.row-fluid .uneditable-input[class*="span"] { + float: none; + margin-left: 0; +} +.input-append input[class*="span"], +.input-append .uneditable-input[class*="span"], +.input-prepend input[class*="span"], +.input-prepend .uneditable-input[class*="span"], +.row-fluid input[class*="span"], +.row-fluid select[class*="span"], +.row-fluid textarea[class*="span"], +.row-fluid .uneditable-input[class*="span"], +.row-fluid .input-prepend [class*="span"], +.row-fluid .input-append [class*="span"] { + display: inline-block; +} +input, +textarea, +.uneditable-input { + margin-left: 0; +} +.controls-row [class*="span"] + [class*="span"] { + margin-left: 20px; +} +input.span12, +textarea.span12, +.uneditable-input.span12 { + width: 926px; +} +input.span11, +textarea.span11, +.uneditable-input.span11 { + width: 846px; +} +input.span10, +textarea.span10, +.uneditable-input.span10 { + width: 766px; +} +input.span9, +textarea.span9, +.uneditable-input.span9 { + width: 686px; +} +input.span8, +textarea.span8, +.uneditable-input.span8 { + width: 606px; +} +input.span7, +textarea.span7, +.uneditable-input.span7 { + width: 526px; +} +input.span6, +textarea.span6, +.uneditable-input.span6 { + width: 446px; +} +input.span5, +textarea.span5, +.uneditable-input.span5 { + width: 366px; +} +input.span4, +textarea.span4, +.uneditable-input.span4 { + width: 286px; +} +input.span3, +textarea.span3, +.uneditable-input.span3 { + width: 206px; +} +input.span2, +textarea.span2, +.uneditable-input.span2 { + width: 126px; +} +input.span1, +textarea.span1, +.uneditable-input.span1 { + width: 46px; +} +.controls-row { + *zoom: 1; +} +.controls-row:before, +.controls-row:after { + display: table; + content: ""; + line-height: 0; +} +.controls-row:after { + clear: both; +} +.controls-row [class*="span"], +.row-fluid .controls-row [class*="span"] { + float: left; +} +.controls-row .checkbox[class*="span"], +.controls-row .radio[class*="span"] { + padding-top: 5px; +} +input[disabled], +select[disabled], +textarea[disabled], +input[readonly], +select[readonly], +textarea[readonly] { + cursor: not-allowed; + background-color: #eeeeee; +} +input[type="radio"][disabled], +input[type="checkbox"][disabled], +input[type="radio"][readonly], +input[type="checkbox"][readonly] { + background-color: transparent; +} +.control-group.warning .control-label, +.control-group.warning .help-block, +.control-group.warning .help-inline { + color: #c09853; +} +.control-group.warning .checkbox, +.control-group.warning .radio, +.control-group.warning input, +.control-group.warning select, +.control-group.warning textarea { + color: #c09853; +} +.control-group.warning input, +.control-group.warning select, +.control-group.warning textarea { + border-color: #c09853; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} +.control-group.warning input:focus, +.control-group.warning select:focus, +.control-group.warning textarea:focus { + border-color: #a47e3c; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e; + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e; +} +.control-group.warning .input-prepend .add-on, +.control-group.warning .input-append .add-on { + color: #c09853; + background-color: #fcf8e3; + border-color: #c09853; +} +.control-group.error .control-label, +.control-group.error .help-block, +.control-group.error .help-inline { + color: #b94a48; +} +.control-group.error .checkbox, +.control-group.error .radio, +.control-group.error input, +.control-group.error select, +.control-group.error textarea { + color: #b94a48; +} +.control-group.error input, +.control-group.error select, +.control-group.error textarea { + border-color: #b94a48; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} +.control-group.error input:focus, +.control-group.error select:focus, +.control-group.error textarea:focus { + border-color: #953b39; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392; + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392; +} +.control-group.error .input-prepend .add-on, +.control-group.error .input-append .add-on { + color: #b94a48; + background-color: #f2dede; + border-color: #b94a48; +} +.control-group.success .control-label, +.control-group.success .help-block, +.control-group.success .help-inline { + color: #468847; +} +.control-group.success .checkbox, +.control-group.success .radio, +.control-group.success input, +.control-group.success select, +.control-group.success textarea { + color: #468847; +} +.control-group.success input, +.control-group.success select, +.control-group.success textarea { + border-color: #468847; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} +.control-group.success input:focus, +.control-group.success select:focus, +.control-group.success textarea:focus { + border-color: #356635; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b; + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b; +} +.control-group.success .input-prepend .add-on, +.control-group.success .input-append .add-on { + color: #468847; + background-color: #dff0d8; + border-color: #468847; +} +.control-group.info .control-label, +.control-group.info .help-block, +.control-group.info .help-inline { + color: #3a87ad; +} +.control-group.info .checkbox, +.control-group.info .radio, +.control-group.info input, +.control-group.info select, +.control-group.info textarea { + color: #3a87ad; +} +.control-group.info input, +.control-group.info select, +.control-group.info textarea { + border-color: #3a87ad; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} +.control-group.info input:focus, +.control-group.info select:focus, +.control-group.info textarea:focus { + border-color: #2d6987; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3; + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3; +} +.control-group.info .input-prepend .add-on, +.control-group.info .input-append .add-on { + color: #3a87ad; + background-color: #d9edf7; + border-color: #3a87ad; +} +input:focus:invalid, +textarea:focus:invalid, +select:focus:invalid { + color: #b94a48; + border-color: #ee5f5b; +} +input:focus:invalid:focus, +textarea:focus:invalid:focus, +select:focus:invalid:focus { + border-color: #e9322d; + -webkit-box-shadow: 0 0 6px #f8b9b7; + -moz-box-shadow: 0 0 6px #f8b9b7; + box-shadow: 0 0 6px #f8b9b7; +} +.form-actions { + padding: 20px 20px 21px; + margin-top: 21px; + margin-bottom: 21px; + background-color: #f5f5f5; + border-top: 1px solid #e5e5e5; + *zoom: 1; +} +.form-actions:before, +.form-actions:after { + display: table; + content: ""; + line-height: 0; +} +.form-actions:after { + clear: both; +} +.help-block, +.help-inline { + color: #595959; +} +.help-block { + display: block; + margin-bottom: 10.5px; +} +.help-inline { + display: inline-block; + *display: inline; + /* IE7 inline-block hack */ + *zoom: 1; + vertical-align: middle; + padding-left: 5px; +} +.input-append, +.input-prepend { + display: inline-block; + margin-bottom: 10.5px; + vertical-align: middle; + font-size: 0; + white-space: nowrap; +} +.input-append input, +.input-prepend input, +.input-append select, +.input-prepend select, +.input-append .uneditable-input, +.input-prepend .uneditable-input, +.input-append .dropdown-menu, +.input-prepend .dropdown-menu, +.input-append .popover, +.input-prepend .popover { + font-size: 16px; +} +.input-append input, +.input-prepend input, +.input-append select, +.input-prepend select, +.input-append .uneditable-input, +.input-prepend .uneditable-input { + position: relative; + margin-bottom: 0; + *margin-left: 0; + vertical-align: top; + -webkit-border-radius: 0 4px 4px 0; + -moz-border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; +} +.input-append input:focus, +.input-prepend input:focus, +.input-append select:focus, +.input-prepend select:focus, +.input-append .uneditable-input:focus, +.input-prepend .uneditable-input:focus { + z-index: 2; +} +.input-append .add-on, +.input-prepend .add-on { + display: inline-block; + width: auto; + height: 21px; + min-width: 16px; + padding: 4px 5px; + font-size: 16px; + font-weight: normal; + line-height: 21px; + text-align: center; + text-shadow: 0 1px 0 #ffffff; + background-color: #eeeeee; + border: 1px solid #ccc; +} +.input-append .add-on, +.input-prepend .add-on, +.input-append .btn, +.input-prepend .btn, +.input-append .btn-group > .dropdown-toggle, +.input-prepend .btn-group > .dropdown-toggle { + vertical-align: top; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} +.input-append .active, +.input-prepend .active { + background-color: #a9dba9; + border-color: #46a546; +} +.input-prepend .add-on, +.input-prepend .btn { + margin-right: -1px; +} +.input-prepend .add-on:first-child, +.input-prepend .btn:first-child { + -webkit-border-radius: 4px 0 0 4px; + -moz-border-radius: 4px 0 0 4px; + border-radius: 4px 0 0 4px; +} +.input-append input, +.input-append select, +.input-append .uneditable-input { + -webkit-border-radius: 4px 0 0 4px; + -moz-border-radius: 4px 0 0 4px; + border-radius: 4px 0 0 4px; +} +.input-append input + .btn-group .btn:last-child, +.input-append select + .btn-group .btn:last-child, +.input-append .uneditable-input + .btn-group .btn:last-child { + -webkit-border-radius: 0 4px 4px 0; + -moz-border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; +} +.input-append .add-on, +.input-append .btn, +.input-append .btn-group { + margin-left: -1px; +} +.input-append .add-on:last-child, +.input-append .btn:last-child, +.input-append .btn-group:last-child > .dropdown-toggle { + -webkit-border-radius: 0 4px 4px 0; + -moz-border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; +} +.input-prepend.input-append input, +.input-prepend.input-append select, +.input-prepend.input-append .uneditable-input { + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} +.input-prepend.input-append input + .btn-group .btn, +.input-prepend.input-append select + .btn-group .btn, +.input-prepend.input-append .uneditable-input + .btn-group .btn { + -webkit-border-radius: 0 4px 4px 0; + -moz-border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; +} +.input-prepend.input-append .add-on:first-child, +.input-prepend.input-append .btn:first-child { + margin-right: -1px; + -webkit-border-radius: 4px 0 0 4px; + -moz-border-radius: 4px 0 0 4px; + border-radius: 4px 0 0 4px; +} +.input-prepend.input-append .add-on:last-child, +.input-prepend.input-append .btn:last-child { + margin-left: -1px; + -webkit-border-radius: 0 4px 4px 0; + -moz-border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; +} +.input-prepend.input-append .btn-group:first-child { + margin-left: 0; +} +input.search-query { + padding-right: 14px; + padding-right: 4px \9; + padding-left: 14px; + padding-left: 4px \9; + /* IE7-8 doesn't have border-radius, so don't indent the padding */ + margin-bottom: 0; + -webkit-border-radius: 15px; + -moz-border-radius: 15px; + border-radius: 15px; +} +/* Allow for input prepend/append in search forms */ +.form-search .input-append .search-query, +.form-search .input-prepend .search-query { + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} +.form-search .input-append .search-query { + -webkit-border-radius: 14px 0 0 14px; + -moz-border-radius: 14px 0 0 14px; + border-radius: 14px 0 0 14px; +} +.form-search .input-append .btn { + -webkit-border-radius: 0 14px 14px 0; + -moz-border-radius: 0 14px 14px 0; + border-radius: 0 14px 14px 0; +} +.form-search .input-prepend .search-query { + -webkit-border-radius: 0 14px 14px 0; + -moz-border-radius: 0 14px 14px 0; + border-radius: 0 14px 14px 0; +} +.form-search .input-prepend .btn { + -webkit-border-radius: 14px 0 0 14px; + -moz-border-radius: 14px 0 0 14px; + border-radius: 14px 0 0 14px; +} +.form-search input, +.form-inline input, +.form-horizontal input, +.form-search textarea, +.form-inline textarea, +.form-horizontal textarea, +.form-search select, +.form-inline select, +.form-horizontal select, +.form-search .help-inline, +.form-inline .help-inline, +.form-horizontal .help-inline, +.form-search .uneditable-input, +.form-inline .uneditable-input, +.form-horizontal .uneditable-input, +.form-search .input-prepend, +.form-inline .input-prepend, +.form-horizontal .input-prepend, +.form-search .input-append, +.form-inline .input-append, +.form-horizontal .input-append { + display: inline-block; + *display: inline; + /* IE7 inline-block hack */ + *zoom: 1; + margin-bottom: 0; + vertical-align: middle; +} +.form-search .hide, +.form-inline .hide, +.form-horizontal .hide { + display: none; +} +.form-search label, +.form-inline label, +.form-search .btn-group, +.form-inline .btn-group { + display: inline-block; +} +.form-search .input-append, +.form-inline .input-append, +.form-search .input-prepend, +.form-inline .input-prepend { + margin-bottom: 0; +} +.form-search .radio, +.form-search .checkbox, +.form-inline .radio, +.form-inline .checkbox { + padding-left: 0; + margin-bottom: 0; + vertical-align: middle; +} +.form-search .radio input[type="radio"], +.form-search .checkbox input[type="checkbox"], +.form-inline .radio input[type="radio"], +.form-inline .checkbox input[type="checkbox"] { + float: left; + margin-right: 3px; + margin-left: 0; +} +.control-group { + margin-bottom: 10.5px; +} +legend + .control-group { + margin-top: 21px; + -webkit-margin-top-collapse: separate; +} +.form-horizontal .control-group { + margin-bottom: 21px; + *zoom: 1; +} +.form-horizontal .control-group:before, +.form-horizontal .control-group:after { + display: table; + content: ""; + line-height: 0; +} +.form-horizontal .control-group:after { + clear: both; +} +.form-horizontal .control-label { + float: left; + width: 160px; + padding-top: 5px; + text-align: right; +} +.form-horizontal .controls { + *display: inline-block; + *padding-left: 20px; + margin-left: 180px; + *margin-left: 0; +} +.form-horizontal .controls:first-child { + *padding-left: 180px; +} +.form-horizontal .help-block { + margin-bottom: 0; +} +.form-horizontal input + .help-block, +.form-horizontal select + .help-block, +.form-horizontal textarea + .help-block, +.form-horizontal .uneditable-input + .help-block, +.form-horizontal .input-prepend + .help-block, +.form-horizontal .input-append + .help-block { + margin-top: 10.5px; +} +.form-horizontal .form-actions { + padding-left: 180px; +} +table { + max-width: 100%; + background-color: transparent; + border-collapse: collapse; + border-spacing: 0; +} +.table { + width: 100%; + margin-bottom: 21px; +} +.table th, +.table td { + padding: 8px; + line-height: 21px; + text-align: left; + vertical-align: top; + border-top: 1px solid #dddddd; +} +.table th { + font-weight: bold; +} +.table thead th { + vertical-align: bottom; +} +.table caption + thead tr:first-child th, +.table caption + thead tr:first-child td, +.table colgroup + thead tr:first-child th, +.table colgroup + thead tr:first-child td, +.table thead:first-child tr:first-child th, +.table thead:first-child tr:first-child td { + border-top: 0; +} +.table tbody + tbody { + border-top: 2px solid #dddddd; +} +.table .table { + background-color: #ffffff; +} +.table-condensed th, +.table-condensed td { + padding: 4px 5px; +} +.table-bordered { + border: 1px solid #dddddd; + border-collapse: separate; + *border-collapse: collapse; + border-left: 0; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} +.table-bordered th, +.table-bordered td { + border-left: 1px solid #dddddd; +} +.table-bordered caption + thead tr:first-child th, +.table-bordered caption + tbody tr:first-child th, +.table-bordered caption + tbody tr:first-child td, +.table-bordered colgroup + thead tr:first-child th, +.table-bordered colgroup + tbody tr:first-child th, +.table-bordered colgroup + tbody tr:first-child td, +.table-bordered thead:first-child tr:first-child th, +.table-bordered tbody:first-child tr:first-child th, +.table-bordered tbody:first-child tr:first-child td { + border-top: 0; +} +.table-bordered thead:first-child tr:first-child > th:first-child, +.table-bordered tbody:first-child tr:first-child > td:first-child, +.table-bordered tbody:first-child tr:first-child > th:first-child { + -webkit-border-top-left-radius: 4px; + -moz-border-radius-topleft: 4px; + border-top-left-radius: 4px; +} +.table-bordered thead:first-child tr:first-child > th:last-child, +.table-bordered tbody:first-child tr:first-child > td:last-child, +.table-bordered tbody:first-child tr:first-child > th:last-child { + -webkit-border-top-right-radius: 4px; + -moz-border-radius-topright: 4px; + border-top-right-radius: 4px; +} +.table-bordered thead:last-child tr:last-child > th:first-child, +.table-bordered tbody:last-child tr:last-child > td:first-child, +.table-bordered tbody:last-child tr:last-child > th:first-child, +.table-bordered tfoot:last-child tr:last-child > td:first-child, +.table-bordered tfoot:last-child tr:last-child > th:first-child { + -webkit-border-bottom-left-radius: 4px; + -moz-border-radius-bottomleft: 4px; + border-bottom-left-radius: 4px; +} +.table-bordered thead:last-child tr:last-child > th:last-child, +.table-bordered tbody:last-child tr:last-child > td:last-child, +.table-bordered tbody:last-child tr:last-child > th:last-child, +.table-bordered tfoot:last-child tr:last-child > td:last-child, +.table-bordered tfoot:last-child tr:last-child > th:last-child { + -webkit-border-bottom-right-radius: 4px; + -moz-border-radius-bottomright: 4px; + border-bottom-right-radius: 4px; +} +.table-bordered tfoot + tbody:last-child tr:last-child td:first-child { + -webkit-border-bottom-left-radius: 0; + -moz-border-radius-bottomleft: 0; + border-bottom-left-radius: 0; +} +.table-bordered tfoot + tbody:last-child tr:last-child td:last-child { + -webkit-border-bottom-right-radius: 0; + -moz-border-radius-bottomright: 0; + border-bottom-right-radius: 0; +} +.table-bordered caption + thead tr:first-child th:first-child, +.table-bordered caption + tbody tr:first-child td:first-child, +.table-bordered colgroup + thead tr:first-child th:first-child, +.table-bordered colgroup + tbody tr:first-child td:first-child { + -webkit-border-top-left-radius: 4px; + -moz-border-radius-topleft: 4px; + border-top-left-radius: 4px; +} +.table-bordered caption + thead tr:first-child th:last-child, +.table-bordered caption + tbody tr:first-child td:last-child, +.table-bordered colgroup + thead tr:first-child th:last-child, +.table-bordered colgroup + tbody tr:first-child td:last-child { + -webkit-border-top-right-radius: 4px; + -moz-border-radius-topright: 4px; + border-top-right-radius: 4px; +} +.table-striped tbody > tr:nth-child(odd) > td, +.table-striped tbody > tr:nth-child(odd) > th { + background-color: #f9f9f9; +} +.table-hover tbody tr:hover > td, +.table-hover tbody tr:hover > th { + background-color: #f5f5f5; +} +table td[class*="span"], +table th[class*="span"], +.row-fluid table td[class*="span"], +.row-fluid table th[class*="span"] { + display: table-cell; + float: none; + margin-left: 0; +} +.table td.span1, +.table th.span1 { + float: none; + width: 44px; + margin-left: 0; +} +.table td.span2, +.table th.span2 { + float: none; + width: 124px; + margin-left: 0; +} +.table td.span3, +.table th.span3 { + float: none; + width: 204px; + margin-left: 0; +} +.table td.span4, +.table th.span4 { + float: none; + width: 284px; + margin-left: 0; +} +.table td.span5, +.table th.span5 { + float: none; + width: 364px; + margin-left: 0; +} +.table td.span6, +.table th.span6 { + float: none; + width: 444px; + margin-left: 0; +} +.table td.span7, +.table th.span7 { + float: none; + width: 524px; + margin-left: 0; +} +.table td.span8, +.table th.span8 { + float: none; + width: 604px; + margin-left: 0; +} +.table td.span9, +.table th.span9 { + float: none; + width: 684px; + margin-left: 0; +} +.table td.span10, +.table th.span10 { + float: none; + width: 764px; + margin-left: 0; +} +.table td.span11, +.table th.span11 { + float: none; + width: 844px; + margin-left: 0; +} +.table td.span12, +.table th.span12 { + float: none; + width: 924px; + margin-left: 0; +} +.table tbody tr.success > td { + background-color: #dff0d8; +} +.table tbody tr.error > td { + background-color: #f2dede; +} +.table tbody tr.warning > td { + background-color: #fcf8e3; +} +.table tbody tr.info > td { + background-color: #d9edf7; +} +.table-hover tbody tr.success:hover > td { + background-color: #d0e9c6; +} +.table-hover tbody tr.error:hover > td { + background-color: #ebcccc; +} +.table-hover tbody tr.warning:hover > td { + background-color: #faf2cc; +} +.table-hover tbody tr.info:hover > td { + background-color: #c4e3f3; +} +[class^="icon-"], +[class*=" icon-"] { + display: inline-block; + width: 14px; + height: 14px; + *margin-right: .3em; + line-height: 14px; + vertical-align: text-top; + background-image: url("../../img/glyphicons-halflings.png"); + background-position: 14px 14px; + background-repeat: no-repeat; + margin-top: 1px; +} +/* White icons with optional class, or on hover/focus/active states of certain elements */ +.icon-white, +.nav-pills > .active > a > [class^="icon-"], +.nav-pills > .active > a > [class*=" icon-"], +.nav-list > .active > a > [class^="icon-"], +.nav-list > .active > a > [class*=" icon-"], +.navbar-inverse .nav > .active > a > [class^="icon-"], +.navbar-inverse .nav > .active > a > [class*=" icon-"], +.dropdown-menu > li > a:hover > [class^="icon-"], +.dropdown-menu > li > a:focus > [class^="icon-"], +.dropdown-menu > li > a:hover > [class*=" icon-"], +.dropdown-menu > li > a:focus > [class*=" icon-"], +.dropdown-menu > .active > a > [class^="icon-"], +.dropdown-menu > .active > a > [class*=" icon-"], +.dropdown-submenu:hover > a > [class^="icon-"], +.dropdown-submenu:focus > a > [class^="icon-"], +.dropdown-submenu:hover > a > [class*=" icon-"], +.dropdown-submenu:focus > a > [class*=" icon-"] { + background-image: url("../../img/glyphicons-halflings-white.png"); +} +.icon-glass { + background-position: 0 0; +} +.icon-music { + background-position: -24px 0; +} +.icon-search { + background-position: -48px 0; +} +.icon-envelope { + background-position: -72px 0; +} +.icon-heart { + background-position: -96px 0; +} +.icon-star { + background-position: -120px 0; +} +.icon-star-empty { + background-position: -144px 0; +} +.icon-user { + background-position: -168px 0; +} +.icon-film { + background-position: -192px 0; +} +.icon-th-large { + background-position: -216px 0; +} +.icon-th { + background-position: -240px 0; +} +.icon-th-list { + background-position: -264px 0; +} +.icon-ok { + background-position: -288px 0; +} +.icon-remove { + background-position: -312px 0; +} +.icon-zoom-in { + background-position: -336px 0; +} +.icon-zoom-out { + background-position: -360px 0; +} +.icon-off { + background-position: -384px 0; +} +.icon-signal { + background-position: -408px 0; +} +.icon-cog { + background-position: -432px 0; +} +.icon-trash { + background-position: -456px 0; +} +.icon-home { + background-position: 0 -24px; +} +.icon-file { + background-position: -24px -24px; +} +.icon-time { + background-position: -48px -24px; +} +.icon-road { + background-position: -72px -24px; +} +.icon-download-alt { + background-position: -96px -24px; +} +.icon-download { + background-position: -120px -24px; +} +.icon-upload { + background-position: -144px -24px; +} +.icon-inbox { + background-position: -168px -24px; +} +.icon-play-circle { + background-position: -192px -24px; +} +.icon-repeat { + background-position: -216px -24px; +} +.icon-refresh { + background-position: -240px -24px; +} +.icon-list-alt { + background-position: -264px -24px; +} +.icon-lock { + background-position: -287px -24px; +} +.icon-flag { + background-position: -312px -24px; +} +.icon-headphones { + background-position: -336px -24px; +} +.icon-volume-off { + background-position: -360px -24px; +} +.icon-volume-down { + background-position: -384px -24px; +} +.icon-volume-up { + background-position: -408px -24px; +} +.icon-qrcode { + background-position: -432px -24px; +} +.icon-barcode { + background-position: -456px -24px; +} +.icon-tag { + background-position: 0 -48px; +} +.icon-tags { + background-position: -25px -48px; +} +.icon-book { + background-position: -48px -48px; +} +.icon-bookmark { + background-position: -72px -48px; +} +.icon-print { + background-position: -96px -48px; +} +.icon-camera { + background-position: -120px -48px; +} +.icon-font { + background-position: -144px -48px; +} +.icon-bold { + background-position: -167px -48px; +} +.icon-italic { + background-position: -192px -48px; +} +.icon-text-height { + background-position: -216px -48px; +} +.icon-text-width { + background-position: -240px -48px; +} +.icon-align-left { + background-position: -264px -48px; +} +.icon-align-center { + background-position: -288px -48px; +} +.icon-align-right { + background-position: -312px -48px; +} +.icon-align-justify { + background-position: -336px -48px; +} +.icon-list { + background-position: -360px -48px; +} +.icon-indent-left { + background-position: -384px -48px; +} +.icon-indent-right { + background-position: -408px -48px; +} +.icon-facetime-video { + background-position: -432px -48px; +} +.icon-picture { + background-position: -456px -48px; +} +.icon-pencil { + background-position: 0 -72px; +} +.icon-map-marker { + background-position: -24px -72px; +} +.icon-adjust { + background-position: -48px -72px; +} +.icon-tint { + background-position: -72px -72px; +} +.icon-edit { + background-position: -96px -72px; +} +.icon-share { + background-position: -120px -72px; +} +.icon-check { + background-position: -144px -72px; +} +.icon-move { + background-position: -168px -72px; +} +.icon-step-backward { + background-position: -192px -72px; +} +.icon-fast-backward { + background-position: -216px -72px; +} +.icon-backward { + background-position: -240px -72px; +} +.icon-play { + background-position: -264px -72px; +} +.icon-pause { + background-position: -288px -72px; +} +.icon-stop { + background-position: -312px -72px; +} +.icon-forward { + background-position: -336px -72px; +} +.icon-fast-forward { + background-position: -360px -72px; +} +.icon-step-forward { + background-position: -384px -72px; +} +.icon-eject { + background-position: -408px -72px; +} +.icon-chevron-left { + background-position: -432px -72px; +} +.icon-chevron-right { + background-position: -456px -72px; +} +.icon-plus-sign { + background-position: 0 -96px; +} +.icon-minus-sign { + background-position: -24px -96px; +} +.icon-remove-sign { + background-position: -48px -96px; +} +.icon-ok-sign { + background-position: -72px -96px; +} +.icon-question-sign { + background-position: -96px -96px; +} +.icon-info-sign { + background-position: -120px -96px; +} +.icon-screenshot { + background-position: -144px -96px; +} +.icon-remove-circle { + background-position: -168px -96px; +} +.icon-ok-circle { + background-position: -192px -96px; +} +.icon-ban-circle { + background-position: -216px -96px; +} +.icon-arrow-left { + background-position: -240px -96px; +} +.icon-arrow-right { + background-position: -264px -96px; +} +.icon-arrow-up { + background-position: -289px -96px; +} +.icon-arrow-down { + background-position: -312px -96px; +} +.icon-share-alt { + background-position: -336px -96px; +} +.icon-resize-full { + background-position: -360px -96px; +} +.icon-resize-small { + background-position: -384px -96px; +} +.icon-plus { + background-position: -408px -96px; +} +.icon-minus { + background-position: -433px -96px; +} +.icon-asterisk { + background-position: -456px -96px; +} +.icon-exclamation-sign { + background-position: 0 -120px; +} +.icon-gift { + background-position: -24px -120px; +} +.icon-leaf { + background-position: -48px -120px; +} +.icon-fire { + background-position: -72px -120px; +} +.icon-eye-open { + background-position: -96px -120px; +} +.icon-eye-close { + background-position: -120px -120px; +} +.icon-warning-sign { + background-position: -144px -120px; +} +.icon-plane { + background-position: -168px -120px; +} +.icon-calendar { + background-position: -192px -120px; +} +.icon-random { + background-position: -216px -120px; + width: 16px; +} +.icon-comment { + background-position: -240px -120px; +} +.icon-magnet { + background-position: -264px -120px; +} +.icon-chevron-up { + background-position: -288px -120px; +} +.icon-chevron-down { + background-position: -313px -119px; +} +.icon-retweet { + background-position: -336px -120px; +} +.icon-shopping-cart { + background-position: -360px -120px; +} +.icon-folder-close { + background-position: -384px -120px; + width: 16px; +} +.icon-folder-open { + background-position: -408px -120px; + width: 16px; +} +.icon-resize-vertical { + background-position: -432px -119px; +} +.icon-resize-horizontal { + background-position: -456px -118px; +} +.icon-hdd { + background-position: 0 -144px; +} +.icon-bullhorn { + background-position: -24px -144px; +} +.icon-bell { + background-position: -48px -144px; +} +.icon-certificate { + background-position: -72px -144px; +} +.icon-thumbs-up { + background-position: -96px -144px; +} +.icon-thumbs-down { + background-position: -120px -144px; +} +.icon-hand-right { + background-position: -144px -144px; +} +.icon-hand-left { + background-position: -168px -144px; +} +.icon-hand-up { + background-position: -192px -144px; +} +.icon-hand-down { + background-position: -216px -144px; +} +.icon-circle-arrow-right { + background-position: -240px -144px; +} +.icon-circle-arrow-left { + background-position: -264px -144px; +} +.icon-circle-arrow-up { + background-position: -288px -144px; +} +.icon-circle-arrow-down { + background-position: -312px -144px; +} +.icon-globe { + background-position: -336px -144px; +} +.icon-wrench { + background-position: -360px -144px; +} +.icon-tasks { + background-position: -384px -144px; +} +.icon-filter { + background-position: -408px -144px; +} +.icon-briefcase { + background-position: -432px -144px; +} +.icon-fullscreen { + background-position: -456px -144px; +} +.dropup, +.dropdown { + position: relative; +} +.dropdown-toggle { + *margin-bottom: -3px; +} +.dropdown-toggle:active, +.open .dropdown-toggle { + outline: 0; +} +.caret { + display: inline-block; + width: 0; + height: 0; + vertical-align: top; + border-top: 4px solid #000000; + border-right: 4px solid transparent; + border-left: 4px solid transparent; + content: ""; +} +.dropdown .caret { + margin-top: 8px; + margin-left: 2px; +} +.dropdown-menu { + position: absolute; + top: 100%; + left: 0; + z-index: 1000; + display: none; + float: left; + min-width: 160px; + padding: 5px 0; + margin: 2px 0 0; + list-style: none; + background-color: #ffffff; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, 0.2); + *border-right-width: 2px; + *border-bottom-width: 2px; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; + -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + -webkit-background-clip: padding-box; + -moz-background-clip: padding; + background-clip: padding-box; +} +.dropdown-menu.pull-right { + right: 0; + left: auto; +} +.dropdown-menu .divider { + *width: 100%; + height: 1px; + margin: 9.5px 1px; + *margin: -5px 0 5px; + overflow: hidden; + background-color: #e5e5e5; + border-bottom: 1px solid #ffffff; +} +.dropdown-menu > li > a { + display: block; + padding: 3px 20px; + clear: both; + font-weight: normal; + line-height: 21px; + color: #333333; + white-space: nowrap; +} +.dropdown-menu > li > a:hover, +.dropdown-menu > li > a:focus, +.dropdown-submenu:hover > a, +.dropdown-submenu:focus > a { + text-decoration: none; + color: #ffffff; + background-color: #0081c2; + background-image: -moz-linear-gradient(top, #0088cc, #0077b3); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0077b3)); + background-image: -webkit-linear-gradient(top, #0088cc, #0077b3); + background-image: -o-linear-gradient(top, #0088cc, #0077b3); + background-image: linear-gradient(to bottom, #0088cc, #0077b3); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0); +} +.dropdown-menu > .active > a, +.dropdown-menu > .active > a:hover, +.dropdown-menu > .active > a:focus { + color: #ffffff; + text-decoration: none; + outline: 0; + background-color: #0081c2; + background-image: -moz-linear-gradient(top, #0088cc, #0077b3); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0077b3)); + background-image: -webkit-linear-gradient(top, #0088cc, #0077b3); + background-image: -o-linear-gradient(top, #0088cc, #0077b3); + background-image: linear-gradient(to bottom, #0088cc, #0077b3); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0); +} +.dropdown-menu > .disabled > a, +.dropdown-menu > .disabled > a:hover, +.dropdown-menu > .disabled > a:focus { + color: #999999; +} +.dropdown-menu > .disabled > a:hover, +.dropdown-menu > .disabled > a:focus { + text-decoration: none; + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + cursor: default; +} +.open { + *z-index: 1000; +} +.open > .dropdown-menu { + display: block; +} +.pull-right > .dropdown-menu { + right: 0; + left: auto; +} +.dropup .caret, +.navbar-fixed-bottom .dropdown .caret { + border-top: 0; + border-bottom: 4px solid #000000; + content: ""; +} +.dropup .dropdown-menu, +.navbar-fixed-bottom .dropdown .dropdown-menu { + top: auto; + bottom: 100%; + margin-bottom: 1px; +} +.dropdown-submenu { + position: relative; +} +.dropdown-submenu > .dropdown-menu { + top: 0; + left: 100%; + margin-top: -6px; + margin-left: -1px; + -webkit-border-radius: 0 6px 6px 6px; + -moz-border-radius: 0 6px 6px 6px; + border-radius: 0 6px 6px 6px; +} +.dropdown-submenu:hover > .dropdown-menu { + display: block; +} +.dropup .dropdown-submenu > .dropdown-menu { + top: auto; + bottom: 0; + margin-top: 0; + margin-bottom: -2px; + -webkit-border-radius: 5px 5px 5px 0; + -moz-border-radius: 5px 5px 5px 0; + border-radius: 5px 5px 5px 0; +} +.dropdown-submenu > a:after { + display: block; + content: " "; + float: right; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; + border-width: 5px 0 5px 5px; + border-left-color: #cccccc; + margin-top: 5px; + margin-right: -10px; +} +.dropdown-submenu:hover > a:after { + border-left-color: #ffffff; +} +.dropdown-submenu.pull-left { + float: none; +} +.dropdown-submenu.pull-left > .dropdown-menu { + left: -100%; + margin-left: 10px; + -webkit-border-radius: 6px 0 6px 6px; + -moz-border-radius: 6px 0 6px 6px; + border-radius: 6px 0 6px 6px; +} +.dropdown .dropdown-menu .nav-header { + padding-left: 20px; + padding-right: 20px; +} +.typeahead { + z-index: 1051; + margin-top: 2px; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} +.well { + min-height: 20px; + padding: 19px; + margin-bottom: 20px; + background-color: #f5f5f5; + border: 1px solid #e3e3e3; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); +} +.well blockquote { + border-color: #ddd; + border-color: rgba(0, 0, 0, 0.15); +} +.well-large { + padding: 24px; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; +} +.well-small { + padding: 9px; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} +.fade { + opacity: 0; + -webkit-transition: opacity 0.15s linear; + -moz-transition: opacity 0.15s linear; + -o-transition: opacity 0.15s linear; + transition: opacity 0.15s linear; +} +.fade.in { + opacity: 1; +} +.collapse { + position: relative; + height: 0; + overflow: hidden; + -webkit-transition: height 0.35s ease; + -moz-transition: height 0.35s ease; + -o-transition: height 0.35s ease; + transition: height 0.35s ease; +} +.collapse.in { + height: auto; +} +.close { + float: right; + font-size: 20px; + font-weight: bold; + line-height: 21px; + color: #000000; + text-shadow: 0 1px 0 #ffffff; + opacity: 0.2; + filter: alpha(opacity=20); +} +.close:hover, +.close:focus { + color: #000000; + text-decoration: none; + cursor: pointer; + opacity: 0.4; + filter: alpha(opacity=40); +} +button.close { + padding: 0; + cursor: pointer; + background: transparent; + border: 0; + -webkit-appearance: none; +} +.btn { + display: inline-block; + *display: inline; + /* IE7 inline-block hack */ + *zoom: 1; + padding: 4px 12px; + margin-bottom: 0; + font-size: 16px; + line-height: 21px; + text-align: center; + vertical-align: middle; + cursor: pointer; + color: #333333; + text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75); + background-color: #f5f5f5; + background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#e6e6e6)); + background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -o-linear-gradient(top, #ffffff, #e6e6e6); + background-image: linear-gradient(to bottom, #ffffff, #e6e6e6); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe6e6e6', GradientType=0); + border-color: #e6e6e6 #e6e6e6 #bfbfbf; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + *background-color: #e6e6e6; + /* Darken IE7 buttons by default so they stand out more given they won't have borders */ + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + border: 1px solid #cccccc; + *border: 0; + border-bottom-color: #b3b3b3; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + *margin-left: .3em; + -webkit-box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05); + -moz-box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05); + box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05); +} +.btn:hover, +.btn:focus, +.btn:active, +.btn.active, +.btn.disabled, +.btn[disabled] { + color: #333333; + background-color: #e6e6e6; + *background-color: #d9d9d9; +} +.btn:active, +.btn.active { + background-color: #cccccc \9; +} +.btn:first-child { + *margin-left: 0; +} +.btn:hover, +.btn:focus { + color: #333333; + text-decoration: none; + background-position: 0 -15px; + -webkit-transition: background-position 0.1s linear; + -moz-transition: background-position 0.1s linear; + -o-transition: background-position 0.1s linear; + transition: background-position 0.1s linear; +} +.btn:focus { + outline: thin dotted #333; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +.btn.active, +.btn:active { + background-image: none; + outline: 0; + -webkit-box-shadow: inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05); + -moz-box-shadow: inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05); + box-shadow: inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05); +} +.btn.disabled, +.btn[disabled] { + cursor: default; + background-image: none; + opacity: 0.65; + filter: alpha(opacity=65); + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; +} +.btn-large { + padding: 11px 19px; + font-size: 20px; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; +} +.btn-large [class^="icon-"], +.btn-large [class*=" icon-"] { + margin-top: 4px; +} +.btn-small { + padding: 2px 10px; + font-size: 13.6px; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} +.btn-small [class^="icon-"], +.btn-small [class*=" icon-"] { + margin-top: 0; +} +.btn-mini [class^="icon-"], +.btn-mini [class*=" icon-"] { + margin-top: -1px; +} +.btn-mini { + padding: 0 6px; + font-size: 12px; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} +.btn-block { + display: block; + width: 100%; + padding-left: 0; + padding-right: 0; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +.btn-block + .btn-block { + margin-top: 5px; +} +input[type="submit"].btn-block, +input[type="reset"].btn-block, +input[type="button"].btn-block { + width: 100%; +} +.btn-primary.active, +.btn-warning.active, +.btn-danger.active, +.btn-success.active, +.btn-info.active, +.btn-inverse.active { + color: rgba(255, 255, 255, 0.75); +} +.btn-primary { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #006dcc; + background-image: -moz-linear-gradient(top, #0088cc, #0044cc); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc)); + background-image: -webkit-linear-gradient(top, #0088cc, #0044cc); + background-image: -o-linear-gradient(top, #0088cc, #0044cc); + background-image: linear-gradient(to bottom, #0088cc, #0044cc); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0044cc', GradientType=0); + border-color: #0044cc #0044cc #002a80; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + *background-color: #0044cc; + /* Darken IE7 buttons by default so they stand out more given they won't have borders */ + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); +} +.btn-primary:hover, +.btn-primary:focus, +.btn-primary:active, +.btn-primary.active, +.btn-primary.disabled, +.btn-primary[disabled] { + color: #ffffff; + background-color: #0044cc; + *background-color: #003bb3; +} +.btn-primary:active, +.btn-primary.active { + background-color: #003399 \9; +} +.btn-warning { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #faa732; + background-image: -moz-linear-gradient(top, #fbb450, #f89406); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406)); + background-image: -webkit-linear-gradient(top, #fbb450, #f89406); + background-image: -o-linear-gradient(top, #fbb450, #f89406); + background-image: linear-gradient(to bottom, #fbb450, #f89406); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450', endColorstr='#fff89406', GradientType=0); + border-color: #f89406 #f89406 #ad6704; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + *background-color: #f89406; + /* Darken IE7 buttons by default so they stand out more given they won't have borders */ + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); +} +.btn-warning:hover, +.btn-warning:focus, +.btn-warning:active, +.btn-warning.active, +.btn-warning.disabled, +.btn-warning[disabled] { + color: #ffffff; + background-color: #f89406; + *background-color: #df8505; +} +.btn-warning:active, +.btn-warning.active { + background-color: #c67605 \9; +} +.btn-danger { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #da4f49; + background-image: -moz-linear-gradient(top, #ee5f5b, #bd362f); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#bd362f)); + background-image: -webkit-linear-gradient(top, #ee5f5b, #bd362f); + background-image: -o-linear-gradient(top, #ee5f5b, #bd362f); + background-image: linear-gradient(to bottom, #ee5f5b, #bd362f); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b', endColorstr='#ffbd362f', GradientType=0); + border-color: #bd362f #bd362f #802420; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + *background-color: #bd362f; + /* Darken IE7 buttons by default so they stand out more given they won't have borders */ + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); +} +.btn-danger:hover, +.btn-danger:focus, +.btn-danger:active, +.btn-danger.active, +.btn-danger.disabled, +.btn-danger[disabled] { + color: #ffffff; + background-color: #bd362f; + *background-color: #a9302a; +} +.btn-danger:active, +.btn-danger.active { + background-color: #942a25 \9; +} +.btn-success { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #5bb75b; + background-image: -moz-linear-gradient(top, #62c462, #51a351); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#51a351)); + background-image: -webkit-linear-gradient(top, #62c462, #51a351); + background-image: -o-linear-gradient(top, #62c462, #51a351); + background-image: linear-gradient(to bottom, #62c462, #51a351); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462', endColorstr='#ff51a351', GradientType=0); + border-color: #51a351 #51a351 #387038; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + *background-color: #51a351; + /* Darken IE7 buttons by default so they stand out more given they won't have borders */ + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); +} +.btn-success:hover, +.btn-success:focus, +.btn-success:active, +.btn-success.active, +.btn-success.disabled, +.btn-success[disabled] { + color: #ffffff; + background-color: #51a351; + *background-color: #499249; +} +.btn-success:active, +.btn-success.active { + background-color: #408140 \9; +} +.btn-info { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #49afcd; + background-image: -moz-linear-gradient(top, #5bc0de, #2f96b4); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#2f96b4)); + background-image: -webkit-linear-gradient(top, #5bc0de, #2f96b4); + background-image: -o-linear-gradient(top, #5bc0de, #2f96b4); + background-image: linear-gradient(to bottom, #5bc0de, #2f96b4); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2f96b4', GradientType=0); + border-color: #2f96b4 #2f96b4 #1f6377; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + *background-color: #2f96b4; + /* Darken IE7 buttons by default so they stand out more given they won't have borders */ + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); +} +.btn-info:hover, +.btn-info:focus, +.btn-info:active, +.btn-info.active, +.btn-info.disabled, +.btn-info[disabled] { + color: #ffffff; + background-color: #2f96b4; + *background-color: #2a85a0; +} +.btn-info:active, +.btn-info.active { + background-color: #24748c \9; +} +.btn-inverse { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #363636; + background-image: -moz-linear-gradient(top, #444444, #222222); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#444444), to(#222222)); + background-image: -webkit-linear-gradient(top, #444444, #222222); + background-image: -o-linear-gradient(top, #444444, #222222); + background-image: linear-gradient(to bottom, #444444, #222222); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff444444', endColorstr='#ff222222', GradientType=0); + border-color: #222222 #222222 #000000; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + *background-color: #222222; + /* Darken IE7 buttons by default so they stand out more given they won't have borders */ + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); +} +.btn-inverse:hover, +.btn-inverse:focus, +.btn-inverse:active, +.btn-inverse.active, +.btn-inverse.disabled, +.btn-inverse[disabled] { + color: #ffffff; + background-color: #222222; + *background-color: #151515; +} +.btn-inverse:active, +.btn-inverse.active { + background-color: #080808 \9; +} +button.btn, +input[type="submit"].btn { + *padding-top: 3px; + *padding-bottom: 3px; +} +button.btn::-moz-focus-inner, +input[type="submit"].btn::-moz-focus-inner { + padding: 0; + border: 0; +} +button.btn.btn-large, +input[type="submit"].btn.btn-large { + *padding-top: 7px; + *padding-bottom: 7px; +} +button.btn.btn-small, +input[type="submit"].btn.btn-small { + *padding-top: 3px; + *padding-bottom: 3px; +} +button.btn.btn-mini, +input[type="submit"].btn.btn-mini { + *padding-top: 1px; + *padding-bottom: 1px; +} +.btn-link, +.btn-link:active, +.btn-link[disabled] { + background-color: transparent; + background-image: none; + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; +} +.btn-link { + border-color: transparent; + cursor: pointer; + color: #0088cc; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} +.btn-link:hover, +.btn-link:focus { + color: #005580; + text-decoration: underline; + background-color: transparent; +} +.btn-link[disabled]:hover, +.btn-link[disabled]:focus { + color: #333333; + text-decoration: none; +} +.btn-group { + position: relative; + display: inline-block; + *display: inline; + /* IE7 inline-block hack */ + *zoom: 1; + font-size: 0; + vertical-align: middle; + white-space: nowrap; + *margin-left: .3em; +} +.btn-group:first-child { + *margin-left: 0; +} +.btn-group + .btn-group { + margin-left: 5px; +} +.btn-toolbar { + font-size: 0; + margin-top: 10.5px; + margin-bottom: 10.5px; +} +.btn-toolbar > .btn + .btn, +.btn-toolbar > .btn-group + .btn, +.btn-toolbar > .btn + .btn-group { + margin-left: 5px; +} +.btn-group > .btn { + position: relative; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} +.btn-group > .btn + .btn { + margin-left: -1px; +} +.btn-group > .btn, +.btn-group > .dropdown-menu, +.btn-group > .popover { + font-size: 16px; +} +.btn-group > .btn-mini { + font-size: 12px; +} +.btn-group > .btn-small { + font-size: 13.6px; +} +.btn-group > .btn-large { + font-size: 20px; +} +.btn-group > .btn:first-child { + margin-left: 0; + -webkit-border-top-left-radius: 4px; + -moz-border-radius-topleft: 4px; + border-top-left-radius: 4px; + -webkit-border-bottom-left-radius: 4px; + -moz-border-radius-bottomleft: 4px; + border-bottom-left-radius: 4px; +} +.btn-group > .btn:last-child, +.btn-group > .dropdown-toggle { + -webkit-border-top-right-radius: 4px; + -moz-border-radius-topright: 4px; + border-top-right-radius: 4px; + -webkit-border-bottom-right-radius: 4px; + -moz-border-radius-bottomright: 4px; + border-bottom-right-radius: 4px; +} +.btn-group > .btn.large:first-child { + margin-left: 0; + -webkit-border-top-left-radius: 6px; + -moz-border-radius-topleft: 6px; + border-top-left-radius: 6px; + -webkit-border-bottom-left-radius: 6px; + -moz-border-radius-bottomleft: 6px; + border-bottom-left-radius: 6px; +} +.btn-group > .btn.large:last-child, +.btn-group > .large.dropdown-toggle { + -webkit-border-top-right-radius: 6px; + -moz-border-radius-topright: 6px; + border-top-right-radius: 6px; + -webkit-border-bottom-right-radius: 6px; + -moz-border-radius-bottomright: 6px; + border-bottom-right-radius: 6px; +} +.btn-group > .btn:hover, +.btn-group > .btn:focus, +.btn-group > .btn:active, +.btn-group > .btn.active { + z-index: 2; +} +.btn-group .dropdown-toggle:active, +.btn-group.open .dropdown-toggle { + outline: 0; +} +.btn-group > .btn + .dropdown-toggle { + padding-left: 8px; + padding-right: 8px; + -webkit-box-shadow: inset 1px 0 0 rgba(255,255,255,.125), inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05); + -moz-box-shadow: inset 1px 0 0 rgba(255,255,255,.125), inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05); + box-shadow: inset 1px 0 0 rgba(255,255,255,.125), inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05); + *padding-top: 5px; + *padding-bottom: 5px; +} +.btn-group > .btn-mini + .dropdown-toggle { + padding-left: 5px; + padding-right: 5px; + *padding-top: 2px; + *padding-bottom: 2px; +} +.btn-group > .btn-small + .dropdown-toggle { + *padding-top: 5px; + *padding-bottom: 4px; +} +.btn-group > .btn-large + .dropdown-toggle { + padding-left: 12px; + padding-right: 12px; + *padding-top: 7px; + *padding-bottom: 7px; +} +.btn-group.open .dropdown-toggle { + background-image: none; + -webkit-box-shadow: inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05); + -moz-box-shadow: inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05); + box-shadow: inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05); +} +.btn-group.open .btn.dropdown-toggle { + background-color: #e6e6e6; +} +.btn-group.open .btn-primary.dropdown-toggle { + background-color: #0044cc; +} +.btn-group.open .btn-warning.dropdown-toggle { + background-color: #f89406; +} +.btn-group.open .btn-danger.dropdown-toggle { + background-color: #bd362f; +} +.btn-group.open .btn-success.dropdown-toggle { + background-color: #51a351; +} +.btn-group.open .btn-info.dropdown-toggle { + background-color: #2f96b4; +} +.btn-group.open .btn-inverse.dropdown-toggle { + background-color: #222222; +} +.btn .caret { + margin-top: 8px; + margin-left: 0; +} +.btn-large .caret { + margin-top: 6px; +} +.btn-large .caret { + border-left-width: 5px; + border-right-width: 5px; + border-top-width: 5px; +} +.btn-mini .caret, +.btn-small .caret { + margin-top: 8px; +} +.dropup .btn-large .caret { + border-bottom-width: 5px; +} +.btn-primary .caret, +.btn-warning .caret, +.btn-danger .caret, +.btn-info .caret, +.btn-success .caret, +.btn-inverse .caret { + border-top-color: #ffffff; + border-bottom-color: #ffffff; +} +.btn-group-vertical { + display: inline-block; + *display: inline; + /* IE7 inline-block hack */ + *zoom: 1; +} +.btn-group-vertical > .btn { + display: block; + float: none; + max-width: 100%; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} +.btn-group-vertical > .btn + .btn { + margin-left: 0; + margin-top: -1px; +} +.btn-group-vertical > .btn:first-child { + -webkit-border-radius: 4px 4px 0 0; + -moz-border-radius: 4px 4px 0 0; + border-radius: 4px 4px 0 0; +} +.btn-group-vertical > .btn:last-child { + -webkit-border-radius: 0 0 4px 4px; + -moz-border-radius: 0 0 4px 4px; + border-radius: 0 0 4px 4px; +} +.btn-group-vertical > .btn-large:first-child { + -webkit-border-radius: 6px 6px 0 0; + -moz-border-radius: 6px 6px 0 0; + border-radius: 6px 6px 0 0; +} +.btn-group-vertical > .btn-large:last-child { + -webkit-border-radius: 0 0 6px 6px; + -moz-border-radius: 0 0 6px 6px; + border-radius: 0 0 6px 6px; +} +.alert { + padding: 8px 35px 8px 14px; + margin-bottom: 21px; + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); + background-color: #fcf8e3; + border: 1px solid #fbeed5; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} +.alert, +.alert h4 { + color: #c09853; +} +.alert h4 { + margin: 0; +} +.alert .close { + position: relative; + top: -2px; + right: -21px; + line-height: 21px; +} +.alert-success { + background-color: #dff0d8; + border-color: #d6e9c6; + color: #468847; +} +.alert-success h4 { + color: #468847; +} +.alert-danger, +.alert-error { + background-color: #f2dede; + border-color: #eed3d7; + color: #b94a48; +} +.alert-danger h4, +.alert-error h4 { + color: #b94a48; +} +.alert-info { + background-color: #d9edf7; + border-color: #bce8f1; + color: #3a87ad; +} +.alert-info h4 { + color: #3a87ad; +} +.alert-block { + padding-top: 14px; + padding-bottom: 14px; +} +.alert-block > p, +.alert-block > ul { + margin-bottom: 0; +} +.alert-block p + p { + margin-top: 5px; +} +.nav { + margin-left: 0; + margin-bottom: 21px; + list-style: none; +} +.nav > li > a { + display: block; +} +.nav > li > a:hover, +.nav > li > a:focus { + text-decoration: none; + background-color: #eeeeee; +} +.nav > li > a > img { + max-width: none; +} +.nav > .pull-right { + float: right; +} +.nav-header { + display: block; + padding: 3px 15px; + font-size: 11px; + font-weight: bold; + line-height: 21px; + color: #999999; + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); + text-transform: uppercase; +} +.nav li + .nav-header { + margin-top: 9px; +} +.nav-list { + padding-left: 15px; + padding-right: 15px; + margin-bottom: 0; +} +.nav-list > li > a, +.nav-list .nav-header { + margin-left: -15px; + margin-right: -15px; + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); +} +.nav-list > li > a { + padding: 3px 15px; +} +.nav-list > .active > a, +.nav-list > .active > a:hover, +.nav-list > .active > a:focus { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2); + background-color: #0088cc; +} +.nav-list [class^="icon-"], +.nav-list [class*=" icon-"] { + margin-right: 2px; +} +.nav-list .divider { + *width: 100%; + height: 1px; + margin: 9.5px 1px; + *margin: -5px 0 5px; + overflow: hidden; + background-color: #e5e5e5; + border-bottom: 1px solid #ffffff; +} +.nav-tabs, +.nav-pills { + *zoom: 1; +} +.nav-tabs:before, +.nav-pills:before, +.nav-tabs:after, +.nav-pills:after { + display: table; + content: ""; + line-height: 0; +} +.nav-tabs:after, +.nav-pills:after { + clear: both; +} +.nav-tabs > li, +.nav-pills > li { + float: left; +} +.nav-tabs > li > a, +.nav-pills > li > a { + padding-right: 12px; + padding-left: 12px; + margin-right: 2px; + line-height: 14px; +} +.nav-tabs { + border-bottom: 1px solid #ddd; +} +.nav-tabs > li { + margin-bottom: -1px; +} +.nav-tabs > li > a { + padding-top: 8px; + padding-bottom: 8px; + line-height: 21px; + border: 1px solid transparent; + -webkit-border-radius: 4px 4px 0 0; + -moz-border-radius: 4px 4px 0 0; + border-radius: 4px 4px 0 0; +} +.nav-tabs > li > a:hover, +.nav-tabs > li > a:focus { + border-color: #eeeeee #eeeeee #dddddd; +} +.nav-tabs > .active > a, +.nav-tabs > .active > a:hover, +.nav-tabs > .active > a:focus { + color: #555555; + background-color: #ffffff; + border: 1px solid #ddd; + border-bottom-color: transparent; + cursor: default; +} +.nav-pills > li > a { + padding-top: 8px; + padding-bottom: 8px; + margin-top: 2px; + margin-bottom: 2px; + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + border-radius: 5px; +} +.nav-pills > .active > a, +.nav-pills > .active > a:hover, +.nav-pills > .active > a:focus { + color: #ffffff; + background-color: #0088cc; +} +.nav-stacked > li { + float: none; +} +.nav-stacked > li > a { + margin-right: 0; +} +.nav-tabs.nav-stacked { + border-bottom: 0; +} +.nav-tabs.nav-stacked > li > a { + border: 1px solid #ddd; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} +.nav-tabs.nav-stacked > li:first-child > a { + -webkit-border-top-right-radius: 4px; + -moz-border-radius-topright: 4px; + border-top-right-radius: 4px; + -webkit-border-top-left-radius: 4px; + -moz-border-radius-topleft: 4px; + border-top-left-radius: 4px; +} +.nav-tabs.nav-stacked > li:last-child > a { + -webkit-border-bottom-right-radius: 4px; + -moz-border-radius-bottomright: 4px; + border-bottom-right-radius: 4px; + -webkit-border-bottom-left-radius: 4px; + -moz-border-radius-bottomleft: 4px; + border-bottom-left-radius: 4px; +} +.nav-tabs.nav-stacked > li > a:hover, +.nav-tabs.nav-stacked > li > a:focus { + border-color: #ddd; + z-index: 2; +} +.nav-pills.nav-stacked > li > a { + margin-bottom: 3px; +} +.nav-pills.nav-stacked > li:last-child > a { + margin-bottom: 1px; +} +.nav-tabs .dropdown-menu { + -webkit-border-radius: 0 0 6px 6px; + -moz-border-radius: 0 0 6px 6px; + border-radius: 0 0 6px 6px; +} +.nav-pills .dropdown-menu { + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; +} +.nav .dropdown-toggle .caret { + border-top-color: #0088cc; + border-bottom-color: #0088cc; + margin-top: 6px; +} +.nav .dropdown-toggle:hover .caret, +.nav .dropdown-toggle:focus .caret { + border-top-color: #005580; + border-bottom-color: #005580; +} +/* move down carets for tabs */ +.nav-tabs .dropdown-toggle .caret { + margin-top: 8px; +} +.nav .active .dropdown-toggle .caret { + border-top-color: #fff; + border-bottom-color: #fff; +} +.nav-tabs .active .dropdown-toggle .caret { + border-top-color: #555555; + border-bottom-color: #555555; +} +.nav > .dropdown.active > a:hover, +.nav > .dropdown.active > a:focus { + cursor: pointer; +} +.nav-tabs .open .dropdown-toggle, +.nav-pills .open .dropdown-toggle, +.nav > li.dropdown.open.active > a:hover, +.nav > li.dropdown.open.active > a:focus { + color: #ffffff; + background-color: #999999; + border-color: #999999; +} +.nav li.dropdown.open .caret, +.nav li.dropdown.open.active .caret, +.nav li.dropdown.open a:hover .caret, +.nav li.dropdown.open a:focus .caret { + border-top-color: #ffffff; + border-bottom-color: #ffffff; + opacity: 1; + filter: alpha(opacity=100); +} +.tabs-stacked .open > a:hover, +.tabs-stacked .open > a:focus { + border-color: #999999; +} +.tabbable { + *zoom: 1; +} +.tabbable:before, +.tabbable:after { + display: table; + content: ""; + line-height: 0; +} +.tabbable:after { + clear: both; +} +.tab-content { + overflow: auto; +} +.tabs-below > .nav-tabs, +.tabs-right > .nav-tabs, +.tabs-left > .nav-tabs { + border-bottom: 0; +} +.tab-content > .tab-pane, +.pill-content > .pill-pane { + display: none; +} +.tab-content > .active, +.pill-content > .active { + display: block; +} +.tabs-below > .nav-tabs { + border-top: 1px solid #ddd; +} +.tabs-below > .nav-tabs > li { + margin-top: -1px; + margin-bottom: 0; +} +.tabs-below > .nav-tabs > li > a { + -webkit-border-radius: 0 0 4px 4px; + -moz-border-radius: 0 0 4px 4px; + border-radius: 0 0 4px 4px; +} +.tabs-below > .nav-tabs > li > a:hover, +.tabs-below > .nav-tabs > li > a:focus { + border-bottom-color: transparent; + border-top-color: #ddd; +} +.tabs-below > .nav-tabs > .active > a, +.tabs-below > .nav-tabs > .active > a:hover, +.tabs-below > .nav-tabs > .active > a:focus { + border-color: transparent #ddd #ddd #ddd; +} +.tabs-left > .nav-tabs > li, +.tabs-right > .nav-tabs > li { + float: none; +} +.tabs-left > .nav-tabs > li > a, +.tabs-right > .nav-tabs > li > a { + min-width: 74px; + margin-right: 0; + margin-bottom: 3px; +} +.tabs-left > .nav-tabs { + float: left; + margin-right: 19px; + border-right: 1px solid #ddd; +} +.tabs-left > .nav-tabs > li > a { + margin-right: -1px; + -webkit-border-radius: 4px 0 0 4px; + -moz-border-radius: 4px 0 0 4px; + border-radius: 4px 0 0 4px; +} +.tabs-left > .nav-tabs > li > a:hover, +.tabs-left > .nav-tabs > li > a:focus { + border-color: #eeeeee #dddddd #eeeeee #eeeeee; +} +.tabs-left > .nav-tabs .active > a, +.tabs-left > .nav-tabs .active > a:hover, +.tabs-left > .nav-tabs .active > a:focus { + border-color: #ddd transparent #ddd #ddd; + *border-right-color: #ffffff; +} +.tabs-right > .nav-tabs { + float: right; + margin-left: 19px; + border-left: 1px solid #ddd; +} +.tabs-right > .nav-tabs > li > a { + margin-left: -1px; + -webkit-border-radius: 0 4px 4px 0; + -moz-border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; +} +.tabs-right > .nav-tabs > li > a:hover, +.tabs-right > .nav-tabs > li > a:focus { + border-color: #eeeeee #eeeeee #eeeeee #dddddd; +} +.tabs-right > .nav-tabs .active > a, +.tabs-right > .nav-tabs .active > a:hover, +.tabs-right > .nav-tabs .active > a:focus { + border-color: #ddd #ddd #ddd transparent; + *border-left-color: #ffffff; +} +.nav > .disabled > a { + color: #999999; +} +.nav > .disabled > a:hover, +.nav > .disabled > a:focus { + text-decoration: none; + background-color: transparent; + cursor: default; +} +.navbar { + overflow: visible; + margin-bottom: 21px; + *position: relative; + *z-index: 2; +} +.navbar-inner { + min-height: 40px; + padding-left: 20px; + padding-right: 20px; + background-color: #fafafa; + background-image: -moz-linear-gradient(top, #ffffff, #f2f2f2); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#f2f2f2)); + background-image: -webkit-linear-gradient(top, #ffffff, #f2f2f2); + background-image: -o-linear-gradient(top, #ffffff, #f2f2f2); + background-image: linear-gradient(to bottom, #ffffff, #f2f2f2); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff2f2f2', GradientType=0); + border: 1px solid #d4d4d4; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065); + -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065); + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065); + *zoom: 1; +} +.navbar-inner:before, +.navbar-inner:after { + display: table; + content: ""; + line-height: 0; +} +.navbar-inner:after { + clear: both; +} +.navbar .container { + width: auto; +} +.nav-collapse.collapse { + height: auto; + overflow: visible; +} +.navbar .brand { + float: left; + display: block; + padding: 9.5px 20px 9.5px; + margin-left: -20px; + font-size: 20px; + font-weight: 200; + color: #777777; + text-shadow: 0 1px 0 #ffffff; +} +.navbar .brand:hover, +.navbar .brand:focus { + text-decoration: none; +} +.navbar-text { + margin-bottom: 0; + line-height: 40px; + color: #777777; +} +.navbar-link { + color: #777777; +} +.navbar-link:hover, +.navbar-link:focus { + color: #333333; +} +.navbar .divider-vertical { + height: 40px; + margin: 0 9px; + border-left: 1px solid #f2f2f2; + border-right: 1px solid #ffffff; +} +.navbar .btn, +.navbar .btn-group { + margin-top: 5px; +} +.navbar .btn-group .btn, +.navbar .input-prepend .btn, +.navbar .input-append .btn, +.navbar .input-prepend .btn-group, +.navbar .input-append .btn-group { + margin-top: 0; +} +.navbar-form { + margin-bottom: 0; + *zoom: 1; +} +.navbar-form:before, +.navbar-form:after { + display: table; + content: ""; + line-height: 0; +} +.navbar-form:after { + clear: both; +} +.navbar-form input, +.navbar-form select, +.navbar-form .radio, +.navbar-form .checkbox { + margin-top: 5px; +} +.navbar-form input, +.navbar-form select, +.navbar-form .btn { + display: inline-block; + margin-bottom: 0; +} +.navbar-form input[type="image"], +.navbar-form input[type="checkbox"], +.navbar-form input[type="radio"] { + margin-top: 3px; +} +.navbar-form .input-append, +.navbar-form .input-prepend { + margin-top: 5px; + white-space: nowrap; +} +.navbar-form .input-append input, +.navbar-form .input-prepend input { + margin-top: 0; +} +.navbar-search { + position: relative; + float: left; + margin-top: 5px; + margin-bottom: 0; +} +.navbar-search .search-query { + margin-bottom: 0; + padding: 4px 14px; + font-family: 'Source Sans Pro', sans-serif; + font-size: 13px; + font-weight: normal; + line-height: 1; + -webkit-border-radius: 15px; + -moz-border-radius: 15px; + border-radius: 15px; +} +.navbar-static-top { + position: static; + margin-bottom: 0; +} +.navbar-static-top .navbar-inner { + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} +.navbar-fixed-top, +.navbar-fixed-bottom { + position: fixed; + right: 0; + left: 0; + z-index: 1030; + margin-bottom: 0; +} +.navbar-fixed-top .navbar-inner, +.navbar-static-top .navbar-inner { + border-width: 0 0 1px; +} +.navbar-fixed-bottom .navbar-inner { + border-width: 1px 0 0; +} +.navbar-fixed-top .navbar-inner, +.navbar-fixed-bottom .navbar-inner { + padding-left: 0; + padding-right: 0; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} +.navbar-static-top .container, +.navbar-fixed-top .container, +.navbar-fixed-bottom .container { + width: 940px; +} +.navbar-fixed-top { + top: 0; +} +.navbar-fixed-top .navbar-inner, +.navbar-static-top .navbar-inner { + -webkit-box-shadow: 0 1px 10px rgba(0,0,0,.1); + -moz-box-shadow: 0 1px 10px rgba(0,0,0,.1); + box-shadow: 0 1px 10px rgba(0,0,0,.1); +} +.navbar-fixed-bottom { + bottom: 0; +} +.navbar-fixed-bottom .navbar-inner { + -webkit-box-shadow: 0 -1px 10px rgba(0,0,0,.1); + -moz-box-shadow: 0 -1px 10px rgba(0,0,0,.1); + box-shadow: 0 -1px 10px rgba(0,0,0,.1); +} +.navbar .nav { + position: relative; + left: 0; + display: block; + float: left; + margin: 0 10px 0 0; +} +.navbar .nav.pull-right { + float: right; + margin-right: 0; +} +.navbar .nav > li { + float: left; +} +.navbar .nav > li > a { + float: none; + padding: 9.5px 15px 9.5px; + color: #777777; + text-decoration: none; + text-shadow: 0 1px 0 #ffffff; +} +.navbar .nav .dropdown-toggle .caret { + margin-top: 8px; +} +.navbar .nav > li > a:focus, +.navbar .nav > li > a:hover { + background-color: transparent; + color: #333333; + text-decoration: none; +} +.navbar .nav > .active > a, +.navbar .nav > .active > a:hover, +.navbar .nav > .active > a:focus { + color: #555555; + text-decoration: none; + background-color: #e5e5e5; + -webkit-box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.125); + -moz-box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.125); + box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.125); +} +.navbar .btn-navbar { + display: none; + float: right; + padding: 7px 10px; + margin-left: 5px; + margin-right: 5px; + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #ededed; + background-image: -moz-linear-gradient(top, #f2f2f2, #e5e5e5); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f2f2f2), to(#e5e5e5)); + background-image: -webkit-linear-gradient(top, #f2f2f2, #e5e5e5); + background-image: -o-linear-gradient(top, #f2f2f2, #e5e5e5); + background-image: linear-gradient(to bottom, #f2f2f2, #e5e5e5); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2f2f2', endColorstr='#ffe5e5e5', GradientType=0); + border-color: #e5e5e5 #e5e5e5 #bfbfbf; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + *background-color: #e5e5e5; + /* Darken IE7 buttons by default so they stand out more given they won't have borders */ + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + -webkit-box-shadow: inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.075); + -moz-box-shadow: inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.075); + box-shadow: inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.075); +} +.navbar .btn-navbar:hover, +.navbar .btn-navbar:focus, +.navbar .btn-navbar:active, +.navbar .btn-navbar.active, +.navbar .btn-navbar.disabled, +.navbar .btn-navbar[disabled] { + color: #ffffff; + background-color: #e5e5e5; + *background-color: #d9d9d9; +} +.navbar .btn-navbar:active, +.navbar .btn-navbar.active { + background-color: #cccccc \9; +} +.navbar .btn-navbar .icon-bar { + display: block; + width: 18px; + height: 2px; + background-color: #f5f5f5; + -webkit-border-radius: 1px; + -moz-border-radius: 1px; + border-radius: 1px; + -webkit-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25); + -moz-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25); + box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25); +} +.btn-navbar .icon-bar + .icon-bar { + margin-top: 3px; +} +.navbar .nav > li > .dropdown-menu:before { + content: ''; + display: inline-block; + border-left: 7px solid transparent; + border-right: 7px solid transparent; + border-bottom: 7px solid #ccc; + border-bottom-color: rgba(0, 0, 0, 0.2); + position: absolute; + top: -7px; + left: 9px; +} +.navbar .nav > li > .dropdown-menu:after { + content: ''; + display: inline-block; + border-left: 6px solid transparent; + border-right: 6px solid transparent; + border-bottom: 6px solid #ffffff; + position: absolute; + top: -6px; + left: 10px; +} +.navbar-fixed-bottom .nav > li > .dropdown-menu:before { + border-top: 7px solid #ccc; + border-top-color: rgba(0, 0, 0, 0.2); + border-bottom: 0; + bottom: -7px; + top: auto; +} +.navbar-fixed-bottom .nav > li > .dropdown-menu:after { + border-top: 6px solid #ffffff; + border-bottom: 0; + bottom: -6px; + top: auto; +} +.navbar .nav li.dropdown > a:hover .caret, +.navbar .nav li.dropdown > a:focus .caret { + border-top-color: #333333; + border-bottom-color: #333333; +} +.navbar .nav li.dropdown.open > .dropdown-toggle, +.navbar .nav li.dropdown.active > .dropdown-toggle, +.navbar .nav li.dropdown.open.active > .dropdown-toggle { + background-color: #e5e5e5; + color: #555555; +} +.navbar .nav li.dropdown > .dropdown-toggle .caret { + border-top-color: #777777; + border-bottom-color: #777777; +} +.navbar .nav li.dropdown.open > .dropdown-toggle .caret, +.navbar .nav li.dropdown.active > .dropdown-toggle .caret, +.navbar .nav li.dropdown.open.active > .dropdown-toggle .caret { + border-top-color: #555555; + border-bottom-color: #555555; +} +.navbar .pull-right > li > .dropdown-menu, +.navbar .nav > li > .dropdown-menu.pull-right { + left: auto; + right: 0; +} +.navbar .pull-right > li > .dropdown-menu:before, +.navbar .nav > li > .dropdown-menu.pull-right:before { + left: auto; + right: 12px; +} +.navbar .pull-right > li > .dropdown-menu:after, +.navbar .nav > li > .dropdown-menu.pull-right:after { + left: auto; + right: 13px; +} +.navbar .pull-right > li > .dropdown-menu .dropdown-menu, +.navbar .nav > li > .dropdown-menu.pull-right .dropdown-menu { + left: auto; + right: 100%; + margin-left: 0; + margin-right: -1px; + -webkit-border-radius: 6px 0 6px 6px; + -moz-border-radius: 6px 0 6px 6px; + border-radius: 6px 0 6px 6px; +} +.navbar-inverse .navbar-inner { + background-color: #1b1b1b; + background-image: -moz-linear-gradient(top, #222222, #111111); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#222222), to(#111111)); + background-image: -webkit-linear-gradient(top, #222222, #111111); + background-image: -o-linear-gradient(top, #222222, #111111); + background-image: linear-gradient(to bottom, #222222, #111111); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222', endColorstr='#ff111111', GradientType=0); + border-color: #252525; +} +.navbar-inverse .brand, +.navbar-inverse .nav > li > a { + color: #999999; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); +} +.navbar-inverse .brand:hover, +.navbar-inverse .nav > li > a:hover, +.navbar-inverse .brand:focus, +.navbar-inverse .nav > li > a:focus { + color: #ffffff; +} +.navbar-inverse .brand { + color: #999999; +} +.navbar-inverse .navbar-text { + color: #999999; +} +.navbar-inverse .nav > li > a:focus, +.navbar-inverse .nav > li > a:hover { + background-color: transparent; + color: #ffffff; +} +.navbar-inverse .nav .active > a, +.navbar-inverse .nav .active > a:hover, +.navbar-inverse .nav .active > a:focus { + color: #ffffff; + background-color: #111111; +} +.navbar-inverse .navbar-link { + color: #999999; +} +.navbar-inverse .navbar-link:hover, +.navbar-inverse .navbar-link:focus { + color: #ffffff; +} +.navbar-inverse .divider-vertical { + border-left-color: #111111; + border-right-color: #222222; +} +.navbar-inverse .nav li.dropdown.open > .dropdown-toggle, +.navbar-inverse .nav li.dropdown.active > .dropdown-toggle, +.navbar-inverse .nav li.dropdown.open.active > .dropdown-toggle { + background-color: #111111; + color: #ffffff; +} +.navbar-inverse .nav li.dropdown > a:hover .caret, +.navbar-inverse .nav li.dropdown > a:focus .caret { + border-top-color: #ffffff; + border-bottom-color: #ffffff; +} +.navbar-inverse .nav li.dropdown > .dropdown-toggle .caret { + border-top-color: #999999; + border-bottom-color: #999999; +} +.navbar-inverse .nav li.dropdown.open > .dropdown-toggle .caret, +.navbar-inverse .nav li.dropdown.active > .dropdown-toggle .caret, +.navbar-inverse .nav li.dropdown.open.active > .dropdown-toggle .caret { + border-top-color: #ffffff; + border-bottom-color: #ffffff; +} +.navbar-inverse .navbar-search .search-query { + color: #ffffff; + background-color: #515151; + border-color: #111111; + -webkit-box-shadow: inset 0 1px 2px rgba(0,0,0,.1), 0 1px 0 rgba(255,255,255,.15); + -moz-box-shadow: inset 0 1px 2px rgba(0,0,0,.1), 0 1px 0 rgba(255,255,255,.15); + box-shadow: inset 0 1px 2px rgba(0,0,0,.1), 0 1px 0 rgba(255,255,255,.15); + -webkit-transition: none; + -moz-transition: none; + -o-transition: none; + transition: none; +} +.navbar-inverse .navbar-search .search-query:-moz-placeholder { + color: #cccccc; +} +.navbar-inverse .navbar-search .search-query:-ms-input-placeholder { + color: #cccccc; +} +.navbar-inverse .navbar-search .search-query::-webkit-input-placeholder { + color: #cccccc; +} +.navbar-inverse .navbar-search .search-query:focus, +.navbar-inverse .navbar-search .search-query.focused { + padding: 5px 15px; + color: #333333; + text-shadow: 0 1px 0 #ffffff; + background-color: #ffffff; + border: 0; + -webkit-box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); + -moz-box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); + box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); + outline: 0; +} +.navbar-inverse .btn-navbar { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #0e0e0e; + background-image: -moz-linear-gradient(top, #151515, #040404); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#151515), to(#040404)); + background-image: -webkit-linear-gradient(top, #151515, #040404); + background-image: -o-linear-gradient(top, #151515, #040404); + background-image: linear-gradient(to bottom, #151515, #040404); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff151515', endColorstr='#ff040404', GradientType=0); + border-color: #040404 #040404 #000000; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + *background-color: #040404; + /* Darken IE7 buttons by default so they stand out more given they won't have borders */ + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); +} +.navbar-inverse .btn-navbar:hover, +.navbar-inverse .btn-navbar:focus, +.navbar-inverse .btn-navbar:active, +.navbar-inverse .btn-navbar.active, +.navbar-inverse .btn-navbar.disabled, +.navbar-inverse .btn-navbar[disabled] { + color: #ffffff; + background-color: #040404; + *background-color: #000000; +} +.navbar-inverse .btn-navbar:active, +.navbar-inverse .btn-navbar.active { + background-color: #000000 \9; +} +.breadcrumb { + padding: 8px 15px; + margin: 0 0 21px; + list-style: none; + background-color: #f5f5f5; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} +.breadcrumb > li { + display: inline-block; + *display: inline; + /* IE7 inline-block hack */ + *zoom: 1; + text-shadow: 0 1px 0 #ffffff; +} +.breadcrumb > li > .divider { + padding: 0 5px; + color: #ccc; +} +.breadcrumb > .active { + color: #999999; +} +.pagination { + margin: 21px 0; +} +.pagination ul { + display: inline-block; + *display: inline; + /* IE7 inline-block hack */ + *zoom: 1; + margin-left: 0; + margin-bottom: 0; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); +} +.pagination ul > li { + display: inline; +} +.pagination ul > li > a, +.pagination ul > li > span { + float: left; + padding: 4px 12px; + line-height: 21px; + text-decoration: none; + background-color: #ffffff; + border: 1px solid #dddddd; + border-left-width: 0; +} +.pagination ul > li > a:hover, +.pagination ul > li > a:focus, +.pagination ul > .active > a, +.pagination ul > .active > span { + background-color: #f5f5f5; +} +.pagination ul > .active > a, +.pagination ul > .active > span { + color: #999999; + cursor: default; +} +.pagination ul > .disabled > span, +.pagination ul > .disabled > a, +.pagination ul > .disabled > a:hover, +.pagination ul > .disabled > a:focus { + color: #999999; + background-color: transparent; + cursor: default; +} +.pagination ul > li:first-child > a, +.pagination ul > li:first-child > span { + border-left-width: 1px; + -webkit-border-top-left-radius: 4px; + -moz-border-radius-topleft: 4px; + border-top-left-radius: 4px; + -webkit-border-bottom-left-radius: 4px; + -moz-border-radius-bottomleft: 4px; + border-bottom-left-radius: 4px; +} +.pagination ul > li:last-child > a, +.pagination ul > li:last-child > span { + -webkit-border-top-right-radius: 4px; + -moz-border-radius-topright: 4px; + border-top-right-radius: 4px; + -webkit-border-bottom-right-radius: 4px; + -moz-border-radius-bottomright: 4px; + border-bottom-right-radius: 4px; +} +.pagination-centered { + text-align: center; +} +.pagination-right { + text-align: right; +} +.pagination-large ul > li > a, +.pagination-large ul > li > span { + padding: 11px 19px; + font-size: 20px; +} +.pagination-large ul > li:first-child > a, +.pagination-large ul > li:first-child > span { + -webkit-border-top-left-radius: 6px; + -moz-border-radius-topleft: 6px; + border-top-left-radius: 6px; + -webkit-border-bottom-left-radius: 6px; + -moz-border-radius-bottomleft: 6px; + border-bottom-left-radius: 6px; +} +.pagination-large ul > li:last-child > a, +.pagination-large ul > li:last-child > span { + -webkit-border-top-right-radius: 6px; + -moz-border-radius-topright: 6px; + border-top-right-radius: 6px; + -webkit-border-bottom-right-radius: 6px; + -moz-border-radius-bottomright: 6px; + border-bottom-right-radius: 6px; +} +.pagination-mini ul > li:first-child > a, +.pagination-small ul > li:first-child > a, +.pagination-mini ul > li:first-child > span, +.pagination-small ul > li:first-child > span { + -webkit-border-top-left-radius: 3px; + -moz-border-radius-topleft: 3px; + border-top-left-radius: 3px; + -webkit-border-bottom-left-radius: 3px; + -moz-border-radius-bottomleft: 3px; + border-bottom-left-radius: 3px; +} +.pagination-mini ul > li:last-child > a, +.pagination-small ul > li:last-child > a, +.pagination-mini ul > li:last-child > span, +.pagination-small ul > li:last-child > span { + -webkit-border-top-right-radius: 3px; + -moz-border-radius-topright: 3px; + border-top-right-radius: 3px; + -webkit-border-bottom-right-radius: 3px; + -moz-border-radius-bottomright: 3px; + border-bottom-right-radius: 3px; +} +.pagination-small ul > li > a, +.pagination-small ul > li > span { + padding: 2px 10px; + font-size: 13.6px; +} +.pagination-mini ul > li > a, +.pagination-mini ul > li > span { + padding: 0 6px; + font-size: 12px; +} +.pager { + margin: 21px 0; + list-style: none; + text-align: center; + *zoom: 1; +} +.pager:before, +.pager:after { + display: table; + content: ""; + line-height: 0; +} +.pager:after { + clear: both; +} +.pager li { + display: inline; +} +.pager li > a, +.pager li > span { + display: inline-block; + padding: 5px 14px; + background-color: #fff; + border: 1px solid #ddd; + -webkit-border-radius: 15px; + -moz-border-radius: 15px; + border-radius: 15px; +} +.pager li > a:hover, +.pager li > a:focus { + text-decoration: none; + background-color: #f5f5f5; +} +.pager .next > a, +.pager .next > span { + float: right; +} +.pager .previous > a, +.pager .previous > span { + float: left; +} +.pager .disabled > a, +.pager .disabled > a:hover, +.pager .disabled > a:focus, +.pager .disabled > span { + color: #999999; + background-color: #fff; + cursor: default; +} +.modal-backdrop { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1040; + background-color: #000000; +} +.modal-backdrop.fade { + opacity: 0; +} +.modal-backdrop, +.modal-backdrop.fade.in { + opacity: 0.8; + filter: alpha(opacity=80); +} +.modal { + position: fixed; + top: 10%; + left: 50%; + z-index: 1050; + width: 560px; + margin-left: -280px; + background-color: #ffffff; + border: 1px solid #999; + border: 1px solid rgba(0, 0, 0, 0.3); + *border: 1px solid #999; + /* IE6-7 */ + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; + -webkit-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); + -moz-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); + box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); + -webkit-background-clip: padding-box; + -moz-background-clip: padding-box; + background-clip: padding-box; + outline: none; +} +.modal.fade { + -webkit-transition: opacity .3s linear, top .3s ease-out; + -moz-transition: opacity .3s linear, top .3s ease-out; + -o-transition: opacity .3s linear, top .3s ease-out; + transition: opacity .3s linear, top .3s ease-out; + top: -25%; +} +.modal.fade.in { + top: 10%; +} +.modal-header { + padding: 9px 15px; + border-bottom: 1px solid #eee; +} +.modal-header .close { + margin-top: 2px; +} +.modal-header h3 { + margin: 0; + line-height: 30px; +} +.modal-body { + position: relative; + overflow-y: auto; + max-height: 400px; + padding: 15px; +} +.modal-form { + margin-bottom: 0; +} +.modal-footer { + padding: 14px 15px 15px; + margin-bottom: 0; + text-align: right; + background-color: #f5f5f5; + border-top: 1px solid #ddd; + -webkit-border-radius: 0 0 6px 6px; + -moz-border-radius: 0 0 6px 6px; + border-radius: 0 0 6px 6px; + -webkit-box-shadow: inset 0 1px 0 #ffffff; + -moz-box-shadow: inset 0 1px 0 #ffffff; + box-shadow: inset 0 1px 0 #ffffff; + *zoom: 1; +} +.modal-footer:before, +.modal-footer:after { + display: table; + content: ""; + line-height: 0; +} +.modal-footer:after { + clear: both; +} +.modal-footer .btn + .btn { + margin-left: 5px; + margin-bottom: 0; +} +.modal-footer .btn-group .btn + .btn { + margin-left: -1px; +} +.modal-footer .btn-block + .btn-block { + margin-left: 0; +} +.tooltip { + position: absolute; + z-index: 1030; + display: block; + visibility: visible; + font-size: 11px; + line-height: 1.4; + opacity: 0; + filter: alpha(opacity=0); +} +.tooltip.in { + opacity: 0.8; + filter: alpha(opacity=80); +} +.tooltip.top { + margin-top: -3px; + padding: 5px 0; +} +.tooltip.right { + margin-left: 3px; + padding: 0 5px; +} +.tooltip.bottom { + margin-top: 3px; + padding: 5px 0; +} +.tooltip.left { + margin-left: -3px; + padding: 0 5px; +} +.tooltip-inner { + max-width: 200px; + padding: 8px; + color: #ffffff; + text-align: center; + text-decoration: none; + background-color: #000000; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} +.tooltip-arrow { + position: absolute; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} +.tooltip.top .tooltip-arrow { + bottom: 0; + left: 50%; + margin-left: -5px; + border-width: 5px 5px 0; + border-top-color: #000000; +} +.tooltip.right .tooltip-arrow { + top: 50%; + left: 0; + margin-top: -5px; + border-width: 5px 5px 5px 0; + border-right-color: #000000; +} +.tooltip.left .tooltip-arrow { + top: 50%; + right: 0; + margin-top: -5px; + border-width: 5px 0 5px 5px; + border-left-color: #000000; +} +.tooltip.bottom .tooltip-arrow { + top: 0; + left: 50%; + margin-left: -5px; + border-width: 0 5px 5px; + border-bottom-color: #000000; +} +.popover { + position: absolute; + top: 0; + left: 0; + z-index: 1010; + display: none; + max-width: 276px; + padding: 1px; + text-align: left; + background-color: #ffffff; + -webkit-background-clip: padding-box; + -moz-background-clip: padding; + background-clip: padding-box; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, 0.2); + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; + -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + white-space: normal; +} +.popover.top { + margin-top: -10px; +} +.popover.right { + margin-left: 10px; +} +.popover.bottom { + margin-top: 10px; +} +.popover.left { + margin-left: -10px; +} +.popover-title { + margin: 0; + padding: 8px 14px; + font-size: 14px; + font-weight: normal; + line-height: 18px; + background-color: #f7f7f7; + border-bottom: 1px solid #ebebeb; + -webkit-border-radius: 5px 5px 0 0; + -moz-border-radius: 5px 5px 0 0; + border-radius: 5px 5px 0 0; +} +.popover-title:empty { + display: none; +} +.popover-content { + padding: 9px 14px; +} +.popover .arrow, +.popover .arrow:after { + position: absolute; + display: block; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} +.popover .arrow { + border-width: 11px; +} +.popover .arrow:after { + border-width: 10px; + content: ""; +} +.popover.top .arrow { + left: 50%; + margin-left: -11px; + border-bottom-width: 0; + border-top-color: #999; + border-top-color: rgba(0, 0, 0, 0.25); + bottom: -11px; +} +.popover.top .arrow:after { + bottom: 1px; + margin-left: -10px; + border-bottom-width: 0; + border-top-color: #ffffff; +} +.popover.right .arrow { + top: 50%; + left: -11px; + margin-top: -11px; + border-left-width: 0; + border-right-color: #999; + border-right-color: rgba(0, 0, 0, 0.25); +} +.popover.right .arrow:after { + left: 1px; + bottom: -10px; + border-left-width: 0; + border-right-color: #ffffff; +} +.popover.bottom .arrow { + left: 50%; + margin-left: -11px; + border-top-width: 0; + border-bottom-color: #999; + border-bottom-color: rgba(0, 0, 0, 0.25); + top: -11px; +} +.popover.bottom .arrow:after { + top: 1px; + margin-left: -10px; + border-top-width: 0; + border-bottom-color: #ffffff; +} +.popover.left .arrow { + top: 50%; + right: -11px; + margin-top: -11px; + border-right-width: 0; + border-left-color: #999; + border-left-color: rgba(0, 0, 0, 0.25); +} +.popover.left .arrow:after { + right: 1px; + border-right-width: 0; + border-left-color: #ffffff; + bottom: -10px; +} +.thumbnails { + margin-left: -20px; + list-style: none; + *zoom: 1; +} +.thumbnails:before, +.thumbnails:after { + display: table; + content: ""; + line-height: 0; +} +.thumbnails:after { + clear: both; +} +.row-fluid .thumbnails { + margin-left: 0; +} +.thumbnails > li { + float: left; + margin-bottom: 21px; + margin-left: 20px; +} +.thumbnail { + display: block; + padding: 4px; + line-height: 21px; + border: 1px solid #ddd; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055); + -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055); + -webkit-transition: all 0.2s ease-in-out; + -moz-transition: all 0.2s ease-in-out; + -o-transition: all 0.2s ease-in-out; + transition: all 0.2s ease-in-out; +} +a.thumbnail:hover, +a.thumbnail:focus { + border-color: #0088cc; + -webkit-box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25); + -moz-box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25); + box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25); +} +.thumbnail > img { + display: block; + max-width: 100%; + margin-left: auto; + margin-right: auto; +} +.thumbnail .caption { + padding: 9px; + color: #555555; +} +.media, +.media-body { + overflow: hidden; + *overflow: visible; + zoom: 1; +} +.media, +.media .media { + margin-top: 15px; +} +.media:first-child { + margin-top: 0; +} +.media-object { + display: block; +} +.media-heading { + margin: 0 0 5px; +} +.media > .pull-left { + margin-right: 10px; +} +.media > .pull-right { + margin-left: 10px; +} +.media-list { + margin-left: 0; + list-style: none; +} +.label, +.badge { + display: inline-block; + padding: 2px 4px; + font-size: 13.536px; + font-weight: bold; + line-height: 14px; + color: #ffffff; + vertical-align: baseline; + white-space: nowrap; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #999999; +} +.label { + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} +.badge { + padding-left: 9px; + padding-right: 9px; + -webkit-border-radius: 9px; + -moz-border-radius: 9px; + border-radius: 9px; +} +.label:empty, +.badge:empty { + display: none; +} +a.label:hover, +a.label:focus, +a.badge:hover, +a.badge:focus { + color: #ffffff; + text-decoration: none; + cursor: pointer; +} +.label-important, +.badge-important { + background-color: #b94a48; +} +.label-important[href], +.badge-important[href] { + background-color: #953b39; +} +.label-warning, +.badge-warning { + background-color: #f89406; +} +.label-warning[href], +.badge-warning[href] { + background-color: #c67605; +} +.label-success, +.badge-success { + background-color: #468847; +} +.label-success[href], +.badge-success[href] { + background-color: #356635; +} +.label-info, +.badge-info { + background-color: #3a87ad; +} +.label-info[href], +.badge-info[href] { + background-color: #2d6987; +} +.label-inverse, +.badge-inverse { + background-color: #333333; +} +.label-inverse[href], +.badge-inverse[href] { + background-color: #1a1a1a; +} +.btn .label, +.btn .badge { + position: relative; + top: -1px; +} +.btn-mini .label, +.btn-mini .badge { + top: 0; +} +@-webkit-keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} +@-moz-keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} +@-ms-keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} +@-o-keyframes progress-bar-stripes { + from { + background-position: 0 0; + } + to { + background-position: 40px 0; + } +} +@keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} +.progress { + overflow: hidden; + height: 21px; + margin-bottom: 21px; + background-color: #f7f7f7; + background-image: -moz-linear-gradient(top, #f5f5f5, #f9f9f9); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f5f5f5), to(#f9f9f9)); + background-image: -webkit-linear-gradient(top, #f5f5f5, #f9f9f9); + background-image: -o-linear-gradient(top, #f5f5f5, #f9f9f9); + background-image: linear-gradient(to bottom, #f5f5f5, #f9f9f9); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#fff9f9f9', GradientType=0); + -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); + -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} +.progress .bar { + width: 0%; + height: 100%; + color: #ffffff; + float: left; + font-size: 12px; + text-align: center; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #0e90d2; + background-image: -moz-linear-gradient(top, #149bdf, #0480be); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#149bdf), to(#0480be)); + background-image: -webkit-linear-gradient(top, #149bdf, #0480be); + background-image: -o-linear-gradient(top, #149bdf, #0480be); + background-image: linear-gradient(to bottom, #149bdf, #0480be); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff149bdf', endColorstr='#ff0480be', GradientType=0); + -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); + -moz-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + -webkit-transition: width 0.6s ease; + -moz-transition: width 0.6s ease; + -o-transition: width 0.6s ease; + transition: width 0.6s ease; +} +.progress .bar + .bar { + -webkit-box-shadow: inset 1px 0 0 rgba(0,0,0,.15), inset 0 -1px 0 rgba(0,0,0,.15); + -moz-box-shadow: inset 1px 0 0 rgba(0,0,0,.15), inset 0 -1px 0 rgba(0,0,0,.15); + box-shadow: inset 1px 0 0 rgba(0,0,0,.15), inset 0 -1px 0 rgba(0,0,0,.15); +} +.progress-striped .bar { + background-color: #149bdf; + background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + -webkit-background-size: 40px 40px; + -moz-background-size: 40px 40px; + -o-background-size: 40px 40px; + background-size: 40px 40px; +} +.progress.active .bar { + -webkit-animation: progress-bar-stripes 2s linear infinite; + -moz-animation: progress-bar-stripes 2s linear infinite; + -ms-animation: progress-bar-stripes 2s linear infinite; + -o-animation: progress-bar-stripes 2s linear infinite; + animation: progress-bar-stripes 2s linear infinite; +} +.progress-danger .bar, +.progress .bar-danger { + background-color: #dd514c; + background-image: -moz-linear-gradient(top, #ee5f5b, #c43c35); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#c43c35)); + background-image: -webkit-linear-gradient(top, #ee5f5b, #c43c35); + background-image: -o-linear-gradient(top, #ee5f5b, #c43c35); + background-image: linear-gradient(to bottom, #ee5f5b, #c43c35); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b', endColorstr='#ffc43c35', GradientType=0); +} +.progress-danger.progress-striped .bar, +.progress-striped .bar-danger { + background-color: #ee5f5b; + background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} +.progress-success .bar, +.progress .bar-success { + background-color: #5eb95e; + background-image: -moz-linear-gradient(top, #62c462, #57a957); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#57a957)); + background-image: -webkit-linear-gradient(top, #62c462, #57a957); + background-image: -o-linear-gradient(top, #62c462, #57a957); + background-image: linear-gradient(to bottom, #62c462, #57a957); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462', endColorstr='#ff57a957', GradientType=0); +} +.progress-success.progress-striped .bar, +.progress-striped .bar-success { + background-color: #62c462; + background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} +.progress-info .bar, +.progress .bar-info { + background-color: #4bb1cf; + background-image: -moz-linear-gradient(top, #5bc0de, #339bb9); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#339bb9)); + background-image: -webkit-linear-gradient(top, #5bc0de, #339bb9); + background-image: -o-linear-gradient(top, #5bc0de, #339bb9); + background-image: linear-gradient(to bottom, #5bc0de, #339bb9); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff339bb9', GradientType=0); +} +.progress-info.progress-striped .bar, +.progress-striped .bar-info { + background-color: #5bc0de; + background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} +.progress-warning .bar, +.progress .bar-warning { + background-color: #faa732; + background-image: -moz-linear-gradient(top, #fbb450, #f89406); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406)); + background-image: -webkit-linear-gradient(top, #fbb450, #f89406); + background-image: -o-linear-gradient(top, #fbb450, #f89406); + background-image: linear-gradient(to bottom, #fbb450, #f89406); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450', endColorstr='#fff89406', GradientType=0); +} +.progress-warning.progress-striped .bar, +.progress-striped .bar-warning { + background-color: #fbb450; + background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} +.accordion { + margin-bottom: 21px; +} +.accordion-group { + margin-bottom: 2px; + border: 1px solid #e5e5e5; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} +.accordion-heading { + border-bottom: 0; +} +.accordion-heading .accordion-toggle { + display: block; + padding: 8px 15px; +} +.accordion-toggle { + cursor: pointer; +} +.accordion-inner { + padding: 9px 15px; + border-top: 1px solid #e5e5e5; +} +.carousel { + position: relative; + margin-bottom: 21px; + line-height: 1; +} +.carousel-inner { + overflow: hidden; + width: 100%; + position: relative; +} +.carousel-inner > .item { + display: none; + position: relative; + -webkit-transition: 0.6s ease-in-out left; + -moz-transition: 0.6s ease-in-out left; + -o-transition: 0.6s ease-in-out left; + transition: 0.6s ease-in-out left; +} +.carousel-inner > .item > img, +.carousel-inner > .item > a > img { + display: block; + line-height: 1; +} +.carousel-inner > .active, +.carousel-inner > .next, +.carousel-inner > .prev { + display: block; +} +.carousel-inner > .active { + left: 0; +} +.carousel-inner > .next, +.carousel-inner > .prev { + position: absolute; + top: 0; + width: 100%; +} +.carousel-inner > .next { + left: 100%; +} +.carousel-inner > .prev { + left: -100%; +} +.carousel-inner > .next.left, +.carousel-inner > .prev.right { + left: 0; +} +.carousel-inner > .active.left { + left: -100%; +} +.carousel-inner > .active.right { + left: 100%; +} +.carousel-control { + position: absolute; + top: 40%; + left: 15px; + width: 40px; + height: 40px; + margin-top: -20px; + font-size: 60px; + font-weight: 100; + line-height: 30px; + color: #ffffff; + text-align: center; + background: #222222; + border: 3px solid #ffffff; + -webkit-border-radius: 23px; + -moz-border-radius: 23px; + border-radius: 23px; + opacity: 0.5; + filter: alpha(opacity=50); +} +.carousel-control.right { + left: auto; + right: 15px; +} +.carousel-control:hover, +.carousel-control:focus { + color: #ffffff; + text-decoration: none; + opacity: 0.9; + filter: alpha(opacity=90); +} +.carousel-indicators { + position: absolute; + top: 15px; + right: 15px; + z-index: 5; + margin: 0; + list-style: none; +} +.carousel-indicators li { + display: block; + float: left; + width: 10px; + height: 10px; + margin-left: 5px; + text-indent: -999px; + background-color: #ccc; + background-color: rgba(255, 255, 255, 0.25); + border-radius: 5px; +} +.carousel-indicators .active { + background-color: #fff; +} +.carousel-caption { + position: absolute; + left: 0; + right: 0; + bottom: 0; + padding: 15px; + background: #333333; + background: rgba(0, 0, 0, 0.75); +} +.carousel-caption h4, +.carousel-caption p { + color: #ffffff; + line-height: 21px; +} +.carousel-caption h4 { + margin: 0 0 5px; +} +.carousel-caption p { + margin-bottom: 0; +} +.hero-unit { + padding: 60px; + margin-bottom: 30px; + font-size: 18px; + font-weight: 200; + line-height: 31.5px; + color: inherit; + background-color: #eeeeee; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; +} +.hero-unit h1 { + margin-bottom: 0; + font-size: 60px; + line-height: 1; + color: inherit; + letter-spacing: -1px; +} +.hero-unit li { + line-height: 31.5px; +} +.pull-right { + float: right; +} +.pull-left { + float: left; +} +.hide { + display: none; +} +.show { + display: block; +} +.invisible { + visibility: hidden; +} +.affix { + position: fixed; +} +/*! + * Bootstrap Responsive v2.3.1 + * + * Copyright 2012 Twitter, Inc + * Licensed under the Apache License v2.0 + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Designed and built with all the love in the world @twitter by @mdo and @fat. + */ +@-ms-viewport { + width: device-width; +} +.hidden { + display: none; + visibility: hidden; +} +.visible-phone { + display: none !important; +} +.visible-tablet { + display: none !important; +} +.hidden-desktop { + display: none !important; +} +.visible-desktop { + display: inherit !important; +} +@media (min-width: 768px) and (max-width: 979px) { + .hidden-desktop { + display: inherit !important; + } + .visible-desktop { + display: none !important ; + } + .visible-tablet { + display: inherit !important; + } + .hidden-tablet { + display: none !important; + } +} +@media (max-width: 767px) { + .hidden-desktop { + display: inherit !important; + } + .visible-desktop { + display: none !important; + } + .visible-phone { + display: inherit !important; + } + .hidden-phone { + display: none !important; + } +} +.visible-print { + display: none !important; +} +@media print { + .visible-print { + display: inherit !important; + } + .hidden-print { + display: none !important; + } +} +@media (min-width: 1200px) { + .row { + margin-left: -30px; + *zoom: 1; + } + .row:before, + .row:after { + display: table; + content: ""; + line-height: 0; + } + .row:after { + clear: both; + } + [class*="span"] { + float: left; + min-height: 1px; + margin-left: 30px; + } + .container, + .navbar-static-top .container, + .navbar-fixed-top .container, + .navbar-fixed-bottom .container { + width: 1170px; + } + .span12 { + width: 1170px; + } + .span11 { + width: 1070px; + } + .span10 { + width: 970px; + } + .span9 { + width: 870px; + } + .span8 { + width: 770px; + } + .span7 { + width: 670px; + } + .span6 { + width: 570px; + } + .span5 { + width: 470px; + } + .span4 { + width: 370px; + } + .span3 { + width: 270px; + } + .span2 { + width: 170px; + } + .span1 { + width: 70px; + } + .offset12 { + margin-left: 1230px; + } + .offset11 { + margin-left: 1130px; + } + .offset10 { + margin-left: 1030px; + } + .offset9 { + margin-left: 930px; + } + .offset8 { + margin-left: 830px; + } + .offset7 { + margin-left: 730px; + } + .offset6 { + margin-left: 630px; + } + .offset5 { + margin-left: 530px; + } + .offset4 { + margin-left: 430px; + } + .offset3 { + margin-left: 330px; + } + .offset2 { + margin-left: 230px; + } + .offset1 { + margin-left: 130px; + } + .row-fluid { + width: 100%; + *zoom: 1; + } + .row-fluid:before, + .row-fluid:after { + display: table; + content: ""; + line-height: 0; + } + .row-fluid:after { + clear: both; + } + .row-fluid [class*="span"] { + display: block; + width: 100%; + min-height: 31px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + float: left; + margin-left: 2.56410256%; + *margin-left: 2.51091107%; + } + .row-fluid [class*="span"]:first-child { + margin-left: 0; + } + .row-fluid .controls-row [class*="span"] + [class*="span"] { + margin-left: 2.56410256%; + } + .row-fluid .span12 { + width: 100%; + *width: 99.94680851%; + } + .row-fluid .span11 { + width: 91.45299145%; + *width: 91.39979996%; + } + .row-fluid .span10 { + width: 82.90598291%; + *width: 82.85279142%; + } + .row-fluid .span9 { + width: 74.35897436%; + *width: 74.30578287%; + } + .row-fluid .span8 { + width: 65.81196581%; + *width: 65.75877432%; + } + .row-fluid .span7 { + width: 57.26495726%; + *width: 57.21176578%; + } + .row-fluid .span6 { + width: 48.71794872%; + *width: 48.66475723%; + } + .row-fluid .span5 { + width: 40.17094017%; + *width: 40.11774868%; + } + .row-fluid .span4 { + width: 31.62393162%; + *width: 31.57074013%; + } + .row-fluid .span3 { + width: 23.07692308%; + *width: 23.02373159%; + } + .row-fluid .span2 { + width: 14.52991453%; + *width: 14.47672304%; + } + .row-fluid .span1 { + width: 5.98290598%; + *width: 5.92971449%; + } + .row-fluid .offset12 { + margin-left: 105.12820513%; + *margin-left: 105.02182215%; + } + .row-fluid .offset12:first-child { + margin-left: 102.56410256%; + *margin-left: 102.45771959%; + } + .row-fluid .offset11 { + margin-left: 96.58119658%; + *margin-left: 96.4748136%; + } + .row-fluid .offset11:first-child { + margin-left: 94.01709402%; + *margin-left: 93.91071104%; + } + .row-fluid .offset10 { + margin-left: 88.03418803%; + *margin-left: 87.92780506%; + } + .row-fluid .offset10:first-child { + margin-left: 85.47008547%; + *margin-left: 85.36370249%; + } + .row-fluid .offset9 { + margin-left: 79.48717949%; + *margin-left: 79.38079651%; + } + .row-fluid .offset9:first-child { + margin-left: 76.92307692%; + *margin-left: 76.81669394%; + } + .row-fluid .offset8 { + margin-left: 70.94017094%; + *margin-left: 70.83378796%; + } + .row-fluid .offset8:first-child { + margin-left: 68.37606838%; + *margin-left: 68.2696854%; + } + .row-fluid .offset7 { + margin-left: 62.39316239%; + *margin-left: 62.28677941%; + } + .row-fluid .offset7:first-child { + margin-left: 59.82905983%; + *margin-left: 59.72267685%; + } + .row-fluid .offset6 { + margin-left: 53.84615385%; + *margin-left: 53.73977087%; + } + .row-fluid .offset6:first-child { + margin-left: 51.28205128%; + *margin-left: 51.1756683%; + } + .row-fluid .offset5 { + margin-left: 45.2991453%; + *margin-left: 45.19276232%; + } + .row-fluid .offset5:first-child { + margin-left: 42.73504274%; + *margin-left: 42.62865976%; + } + .row-fluid .offset4 { + margin-left: 36.75213675%; + *margin-left: 36.64575377%; + } + .row-fluid .offset4:first-child { + margin-left: 34.18803419%; + *margin-left: 34.08165121%; + } + .row-fluid .offset3 { + margin-left: 28.20512821%; + *margin-left: 28.09874523%; + } + .row-fluid .offset3:first-child { + margin-left: 25.64102564%; + *margin-left: 25.53464266%; + } + .row-fluid .offset2 { + margin-left: 19.65811966%; + *margin-left: 19.55173668%; + } + .row-fluid .offset2:first-child { + margin-left: 17.09401709%; + *margin-left: 16.98763412%; + } + .row-fluid .offset1 { + margin-left: 11.11111111%; + *margin-left: 11.00472813%; + } + .row-fluid .offset1:first-child { + margin-left: 8.54700855%; + *margin-left: 8.44062557%; + } + input, + textarea, + .uneditable-input { + margin-left: 0; + } + .controls-row [class*="span"] + [class*="span"] { + margin-left: 30px; + } + input.span12, + textarea.span12, + .uneditable-input.span12 { + width: 1156px; + } + input.span11, + textarea.span11, + .uneditable-input.span11 { + width: 1056px; + } + input.span10, + textarea.span10, + .uneditable-input.span10 { + width: 956px; + } + input.span9, + textarea.span9, + .uneditable-input.span9 { + width: 856px; + } + input.span8, + textarea.span8, + .uneditable-input.span8 { + width: 756px; + } + input.span7, + textarea.span7, + .uneditable-input.span7 { + width: 656px; + } + input.span6, + textarea.span6, + .uneditable-input.span6 { + width: 556px; + } + input.span5, + textarea.span5, + .uneditable-input.span5 { + width: 456px; + } + input.span4, + textarea.span4, + .uneditable-input.span4 { + width: 356px; + } + input.span3, + textarea.span3, + .uneditable-input.span3 { + width: 256px; + } + input.span2, + textarea.span2, + .uneditable-input.span2 { + width: 156px; + } + input.span1, + textarea.span1, + .uneditable-input.span1 { + width: 56px; + } + .thumbnails { + margin-left: -30px; + } + .thumbnails > li { + margin-left: 30px; + } + .row-fluid .thumbnails { + margin-left: 0; + } +} +@media (min-width: 768px) and (max-width: 979px) { + .row { + margin-left: -20px; + *zoom: 1; + } + .row:before, + .row:after { + display: table; + content: ""; + line-height: 0; + } + .row:after { + clear: both; + } + [class*="span"] { + float: left; + min-height: 1px; + margin-left: 20px; + } + .container, + .navbar-static-top .container, + .navbar-fixed-top .container, + .navbar-fixed-bottom .container { + width: 724px; + } + .span12 { + width: 724px; + } + .span11 { + width: 662px; + } + .span10 { + width: 600px; + } + .span9 { + width: 538px; + } + .span8 { + width: 476px; + } + .span7 { + width: 414px; + } + .span6 { + width: 352px; + } + .span5 { + width: 290px; + } + .span4 { + width: 228px; + } + .span3 { + width: 166px; + } + .span2 { + width: 104px; + } + .span1 { + width: 42px; + } + .offset12 { + margin-left: 764px; + } + .offset11 { + margin-left: 702px; + } + .offset10 { + margin-left: 640px; + } + .offset9 { + margin-left: 578px; + } + .offset8 { + margin-left: 516px; + } + .offset7 { + margin-left: 454px; + } + .offset6 { + margin-left: 392px; + } + .offset5 { + margin-left: 330px; + } + .offset4 { + margin-left: 268px; + } + .offset3 { + margin-left: 206px; + } + .offset2 { + margin-left: 144px; + } + .offset1 { + margin-left: 82px; + } + .row-fluid { + width: 100%; + *zoom: 1; + } + .row-fluid:before, + .row-fluid:after { + display: table; + content: ""; + line-height: 0; + } + .row-fluid:after { + clear: both; + } + .row-fluid [class*="span"] { + display: block; + width: 100%; + min-height: 31px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + float: left; + margin-left: 2.76243094%; + *margin-left: 2.70923945%; + } + .row-fluid [class*="span"]:first-child { + margin-left: 0; + } + .row-fluid .controls-row [class*="span"] + [class*="span"] { + margin-left: 2.76243094%; + } + .row-fluid .span12 { + width: 100%; + *width: 99.94680851%; + } + .row-fluid .span11 { + width: 91.43646409%; + *width: 91.3832726%; + } + .row-fluid .span10 { + width: 82.87292818%; + *width: 82.81973669%; + } + .row-fluid .span9 { + width: 74.30939227%; + *width: 74.25620078%; + } + .row-fluid .span8 { + width: 65.74585635%; + *width: 65.69266486%; + } + .row-fluid .span7 { + width: 57.18232044%; + *width: 57.12912895%; + } + .row-fluid .span6 { + width: 48.61878453%; + *width: 48.56559304%; + } + .row-fluid .span5 { + width: 40.05524862%; + *width: 40.00205713%; + } + .row-fluid .span4 { + width: 31.49171271%; + *width: 31.43852122%; + } + .row-fluid .span3 { + width: 22.9281768%; + *width: 22.87498531%; + } + .row-fluid .span2 { + width: 14.36464088%; + *width: 14.31144939%; + } + .row-fluid .span1 { + width: 5.80110497%; + *width: 5.74791348%; + } + .row-fluid .offset12 { + margin-left: 105.52486188%; + *margin-left: 105.4184789%; + } + .row-fluid .offset12:first-child { + margin-left: 102.76243094%; + *margin-left: 102.65604796%; + } + .row-fluid .offset11 { + margin-left: 96.96132597%; + *margin-left: 96.85494299%; + } + .row-fluid .offset11:first-child { + margin-left: 94.19889503%; + *margin-left: 94.09251205%; + } + .row-fluid .offset10 { + margin-left: 88.39779006%; + *margin-left: 88.29140708%; + } + .row-fluid .offset10:first-child { + margin-left: 85.63535912%; + *margin-left: 85.52897614%; + } + .row-fluid .offset9 { + margin-left: 79.83425414%; + *margin-left: 79.72787116%; + } + .row-fluid .offset9:first-child { + margin-left: 77.0718232%; + *margin-left: 76.96544023%; + } + .row-fluid .offset8 { + margin-left: 71.27071823%; + *margin-left: 71.16433525%; + } + .row-fluid .offset8:first-child { + margin-left: 68.50828729%; + *margin-left: 68.40190431%; + } + .row-fluid .offset7 { + margin-left: 62.70718232%; + *margin-left: 62.60079934%; + } + .row-fluid .offset7:first-child { + margin-left: 59.94475138%; + *margin-left: 59.8383684%; + } + .row-fluid .offset6 { + margin-left: 54.14364641%; + *margin-left: 54.03726343%; + } + .row-fluid .offset6:first-child { + margin-left: 51.38121547%; + *margin-left: 51.27483249%; + } + .row-fluid .offset5 { + margin-left: 45.5801105%; + *margin-left: 45.47372752%; + } + .row-fluid .offset5:first-child { + margin-left: 42.81767956%; + *margin-left: 42.71129658%; + } + .row-fluid .offset4 { + margin-left: 37.01657459%; + *margin-left: 36.91019161%; + } + .row-fluid .offset4:first-child { + margin-left: 34.25414365%; + *margin-left: 34.14776067%; + } + .row-fluid .offset3 { + margin-left: 28.45303867%; + *margin-left: 28.3466557%; + } + .row-fluid .offset3:first-child { + margin-left: 25.69060773%; + *margin-left: 25.58422476%; + } + .row-fluid .offset2 { + margin-left: 19.88950276%; + *margin-left: 19.78311978%; + } + .row-fluid .offset2:first-child { + margin-left: 17.12707182%; + *margin-left: 17.02068884%; + } + .row-fluid .offset1 { + margin-left: 11.32596685%; + *margin-left: 11.21958387%; + } + .row-fluid .offset1:first-child { + margin-left: 8.56353591%; + *margin-left: 8.45715293%; + } + input, + textarea, + .uneditable-input { + margin-left: 0; + } + .controls-row [class*="span"] + [class*="span"] { + margin-left: 20px; + } + input.span12, + textarea.span12, + .uneditable-input.span12 { + width: 710px; + } + input.span11, + textarea.span11, + .uneditable-input.span11 { + width: 648px; + } + input.span10, + textarea.span10, + .uneditable-input.span10 { + width: 586px; + } + input.span9, + textarea.span9, + .uneditable-input.span9 { + width: 524px; + } + input.span8, + textarea.span8, + .uneditable-input.span8 { + width: 462px; + } + input.span7, + textarea.span7, + .uneditable-input.span7 { + width: 400px; + } + input.span6, + textarea.span6, + .uneditable-input.span6 { + width: 338px; + } + input.span5, + textarea.span5, + .uneditable-input.span5 { + width: 276px; + } + input.span4, + textarea.span4, + .uneditable-input.span4 { + width: 214px; + } + input.span3, + textarea.span3, + .uneditable-input.span3 { + width: 152px; + } + input.span2, + textarea.span2, + .uneditable-input.span2 { + width: 90px; + } + input.span1, + textarea.span1, + .uneditable-input.span1 { + width: 28px; + } +} +@media (max-width: 767px) { + body { + padding-left: 20px; + padding-right: 20px; + } + .navbar-fixed-top, + .navbar-fixed-bottom, + .navbar-static-top { + margin-left: -20px; + margin-right: -20px; + } + .container-fluid { + padding: 0; + } + .dl-horizontal dt { + float: none; + clear: none; + width: auto; + text-align: left; + } + .dl-horizontal dd { + margin-left: 0; + } + .container { + width: auto; + } + .row-fluid { + width: 100%; + } + .row, + .thumbnails { + margin-left: 0; + } + .thumbnails > li { + float: none; + margin-left: 0; + } + [class*="span"], + .uneditable-input[class*="span"], + .row-fluid [class*="span"] { + float: none; + display: block; + width: 100%; + margin-left: 0; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + } + .span12, + .row-fluid .span12 { + width: 100%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + } + .row-fluid [class*="offset"]:first-child { + margin-left: 0; + } + .input-large, + .input-xlarge, + .input-xxlarge, + input[class*="span"], + select[class*="span"], + textarea[class*="span"], + .uneditable-input { + display: block; + width: 100%; + min-height: 31px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + } + .input-prepend input, + .input-append input, + .input-prepend input[class*="span"], + .input-append input[class*="span"] { + display: inline-block; + width: auto; + } + .controls-row [class*="span"] + [class*="span"] { + margin-left: 0; + } + .modal { + position: fixed; + top: 20px; + left: 20px; + right: 20px; + width: auto; + margin: 0; + } + .modal.fade { + top: -100px; + } + .modal.fade.in { + top: 20px; + } +} +@media (max-width: 480px) { + .nav-collapse { + -webkit-transform: translate3d(0, 0, 0); + } + .page-header h1 small { + display: block; + line-height: 21px; + } + input[type="checkbox"], + input[type="radio"] { + border: 1px solid #ccc; + } + .form-horizontal .control-label { + float: none; + width: auto; + padding-top: 0; + text-align: left; + } + .form-horizontal .controls { + margin-left: 0; + } + .form-horizontal .control-list { + padding-top: 0; + } + .form-horizontal .form-actions { + padding-left: 10px; + padding-right: 10px; + } + .media .pull-left, + .media .pull-right { + float: none; + display: block; + margin-bottom: 10px; + } + .media-object { + margin-right: 0; + margin-left: 0; + } + .modal { + top: 10px; + left: 10px; + right: 10px; + } + .modal-header .close { + padding: 10px; + margin: -10px; + } + .carousel-caption { + position: static; + } +} +@media (max-width: 979px) { + body { + padding-top: 0; + } + .navbar-fixed-top, + .navbar-fixed-bottom { + position: static; + } + .navbar-fixed-top { + margin-bottom: 21px; + } + .navbar-fixed-bottom { + margin-top: 21px; + } + .navbar-fixed-top .navbar-inner, + .navbar-fixed-bottom .navbar-inner { + padding: 5px; + } + .navbar .container { + width: auto; + padding: 0; + } + .navbar .brand { + padding-left: 10px; + padding-right: 10px; + margin: 0 0 0 -5px; + } + .nav-collapse { + clear: both; + } + .nav-collapse .nav { + float: none; + margin: 0 0 10.5px; + } + .nav-collapse .nav > li { + float: none; + } + .nav-collapse .nav > li > a { + margin-bottom: 2px; + } + .nav-collapse .nav > .divider-vertical { + display: none; + } + .nav-collapse .nav .nav-header { + color: #777777; + text-shadow: none; + } + .nav-collapse .nav > li > a, + .nav-collapse .dropdown-menu a { + padding: 9px 15px; + font-weight: bold; + color: #777777; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; + } + .nav-collapse .btn { + padding: 4px 10px 4px; + font-weight: normal; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + } + .nav-collapse .dropdown-menu li + li a { + margin-bottom: 2px; + } + .nav-collapse .nav > li > a:hover, + .nav-collapse .nav > li > a:focus, + .nav-collapse .dropdown-menu a:hover, + .nav-collapse .dropdown-menu a:focus { + background-color: #f2f2f2; + } + .navbar-inverse .nav-collapse .nav > li > a, + .navbar-inverse .nav-collapse .dropdown-menu a { + color: #999999; + } + .navbar-inverse .nav-collapse .nav > li > a:hover, + .navbar-inverse .nav-collapse .nav > li > a:focus, + .navbar-inverse .nav-collapse .dropdown-menu a:hover, + .navbar-inverse .nav-collapse .dropdown-menu a:focus { + background-color: #111111; + } + .nav-collapse.in .btn-group { + margin-top: 5px; + padding: 0; + } + .nav-collapse .dropdown-menu { + position: static; + top: auto; + left: auto; + float: none; + display: none; + max-width: none; + margin: 0 15px; + padding: 0; + background-color: transparent; + border: none; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; + } + .nav-collapse .open > .dropdown-menu { + display: block; + } + .nav-collapse .dropdown-menu:before, + .nav-collapse .dropdown-menu:after { + display: none; + } + .nav-collapse .dropdown-menu .divider { + display: none; + } + .nav-collapse .nav > li > .dropdown-menu:before, + .nav-collapse .nav > li > .dropdown-menu:after { + display: none; + } + .nav-collapse .navbar-form, + .nav-collapse .navbar-search { + float: none; + padding: 10.5px 15px; + margin: 10.5px 0; + border-top: 1px solid #f2f2f2; + border-bottom: 1px solid #f2f2f2; + -webkit-box-shadow: inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.1); + -moz-box-shadow: inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.1); + box-shadow: inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.1); + } + .navbar-inverse .nav-collapse .navbar-form, + .navbar-inverse .nav-collapse .navbar-search { + border-top-color: #111111; + border-bottom-color: #111111; + } + .navbar .nav-collapse .nav.pull-right { + float: none; + margin-left: 0; + } + .nav-collapse, + .nav-collapse.collapse { + overflow: hidden; + height: 0; + } + .navbar .btn-navbar { + display: block; + } + .navbar-static .navbar-inner { + padding-left: 10px; + padding-right: 10px; + } +} +@media (min-width: 979px + 1) { + .nav-collapse.collapse { + height: auto !important; + overflow: visible !important; + } +} diff --git a/5.0/_static/css/spc-extend.css b/5.0/_static/css/spc-extend.css new file mode 100644 index 000000000..264a62bfb --- /dev/null +++ b/5.0/_static/css/spc-extend.css @@ -0,0 +1,92 @@ +body { + background-color: white; +} +.container { + width: 90%; +} +.underline { + border-bottom: 1.5px solid #eeeeee; +} +.header { + margin-top: 15px; + margin-bottom: 15px; + margin-left: 0px; + margin-right: 0px; +} +.spc-navbar { + margin-top: 15px; + margin-bottom: 5px; + margin-left: 0px; + margin-right: 0px; +} +.spc-navbar .nav-pills { + margin-bottom: 0px; + font-size: 12px; +} +.spc-navbar .nav-pills > li > a { + padding-top: 2.5px; + padding-bottom: 2.5px; +} +.spc-page-title h1, +.spc-page-title h2, +.spc-page-title h3, +.spc-page-title h4 { + font-weight: normal; + border-bottom: 1.5px solid #eeeeee; +} +.tags .btn { + border: none; + font-size: 9.5px; + font-weight: bold; +} +.spc-search-result-title h1, +.spc-search-result-title h2, +.spc-search-result-title h3, +.spc-search-result-title h4 { + font-weight: normal; +} +.spc-snippet-header { + margin-bottom: 5px; +} +.spc-snippet-info { + padding-top: 10px; +} +.spc-snippet-info .dl-horizontal { + margin: 5px; +} +.spc-snippet-info .dl-horizontal dt { + font-weight: normal; +} +.spc-snippet-body { + padding: 10px; +} +.spc-snippet-body .accordion-group { + border: none; +} +.spc-snippet-body .accordion-heading { + text-transform: uppercase; + font-size: 14px; + border-bottom: 1px solid #e5e5e5; +} +.spc-snippet-body .accordion-heading .accordion-toggle { + padding-top: 10px; + padding-bottom: 5px; +} +.spc-rightsidebar { + color: #555555; +} +.spc-rightsidebar .navigation { + padding: 2px 10px; + font-size: 11.9px; +} +.spc-rightsidebar .navigation .nav-title { + font-weight: bold; + text-transform: uppercase; +} +.spc-rightsidebar .navigation li { + margin: 5px; +} +.footer { + padding: 5px; + font-size: small; +} diff --git a/5.0/_static/doctools.js b/5.0/_static/doctools.js new file mode 100644 index 000000000..b33f87fcb --- /dev/null +++ b/5.0/_static/doctools.js @@ -0,0 +1,314 @@ +/* + * doctools.js + * ~~~~~~~~~~~ + * + * Sphinx JavaScript utilities for all documentation. + * + * :copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +/** + * select a different prefix for underscore + */ +$u = _.noConflict(); + +/** + * make the code below compatible with browsers without + * an installed firebug like debugger +if (!window.console || !console.firebug) { + var names = ["log", "debug", "info", "warn", "error", "assert", "dir", + "dirxml", "group", "groupEnd", "time", "timeEnd", "count", "trace", + "profile", "profileEnd"]; + window.console = {}; + for (var i = 0; i < names.length; ++i) + window.console[names[i]] = function() {}; +} + */ + +/** + * small helper function to urldecode strings + */ +jQuery.urldecode = function(x) { + return decodeURIComponent(x).replace(/\+/g, ' '); +}; + +/** + * small helper function to urlencode strings + */ +jQuery.urlencode = encodeURIComponent; + +/** + * This function returns the parsed url parameters of the + * current request. Multiple values per key are supported, + * it will always return arrays of strings for the value parts. + */ +jQuery.getQueryParameters = function(s) { + if (typeof s === 'undefined') + s = document.location.search; + var parts = s.substr(s.indexOf('?') + 1).split('&'); + var result = {}; + for (var i = 0; i < parts.length; i++) { + var tmp = parts[i].split('=', 2); + var key = jQuery.urldecode(tmp[0]); + var value = jQuery.urldecode(tmp[1]); + if (key in result) + result[key].push(value); + else + result[key] = [value]; + } + return result; +}; + +/** + * highlight a given string on a jquery object by wrapping it in + * span elements with the given class name. + */ +jQuery.fn.highlightText = function(text, className) { + function highlight(node, addItems) { + if (node.nodeType === 3) { + var val = node.nodeValue; + var pos = val.toLowerCase().indexOf(text); + if (pos >= 0 && + !jQuery(node.parentNode).hasClass(className) && + !jQuery(node.parentNode).hasClass("nohighlight")) { + var span; + var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg"); + if (isInSVG) { + span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); + } else { + span = document.createElement("span"); + span.className = className; + } + span.appendChild(document.createTextNode(val.substr(pos, text.length))); + node.parentNode.insertBefore(span, node.parentNode.insertBefore( + document.createTextNode(val.substr(pos + text.length)), + node.nextSibling)); + node.nodeValue = val.substr(0, pos); + if (isInSVG) { + var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect"); + var bbox = node.parentElement.getBBox(); + rect.x.baseVal.value = bbox.x; + rect.y.baseVal.value = bbox.y; + rect.width.baseVal.value = bbox.width; + rect.height.baseVal.value = bbox.height; + rect.setAttribute('class', className); + addItems.push({ + "parent": node.parentNode, + "target": rect}); + } + } + } + else if (!jQuery(node).is("button, select, textarea")) { + jQuery.each(node.childNodes, function() { + highlight(this, addItems); + }); + } + } + var addItems = []; + var result = this.each(function() { + highlight(this, addItems); + }); + for (var i = 0; i < addItems.length; ++i) { + jQuery(addItems[i].parent).before(addItems[i].target); + } + return result; +}; + +/* + * backward compatibility for jQuery.browser + * This will be supported until firefox bug is fixed. + */ +if (!jQuery.browser) { + jQuery.uaMatch = function(ua) { + ua = ua.toLowerCase(); + + var match = /(chrome)[ \/]([\w.]+)/.exec(ua) || + /(webkit)[ \/]([\w.]+)/.exec(ua) || + /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || + /(msie) ([\w.]+)/.exec(ua) || + ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || + []; + + return { + browser: match[ 1 ] || "", + version: match[ 2 ] || "0" + }; + }; + jQuery.browser = {}; + jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true; +} + +/** + * Small JavaScript module for the documentation. + */ +var Documentation = { + + init : function() { + this.fixFirefoxAnchorBug(); + this.highlightSearchWords(); + this.initIndexTable(); + if (DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) { + this.initOnKeyListeners(); + } + }, + + /** + * i18n support + */ + TRANSLATIONS : {}, + PLURAL_EXPR : function(n) { return n === 1 ? 0 : 1; }, + LOCALE : 'unknown', + + // gettext and ngettext don't access this so that the functions + // can safely bound to a different name (_ = Documentation.gettext) + gettext : function(string) { + var translated = Documentation.TRANSLATIONS[string]; + if (typeof translated === 'undefined') + return string; + return (typeof translated === 'string') ? translated : translated[0]; + }, + + ngettext : function(singular, plural, n) { + var translated = Documentation.TRANSLATIONS[singular]; + if (typeof translated === 'undefined') + return (n == 1) ? singular : plural; + return translated[Documentation.PLURALEXPR(n)]; + }, + + addTranslations : function(catalog) { + for (var key in catalog.messages) + this.TRANSLATIONS[key] = catalog.messages[key]; + this.PLURAL_EXPR = new Function('n', 'return +(' + catalog.plural_expr + ')'); + this.LOCALE = catalog.locale; + }, + + /** + * add context elements like header anchor links + */ + addContextElements : function() { + $('div[id] > :header:first').each(function() { + $('\u00B6'). + attr('href', '#' + this.id). + attr('title', _('Permalink to this headline')). + appendTo(this); + }); + $('dt[id]').each(function() { + $('\u00B6'). + attr('href', '#' + this.id). + attr('title', _('Permalink to this definition')). + appendTo(this); + }); + }, + + /** + * workaround a firefox stupidity + * see: https://bugzilla.mozilla.org/show_bug.cgi?id=645075 + */ + fixFirefoxAnchorBug : function() { + if (document.location.hash && $.browser.mozilla) + window.setTimeout(function() { + document.location.href += ''; + }, 10); + }, + + /** + * highlight the search words provided in the url in the text + */ + highlightSearchWords : function() { + var params = $.getQueryParameters(); + var terms = (params.highlight) ? params.highlight[0].split(/\s+/) : []; + if (terms.length) { + var body = $('div.body'); + if (!body.length) { + body = $('body'); + } + window.setTimeout(function() { + $.each(terms, function() { + body.highlightText(this.toLowerCase(), 'highlighted'); + }); + }, 10); + $('') + .appendTo($('#searchbox')); + } + }, + + /** + * init the domain index toggle buttons + */ + initIndexTable : function() { + var togglers = $('img.toggler').click(function() { + var src = $(this).attr('src'); + var idnum = $(this).attr('id').substr(7); + $('tr.cg-' + idnum).toggle(); + if (src.substr(-9) === 'minus.png') + $(this).attr('src', src.substr(0, src.length-9) + 'plus.png'); + else + $(this).attr('src', src.substr(0, src.length-8) + 'minus.png'); + }).css('display', ''); + if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) { + togglers.click(); + } + }, + + /** + * helper function to hide the search marks again + */ + hideSearchWords : function() { + $('#searchbox .highlight-link').fadeOut(300); + $('span.highlighted').removeClass('highlighted'); + }, + + /** + * make the url absolute + */ + makeURL : function(relativeURL) { + return DOCUMENTATION_OPTIONS.URL_ROOT + '/' + relativeURL; + }, + + /** + * get the current relative url + */ + getCurrentURL : function() { + var path = document.location.pathname; + var parts = path.split(/\//); + $.each(DOCUMENTATION_OPTIONS.URL_ROOT.split(/\//), function() { + if (this === '..') + parts.pop(); + }); + var url = parts.join('/'); + return path.substring(url.lastIndexOf('/') + 1, path.length - 1); + }, + + initOnKeyListeners: function() { + $(document).keyup(function(event) { + var activeElementType = document.activeElement.tagName; + // don't navigate when in search box or textarea + if (activeElementType !== 'TEXTAREA' && activeElementType !== 'INPUT' && activeElementType !== 'SELECT') { + switch (event.keyCode) { + case 37: // left + var prevHref = $('link[rel="prev"]').prop('href'); + if (prevHref) { + window.location.href = prevHref; + return false; + } + case 39: // right + var nextHref = $('link[rel="next"]').prop('href'); + if (nextHref) { + window.location.href = nextHref; + return false; + } + } + } + }); + } +}; + +// quick alias for translations +_ = Documentation.gettext; + +$(document).ready(function() { + Documentation.init(); +}); diff --git a/5.0/_static/documentation_options.js b/5.0/_static/documentation_options.js new file mode 100644 index 000000000..3ceefbbee --- /dev/null +++ b/5.0/_static/documentation_options.js @@ -0,0 +1,11 @@ +var DOCUMENTATION_OPTIONS = { + URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'), + VERSION: 'unknown', + LANGUAGE: 'None', + COLLAPSE_INDEX: false, + BUILDER: 'html', + FILE_SUFFIX: '.html', + HAS_SOURCE: true, + SOURCELINK_SUFFIX: '.txt', + NAVIGATION_WITH_KEYS: false +}; \ No newline at end of file diff --git a/5.0/_static/enthought.css b/5.0/_static/enthought.css new file mode 100644 index 000000000..32ddcacb7 --- /dev/null +++ b/5.0/_static/enthought.css @@ -0,0 +1,412 @@ +/* -*- css -*- + * + * sphinxdoc.css_t + * ~~~~~~~~~~~~~~~ + * + * Sphinx stylesheet -- sphinxdoc theme. Originally created by + * Armin Ronacher for Werkzeug. + * + * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +@import url("basic.css"); + +/* @import url("css/scipy-central.css"); */ + +@font-face { + font-family: 'Source Code Pro'; + font-weight: 400; + font-style: normal; + font-stretch: normal; + src: local('Source Code Pro'), local('SourceCodePro-Regular'), url('fonts/SourceCodePro-Regular.otf.woff') format('woff'); +} + +@font-face { + font-family: 'Source Code Pro'; + font-weight: 600; + font-style: normal; + font-stretch: normal; + src: local('Source Code Pro Semibold'), local('SourceCodePro-Semibold'), url('fonts/SourceCodePro-Semibold.otf.woff') format('woff'); +} + +@font-face{ + font-family: 'Source Sans Pro'; + font-weight: 400; + font-style: normal; + font-stretch: normal; + src: local('Source Sans Pro'), local('SourceSansPro-Regular'), url('fonts/SourceSansPro-Regular.otf.woff') format('woff'); +} + +@font-face{ + font-family: 'Source Sans Pro'; + font-weight: 600; + font-style: normal; + font-stretch: normal; + src: local('Source Sans Pro Semibold'), local('SourceSansPro-Semibold'), url('fonts/SourceSansPro-Semibold.otf.woff') format('woff'); +} + +@font-face{ + font-family: 'Source Sans Pro'; + font-weight: 400; + font-style: italic; + font-stretch: normal; + src: local('Source Sans Pro Italic'), local('SourceSansPro-It'), url('fonts/SourceSansPro-It.otf.woff') format('woff'); +} + +@font-face{ + font-family: 'Source Sans Pro'; + font-weight: 600; + font-style: italic; + font-stretch: normal; + src: local('Source Sans Pro Semibold Italic'), local('SourceSansPro-SemiboldIt'), url('fonts/SourceSansPro-SemiboldIt.otf.woff') format('woff'); +} + +/* + * General tweaks + */ + +div.container-navbar-bottom { + margin-top: 0; +} + +div.container-navbar-bottom div.spc-navbar { + margin-top: 0; +} + +div.spc-navbar { + margin: 0; +} + +tt { + color: inherit; + font: inherit; +} + +tt.literal { + font-family: 'Source Code Pro', monospace; + padding-left: 2px; + background-color: rgb(242, 242, 242); +} + +a tt.literal { + border-bottom: none; + background-color: inherit; +} + +tt.xref { + border-bottom: none; + background-color: inherit; + font-weight: inherit; + padding-left: 0px; +} + +tt.descclassname { + font-family: 'Source Code Pro', monospace; +} + +tt.descname { + font-family: 'Source Code Pro', monospace; + font-size: 20px; +} + +code { + color: inherit; + font: inherit; + padding: 2px 0px; + border: inherit; +} + +code.literal { + font-family: monospace; + padding-left: 2px; + background-color: rgb(242, 242, 242); +} + +a code.literal { + border: none; + background-color: inherit; +} + +code.xref { + font-family: inherit; + border-bottom: none; + background-color: inherit; + padding-left: 0px; +} + +code.descname { + font-size: 16px; +} + +dl.class, +dl.function, +dl.data +{ + border-top: 2px solid #888; + padding-top: 1px; +} + +dl.attribute, +dl.classmethod, +dl.staticmethod, +dl.method +{ + border-top: 1px solid #aaa; + padding-top: 1px; +} + +dl.attribute > dt > tt.descname, +dl.classmethod > dt > tt.descname, +dl.staticmethod > dt > tt.descname, +dl.method > dt > tt.descname +{ + font-size: inherit; +} + +dl.class > dt, +dl.attribute > dt, +dl.data > dt, +dl.function > dt, +dl.exception > dt, +dl.classmethod > dt, +dl.staticmethod > dt, +dl.method > dt +{ + font-weight: normal; + /* Fake a hanging indent. If the text has to linewrap, it will indent more + * than the following
. + */ + text-indent: -60px; + padding-left: 60px; +} + +pre { + border-radius: 0; + border: none; + font-family: 'Source Code Pro', monospace; +} + +.nav-pills > .active > a, +.nav-pills > .active > a:hover, +.nav-pills > .active > a:focus { + color: #ffffff; + background-color: #253370; +} + +.nav-pills.pull-right > li { + float: right; +} + +a { + color: #253370; + text-decoration: none; +} + +/* + * Field lists + */ + +table.field-list { + border-collapse: collapse; + border-spacing: 5px; + margin-left: 1px; + border-left: 5px solid #DFDFDF !important; +} + +table.field-list th.field-name { + display: block; + text-align: left; + padding: 1px 8px 1px 5px; + white-space: nowrap; + background-color: #DFDFDF; +} + +table.field-list td.field-body { + display: block; + border-left: none !important; + padding-left: 1em; +} + +table.field-list td.field-body > ul { + padding-left: 1em; +} + +table.field-list td.field-body > p, +table.field-list td.field-body > ul > li > p +{ + display: inline; +} + +table.field-list td.field-body > p > strong { + font-style: normal; +} + +td.field-body blockquote { + border-left: none; + margin: 0; + padding-left: 30px; +} + +td.field-body blockquote p, +dl.class blockquote p, +dl.function blockquote p, +dl.classmethod blockquote p, +dl.staticmethod blockquote p, +dl.method blockquote p +{ + font-family: inherit; + font-size: inherit; + font-weight: inherit; + line-height: inherit; +} + + +/* + * Sidebars and top logo + */ + +div.sphinxsidebarwrapper { + overflow: hidden; +} + +div.sphinxsidebarwrapper p.logo { + text-align: center; +} + +div.spc-rightsidebar h3 { + font-size: 120%; + line-height: inherit; + border-bottom: 1px solid #656565; +} + +div.spc-rightsidebar h4 { + font-size: 110%; + line-height: inherit; + border-bottom: 1px solid #656565; +} + +form.search > input[type="text"] { + width: auto; +} + +/* + * Headers + */ + +h1 a { color: #333333; } +h2 a { color: #333333; } +h3 a { color: #333333; } +h4 a { color: #333333; } +h5 a { color: #333333; } +h6 a { color: #333333; } + +h1 tt { font: inherit; border-bottom: none; } +h2 tt { font: inherit; border-bottom: none; } +h3 tt { font: inherit; border-bottom: none; } +h4 tt { font: inherit; border-bottom: none; } +h5 tt { font: inherit; border-bottom: none; } +h6 tt { font: inherit; border-bottom: none; } + +div#spc-section-body h1, h2, h3, h4, h5, h6{ + color: #444444; + font-weight: normal; + border-bottom: 0px solid #656565; + margin-bottom: 0.5em; +} +div#spc-section-body h1 { font-size: 200%; font-weight: bold; color: #333333; } +div#spc-section-body h2 { font-size: 160%; font-weight: bold; } +div#spc-section-body h3 { font-size: 140%; } +div#spc-section-body h4 { font-size: 120%; border-bottom: none; } +div#spc-section-body h5 { font-size: 110%; border-bottom: none; } +div#spc-section-body h6 { font-size: 100%; border-bottom: none; } + +/* Styling for hyperlinks */ +div#spc-section-body a{ + text-decoration: none; +} +div#spc-section-body a:hover{ + text-decoration: underline; +} + +/* Styling for images and figures: images are inline, figures are centered */ +div#spc-section-body .align-center{ + text-align: center; +} + +p.rubric { + color: #333333; + font-size: 120%; + font-weight: normal; + border-bottom: 1px solid #DDDDDD; +} + +.highlight-python pre { + border-top: 1px solid #656565; + border-bottom: 1px solid #656565; +} + +/* Docutils uses this tag for footnote backrefs. By coincidence, Bootstrap also + * uses this class and assigns an obnoxious color to it. Use a less obnoxious + * color. + */ +td.label { + background-color: rgb(242, 242, 242); +} + +/* + * Tables + */ + +table.citation { + border: none; +} + +table.docutils td, table.docutils th { + border: none; +} + +table.docutils { + margin-bottom: 9.5px; +} + + +/* + * Admonitions + */ + +p.admonition-title { + display: inline; +} + +p.admonition-title:after { + content: ":"; +} + +div.seealso { + background-color: #ffc; + border: 1px solid #ff6; +} + +div.seealso dt { + float: left; + clear: left; + min-width: 4em; + padding-right: 1em; +} + +div.seealso dd { + margin-top: 0; + margin-bottom: 0; +} + +div.warning { + background-color: #ffe4e4; + border: 1px solid #f66; +} + +div.note { + background-color: #eee; + border: 1px solid #ccc; +} \ No newline at end of file diff --git a/5.0/_static/file.png b/5.0/_static/file.png new file mode 100644 index 0000000000000000000000000000000000000000..a858a410e4faa62ce324d814e4b816fff83a6fb3 GIT binary patch literal 286 zcmV+(0pb3MP)s`hMrGg#P~ix$^RISR_I47Y|r1 z_CyJOe}D1){SET-^Amu_i71Lt6eYfZjRyw@I6OQAIXXHDfiX^GbOlHe=Ae4>0m)d(f|Me07*qoM6N<$f}vM^LjV8( literal 0 HcmV?d00001 diff --git a/5.0/_static/fonts/SourceCodePro-Regular.otf.woff b/5.0/_static/fonts/SourceCodePro-Regular.otf.woff new file mode 100644 index 0000000000000000000000000000000000000000..46e7bd058b145d1524eb58999a1a38b48bd4dea4 GIT binary patch literal 57712 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 literal 0 HcmV?d00001 diff --git a/5.0/_static/fonts/SourceSansPro-It.otf.woff b/5.0/_static/fonts/SourceSansPro-It.otf.woff new file mode 100644 index 0000000000000000000000000000000000000000..9cae9b4b9ba35f8241580f8f86e05827f64ff356 GIT binary patch literal 53852 zcmZU)W0WR6)GmCtr)}G|ZQHiZ>1o@xF>T{++cu|dP1~Ht*Ux#s_xw3o*R?9y**mFJ zR;sd-$}dF;2}LzEMF3zy4}bsw06+wP06+l3-?9HEhzP5S0|5PK-x)mrF&aAsC@LXA z1OOmR0RSm90KiCpUs6q_n5wkox1JIJfD8u!V7(tyo;gX1iAw+gYk8xvyVBlhsg~|H{r)mvz(?M-zz4 zZF0^ILaLO({7VECjC~g#l`qo*{|TbrID)TWEy{2wTby0cUihm9MT~yLZ=0k z*r%*F>CZ#|3g`_O!iaR7$z76{mX$8de6^S{oq3(fv?cLsePB&3f01Q|(JNf3r@cX& zo=e9_KgIVlaSmlzP{dGNgnt{}$M(woYv<>|#{BEzS<{y>lm3gILAm)IIIQYcyV=+V_g*oKo<;8;W+j`dZTCeb2y7E`)J*x# z?YV0nHrJ{1v2+f_py}1+lea!{jJAv@BaNP&t6SXB@k!IL5NPm7W0>|azvZr+emA<+ zd;Q4Vq3aQ%ZS=>$>NlwDv2>2yuJ*IKMbod|Zf=9yY;6;9R1@5hS{S7Ji^#T;mX?k& zrI+j(io0cI2>g{{i+*%5l1nC=Jhf6eNeI`Z;7HF9H)=Y{X`?_HiBU&)!!-xCt=c{z zp|qi3GA%Et!gozRSY33SYhped@47vWqWRBwPTM{0kH&fI^ka<|F{g=CT?bBC&120; zbsJM>T?3z#3B%Mc{#tI?N8v(VX+D1%U1aOU&CeQ#$Y_q-9}se>z%Bxg|Rkz6yZb2_E=dlSyiPo_ipa7WQc zF_}l>%5^X?3N$YJn~+j-%UT{ybpJnk zNd&LLf3L$U%kyS8VmmL+b?rzec(oJGHpheKq#Hg>Y`sVKl7r76xmC6aaTJ`22=9Fc zO;@r{9>Y$&VBh=fcO15C?}JCS4T{#>TlpL=ZzZbQPg}M$P&@4%*a+|NTie_FXpktD zi`Y~>XMS@;EHEkQhx3;hWL;$M4C zPMp{$y1FOU2@1anVVL`pS@vXRHTZi1X7;6J9vNL^$&IRSqIa_tc6VWs?3y!wq##{{fHU*y;$|PT*wAGIq zgs1zP=Zo#3v%wLu_UJ}%piNFlTS@Q~-eIdmm71hqc9Vjx(Cj*)HfD?FaL1f|@Lx}J+sG6Z3#FJ?=ay{n4k6Z8d7A*Wa6>Dwg4MW`EdH!m*s?dM^4bW`|s z&QqL+FWUG6AQVb#d_hT&enl;!Pe3C$XKo7===h(drPZxL1`nG15JLJ?i%4L!0@D-S zD|`qm#;j;X_o+_NWe!c2{%>L|Q?S1YI4hYNS+J_KIc-_H`kmbTp z85*TS{uH_B&A5zPq(_85X)p2dp4;5V{awk)9pf?Fl@l35zuupuOC+&CiUnBU#92JO zdp5U!cLuS0?458;>@6;l3};qIF!bOVcOrViKJgCRei>KN+A1IU*1IiHipTX#-A7C& z<9PSQ{8~l2;@dj{QNsOcU$D5|;J7Q>5Z)fyP3!o_sVm$Q%oa6wH)?2Cq=mkFyEt6y zTFAsL#?kzh`mFRXa>Hyfu4^Q7G9O7!*g}c^_zsv#StV<5(VZ{ST&NEkjWy1%&xjZQj+oEC3li*u-GAvG|i3CwSo_sj68Z1njVNZ^vbiGN=v?R z>hu`-rg-u~u^#lyhl}|-jR>011yYLr{n%_eMV?Dpd=}%{oAXaF3;)p4|K7jLX`XIs z;us^n*BZ(7;Godbt8dC%(v(s}cJoqNVxS^yKfvL#Njk@f{xO8q!+=UlW%NU2{*IaX z(GUVc7Kjwm-;W%oEa5F8y|?nM2Ue&bQw3LS zZLzQEukF9v7HiFZ{|QN@+kYE~5GvAH<1FKQ%0OxT3d+5PxaVSuYZ+031LwS5&i6D* z�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⪤d~1{g{kMTUU4 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 r{KsP#d6i9j{Y$~-QBI|#hs7e|GhWgym@DK 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 k#&V4N^f*%@W(!qelX4V1n4 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 LKG44a?VOkjIFJon literal 0 HcmV?d00001 diff --git a/5.0/_static/fonts/SourceSansPro-Semibold.otf.woff b/5.0/_static/fonts/SourceSansPro-Semibold.otf.woff new file mode 100644 index 0000000000000000000000000000000000000000..475c49971499e548091c83a5fbc1dffce9b6da1d GIT binary patch literal 134540 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

LIW=YP*YbEl0fBkhm0ESEauK%Z zYi2M78?&uvoCeWc@XN6nOD;~qRraRA=IpDc5HV zHoQ=klqDWaEQOdxYZ#TZE?u4!w+{y{foD_1%M$_yu69E2yc0^ho;On=^o+N(q|Pl0 zp1&Y23wfOX#?C;!2FlKB#CNOwqppZeylg&fk#FADAve31O>%N)8x!<}Jk<8ZF+l=E zIp(-^aWUF=CAI2GYSWQary?&)MVuE6JSt8ZVU5bN3SnJ{kYr$ZQtqwu4Az&#HG5ao zW-rg-joybetSSxQ>i(uSmIHTMofkpcCd~pmZQdEK=z+dP>EFYzbmA}i!ynE&Q=*D&%21g%Rg%)pkgidlkJ4g!g`Hq#C#f95X72+(+^=r; zJ$mAfdHHe(o^Lmh2p!2>sbuKz-@;(_d% z6O{c}dry7A9aM4{lXKkVT0cw;oY%2Qusfj{Asq6JO&W_bol4-TbTZH|hqBeTHVgqS`6_ZuSQEzM$Gc zM6K0e&uM`oyFnM;Q4!ujd-nRp>5iuhfT-CHhHu_$>kK$nRLJor^!-V|*)7x?Fh1UlfLAEcb<^~opz{Cw=#~Z-;xmN`tOUV5On&SgME!*3`CqS3m z3*gfebThM0wX;vHF?eC7{(@Wjgtjs>IMzC;4%ANlZB$Tj0QF%(9+Q8|tgw{5%J@c$ zoV`JJe~qm$<`jUna=Is0QbCYCF5;oNLg_`W+Y``CRigRU^{6Ll<3%tSpB?3eg*bTNX-aPH;Zi z?umrN1MyED3YS~6edPfks@-m+Rw?j}BJ4_)fTr?VC9G+Pih7iYl*O(Tz*q-%s}$_j z+>forFgK+vq_z`p~T*b#EXt`c_y7Pp$nA^rzlF$l6*0 z^rybQUMY2USQvu7J_dDmP?%wZU2`at5 zZoTP^J$iN7Id`cKMB|r-bXQ_#nqzuZdraE@{tTwQO?(`IH1u(st7e2v^dBKLjB#74 zX7Z)695FO3aogt5%Vm^I%$sI=3{|EUycJ#w&0T4{d{W$JWqB=$<12ik$|PLVK6>1c z<%ZF|dK@I=n%#O_kmU{^-Y<~wP46~*=I0*M-FoaI=VH)(A^|k2=IG8RU(vcm0Oc)b z%MYv{?Y=^QvX=9f2l$WXt+MUej)xA<7GJsR+3WKz&)lsVgp;6GFduQhQiSuk2P7Y5 zf_Z}T{Z|wpLBDdjv+0N9SGiZ14>KQ0zcRp_-)za_@@w>`s9(ib`Scm!)ajMWN7v78 zguH)ZxRS!Gn*LA*9L0b#L$bKO%&dle4(G%&eOEQ3h;&hMBRhxGsW8Mp|ipVj%N~w6~F-MusZUyqZzs z$Z~#7+h{GVnRK<0=~4!Ae%6^WTLxo((V1ye27#%kFeB$kbAJ4Rm7+NA)ao_;(=-pu zcy23THN{^TR+@IeN*Q&76TEi=j_aiSe* zCdCmc)8dA8ak|!oGgCimTDoaU+u{VPlk~rItH=p%rtJ+=^tAO!L8mYcWAsdtBiV+9 z4U;QYV4BI%mY@0u-}0y3>|0Uer&IEm@+v@f79cu^Ga+SK@KUg9tjn4?No5-4v1rXw zlWue5av$n3Z_Q?xj(23!FyCq9n(j7fd!Msvw8iE%$=k4vOAk2-dGz;X2A7o}o#?3a zWd)ZlHx1=T$!X5pl$XtqQ7?@zQ|Ut4-i>3uSbX*>P?$V*C9 zqd9hQN!?0E(PWBhG<}>gxj~@0lE$oNhHVv^wNJG%+1f;FP_=2%+6Bj=z`C)@z^Kv~ z%w%c?^2>$j)^PtOOyPOnVMSeZ4`7H3KP3L{GxU!Q`a1qaoN-`Isr3B1diXwnB=>Z7 zgtyuGXUJ;Klji#PQ!_<#+@;mq>Zhx}Ohb@$dGaIgdvv3@(fXS4`c!iZp0(KesB5#1 zwcC2XYtyE9NbUi-9SJ-ig5|&3BHQM8MUZP5ebvLYyK9oRwq-r!3BF@w%jUXnd|*kHYE{x*?$m@v zYkjHC#fHbLsLBp!CA2v{ra{KK^6{!6vaxHMS6^)f@{n9{jjd_Yy3@uM#TsUPuDL$q ziE@2S*C?^_z;PE`S=Mb<$AxWE2ROs?*wmhSRi)(Ij$h35V9z6Hb{6lMgkLo*elBgX zDF<8zb1ug(o&R=^)e&Ze=+@SpbVA6aREzr(#h=7?t0<(hAqu`V@kPMbIzIGYkz#z0b_ zDG=Q)lxzMC61+`I3VPQRcY2Z|K+9grb}+&_xhpQQkPqs>ll$8JtMj)_rlLr<;9LMS zZZBFJvf2@)c5;P-OY8x8>=8C@KT8|>@)4(Y>iKQ5Yj~F$wXpb*#F*FKA$i)l6PWEt z40{TW6E53sv1?l+6mZL2zPRs!0Wl+y$Y)o@_?d zL~c2Nj1XzKik5hBEJ?G&^7UtR>g%KOXiG10>kGS0uLJu-Pt%9;lN#n7(uqH3;D!4o zHwWqyr-9$>z|Rz)yzAA+DIm%ilvUjA^F^}g?bG`)p5?(Xy&|{fEQWMQ3*^GlD;FR* z_-PzsdEWeXs4}J%CRj0JLn*qbR7a^xQqEdla2D(5Mo=tTW}RGex5&&Qg;o|~S*BU7 zTE2J&)ud}9pSg%*S+Z6DUruxu%vm&5dS7XxLP2z{#-dSiHj0f(cdmnTHL}SOqU1qt z=7@fLBb+k^t~~dEPo&1{zTy<6>im*s7i!8A_ch|VeXo$g!Gtm!_uVQY2A+O?Sj zGJ87PiBnl^_R%F@m{Z2=lsmho95&dDoOn`Z;nE(YCHXq-G7NuZmUaIRlSk!&EH3K(D2sD_Is`Kw>m&w{{-9Zj*9D z!=>8VZwj;BydTVU>AuVL)Yoae{(&rfL{ft6J>7?WsQtl{MRDA`uCW2@(~0Jj z6~j9vKmap4+;(Dw^vaIllNHCi70OG?PhclTfRP{PmKPTlF2e$kta^;xC!QibA?y%N zpCJ^&LcFby(r?h0p)IK%)gRK-)>sRlinR22heB(X5-|y;t?T}m>KEV6uiZb@4UvAD()dPP?Ie{5q*^*O_Cp4rv;!<_?y|Ec9C z9JDlC8B_H*;z=)7;kc>sA2JxLINBs_5Aq7??GsKuh1;i^cfRT1S};rxEPcD}G4{c> zR1*uMeevzt_DQyMxK&)ghEHM>23+GeMKUfT_N=YS;1aqNP^i$2#lHM&Gj#q`(sCgX zb-XFBvJI{gmx-+gG-ujeT(WJDvmswoyMHy$%-9fPod_HM+X8&Hz;p`iOqHm+1y~+e z&Kn%9h`5>56WzO-rgjL~+X3(NB0;)Z92&JYV_er;x3u`TI{xSalAM$=r7f6)Q|7_S zGa?nSDCa#iQwP=&EgH3_Ox$TUA=sIOHATv#vm&0TUu@8}Eab-;Y&S9t0TSOqO3=fA ziJzl7#9*o308SCfDFUIUvK-^>x{EF`m7JQR=8ae`d$z0IuGX4~!+sN1g27ep9H$r2 z^p@>?A=&)-**7)lRU3Mh6dg0}F2UpzmDf$^Bzhxj;`N2X!#TW`cFh`kxMHZ9f9%j(?LN33pItyLSuY=f8Qv91jpx3;mF$FlZh?JrlT zZslB)*;nX0YvY@z63Spmt~pQUon%AxOToJvoLO9?xB3yQ!h|_}4dO?Xs}DYVu)SKN zBvA#Aa4LdX@PxA4e`JFYOLoKO$p@8Z#AvUsbqlucHKSv*8wAY?_--E*DaoLYV^GczWZlyxEs8zJ0ab-xym=*Rs zl0mrN73tk+Mh4v&bg5siPqsda8sK^wF3<=>FSabrkciT{piu_hj`_}Ec8$lCFQM$O zF%p0K(F!~YH)ly!A}K|<)%5OfPn=kXzOH7}p^`)+nU{XeSc^0T{Bxm{Rw*_!rnTg+ z5yxN6+6qb73QCAA=glva%PXwmCMgq_lPIq!`Lig4x~=PyO6?GSHh!8_UGG60r@YS| zcfa6{1!cAP_)biq=EjjOTkMHbU`Qr9_=brFNl)25So%DLgO*@FUb^$!PT3v`3r(yw z+G&ELSB2ZhF{wnZ`t&q$-Tn0dK8Lrhq(tuO@HEluoRl*j(z75;EF754uCz46%dMDm zkJ+u8HgZcZ+MsZT}BQ>|PBdZXJo@m~LnwZpon2EFEo$z_+{y zofX#U);Y=$?UZkvO}j?)Vcr$Vk2md9cb>~i4Oi8?$<9wz?Nnw?9B>+L$kAZW$0E1)J62Twqmo5otTaK49%dd@XEk*Zrh*C{^En_4?g(MKdeh1C~s7eS63I8 zN9n6zM8V&GHV_d7*}*&I5fzX?;#E`+5y6o_(Cn7MkZ%1|Qa4S|bX+tBadd>8k&r-; zzK@3RUknip1DJdC`q59o1ty-v^wERBooyY!#C27fvfB62HGJQ#vPlH{M>u+fO$>&` z_~eEm|FDQnhe^jd({a*7Epm3AY%1puDNG1wU-Jtdk)klE;(q6BNfVNVY}+405vJL- z7r99Gc*?b(*=CXMv)YYlm%%P)t@G>QS-l0rjYwp1?AKYWhR+^mxyz!_I83h>tcK-E zdJ#VxH>X~vkt6a$%&*^%ktOnrPIlgp(Pf0&Pfp%3ND@RsPY&KOXfh&I^BYFm@9M2g z!ySvm(myMegcxEBG6tBFEgM#BI^)liNb8xjN1Wmp=Tua8 zo=IZ$Ge=lZ&cha8rkOHKny!VN@)rkHckLaGAIKM*G2rz&Gtbw3_w5QkB`!{7$__PP zqZbp)5M?4`u=2dn!Q3zXN>}Wsuyb#R-IfxN^H1?d4?+*X|H<rOE+Irg%u&Vu~dvibQdUXn@yX|&CeEg>RZTHt))fM(wuU-sSus}>d zqp&wl-kIA|StGg8oD43%Eo_W~#V<2!{00xSYAmwme70vGAmfS=IT}@7svu@|^u+Xm z@fC-xpfHtTfPEYj3foHCSemgIn<;vAXmSW|hO{?(ea66A&xpq*)8Gpf(OA^Ces(#x%RX-mQtn##ds>c zAI5zRy$oZgxRN?=Dv3^dv%Un${bf3|j*|EAU2fV@9e2Qs$b_5mC*09|+Gh|6P8#n} zg7p!fS-Wl@BbLLEKw)-P_i4Pv5{7cz{8a>ZQ0#%x;e#YrJ(|m=x z8P2_*!KeR4j5p`JdiGwdGjG@n_=KCGC;YswbVItUaW-RdheiUm8>5e$emh&XC{(wc zmYUJQ7nTfKn_=7t#$dj*yGYSD{o7bUil~Ig#esOC95z@z`Ivm-9r>Mu^!P#l-=SfO zu<5!*#*zG#0ohpUSCTrEwbox{DI@(u)3NVGW3a2TrYaH|{6nPU7QCkkPSMEXDT0%` z-=HJuAr%Cnr6lcxMEbE=#b_s{V!ZTNV@`hyt74lpSrg9`S%yqbzKByOYLCA!tI}c& zm*dQt7F5L&($o6b!wYVzgj%Kg-6ZozoXOb-k_W?#xf6HyY4Ap#f95HqArruk6P>hU zJ5TP&vOc-P9;}ORsj>PRapp_X+tX}KGWtJ%-aEN@fX=z9bjExFk6ijWI`z1OZz^w0Rbic?Q!MmL(LYAO_~IuAww* zF;+wLd5l&J$B?(tzq%3k0rx)lHTPckMRk)(h9#WL7@Hy4g$=jxHyGBA|fwnEzmZdlwtpjf3{M|#>Vd-{7k2qkCr+( zY*j2%pez};dVoAe8^sZ5A6!4s_Q=I0B}np%mpV{lcZ<*_)k~l{$aJjQFo#XhI&d{= zoJjVHhB5Bmpp7y}9xKG~DPu0eG9evlGljO?_seNP`Z{HL`f2Ib!?GppA;pvFT9@Ke z?2{gAlu@K~OLjzO;)yPWPJeB<7JK?AEyjp9$oZWR5B0x37Eh1`wUXYr?NW_#H;K|{ zBh5TtMe;X^cLV zOMQatfB;>Qs&| z+i0~gl@3ZjL#5vsc7Ub>g*@}w;kx^NF_7k2q%d}+Y93e|$(g;HCgUTcNh8!9^is++ z*j}~21y(Q8N;kYXYq2t~gYPBuJbv~+;5WMy-4~?g^djro9Wn7_TCmPnt6*PO^|^GF zcf!@Z05YWGe=FWNR6@+rrB`DNaT%RNeIRMN{${qUZMm(*Er!;S+VD`=%=Z+jiN(X_ z;(kh4IjvhB-ooZ0V`Xb*UqU&KJV#-pwN>5dY{l4X9!?%q8K`(pd(L{UyKNcbLjzEB zGyfqm(C@nmmO?fqnUGLVx^eDQA6WFxba!b)T0K1@g&rC=-J z{(I*>7MV0jv91`YG^W@&nV(_8dOl;V-w;1J#L`i)x<8r1rnPD<)xcAxP?lcyp(H1j zo!!pp5+*++LAc1S(a0nV0vGr`fJqjPx-X0rLd3!Dh#i|1O2jc#MmWz7cUTe>8t7Hn zt~*eM(<>F~x%#va?}{;bAvn9SMK^Z=J&XL(od=f}lLzF_H8H*=5`U(u`5gMT$c+HtgV>=MTX1BhdF%y?3IU|>sE6{uyqIJvy`}b%1Z}574+P^rc z;E@o6{)Pe$dG7L{NIg$YB=1gX`#H!3?Z--_EV@6?yab`Keo@vTk%{%8`4zm5R`KukunaLdML}SJ&?^xiR z|2l9fh&8Y|kQdYj>@WzLKSWR>8p%2+0h1_#;lHau%TCm6H^-Cql)rwV*$3Dn*Sq_v5k$2zr4r4~aiLfu{kx>T~cb7#S4M>)>iN-aUi z%iqgu|ErsS)~(=5D~~0wMZikn+J7lv3w)6}PIsgL?cl`1bXXSr;H= zUOl7#pJEPY+c|H?MC*Uqi20wBxI9P!s$gbau&SS3@S5P9{&xg7Iv_@bmy#9%+IfkW zk|zjs4`FJP4G0x4d2jSwz##k|G(UKLdH#B+)%o`si{&}@DOn}RlSg-gOy?Z2+%;0| z{DtMj2;Go!eu7p7#Y2d>St>~QEb#oOy0@uI)OkM4vdy8hpySW0$436_DN3DNOEeZnzwpNB^71=ooDz-f+V&a(E5*odG! zhE*{TtztOTnZ`h8df_s$a-LSVyoi^NcF8G(!@6xX5~=v&Ljh!@&m<^7R2?ODneeVQutA}GRfYPl2Yk8yfP6J( zjCphe`@)5!tNdmU!Wdn@MAk4o9#fn#c>ns3MhkR|1*bD-?oAuT8H_HvO!MVUkxL`Q znV2rLnNXau&3~DP>ik4)!x1YG$x1g@OOwbW-GMAqV#4CU71G$je9*OSvQpKV4$4R_ z)i}+{CC#r`j78TtQ9<@h4%WnzuL!Dj_stI%v_O3;pI$72B2Ol-I#c-2tkk1n8#*_8 z_%hp`?fF%R6d;vdj`8m+NZ}>QRiWT%w?$q`A>p@(uLOH2Noc36zh2L(7B!C3;Wz4s z?hx{h6Rvuf3mt(*Lq)SIWXH8!6XqDsFT-CK(~^PND!N^UxNKd(&A;Jl0Ep|oO_Gp@ zGq*AO91C)~sMJrrVD_R|@~G-sb_Us2F_!-7zM_(h4gcW+Pk-^t-MApY4K~T${<1-V z7dOEd{$cLd!E}?;qPTZl-&euqre(Cwo+rL$&t>C|NEPD}X3RB1GltlBFpQW~R)H=q zPj~?dyXMgSQ)sqYY1|GsLL55R#~s0DSi#$>CAFtoIQC(wLLfHD$tFsvO7PanbMrx9P=dZZx;DI zh!&N3Y~;N`2#Jm8v-q$8w4%rDM*rk zJ5W+!J7A1oS9>4v*Q>HXF6D_*1r#~f0%{VdC4?k63uIA%rhofdRHf3b54JhI`L7e_ z8TlEkJcB$Y0keQ92muWBV}DWqDR66@nNH~P0DggAzg!Y z{p_j{M#=21t)cf+L1)TK_NE7l+fCY)h^K+haW3eearllDq6#G-Vd zo@IkcCF;i&3be^_d4dZ}$RE!Xe-C4B5*0$u+J-oZg2$b%nN0bam~T=U&V(^0c`CF- zUkFvaxSR`PY%BVg*$@J~Dt9v*eg z@4Ur=(1hd;|Ep95Lr{fe->=fy{GoJEC7~W5&C-B^QIN!O#*gil6fnZiYtA6-bYT+&r=A4r@_fWP0!_U{E&YZ$l)FCiOz^komSl8;he!1W99pa0gwSoa+ z+&2*jAKU0gv+e&eSyg;NfA z7o*4jgZx<0NmZp3$Si4e+t_un?6W;=%`J6dV;J5gD7;FQM-(XnaY6ApOuql?`5&g3 z>euwJ>32{}&&3LVMYwM$mHq!l&p3S-Z3Te9@zDV0o&F z`cY&PyPTwT-Lb0b6a|-QvyL(Y%pXgIwyAO8fG1Lnr8>(tb};YyysXI3T40RXT1E-0 z)6HTEg?5rx?CKh7;-BIfMx-e`ErC3L!B86Jsj=Blk*LkcH;3Yfn z>qE40PrG$r4>9SK5p-`Ptk~qS^d$ycScJo2RpSiIt>QMJ1P80Pr6;cCQM=9uXzi;jeGS`-4+YsgZg8V0dsmAwGE zF#*Tz+n*=PMqHOP0~QXhMcHB>mB6!-aMYcuu4u|j&Lba+hGoVad5CGBf)7|2CG#lc zld5pURf(rZ#6qWSp-g3cT1EPCnoEM+w9Ms_ote`jVr=oFRRC9N^B`G ztCvM8lg{p~a~varqkx|i8iDsyi3u6CoqX6k=RW!*vQBmuGMzf9#&uPFo-yg)o9t!f zFG;t5sxVf;#mtN@g?85a;c9oP*Y-TLHO6XkkAvDFnaLm;Wt1P=%Bn9f=_>YEJ&)Mi zKOLAvl)}I$+0EY0_*l8E_~v~6BGi1ST&f2ymDRI$#CdR3y0pcnvGX{y*ZrHF$*WLZ z@N5iFp!Nft@z!7ihwLH7&bjJiRT ztCtNb3q(Z+s~^$K*UMfwps%6;ku%YSnYum3ukf{86%Em74T(!k9GYrNa?Rx~{h!`^ zUs`rGf!0^-OX4Hx{RJ~+V)cDSHFil`82E#2Z&_SlmBr+lA4bI6qij}Fo#4X*`Bp>V zn)$)kRq$}9ySwyTPoIiZ5Nn~3)vqr6kw)=R6#-$Hp=hEJde?f|lMLrG@n0k#@1k!? z(Obi}ZFS;)yAB&a35G0wR$JVR)g#QmO}pkZ7P0Il7xa%q!Rx&akyhtq5uWWO6X92F z*gHS_jWRvqn^}A3if5>ZJnJp`%01#nc;_aA(b9zuuIp8SdLHOK!VSWKY~2Qr+l|on z*=IVIMLWtry%Bhr}Qp5(7A%>0_@$dk5p#?!sjL(7jg`wkoK1!CvW)CcF;2s z&|QB2i>I%EYOCp@MT>iJEAH-4w73*^D=xv^y@lXX+@ZKbaHlxMgG(sx6bUYm?|*N- zwdT&8nX_jySvM=WGdZ*O({KCRuBbZ$*?{5(6Y@7m#YT(0~4n7I95YkpKz>upmvJ0QKyXSXE1dn=EG@znhHa* zt_j*}bVzIE3Xkf>a-RCAVi|*}(5*_Zz)BoWLbRX;msy}XSxGVs{mQ{940HTC;qp+U z-$?}$7c-riaKobO9`kNqF%&Py5AtJ>*=zZl{7>%gpv^wwLssW$&0izzJ|RGE*#5IK zfrSqwBqayg68l)65LON-%th`Q_2M`4^=hE6nS~4k?;zmh)vEq&MLY1i2J)k}_Wr)G zM>tWw>N9Vn#Ja|bux;*WNnSaYZXLn_xz`jBEjC$p2*s>vI|ikC#DuxRr7`5KuPkk0v@j*&nvY#=V zcJI4y`|DFQXpB^}v)>xGGQW&rVNj&k#Qh~*+@q9;Ijt4)+xaux6NO)NmLJAD^m@## zF=+xq&GhU>_5OsOn+;7H{Oqs(z6<=^4gNb6;#lj~NH=QATsy>m;acxNn@!fi!CMWf zKfFF;t2gWnRgVEtG*e!k@$Anoc(Gf0i?%t!uT8i1!kI5Xy^wTlHE#}=-P>_Nt3mLf z?*~1^K5*v?Tjb2Q(FMLr7*bAbC-_y9$Vr)9 zbftF_bBbjmahAkCn=Q##9D8Dj;Cn4l;6X18|E*k`o^vEmp|gj-Gli$F6a!-^FS|Y@ z!re=&=cRJY5yZ;4fYwKvsH$Rc5|MZ34ChUtE*Locnvrk~N#Qv0&S(`U6?i-z$nkty zLxEA)-5A%eF5;wLE~0vTPn}f=w9Cz;( zZysR0gUALBMDim)CWeR=i5uvj-Ooc-Ai`C;kY$KqO#np)DSd=9+KP@@98Ys?=p}^L zF%lH}=xw1T!=ID+*zn3-b|%GlRNzQY-^-rAg`9^${k0HL;cs3%Lao|)?J#+5 zeG8t{`076ad;X(0;<1~@eDP}~n@N#gPSuB;hYh|VV3@8nd{BIAT9N{qd`db&*PptE zN*CjZ-nOjgZD!izPczAC4ofr8tW_L2`N@}l0wnKZ$b3}}_ul<$JC%=3ZoKJ zLFE_Iw{ z2)7MoOfahsGNg%6n_GLX{xB=JRbBMCVtob*7kh#$uN^z@hwHIUqut=)k8ikmIp!$g zZvxRg$obw4dy+!c(f_13(2a1Set|K9wz(a=x*jx5ETRgX_4E>38nDt9=CZtVnh6@m z&dt!;6ITaZ5XxO5^{}`ZRC=@t_MYokc-m<96lb4o25ascwXzzW`_`f^tH?bawc+L4 z&-N4$w!67W`zCHE7kW+{Kt<~{O&30JNJpYjqG zvzKOhTrOp8g7?!*og%HVJFXuC1|`M}Z1UHJnUzt4=7i_qkCe=eyE)t`y{*N2~VUN?$fMFWF+yI+$a#lTI39t$|1#T7OZvo2=f@b??uAC}TWE=3@!j(tz){Xl56^W$LGvuz8Fy!etPb~zaU31T z2@cj<8gO5v zdL}|amdPxXb?^v3S_n$=?PweCK``IjPERbwUOUCuHXjKOHEnJs(XF|pI+37d{xyQL zWYx9cwb5g!WUAPHy`h<1XOxgxS>AXeCb}2ANP|449kEo01kqjFBQ3q_k}H%umL=*O zg+A({njc@)0UGhmF2=5Kf%k!x1BnEhaF}VjSejJ`d3{y7e@bLgI8g1O zBlaC-lI~;?_lj*{g!m-7m=|=lTGOMY-79}{CJUFEVk&DQ_WKD(5%&;#%aCo}m29sT zH3=UA-f7rv1CcL8LyB3*ICfE7RBDIC{#-}~anVc*JBgyN7OMOc{>9Nk#_DLK&5UxL zyh9S=EVXziRm2lXVm;6G`$71Y@Z$!ADrduxuUh)vo9e;q^~{D8U$XdCCGp?}qj_a5 zV7w4SMY2_jrqv3YeuY4e((?2Ikc1YSPqZmeb55OPVOmCb@eCsyn|dA)GQuZQ`}jkUd41Wl?|Y z;+v7%izhoglqxZ$&pAkgZdeP=?!)L*D$J2}pC>G^W9|$N9D9;}CW)kJ#t9pnc=k^x zuR3k)g`N$d`0iT(_&f-sr@i5JYT)bk9=&RxaPo_DudA*DmYo)y#2_#>>2uZy*0XoJ zHJdCNJEB2{IlfUz*MYB5-{M}uI2UGjlN}dABU>AmW&s)oO+A%{#S7SD2{*&E-}Pfb z>3@QQxq9xsLwb8qmqoh68V;i5`da~?3e^Ys_M+Avt-RzHr)}aZt$&Nkz0o7voY^>I zQ&b9)m?kcBn94~X|N9qzpBuHXIjg5{j3w!X`e5|7Z*NdR`z$2*LScA*#^mvQR7(fyHss_wqO%F1yoZ{lo!m6cIH$@<}Q zBC8&XB(wxq5~~VbHF5-i!gRHBm(4O;Jl8EqTM}9f)~wsG$jtH6KMYGf+&FVi*=1PH z#-B@0d%d$u$}CuLZwYqWoP=1gu>`srJW;**QwI{HBU`fsK1l6)A-xl=J^d53$mNK) z>V;KUwW!+`s}o3EtAUJ{o^{3pg;^lJg`*0zN8QG8(0EO`yz7Jx@;4IYhuKRLiwP3H z-C5^i41)&@!zYYw@DI78iOO4SgxUj)SL+}FLAhQo-lYca$3Rr02aKTjao;8RRE{12 zL0p90)};0Qoqvv7h13ucl850@qbH2x%d=QWM9rZf5vN=o+D;wa?i!xiByWXeYu;9@ zWor5k%3*@Rrc6YVYWc);B^0A>)1sFt#mBCji64DfBbFt;1YhI&@0(HC7=SBmb*Cq z;=D5;8efp%NH@Fu)g_?4DZTM?vaWLNOT1U#$OME+cbq{nz_uql{A8TQUQy^&CGboE zzyXO@?;7@P{X(_WAB^a%E&QY0*5VThA^fij$f8!LR$b7(8Lej9+-uAQlC3g$q*Y?? z5dwqQlTZvE{yr+Jwd`MTyE>t+^u9>PU4q}v5s)mIA#wZ?2+1ze0PV}>Ui4?8?CyRv z2uahvhivJ8Zj9M`M{mw0ArYCv-`8Hk#}6SBHw*L-ts4D`!tzfy3kjl;Z( zw80mZg-h3Gwt|a!NlhO>Oi?itp)&3-LFco@-H9iTz=($y_pTg;AoTP`mdUtzr^z4^)`$PRZ z+J#FAQqF0r3*V~`0kP2`&kTHNb$eaGHPrPqjX9UUjC;D`1DEhi00q+&DEa=>t2qdf z37_Ri6iT`{4qnTizlo>bwTUHTqJKK&wqHZFd2{;b;7~C#^>=HbC>mQ~1<^yTzf3r~ z99|n+fN%JT?rugzaQ9OG729i2KGLh?V>ltKxiS{;O^|ZZ)D|xV<1C5XefZVD9GwN>%>8}mwg2Hc z#^q(ZcMTgebVSWl^o?WsVRPfb>IFipZ=prIn_hg=$r%4~9dw&4k3;m@k;b9XBIeU7 zSFc^O$1!y=`=tl4(Tqe+)K%JB)iWI+J!fa6Z=_0_iF8&MY_m^ob9cJ!*POUi;2Hlj zYZZA(4t8UwK|o;8rfN}OBxr4d(;ka6`US?e?YC&5$U>TAg45rrCb`r1KWy}xWJdRd zZmqF`ZCo{9E`GX#&w6~D1tbj#*S;`fv`^JtvbN|x?O2V9;#huf1*@{3U9_cT>ym1q zZ17Pm>zkh4855;kbuU%HW~shu*h3lJXFebMXdX62_5+GtF&@baqC?6KG5!D){`qH} zw4ln>=uX%-6%fVI3<^Dt2`O^rXBC@3Axfz6r%=FLqVGEKp^Da)acyLvDi~25sLN7% zx#*ED_VwQhyVw){vLpU5Rb`&{q19=_umEdhaT`g{-4^m`S2RkH?xy;c zf=s#hP68r$7l|AV(!NSDkeKT)*I=$r1e%~5X4r#$;Hdli(ja+3;*qDYqHRUVbuQQO zdxP{Q@9%(Lg1K@oOPC#|XZc?rS)yAeZ;zh31xto>>>1Elk=T>b*ImC+qFZ5RTQ*KQ zqs`|R=by;ZIvH;n7IjkkBuSF-<*&6FQ9TabZ>!Rb6BRN`)|bZ5as*JpByYm+B8NM+ ze2{CUhjmKgJ%6lk%|6wOeGh)!kpPzV9;1-#x#Lj1r+dh^?N3cXp~T7`ylyreG5wOm zTkdq?B(OOm{gv1fCS`qIhHr2SNtnq_GP4xyxkPZR>X9iRV>z`Ok^6uofJ^l2}jkHs>)s&~$YX2|Wk;Wzvh@dDNW_VvsNWjj@vYTY7H_>Bmq(b6SZ6 zDvrj;ntc8!%bGR|encI(4%Anz=xKXFXIaMKhH{$r$YUQMPs@_zw7OhuS79Y{I^a1w9jG&dTi+UQG+|ArW3Lu5P9 z`r5`+->ZJ{ae4RiQCBOyN6_GsA$|jv74$NWB%ns)&k(gg+PS$Vj6R|d^C|KCH`1mt zVEKn$yP>z|$CeH@3|VKHHq|n3PLa(ic3Hvk=L;nf;kSlkLY<(8yUOn)VL?^FBd03# z%-DO5Q5-3s1{)yRuD`ry->k1lUI)BvkTz?y4--Ce;~JPKNtf!p<(!0W8c^#!h1DP` z{@H>o3*s6@ifmTof3*4)oPEE@wC>Bhv=EuJjG4TEr-eAZ(O+1$E zH|JB?bqK0#zwSR3Xll;EOz!TZN`e19KqLDU!;fz+Xx!wW3(Z56W-c-?tue1#pWvo? z-Kj^!qgJ&{gsjm35O~lQR0pmK6{)@KuKV0W?NDuhy{s`{&dM$au|ZVS_);w)6W$C| zeMMR@!_%yMvclNMd%H+zsfccM$z}Z_mrmlZYWQM^20nG!75!I3gCu%K8kF$&nn*6f zAlBF6w@pWPQEqR`gc%y=|U|wp+tl5*YlU&fZ<2N z#!s)Jp;a#dV|OIg-g&VLBFk5#sqjm(j4YMcY?E?7bSHR84*U&uWuoB0HxplnaGvyz zL3#-gxW(R7UH@37cQ%PeQ^NEgw?ZLK@T%ia5II-N*T%P%p2|CBgqkGnd9a{g`Jzqu z2~U-k5mK#$-?GVe^|$KE>GS-VyLyV*1uQuOG_2Ra0veOx8()fmI`RBoz2CV@fO~#t zh*(t#)w*W6x967M$>qwJO?3JW*@40bh@!4zi_m(C) z*_AqP<-B0eAZ;3se`wR%uV0kwKmK?j32$A!(0P;%alV@Gy)b$!w?Ays+`XME5Hz#e z8GM#H-MKBuWi=oOxXihdO*n7>MlsNgrL#!Y{(YtSj3Iy5vc+ifDNl1g8?$x6!;?C) zudA>X2576+ z&MgoS7Tn(+thZ;b?gV5jqzC?WhOlvnw1P_w^}(OIt+k8cH|N#h=8HzkUp7T&XC_ZP zZya>5NJ0Jq&VaL6*^z-QT1!lSjPi)Rw;_w5rDspWBB<}oHLAJnN} zNt?wM)uxQ;6}(nXW{rWji8iFluuT;dh<;ORi>b*y<>ign>k?FNI)MTC%yER(32Wpk zzJ_t9=^S5sGalks-^DuWJ&IA#U>+&QY~R8a;8s#?k3a5KuKG$Hfxg$}Zc`T_;X`MxlG%LK-B>j9#_L(O5#ydI0fe@7Y z7x5fe>&G7yh#ZtqnMD&hah*#LHm;s*>bPGNHn$o;t2%?)h?n|LRGcEwT{TK>o-*HF zWU(>`3yxp2*S!aR?)NF6=Q>sg^RiaVVAvalbvW2JKxvpfROc39_yE|~$(4`on)ny` zm+N=Pb7YSz)zsaZ(ce7`AC52gjJ}m;+LsY$fj9SbJ*(|W+h*9g*0I(u(k9X&(%IRT;O^~h)9K&g-}%&$;Em;y zza_RJIvSYeU+*9F54iw`?9@>%b?*Eygd#ZUyn2Q?w=Ju8?|EXD>^E7z%VBli+GFT)g_DKH8#~&i|&ipi!qB6i!6&(=Lp_eB6u{j zs~f``n;T=fd*{dJ`{#eBT<_fOT<_fPysf9k)J!63} zPgv(`8{QokZjiGcBsOJyjf^czw4iFz4rYVFKFwIX%f5ObwE{2BSP>JiBu^4^Rt)c~ z6zJp8wgZ=u^6$@Gj3&4oF-wC7=BV1az@ zH#0E{F@Ttjm~$;(YbIs3X{9$<7+Pyk$6p?IEZVHlUB_RA1tx$Nu2za^tbs@DJ0GRb z)bcBHk9kt7UW>gdxIOz6L5#dUjkB9@)A1Ot&g+2@iVBYb4m1-otQB*1H&u z7YiMxcjfj0#1K_KD54|UoMGF9b-E3$5Vdm*xwz}kf!0QL3$0%HV@C|SKNYkaFJj{5G+r(vF zJVfa--8AS()GV~^HEFty>9Q<7L^f%*?KEkzP2sW9`#f`wz7N#^O0% z?{I1`>LqDMB=0Cwe&f03q?Q`#`j%;A!zDq61WjR~*W)`4Q8r&V@3}FGr%>p*P2Qn{`Tks%$R?H^R%E^1MGifYoT=q;>njTxO>EY#oEh4R2 z9&_ZVncFWiI=Kn1s{r4IE@>0`y5yxX(!J~}h+A`jQf-p+MDrCn&u}c?&L1D1er0}! z5CG+;*4O!fm4JzW5hI%(fD@lM*Im}rAE|3O`N|qxt&VZE%*x#HqF-K`8w)G;ZNhSK zY6q1`_CHB#rZg?U(tHZWmGo5;;63~3tjb!odGKdG(--X*712rxO^)((up5+Xf0qq_ z;p0;St%Wu;Z#;q@=N_k+n=thth9)QV+Ga0@(OyGs=EknIKDuEnv#GoG+lpSzZlY&? z!_9x7ITUs->OkKT-)!Gb-$>swp^>hM9KdFcoI!3BnETD)b?AADwTb!CR4F!-$H0yL zmn+hR1@EIz%wxD8QQ%XjCy}o5fVPJ5q%oTjn{f@NuA@Yz(U}7l#`>5%o?{%kML|-L z5aiG?$1axhk%%VlTFtAcVO%it5y~{RpmDrpQ)ntcJpHB9iOZzGD-egLZC(!(qjOqM z@~mT=aWJ+AD71mK@yzCNN@BYLKWt#DpKyuEcPb1Nx9A30Qd)$6Hlc7PL%4I`?y_%6 zHG6jOY;N3RWEpwPCIg7ti?T0uyq&%_8Pz?gAt$LrdQs$a-EipXU;ml~Cf7wIlA-8Wrr>sR}L)h0#|w`&m6Yuc~vYbw;n>l;yh5Qf!tXn z8{h3LNdv$0(vt|#1m|BC)W=#4Uk_mPJfug#$<@d`$Xd|M)eQ6hqWn@O8ybw7gw%_y zbREUnN5lThX+>*LBK#Jo^s{~*di#CCV?tC5Mg1#n(MHUA@ZzG>i1PR3lwDe?Jr5yr z&$^_gf?Z)>@&p$7SyN6gNG1ejo|R`JqLGg z5n#cHk5IkxyFFD=D89kAZHFQu!NXWhM@YVrL7w`Yalcb&)ngRGf-Y3XH<%CRNaCOV z&*p_y=Iv_LYE|j#=J{3NGX(%^j=Bo@sCBWGdQ;ezT>4ec#fKr`nZ*usS8}f+%hDDmict z^HQ-$J$6iA5r-8B@!CwSNZS2`+aDF(pqGgunA-;5rsB1VMHkEmVQ#a>1qbb)A>M{=*ditACT!L9;gO-yLIz}CUe!|M;D zZxWZG%aLLSK)9P0wSbtQv}m`RYyW^p9H0>cCaHos37xGl-J#D8f<<}TB9S#ZJjLuB z?}pE)kF1fZop9zHFLIW5Y8%R`7vFK0RoC}NKrCN*afyTQd;0e;Qn1p>QP|26oA-{P z9Nr<;BfkMluK7kqpoeEXF|tKg2-MsLO!lFpyHp(C@)+Mrof#^Ny56XH6}$Mid8msd#TIX*dn3Hu(MOW%a8PPVe-kz2@cO4elJmgrL{X;QDuX4s96FN;B& z<|gpjNQm%A^hy%+mKZ?P4QJn{-7?BH8qDZj@C*glgCvdWa{0~-o3=+ai;{+zMN0I5!?h6bCjk9uXMd-NP;C?cKAk-sbGxs!uf(YD#6Hqqmr)nx{;kXePdA%o zGS${MwH;Ls!VQrEIA=S@B=tc6>euAg-Dd=X z8elsff#XNk)!>;o(fxEmRNjb1P)lddDtrGQ0W@i)<1WkNE~bA@(b6`0eJ@4fx9%=l zfc|~!_6N}`1H!LdSXoF(OC+wSeC8kc%$@BAnIsIwqj4mRQp<~*Es7qR?0)yyk!QjN zD^NQ#;~9Ji9tG5_q#3wJN9)Wo&UU<)nwREjidw)T20mN&1!=zi+0yz^HnSwnB>UV| z!;%W#-DYb?l}X|6S_5QK$HgD!ib+5rMc4ZSu zo@JN&^)-5zV)o<+`$P+ykS9DE;HehUpJP-7iF!P!9Mk_=Z5ionfv|5ckRM|%KZ`8n z&ZN47$#yR^ys5->H{!Cc!Jl1=JLEQKI$*S?K)%z!SjPKA3qW?2Qq2-!r_xjk%v?8QDg=_S?U{`-kEK_axXsQ4CJ{VETd zC*Q=ce?il`vdY?_PpM~^_%`#1?dynds889SLT2yS)B@ZI851W651nW&j&Dg*Sj0Th z+FJACJswLi z%BN1?ZWyr_OcuTe%S1Kw#j5}NUcA9YJw=Z8Or-S7gU;b>4b&#=_f6Pm2E)S!r2y;E z!zp0f4wxNn8=#?1054KiX!kfi?v|bv9l*%>%6j zhCAGjfmX`qtg#9w#lrcYSV60b9zQ0hq)k`1@MI>)l_oY2f?{4h1{lbjg?HNGiJNV} zya%tOnva~u9T~rtsk!k18!bS0#<=f|LoY#39+c(5`8{G)1wGy zrbC58h zNrhb?Y1|k+5p@IIgz@&!X~uzc<3}pN#qG*TOw_4SHX*kfGNr}A zOBJ3o^#*92V83P)%L)v8c#lWfW}HXklRe931;;SQ`PKICj}n0}N=-73q2Hg>9A(-U zm-OHH8|XVU05%n8_98cJiyK1Vjll9wBn|I;=?&4S4u4M1JGJ9;9`cPqiNJ&^f3W0@ z;)!eeqq?LV{)gkdFoNxU{SVmZ|-s{?sEMO%>+xea7#6BOHFV24|IlX6O(fOg| zhMBsz2lb{Z&E|zd&1|v(O{7mS@?}u)aZF0oqra35S!CBDiD(7i*P=yRnHn=SfTaci zRJdrXR5KfIPz>KJD{dc0046C@H?>rgvs9z;C__q=T$w7>Qu2eTVVaJV-?*-XyhWJAD&Dhhj!e>?t3bojGo6d;=XRg^o!?_I6Ik`pS=3Y8~=%72E+!$IY#q4NI6 zNwmjF@nD)+Filxy_zX>Wh1^`BT#%H>Nm4(U<_P>>gL#46OE4x7jHv*|B(IUHmQ!;_xf%GT%cIMaG;lBBK8%#bC6+jTFJY<+&;`tnEG#bQ zU)tQjSBnOhq#x-TQMd$T|^e_ofZd9Kp=J$@a1^p_{_K2_0 zcO%ElW-eDPQmc9Zs=^XUC2Is7Q&*t>uXB8QoX`izbJMSI={njN4Q5Z_wXOvg?CTGsDB*DhL5TaJ?` z`v~_Nc$2_U)iCi*8M&apnNyrLeVQ{tk!GT1qOg!%g%gF@LBDZ7)b$6f6Uwd6X9;A^ zFY}KK&uC|xT?VL+;p~+BO#T#Zt0HD3u$fNXa zF85VQ^~7|sx9?;%#?`P+zYeyc4zyKcSZ7$b!+N%K6JdJoyyYJE+roIL*8J-5_wMY+ zD=P*!OO`8hr7KfZp*l37IxFnfp~SVhF~`X4~8)j49(BgIYODPXw$ z=CSPN@z+h^q^?wz&0~d4?lHra-I}T08qG1o=q^tiKF<9q*;iM`zqV)IC-Uk`~;JguE1%bspQN^5~iiA=}?j`J!-JlSVeqh@+n>d) zcFf&&lihZ*;lPgM>&7u(q1`Tr;Sje!N|Q=M6}w$2w!VIeZ_2CrKCAhH|I5Wd;+Y_E zN84b7`B&PRS5WDTLct5L?Bz)5X+Gh$jN?pId^LX)B;IWojB6MCVfGch=!IDN$zAwN zRb(~41tcD58_aDN{5bP!Irsm3JW;ncpY}!&A7K5;c5{@+h$Y6x3U<*8c0pUhJ22z1 zG2A>M-0mb+rHGav(&Fa0Gv;tr?0_YpHYT=7MmF`IX_|~|@QiE?v2bnG0yP5aSI+WN zOdu2m5Q>T&Ue*jf?L0l1Dq!o{O+>vz!;aWso||@t8}y^Ad$jFcJA>~65B|^7Xjf}JOw>aF^LY!#f;Tf2`&FB^9$ zp8nAI!2MWS9HI~lC!1ZD^(IA5j`oLFH>+;Ph#c+IvB#_@l%kejcc(la?UrXL1CY2h z+mA;?e%g2V?T5*WbhfbUavIkF>^{9a-j%S%^5VL^lMI$;7boy?Kx-Jy{g3LT>TFg# zNsAR&Evxg-^z8J;z7y#A`vYS9v7kB9FwPTKm~GaBb&udCD<_&X-Y`xRv7Ez{pgCX| zCqY2zkF!jgzI|HJZpi!cNH~5u-gS_59^jX>G&_hOiKo~-4p*}=tFdTXAnj6Okg)Sajm zR7xWmIXuHEu}TiV1y$&mY~ieoAzO11S-dAsTfmBtOjmCpk{!x=w|+kk zu7*D58{|{h6`SW)PghT?B?JCj9{>nf!Rig{vC$=5%+_4YX@AP~sEf_jW1~IIw5n6J zZ3E-nJwI2cL*t+retHi0?>8_SYNb_;R%{MWj1QPD*XuChlgk!nUi_in@R~Lv9M$!I zCz^CwZ~Z>e`k=-jDG_0c#UGcPzv+yA$h#|~e>@|`KN%?3^OqgE@w22Covyg~>_2mT zDmm>=B05d+U*+IbCcga^f40ti1=%X>TDtq_BNOaF&$pp$|DW-@;^_Uw$>mg*zGqE^lp035)f`Qgl#)uS z2&809{rYcCOWy%G#pM@MMLhtk^N^lwg+On1sIlM;kJJ>JT zppNB#26+W^*d2o|9KiSvpj#yU)nbZmc;KtpYDtHCHfC_)_Y4uvS`61z4X9tk1$VQ=uMcjH>@i zKOhW5o{)*~#KlRAL9SxLS@f0GqXE`W(F~9%MDS%H19)+Cc&Muzr1dO-^~1PkaWn(M z-;pO&BKY3>Q4eHEM_A#Q#nBCrC`It4Ap@{QpEZyEP8UT6GX*|5_(4~W1ED=_Stc)* zkJl?VyG`Ndq3N@c7b=uJl!5oJH-C|4{KbpJTgjnO7F-t z`lJ(%%m2Xs{{w?~ZT^R{lll*m()YiP2lI32JC73xwY{{no5}^8KPJy&Z_tvCS(ZtEEm+*5IYi&*3c(RA(H6ThI00AD0XDZ>@z>`a@k?n;==mf zN?QINJ2=dV+nM-3%bMqZmM>^mD7OE$OjJCSYgwH8LuVq_GSRRxA@_OKWE+5grxuOa zJI-vKFZ=clLDq{2g9zPVCs$kmq3?(wI zbgl)5?Wp!xwe>#ap@&#CqtWF-j93+;zVg8fqEx|nb)d8?oiH~u*AI2z*I30^E3Ium z-%v6JcAFbOW0#%@tCLL*brM&-5^;^~wCzw_td88&j5!o|}RTm=rY? zD{V9+Kj;`M?O{z@gl2)AOB^nkFdI1)E8=0jUMRYQ*~&RSlX>}lDwfG;I%5Fi{VA8@ z@Syn0_1D|aiNqdi-GXW1Fwgb?&05)M&vh#G+7!|mck;lt(Amh7y{`|%$A?6NdJiS* zF~!2Tvw2BThD3nFKYG6%W4S%7f4+~8mP@REs0`-M_L-#Bh@saYRftWKU|r18`=i3T zn4@>S#Ce?9~Ae2=0$L}}3@X;DY83m@6N{6!CGC4Gg0gqI)I z=8=6?)(76gvwl$!!kvrmX5f129SmDPlceq8?k8RkZnl0=X|6G8>J6Z@A2#sj(}(sf zqF+c{@>u4HEK!nvI=?@`M@iQH6&?UeQ_n|9ci_EaaE~O>-T3}L=7U)xtP!DLlDs8J zu{t?XWJe}Wc}1+K71uF1^sjNfYoyv1Z|D#?qJCDk3CFB5>u1Fh{m+8(+S^o7q%k zSMiShKB0FlEg0DM3;F#*fWd@;v$M2vc5`-gf`OBkRn~&R zbTIR9dT;;mAMhOs_l`t|fqm2YU$1u#!^sTa&p!<*`||Z;1Iau4mmA%!Pt|et8!cms z=7_>^c(RBm$_KH5*#tm-BQ{2-BAL0`pX*K~r$yiAFgAWFZfx3E&w!zuPd&k1`O_lP zkq1J(P0vBoLcNf?=P7te>@aWFi^MC=fb>Wo@x76d;`MMGzHztdte?`zH1FpGy}zlW z;f8*Sf>)1CZ1{*;!HfWlEBl8iT!KcBUml@@7v>%o4;JOzCXSbEx34oz@-H@C(U%5n zMDzY&w06Q7Tq1Nfj@VJG*0G<=7?V#nyJ}nP+G;5Ind>d&Sj-ld#+V0q@u z!J;`k1eJoR8d)>^vMTrr*LngCqtB@X-@U!yNEDM1s6*vxVKzCW6_SyWrO_ln^^Hx9 zXs`f;SYry#;;-l|lzs6uvw9!7H#4WA%6RUNVK{fH=JRXznFo>n*rFG3;#OFq;_1W* z40YnbA{DX;N+MGs4@t^@sW(C&ll)Y$iswlYn&Qw}bG-G4IZA(m)FGlM;p41$3Mn2~@jErr8(j#U#Xf%e#fklUsitYuL(w0+xHcLE7Hu z^|#G;jwJ4<4;w6#)mYeE)b-A2C@3`8hrSYow$ndhsg=00hNAPGoxU=>>j~(6*5#y` zf0y?tjhFeTw?4LsqUMl&v*ik8W(nYh5cfK)@2j~YLRw2{1m!kT zY93~x^xZ+z2!9=dw#96jxG281v(a#Wbqk8fv1!r$JhD@IIog@=dKErte@p^6-y>t& z&7*akL}Ud^w)si5(q!$`-ik`=r7GgC0)i|jqeEO05R_a*ajGq;9yDh1u~t{U(|4Yp z{)>`j=e)eBZhf&|vRq3Nwe_U>8j)kWn|Nw0m?tEhJ(5PuTX?(V{OfYdv+r2+kMme2 z4Nd3^*Y?{I*I7>a2hE` zwD~U9gwLx-V~*IkH*mIbmaPS&aZ)%~x|&G$(_CQKgnbKztQV0T$QScAiIRB_`-^HV z6J~{=?G{T3ufg({M$_%GAYFF(fb_M49li@1!nWKf8}thjOhAfVTON*j!Q8KO{`>8o z+a$;ojplf=SENR59CHJ^pcOGR+Gp=|3vE`$ktOFaZG76H!4LReN`S<@tzfon2jeqsd8BYUM~ zly%41Q9)}iLB)(~{--i`9CcO_6Z%$PStx!zOoYa@hGze!(Whq>XClNmyPtT7Tl;?D z2JFpRCOd_?t>XG)u5nPrF{*;Gnng(#_#sgH(H%Tpn^?MoY41Gp70EOf693p<=Hr0J zp5}{RcgY!Ic_k}lcXD8iT3e54exqS?5!Z%I6x21HUU@%>EzehaPyy38COPY)umgT4 zm)zy>gl2+j_vvql9>+#Y1oVXC}g zi>y7c3Qy%~H|nfs^X!z z-vKH8ns`*HQKla;Gcx?kp|%3=QLOEK7X~_PHYdlIh|nM_2Q-T&42*6c_0vaPF6KgS zQ!?0odLfE<3~vEQ2S`mMi>$s5I4(R}eO~Xo)YpvG_SFM!3wfxo{3?G~>vX(fT+&wU zm}x)YHKcHJyqA`~wf7(bS8dB(iaBbaz%6_O;X;xody*D^TW=^6HQ*CX=-9xMnx2Bk zu<$^@w+Uwn&QGM9kFZAmn@UaQOx>a!>Qr4Kiv%{=D6?_+Or-hxEo1{7uK+ zxJ^)xQAgY)@1e3^kkG%<1ts7bu)LmoG>gx(Vq^=1ZQf2& zm({V~cw4^BNZ(5aZ6Y5nR_^4<$gW^)pgD36_C3tA%a{&lRnAr0>uRe`ZT+7BJqW`0 zGYg&(4zLsep#gw%9RUz33uZcrunRHaS^l@V4uvbjqt}2{AOwSkA0glm=FJR;L@26G z50{jb#N!<7WjxH05c{S&ui7e#egyfqNSDzoeZn;dV^5xR{i%yx@BNN&6UOh4%_LGkl zcAU@raMtZrD8=_}`O@_=iW_i)?;gFrP9{Spcb8x1xP%AvPr zx!LY=Ih--kFb|0xhg#kjmT%aXfNgObU+72|_ce?)PWA$ypRJX^E_!5}P$|rg_;TCf z*_iuo@c}XSufpr>ks0?QA2tjYO-PkcubVA5LlkaG&6G$>Ot5bK;!2vJ-cdamVSFA< zdnxPREmvF?ax&xk1-G=L`rc5AHOk>4sfgTKA~c$|WwdY4uE|l~c5-k2VO{nmn6^1E zZP!oJ7P_ifL_*}+_BGITR{5O)}IWSrXD@?Sp zv?UPjzMl`SHtw-lLd+SqI+D(yQAq?@Ak8}zONa!JX!xacCWQdIJ8#}3RGlK|;qW`L z;X`yBle^7KPah?MAO~c4vnL@M$d0GLdk>8@(-9^AHy!_x%a5)1zZukY{HW^sX`rWS z(nS0=eP!9=v8SnVw~EfU%4fe1JQ(uk`H4EyJ@33ki(_AhYmmk4DKsZyoqnS<*ekQ<~6LP zzavv}9c;qiG`@4YW2e37Si4w%(8-Td+Y~cH3?Gq&={{SRzjr#GsMfkwZ{p7W)33eP zJ#Q~)>TDgokoUwe@S9{m*RWnknneE*B%*o3F%Wp7adQkPGav zziku}rq`_8(C4t?)1#oyo$cWR6TL|}70u6M)2vqG23V32>CXN^<_f_<14#QKm*ffq z>g~aH_l`HmW`TC!^*s*=pEzG2}Wk&LriB%#``IZLElImFgKKgH^&#--jgP$mAH>pnz zXl(l2UU*6yXr-}fF;DOmo-tMDG@bR{A)xs4kx5%GU zQ)cZo?`3G#1t3O$L3ete9Mm4thbW<2o_!Ln27AtiM;^vi>k@999{0iPBq%qQP$OkM z2R6$lr%J5X`K{3=(<(`3x4F9slwQ?Sx7K1?mOOQ1o7VgO68nNz`PJ7K=(YqIydX7t zE|6OMWtoncLqhLFRM)cv>OIz-UW5MZPQL|gE18~S*g<#Kc9-`nY;wv&9HRQW2Pyn*^)Lhu;seao z39bV({?}iS=JYHiOYsyV`W2xlZGGNag1c| zP-!e*t)gl3?7GK}VJVBk<8!z}{>zVk1{B?ou20Vxc0C)uWR@!8Sk`x_vR40oSY*^i z!!IJL-ZCe)2*=cI577JCA$(WC1?P4w=>zT-y2yXEc|-Pb+~b48)XS5d2b}h$KGxgj zZa!Jiwj)}rJ@joow&7Wkj%7`{xqPnJsxX>$SbnFXoxklpGx1`F`YB4)#mpmGYVGbh z)={osVl5;VZ`Qc#Ln55+)|{$PFflrl{QHMs-3*VEA@fe1DbY;Xv*^^$Bo{gD;g(nW zN1YD(j7RFB4p-MUAQ%3&`ca*HPuGR literal 0 HcmV?d00001 diff --git a/5.0/_static/fonts/SourceSansPro-SemiboldIt.otf.woff b/5.0/_static/fonts/SourceSansPro-SemiboldIt.otf.woff new file mode 100644 index 0000000000000000000000000000000000000000..62c2997d3f3b600f1c319c5901f293cdfa01090c GIT binary patch literal 54260 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 literal 0 HcmV?d00001 diff --git a/5.0/_static/img/contents.png b/5.0/_static/img/contents.png new file mode 100644 index 0000000000000000000000000000000000000000..7fb82154a1748d507925865d3fbf7508d62483e5 GIT binary patch literal 202 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 C5^xg$ literal 0 HcmV?d00001 diff --git a/5.0/_static/img/favicon.ico b/5.0/_static/img/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..6ff4b1d2f0390ad13f32193b425a9c7a0368459d GIT binary patch literal 4286 zcmchYF^|(g5QR5#kus@ho9neq?Ve*k z4ygkS?(tG#{oC{X9QR|l&-8|tEj6&N3{uYqy&!sedyP5P!1JfB1>Om?hO{2oG4?8L z_5z+{H`nOCmzYnQdD=Vup|(A#c^%-hADz!K_(O>Ow(D-*L--4@)&0`et&M>nL%C8% z2)`Di`G<~y&$CfFb=15){jbG@vJS<8KZAHSYToDKvo^4KHfsBNPCjdBUS0Y7HSm11 zmz}?l|KWp|?z!$a#j}+(eM8?v*AIsFExC1YJ`UyDxDNan{!7SZYR}x8+8MJf?H!7XbZ_#1~=c;DsnzrK4apPt_j&V_f| z-+Jq{L~N|pK>oM)7xMY#0~_1CFMPSykMmFtHFMo}_9}yFX9N7Ba~(eWz&c%?_RN?X z-|8xvY@8 rLhfqYlT~G;kZe-Si$O8Ej*uU1qhe5*Y$C?YrRe?TY{9E0a1r?fajpt` literal 0 HcmV?d00001 diff --git a/5.0/_static/img/glyphicons-halflings-white.png b/5.0/_static/img/glyphicons-halflings-white.png new file mode 100644 index 0000000000000000000000000000000000000000..3bf6484a29d8da269f9bc874b25493a45fae3bae GIT binary patch literal 8777 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 B7G?kd literal 0 HcmV?d00001 diff --git a/5.0/_static/img/glyphicons-halflings.png b/5.0/_static/img/glyphicons-halflings.png new file mode 100644 index 0000000000000000000000000000000000000000..a9969993201f9cee63cf9f49217646347297b643 GIT binary patch literal 12799 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 literal 0 HcmV?d00001 diff --git a/5.0/_static/img/navigation.png b/5.0/_static/img/navigation.png new file mode 100644 index 0000000000000000000000000000000000000000..1081dc1439fb984dfa7ef627afe3c7dc476fdbce GIT binary patch literal 218 zcmeAS@N?(olHy`uVBq!ia0vp^j6iI|!3HFkf4uMuBv2gW?!>U}oXkrghqJ&VvY3H^ zTNs2H8D`Cq01C2~c>21s-(chw7$R|bZ|_0D0|q>YSbqDzW^|HYIk%*-&O)*Wip zr1+)TIKaUJCb)3nLQ_*yOiaw=qF!qFketMoIjjxu+*ELIq3^F;|~!D}xJWooB?$cBKq{?z$?hNo|cNFAPo` zO&OTQC}y}+m@%P=WhA$rVnnh{7SX(PoVYv)TAFz|#t}bjGsZt@H(&cIJk_o%TK$P8 z&W+_ANA|oaG-UsCh~PsE`m$&m=VJlYpSd>1NRj}UnXH$GV0woD@cLQj6Rf^=McCnB zg+*<5$0ufhHcqTUQ*f2Fs7ya!*`Tg8=_L@pu5UEdC?Ry0lRXBRtnC}?^b)ls@VafK zO%*|@Ha-=PlK$X9<|fzB!opOt@XO$X`Jt~6aj!psv%Ex;MJIGlWF>?xz2uIpJiAKi zUU(I#D$IsD3p&`sc{@Nbp8*)XG|-*Sjba<3B*P`0w9?g%FRbS|WtQ;(0x-bSj(V~G z?4Y5JcE!V7`lxm!L-?}b+%G>94EO7IpV&3_4tR)Wogv4hEE$zEEmhH_5p^s$wHroE z_p-^!FhfOEG0*xDLq_h$1t?7264baJFYhw2^8Bu3sI^Uzy^-#PG;NcZMrou5t1a~!Fx&L zqTF*i=y!*63|dySwPRn6=THf65kfW}3iGuN^tGOo+2(TI1ubJI`x>N2qH}6(;WgLl z#tqoCOiY-u4SJrP5{p%@gmkIg(nPtn9%DBmp{~1FXHu%CKrikbOKtgyB*o)S8D6y^ zd?Z02c4RIuOX!iQ^4JKKth?L+w!SSv^kMTOXQ2;*||OP8`L)bqdZ6=hG3znjBdyouQC- z2{|0(#LB*c*Y@m8w%x8e$)!>@aePd#Gz1{;x;$GLm$RD-;+&E4OTfN^l#oYz+pl{9 ztGltv^dIhTL5CU(YkuB>N{G0Dv}w(IDcts{ZL`6#^nPt?i$=m{D23_}=zdLGKobnbKO&IVDbBU4*Z5%!iq zwd4gy;B|a{6z_Y3ZCGELzjfi*#NXQob_F!etMeQXTP?>q_esTGgPMt!lYSi3Dk%lV zFj`-5x050$NZ}(6i)}hQkYTlvY8Qp-Z!otT;M%|JB!Kwrq1mAuVh-s8J`2(Z^xnPt zR!4ECmU?MGIlsK8xxEj(F5}*c?K-Fx9TCTsJOoyoO->S0t0Ql}USDv~FFZ_3wlL+k zE`6{8Ds$jwATl0?qVdNe@X*G7G+tihnA%zLE@dA$P0U95A6u*S^xk5 literal 0 HcmV?d00001 diff --git a/5.0/_static/jquery-3.4.1.js b/5.0/_static/jquery-3.4.1.js new file mode 100644 index 000000000..773ad95c5 --- /dev/null +++ b/5.0/_static/jquery-3.4.1.js @@ -0,0 +1,10598 @@ +/*! + * jQuery JavaScript Library v3.4.1 + * https://jquery.com/ + * + * Includes Sizzle.js + * https://sizzlejs.com/ + * + * Copyright JS Foundation and other contributors + * Released under the MIT license + * https://jquery.org/license + * + * Date: 2019-05-01T21:04Z + */ +( function( global, factory ) { + + "use strict"; + + if ( typeof module === "object" && typeof module.exports === "object" ) { + + // For CommonJS and CommonJS-like environments where a proper `window` + // is present, execute the factory and get jQuery. + // For environments that do not have a `window` with a `document` + // (such as Node.js), expose a factory as module.exports. + // This accentuates the need for the creation of a real `window`. + // e.g. var jQuery = require("jquery")(window); + // See ticket #14549 for more info. + module.exports = global.document ? + factory( global, true ) : + function( w ) { + if ( !w.document ) { + throw new Error( "jQuery requires a window with a document" ); + } + return factory( w ); + }; + } else { + factory( global ); + } + +// Pass this if window is not defined yet +} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) { + +// Edge <= 12 - 13+, Firefox <=18 - 45+, IE 10 - 11, Safari 5.1 - 9+, iOS 6 - 9.1 +// throw exceptions when non-strict code (e.g., ASP.NET 4.5) accesses strict mode +// arguments.callee.caller (trac-13335). But as of jQuery 3.0 (2016), strict mode should be common +// enough that all such attempts are guarded in a try block. +"use strict"; + +var arr = []; + +var document = window.document; + +var getProto = Object.getPrototypeOf; + +var slice = arr.slice; + +var concat = arr.concat; + +var push = arr.push; + +var indexOf = arr.indexOf; + +var class2type = {}; + +var toString = class2type.toString; + +var hasOwn = class2type.hasOwnProperty; + +var fnToString = hasOwn.toString; + +var ObjectFunctionString = fnToString.call( Object ); + +var support = {}; + +var isFunction = function isFunction( obj ) { + + // Support: Chrome <=57, Firefox <=52 + // In some browsers, typeof returns "function" for HTML elements + // (i.e., `typeof document.createElement( "object" ) === "function"`). + // We don't want to classify *any* DOM node as a function. + return typeof obj === "function" && typeof obj.nodeType !== "number"; + }; + + +var isWindow = function isWindow( obj ) { + return obj != null && obj === obj.window; + }; + + + + + var preservedScriptAttributes = { + type: true, + src: true, + nonce: true, + noModule: true + }; + + function DOMEval( code, node, doc ) { + doc = doc || document; + + var i, val, + script = doc.createElement( "script" ); + + script.text = code; + if ( node ) { + for ( i in preservedScriptAttributes ) { + + // Support: Firefox 64+, Edge 18+ + // Some browsers don't support the "nonce" property on scripts. + // On the other hand, just using `getAttribute` is not enough as + // the `nonce` attribute is reset to an empty string whenever it + // becomes browsing-context connected. + // See https://github.com/whatwg/html/issues/2369 + // See https://html.spec.whatwg.org/#nonce-attributes + // The `node.getAttribute` check was added for the sake of + // `jQuery.globalEval` so that it can fake a nonce-containing node + // via an object. + val = node[ i ] || node.getAttribute && node.getAttribute( i ); + if ( val ) { + script.setAttribute( i, val ); + } + } + } + doc.head.appendChild( script ).parentNode.removeChild( script ); + } + + +function toType( obj ) { + if ( obj == null ) { + return obj + ""; + } + + // Support: Android <=2.3 only (functionish RegExp) + return typeof obj === "object" || typeof obj === "function" ? + class2type[ toString.call( obj ) ] || "object" : + typeof obj; +} +/* global Symbol */ +// Defining this global in .eslintrc.json would create a danger of using the global +// unguarded in another place, it seems safer to define global only for this module + + + +var + version = "3.4.1", + + // Define a local copy of jQuery + jQuery = function( selector, context ) { + + // The jQuery object is actually just the init constructor 'enhanced' + // Need init if jQuery is called (just allow error to be thrown if not included) + return new jQuery.fn.init( selector, context ); + }, + + // Support: Android <=4.0 only + // Make sure we trim BOM and NBSP + rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g; + +jQuery.fn = jQuery.prototype = { + + // The current version of jQuery being used + jquery: version, + + constructor: jQuery, + + // The default length of a jQuery object is 0 + length: 0, + + toArray: function() { + return slice.call( this ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + + // Return all the elements in a clean array + if ( num == null ) { + return slice.call( this ); + } + + // Return just the one element from the set + return num < 0 ? this[ num + this.length ] : this[ num ]; + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems ) { + + // Build a new jQuery matched element set + var ret = jQuery.merge( this.constructor(), elems ); + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + each: function( callback ) { + return jQuery.each( this, callback ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map( this, function( elem, i ) { + return callback.call( elem, i, elem ); + } ) ); + }, + + slice: function() { + return this.pushStack( slice.apply( this, arguments ) ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + eq: function( i ) { + var len = this.length, + j = +i + ( i < 0 ? len : 0 ); + return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] ); + }, + + end: function() { + return this.prevObject || this.constructor(); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: push, + sort: arr.sort, + splice: arr.splice +}; + +jQuery.extend = jQuery.fn.extend = function() { + var options, name, src, copy, copyIsArray, clone, + target = arguments[ 0 ] || {}, + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + + // Skip the boolean and the target + target = arguments[ i ] || {}; + i++; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !isFunction( target ) ) { + target = {}; + } + + // Extend jQuery itself if only one argument is passed + if ( i === length ) { + target = this; + i--; + } + + for ( ; i < length; i++ ) { + + // Only deal with non-null/undefined values + if ( ( options = arguments[ i ] ) != null ) { + + // Extend the base object + for ( name in options ) { + copy = options[ name ]; + + // Prevent Object.prototype pollution + // Prevent never-ending loop + if ( name === "__proto__" || target === copy ) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject( copy ) || + ( copyIsArray = Array.isArray( copy ) ) ) ) { + src = target[ name ]; + + // Ensure proper type for the source value + if ( copyIsArray && !Array.isArray( src ) ) { + clone = []; + } else if ( !copyIsArray && !jQuery.isPlainObject( src ) ) { + clone = {}; + } else { + clone = src; + } + copyIsArray = false; + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +jQuery.extend( { + + // Unique for each copy of jQuery on the page + expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), + + // Assume jQuery is ready without the ready module + isReady: true, + + error: function( msg ) { + throw new Error( msg ); + }, + + noop: function() {}, + + isPlainObject: function( obj ) { + var proto, Ctor; + + // Detect obvious negatives + // Use toString instead of jQuery.type to catch host objects + if ( !obj || toString.call( obj ) !== "[object Object]" ) { + return false; + } + + proto = getProto( obj ); + + // Objects with no prototype (e.g., `Object.create( null )`) are plain + if ( !proto ) { + return true; + } + + // Objects with prototype are plain iff they were constructed by a global Object function + Ctor = hasOwn.call( proto, "constructor" ) && proto.constructor; + return typeof Ctor === "function" && fnToString.call( Ctor ) === ObjectFunctionString; + }, + + isEmptyObject: function( obj ) { + var name; + + for ( name in obj ) { + return false; + } + return true; + }, + + // Evaluates a script in a global context + globalEval: function( code, options ) { + DOMEval( code, { nonce: options && options.nonce } ); + }, + + each: function( obj, callback ) { + var length, i = 0; + + if ( isArrayLike( obj ) ) { + length = obj.length; + for ( ; i < length; i++ ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } else { + for ( i in obj ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } + + return obj; + }, + + // Support: Android <=4.0 only + trim: function( text ) { + return text == null ? + "" : + ( text + "" ).replace( rtrim, "" ); + }, + + // results is for internal usage only + makeArray: function( arr, results ) { + var ret = results || []; + + if ( arr != null ) { + if ( isArrayLike( Object( arr ) ) ) { + jQuery.merge( ret, + typeof arr === "string" ? + [ arr ] : arr + ); + } else { + push.call( ret, arr ); + } + } + + return ret; + }, + + inArray: function( elem, arr, i ) { + return arr == null ? -1 : indexOf.call( arr, elem, i ); + }, + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + merge: function( first, second ) { + var len = +second.length, + j = 0, + i = first.length; + + for ( ; j < len; j++ ) { + first[ i++ ] = second[ j ]; + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, invert ) { + var callbackInverse, + matches = [], + i = 0, + length = elems.length, + callbackExpect = !invert; + + // Go through the array, only saving the items + // that pass the validator function + for ( ; i < length; i++ ) { + callbackInverse = !callback( elems[ i ], i ); + if ( callbackInverse !== callbackExpect ) { + matches.push( elems[ i ] ); + } + } + + return matches; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var length, value, + i = 0, + ret = []; + + // Go through the array, translating each of the items to their new values + if ( isArrayLike( elems ) ) { + length = elems.length; + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + + // Go through every key on the object, + } else { + for ( i in elems ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + } + + // Flatten any nested arrays + return concat.apply( [], ret ); + }, + + // A global GUID counter for objects + guid: 1, + + // jQuery.support is not used in Core but other projects attach their + // properties to it so it needs to exist. + support: support +} ); + +if ( typeof Symbol === "function" ) { + jQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ]; +} + +// Populate the class2type map +jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ), +function( i, name ) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); +} ); + +function isArrayLike( obj ) { + + // Support: real iOS 8.2 only (not reproducible in simulator) + // `in` check used to prevent JIT error (gh-2145) + // hasOwn isn't used here due to false negatives + // regarding Nodelist length in IE + var length = !!obj && "length" in obj && obj.length, + type = toType( obj ); + + if ( isFunction( obj ) || isWindow( obj ) ) { + return false; + } + + return type === "array" || length === 0 || + typeof length === "number" && length > 0 && ( length - 1 ) in obj; +} +var Sizzle = +/*! + * Sizzle CSS Selector Engine v2.3.4 + * https://sizzlejs.com/ + * + * Copyright JS Foundation and other contributors + * Released under the MIT license + * https://js.foundation/ + * + * Date: 2019-04-08 + */ +(function( window ) { + +var i, + support, + Expr, + getText, + isXML, + tokenize, + compile, + select, + outermostContext, + sortInput, + hasDuplicate, + + // Local document vars + setDocument, + document, + docElem, + documentIsHTML, + rbuggyQSA, + rbuggyMatches, + matches, + contains, + + // Instance-specific data + expando = "sizzle" + 1 * new Date(), + preferredDoc = window.document, + dirruns = 0, + done = 0, + classCache = createCache(), + tokenCache = createCache(), + compilerCache = createCache(), + nonnativeSelectorCache = createCache(), + sortOrder = function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + } + return 0; + }, + + // Instance methods + hasOwn = ({}).hasOwnProperty, + arr = [], + pop = arr.pop, + push_native = arr.push, + push = arr.push, + slice = arr.slice, + // Use a stripped-down indexOf as it's faster than native + // https://jsperf.com/thor-indexof-vs-for/5 + indexOf = function( list, elem ) { + var i = 0, + len = list.length; + for ( ; i < len; i++ ) { + if ( list[i] === elem ) { + return i; + } + } + return -1; + }, + + booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped", + + // Regular expressions + + // http://www.w3.org/TR/css3-selectors/#whitespace + whitespace = "[\\x20\\t\\r\\n\\f]", + + // http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier + identifier = "(?:\\\\.|[\\w-]|[^\0-\\xa0])+", + + // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors + attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace + + // Operator (capture 2) + "*([*^$|!~]?=)" + whitespace + + // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]" + "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace + + "*\\]", + + pseudos = ":(" + identifier + ")(?:\\((" + + // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments: + // 1. quoted (capture 3; capture 4 or capture 5) + "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" + + // 2. simple (capture 6) + "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" + + // 3. anything else (capture 2) + ".*" + + ")\\)|)", + + // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter + rwhitespace = new RegExp( whitespace + "+", "g" ), + rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), + + rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), + rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ), + rdescend = new RegExp( whitespace + "|>" ), + + rpseudo = new RegExp( pseudos ), + ridentifier = new RegExp( "^" + identifier + "$" ), + + matchExpr = { + "ID": new RegExp( "^#(" + identifier + ")" ), + "CLASS": new RegExp( "^\\.(" + identifier + ")" ), + "TAG": new RegExp( "^(" + identifier + "|[*])" ), + "ATTR": new RegExp( "^" + attributes ), + "PSEUDO": new RegExp( "^" + pseudos ), + "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace + + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), + "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), + // For use in libraries implementing .is() + // We use this for POS matching in `select` + "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + + whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) + }, + + rhtml = /HTML$/i, + rinputs = /^(?:input|select|textarea|button)$/i, + rheader = /^h\d$/i, + + rnative = /^[^{]+\{\s*\[native \w/, + + // Easily-parseable/retrievable ID or TAG or CLASS selectors + rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, + + rsibling = /[+~]/, + + // CSS escapes + // http://www.w3.org/TR/CSS21/syndata.html#escaped-characters + runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ), + funescape = function( _, escaped, escapedWhitespace ) { + var high = "0x" + escaped - 0x10000; + // NaN means non-codepoint + // Support: Firefox<24 + // Workaround erroneous numeric interpretation of +"0x" + return high !== high || escapedWhitespace ? + escaped : + high < 0 ? + // BMP codepoint + String.fromCharCode( high + 0x10000 ) : + // Supplemental Plane codepoint (surrogate pair) + String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); + }, + + // CSS string/identifier serialization + // https://drafts.csswg.org/cssom/#common-serializing-idioms + rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g, + fcssescape = function( ch, asCodePoint ) { + if ( asCodePoint ) { + + // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER + if ( ch === "\0" ) { + return "\uFFFD"; + } + + // Control characters and (dependent upon position) numbers get escaped as code points + return ch.slice( 0, -1 ) + "\\" + ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " "; + } + + // Other potentially-special ASCII characters get backslash-escaped + return "\\" + ch; + }, + + // Used for iframes + // See setDocument() + // Removing the function wrapper causes a "Permission Denied" + // error in IE + unloadHandler = function() { + setDocument(); + }, + + inDisabledFieldset = addCombinator( + function( elem ) { + return elem.disabled === true && elem.nodeName.toLowerCase() === "fieldset"; + }, + { dir: "parentNode", next: "legend" } + ); + +// Optimize for push.apply( _, NodeList ) +try { + push.apply( + (arr = slice.call( preferredDoc.childNodes )), + preferredDoc.childNodes + ); + // Support: Android<4.0 + // Detect silently failing push.apply + arr[ preferredDoc.childNodes.length ].nodeType; +} catch ( e ) { + push = { apply: arr.length ? + + // Leverage slice if possible + function( target, els ) { + push_native.apply( target, slice.call(els) ); + } : + + // Support: IE<9 + // Otherwise append directly + function( target, els ) { + var j = target.length, + i = 0; + // Can't trust NodeList.length + while ( (target[j++] = els[i++]) ) {} + target.length = j - 1; + } + }; +} + +function Sizzle( selector, context, results, seed ) { + var m, i, elem, nid, match, groups, newSelector, + newContext = context && context.ownerDocument, + + // nodeType defaults to 9, since context defaults to document + nodeType = context ? context.nodeType : 9; + + results = results || []; + + // Return early from calls with invalid selector or context + if ( typeof selector !== "string" || !selector || + nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) { + + return results; + } + + // Try to shortcut find operations (as opposed to filters) in HTML documents + if ( !seed ) { + + if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) { + setDocument( context ); + } + context = context || document; + + if ( documentIsHTML ) { + + // If the selector is sufficiently simple, try using a "get*By*" DOM method + // (excepting DocumentFragment context, where the methods don't exist) + if ( nodeType !== 11 && (match = rquickExpr.exec( selector )) ) { + + // ID selector + if ( (m = match[1]) ) { + + // Document context + if ( nodeType === 9 ) { + if ( (elem = context.getElementById( m )) ) { + + // Support: IE, Opera, Webkit + // TODO: identify versions + // getElementById can match elements by name instead of ID + if ( elem.id === m ) { + results.push( elem ); + return results; + } + } else { + return results; + } + + // Element context + } else { + + // Support: IE, Opera, Webkit + // TODO: identify versions + // getElementById can match elements by name instead of ID + if ( newContext && (elem = newContext.getElementById( m )) && + contains( context, elem ) && + elem.id === m ) { + + results.push( elem ); + return results; + } + } + + // Type selector + } else if ( match[2] ) { + push.apply( results, context.getElementsByTagName( selector ) ); + return results; + + // Class selector + } else if ( (m = match[3]) && support.getElementsByClassName && + context.getElementsByClassName ) { + + push.apply( results, context.getElementsByClassName( m ) ); + return results; + } + } + + // Take advantage of querySelectorAll + if ( support.qsa && + !nonnativeSelectorCache[ selector + " " ] && + (!rbuggyQSA || !rbuggyQSA.test( selector )) && + + // Support: IE 8 only + // Exclude object elements + (nodeType !== 1 || context.nodeName.toLowerCase() !== "object") ) { + + newSelector = selector; + newContext = context; + + // qSA considers elements outside a scoping root when evaluating child or + // descendant combinators, which is not what we want. + // In such cases, we work around the behavior by prefixing every selector in the + // list with an ID selector referencing the scope context. + // Thanks to Andrew Dupont for this technique. + if ( nodeType === 1 && rdescend.test( selector ) ) { + + // Capture the context ID, setting it first if necessary + if ( (nid = context.getAttribute( "id" )) ) { + nid = nid.replace( rcssescape, fcssescape ); + } else { + context.setAttribute( "id", (nid = expando) ); + } + + // Prefix every selector in the list + groups = tokenize( selector ); + i = groups.length; + while ( i-- ) { + groups[i] = "#" + nid + " " + toSelector( groups[i] ); + } + newSelector = groups.join( "," ); + + // Expand context for sibling selectors + newContext = rsibling.test( selector ) && testContext( context.parentNode ) || + context; + } + + try { + push.apply( results, + newContext.querySelectorAll( newSelector ) + ); + return results; + } catch ( qsaError ) { + nonnativeSelectorCache( selector, true ); + } finally { + if ( nid === expando ) { + context.removeAttribute( "id" ); + } + } + } + } + } + + // All others + return select( selector.replace( rtrim, "$1" ), context, results, seed ); +} + +/** + * Create key-value caches of limited size + * @returns {function(string, object)} Returns the Object data after storing it on itself with + * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) + * deleting the oldest entry + */ +function createCache() { + var keys = []; + + function cache( key, value ) { + // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) + if ( keys.push( key + " " ) > Expr.cacheLength ) { + // Only keep the most recent entries + delete cache[ keys.shift() ]; + } + return (cache[ key + " " ] = value); + } + return cache; +} + +/** + * Mark a function for special use by Sizzle + * @param {Function} fn The function to mark + */ +function markFunction( fn ) { + fn[ expando ] = true; + return fn; +} + +/** + * Support testing using an element + * @param {Function} fn Passed the created element and returns a boolean result + */ +function assert( fn ) { + var el = document.createElement("fieldset"); + + try { + return !!fn( el ); + } catch (e) { + return false; + } finally { + // Remove from its parent by default + if ( el.parentNode ) { + el.parentNode.removeChild( el ); + } + // release memory in IE + el = null; + } +} + +/** + * Adds the same handler for all of the specified attrs + * @param {String} attrs Pipe-separated list of attributes + * @param {Function} handler The method that will be applied + */ +function addHandle( attrs, handler ) { + var arr = attrs.split("|"), + i = arr.length; + + while ( i-- ) { + Expr.attrHandle[ arr[i] ] = handler; + } +} + +/** + * Checks document order of two siblings + * @param {Element} a + * @param {Element} b + * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b + */ +function siblingCheck( a, b ) { + var cur = b && a, + diff = cur && a.nodeType === 1 && b.nodeType === 1 && + a.sourceIndex - b.sourceIndex; + + // Use IE sourceIndex if available on both nodes + if ( diff ) { + return diff; + } + + // Check if b follows a + if ( cur ) { + while ( (cur = cur.nextSibling) ) { + if ( cur === b ) { + return -1; + } + } + } + + return a ? 1 : -1; +} + +/** + * Returns a function to use in pseudos for input types + * @param {String} type + */ +function createInputPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for buttons + * @param {String} type + */ +function createButtonPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for :enabled/:disabled + * @param {Boolean} disabled true for :disabled; false for :enabled + */ +function createDisabledPseudo( disabled ) { + + // Known :disabled false positives: fieldset[disabled] > legend:nth-of-type(n+2) :can-disable + return function( elem ) { + + // Only certain elements can match :enabled or :disabled + // https://html.spec.whatwg.org/multipage/scripting.html#selector-enabled + // https://html.spec.whatwg.org/multipage/scripting.html#selector-disabled + if ( "form" in elem ) { + + // Check for inherited disabledness on relevant non-disabled elements: + // * listed form-associated elements in a disabled fieldset + // https://html.spec.whatwg.org/multipage/forms.html#category-listed + // https://html.spec.whatwg.org/multipage/forms.html#concept-fe-disabled + // * option elements in a disabled optgroup + // https://html.spec.whatwg.org/multipage/forms.html#concept-option-disabled + // All such elements have a "form" property. + if ( elem.parentNode && elem.disabled === false ) { + + // Option elements defer to a parent optgroup if present + if ( "label" in elem ) { + if ( "label" in elem.parentNode ) { + return elem.parentNode.disabled === disabled; + } else { + return elem.disabled === disabled; + } + } + + // Support: IE 6 - 11 + // Use the isDisabled shortcut property to check for disabled fieldset ancestors + return elem.isDisabled === disabled || + + // Where there is no isDisabled, check manually + /* jshint -W018 */ + elem.isDisabled !== !disabled && + inDisabledFieldset( elem ) === disabled; + } + + return elem.disabled === disabled; + + // Try to winnow out elements that can't be disabled before trusting the disabled property. + // Some victims get caught in our net (label, legend, menu, track), but it shouldn't + // even exist on them, let alone have a boolean value. + } else if ( "label" in elem ) { + return elem.disabled === disabled; + } + + // Remaining elements are neither :enabled nor :disabled + return false; + }; +} + +/** + * Returns a function to use in pseudos for positionals + * @param {Function} fn + */ +function createPositionalPseudo( fn ) { + return markFunction(function( argument ) { + argument = +argument; + return markFunction(function( seed, matches ) { + var j, + matchIndexes = fn( [], seed.length, argument ), + i = matchIndexes.length; + + // Match elements found at the specified indexes + while ( i-- ) { + if ( seed[ (j = matchIndexes[i]) ] ) { + seed[j] = !(matches[j] = seed[j]); + } + } + }); + }); +} + +/** + * Checks a node for validity as a Sizzle context + * @param {Element|Object=} context + * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value + */ +function testContext( context ) { + return context && typeof context.getElementsByTagName !== "undefined" && context; +} + +// Expose support vars for convenience +support = Sizzle.support = {}; + +/** + * Detects XML nodes + * @param {Element|Object} elem An element or a document + * @returns {Boolean} True iff elem is a non-HTML XML node + */ +isXML = Sizzle.isXML = function( elem ) { + var namespace = elem.namespaceURI, + docElem = (elem.ownerDocument || elem).documentElement; + + // Support: IE <=8 + // Assume HTML when documentElement doesn't yet exist, such as inside loading iframes + // https://bugs.jquery.com/ticket/4833 + return !rhtml.test( namespace || docElem && docElem.nodeName || "HTML" ); +}; + +/** + * Sets document-related variables once based on the current document + * @param {Element|Object} [doc] An element or document object to use to set the document + * @returns {Object} Returns the current document + */ +setDocument = Sizzle.setDocument = function( node ) { + var hasCompare, subWindow, + doc = node ? node.ownerDocument || node : preferredDoc; + + // Return early if doc is invalid or already selected + if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) { + return document; + } + + // Update global variables + document = doc; + docElem = document.documentElement; + documentIsHTML = !isXML( document ); + + // Support: IE 9-11, Edge + // Accessing iframe documents after unload throws "permission denied" errors (jQuery #13936) + if ( preferredDoc !== document && + (subWindow = document.defaultView) && subWindow.top !== subWindow ) { + + // Support: IE 11, Edge + if ( subWindow.addEventListener ) { + subWindow.addEventListener( "unload", unloadHandler, false ); + + // Support: IE 9 - 10 only + } else if ( subWindow.attachEvent ) { + subWindow.attachEvent( "onunload", unloadHandler ); + } + } + + /* Attributes + ---------------------------------------------------------------------- */ + + // Support: IE<8 + // Verify that getAttribute really returns attributes and not properties + // (excepting IE8 booleans) + support.attributes = assert(function( el ) { + el.className = "i"; + return !el.getAttribute("className"); + }); + + /* getElement(s)By* + ---------------------------------------------------------------------- */ + + // Check if getElementsByTagName("*") returns only elements + support.getElementsByTagName = assert(function( el ) { + el.appendChild( document.createComment("") ); + return !el.getElementsByTagName("*").length; + }); + + // Support: IE<9 + support.getElementsByClassName = rnative.test( document.getElementsByClassName ); + + // Support: IE<10 + // Check if getElementById returns elements by name + // The broken getElementById methods don't pick up programmatically-set names, + // so use a roundabout getElementsByName test + support.getById = assert(function( el ) { + docElem.appendChild( el ).id = expando; + return !document.getElementsByName || !document.getElementsByName( expando ).length; + }); + + // ID filter and find + if ( support.getById ) { + Expr.filter["ID"] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + return elem.getAttribute("id") === attrId; + }; + }; + Expr.find["ID"] = function( id, context ) { + if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { + var elem = context.getElementById( id ); + return elem ? [ elem ] : []; + } + }; + } else { + Expr.filter["ID"] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + var node = typeof elem.getAttributeNode !== "undefined" && + elem.getAttributeNode("id"); + return node && node.value === attrId; + }; + }; + + // Support: IE 6 - 7 only + // getElementById is not reliable as a find shortcut + Expr.find["ID"] = function( id, context ) { + if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { + var node, i, elems, + elem = context.getElementById( id ); + + if ( elem ) { + + // Verify the id attribute + node = elem.getAttributeNode("id"); + if ( node && node.value === id ) { + return [ elem ]; + } + + // Fall back on getElementsByName + elems = context.getElementsByName( id ); + i = 0; + while ( (elem = elems[i++]) ) { + node = elem.getAttributeNode("id"); + if ( node && node.value === id ) { + return [ elem ]; + } + } + } + + return []; + } + }; + } + + // Tag + Expr.find["TAG"] = support.getElementsByTagName ? + function( tag, context ) { + if ( typeof context.getElementsByTagName !== "undefined" ) { + return context.getElementsByTagName( tag ); + + // DocumentFragment nodes don't have gEBTN + } else if ( support.qsa ) { + return context.querySelectorAll( tag ); + } + } : + + function( tag, context ) { + var elem, + tmp = [], + i = 0, + // By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too + results = context.getElementsByTagName( tag ); + + // Filter out possible comments + if ( tag === "*" ) { + while ( (elem = results[i++]) ) { + if ( elem.nodeType === 1 ) { + tmp.push( elem ); + } + } + + return tmp; + } + return results; + }; + + // Class + Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) { + if ( typeof context.getElementsByClassName !== "undefined" && documentIsHTML ) { + return context.getElementsByClassName( className ); + } + }; + + /* QSA/matchesSelector + ---------------------------------------------------------------------- */ + + // QSA and matchesSelector support + + // matchesSelector(:active) reports false when true (IE9/Opera 11.5) + rbuggyMatches = []; + + // qSa(:focus) reports false when true (Chrome 21) + // We allow this because of a bug in IE8/9 that throws an error + // whenever `document.activeElement` is accessed on an iframe + // So, we allow :focus to pass through QSA all the time to avoid the IE error + // See https://bugs.jquery.com/ticket/13378 + rbuggyQSA = []; + + if ( (support.qsa = rnative.test( document.querySelectorAll )) ) { + // Build QSA regex + // Regex strategy adopted from Diego Perini + assert(function( el ) { + // Select is set to empty string on purpose + // This is to test IE's treatment of not explicitly + // setting a boolean content attribute, + // since its presence should be enough + // https://bugs.jquery.com/ticket/12359 + docElem.appendChild( el ).innerHTML = "" + + ""; + + // Support: IE8, Opera 11-12.16 + // Nothing should be selected when empty strings follow ^= or $= or *= + // The test attribute must be unknown in Opera but "safe" for WinRT + // https://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section + if ( el.querySelectorAll("[msallowcapture^='']").length ) { + rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); + } + + // Support: IE8 + // Boolean attributes and "value" are not treated correctly + if ( !el.querySelectorAll("[selected]").length ) { + rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); + } + + // Support: Chrome<29, Android<4.4, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.8+ + if ( !el.querySelectorAll( "[id~=" + expando + "-]" ).length ) { + rbuggyQSA.push("~="); + } + + // Webkit/Opera - :checked should return selected option elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + // IE8 throws error here and will not see later tests + if ( !el.querySelectorAll(":checked").length ) { + rbuggyQSA.push(":checked"); + } + + // Support: Safari 8+, iOS 8+ + // https://bugs.webkit.org/show_bug.cgi?id=136851 + // In-page `selector#id sibling-combinator selector` fails + if ( !el.querySelectorAll( "a#" + expando + "+*" ).length ) { + rbuggyQSA.push(".#.+[+~]"); + } + }); + + assert(function( el ) { + el.innerHTML = "" + + ""; + + // Support: Windows 8 Native Apps + // The type and name attributes are restricted during .innerHTML assignment + var input = document.createElement("input"); + input.setAttribute( "type", "hidden" ); + el.appendChild( input ).setAttribute( "name", "D" ); + + // Support: IE8 + // Enforce case-sensitivity of name attribute + if ( el.querySelectorAll("[name=d]").length ) { + rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" ); + } + + // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) + // IE8 throws error here and will not see later tests + if ( el.querySelectorAll(":enabled").length !== 2 ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Support: IE9-11+ + // IE's :disabled selector does not pick up the children of disabled fieldsets + docElem.appendChild( el ).disabled = true; + if ( el.querySelectorAll(":disabled").length !== 2 ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Opera 10-11 does not throw on post-comma invalid pseudos + el.querySelectorAll("*,:x"); + rbuggyQSA.push(",.*:"); + }); + } + + if ( (support.matchesSelector = rnative.test( (matches = docElem.matches || + docElem.webkitMatchesSelector || + docElem.mozMatchesSelector || + docElem.oMatchesSelector || + docElem.msMatchesSelector) )) ) { + + assert(function( el ) { + // Check to see if it's possible to do matchesSelector + // on a disconnected node (IE 9) + support.disconnectedMatch = matches.call( el, "*" ); + + // This should fail with an exception + // Gecko does not error, returns false instead + matches.call( el, "[s!='']:x" ); + rbuggyMatches.push( "!=", pseudos ); + }); + } + + rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") ); + rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") ); + + /* Contains + ---------------------------------------------------------------------- */ + hasCompare = rnative.test( docElem.compareDocumentPosition ); + + // Element contains another + // Purposefully self-exclusive + // As in, an element does not contain itself + contains = hasCompare || rnative.test( docElem.contains ) ? + function( a, b ) { + var adown = a.nodeType === 9 ? a.documentElement : a, + bup = b && b.parentNode; + return a === bup || !!( bup && bup.nodeType === 1 && ( + adown.contains ? + adown.contains( bup ) : + a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 + )); + } : + function( a, b ) { + if ( b ) { + while ( (b = b.parentNode) ) { + if ( b === a ) { + return true; + } + } + } + return false; + }; + + /* Sorting + ---------------------------------------------------------------------- */ + + // Document order sorting + sortOrder = hasCompare ? + function( a, b ) { + + // Flag for duplicate removal + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + // Sort on method existence if only one input has compareDocumentPosition + var compare = !a.compareDocumentPosition - !b.compareDocumentPosition; + if ( compare ) { + return compare; + } + + // Calculate position if both inputs belong to the same document + compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ? + a.compareDocumentPosition( b ) : + + // Otherwise we know they are disconnected + 1; + + // Disconnected nodes + if ( compare & 1 || + (!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) { + + // Choose the first element that is related to our preferred document + if ( a === document || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) { + return -1; + } + if ( b === document || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) { + return 1; + } + + // Maintain original order + return sortInput ? + ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : + 0; + } + + return compare & 4 ? -1 : 1; + } : + function( a, b ) { + // Exit early if the nodes are identical + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + var cur, + i = 0, + aup = a.parentNode, + bup = b.parentNode, + ap = [ a ], + bp = [ b ]; + + // Parentless nodes are either documents or disconnected + if ( !aup || !bup ) { + return a === document ? -1 : + b === document ? 1 : + aup ? -1 : + bup ? 1 : + sortInput ? + ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : + 0; + + // If the nodes are siblings, we can do a quick check + } else if ( aup === bup ) { + return siblingCheck( a, b ); + } + + // Otherwise we need full lists of their ancestors for comparison + cur = a; + while ( (cur = cur.parentNode) ) { + ap.unshift( cur ); + } + cur = b; + while ( (cur = cur.parentNode) ) { + bp.unshift( cur ); + } + + // Walk down the tree looking for a discrepancy + while ( ap[i] === bp[i] ) { + i++; + } + + return i ? + // Do a sibling check if the nodes have a common ancestor + siblingCheck( ap[i], bp[i] ) : + + // Otherwise nodes in our document sort first + ap[i] === preferredDoc ? -1 : + bp[i] === preferredDoc ? 1 : + 0; + }; + + return document; +}; + +Sizzle.matches = function( expr, elements ) { + return Sizzle( expr, null, null, elements ); +}; + +Sizzle.matchesSelector = function( elem, expr ) { + // Set document vars if needed + if ( ( elem.ownerDocument || elem ) !== document ) { + setDocument( elem ); + } + + if ( support.matchesSelector && documentIsHTML && + !nonnativeSelectorCache[ expr + " " ] && + ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && + ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { + + try { + var ret = matches.call( elem, expr ); + + // IE 9's matchesSelector returns false on disconnected nodes + if ( ret || support.disconnectedMatch || + // As well, disconnected nodes are said to be in a document + // fragment in IE 9 + elem.document && elem.document.nodeType !== 11 ) { + return ret; + } + } catch (e) { + nonnativeSelectorCache( expr, true ); + } + } + + return Sizzle( expr, document, null, [ elem ] ).length > 0; +}; + +Sizzle.contains = function( context, elem ) { + // Set document vars if needed + if ( ( context.ownerDocument || context ) !== document ) { + setDocument( context ); + } + return contains( context, elem ); +}; + +Sizzle.attr = function( elem, name ) { + // Set document vars if needed + if ( ( elem.ownerDocument || elem ) !== document ) { + setDocument( elem ); + } + + var fn = Expr.attrHandle[ name.toLowerCase() ], + // Don't get fooled by Object.prototype properties (jQuery #13807) + val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ? + fn( elem, name, !documentIsHTML ) : + undefined; + + return val !== undefined ? + val : + support.attributes || !documentIsHTML ? + elem.getAttribute( name ) : + (val = elem.getAttributeNode(name)) && val.specified ? + val.value : + null; +}; + +Sizzle.escape = function( sel ) { + return (sel + "").replace( rcssescape, fcssescape ); +}; + +Sizzle.error = function( msg ) { + throw new Error( "Syntax error, unrecognized expression: " + msg ); +}; + +/** + * Document sorting and removing duplicates + * @param {ArrayLike} results + */ +Sizzle.uniqueSort = function( results ) { + var elem, + duplicates = [], + j = 0, + i = 0; + + // Unless we *know* we can detect duplicates, assume their presence + hasDuplicate = !support.detectDuplicates; + sortInput = !support.sortStable && results.slice( 0 ); + results.sort( sortOrder ); + + if ( hasDuplicate ) { + while ( (elem = results[i++]) ) { + if ( elem === results[ i ] ) { + j = duplicates.push( i ); + } + } + while ( j-- ) { + results.splice( duplicates[ j ], 1 ); + } + } + + // Clear input after sorting to release objects + // See https://github.com/jquery/sizzle/pull/225 + sortInput = null; + + return results; +}; + +/** + * Utility function for retrieving the text value of an array of DOM nodes + * @param {Array|Element} elem + */ +getText = Sizzle.getText = function( elem ) { + var node, + ret = "", + i = 0, + nodeType = elem.nodeType; + + if ( !nodeType ) { + // If no nodeType, this is expected to be an array + while ( (node = elem[i++]) ) { + // Do not traverse comment nodes + ret += getText( node ); + } + } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { + // Use textContent for elements + // innerText usage removed for consistency of new lines (jQuery #11153) + if ( typeof elem.textContent === "string" ) { + return elem.textContent; + } else { + // Traverse its children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + ret += getText( elem ); + } + } + } else if ( nodeType === 3 || nodeType === 4 ) { + return elem.nodeValue; + } + // Do not include comment or processing instruction nodes + + return ret; +}; + +Expr = Sizzle.selectors = { + + // Can be adjusted by the user + cacheLength: 50, + + createPseudo: markFunction, + + match: matchExpr, + + attrHandle: {}, + + find: {}, + + relative: { + ">": { dir: "parentNode", first: true }, + " ": { dir: "parentNode" }, + "+": { dir: "previousSibling", first: true }, + "~": { dir: "previousSibling" } + }, + + preFilter: { + "ATTR": function( match ) { + match[1] = match[1].replace( runescape, funescape ); + + // Move the given value to match[3] whether quoted or unquoted + match[3] = ( match[3] || match[4] || match[5] || "" ).replace( runescape, funescape ); + + if ( match[2] === "~=" ) { + match[3] = " " + match[3] + " "; + } + + return match.slice( 0, 4 ); + }, + + "CHILD": function( match ) { + /* matches from matchExpr["CHILD"] + 1 type (only|nth|...) + 2 what (child|of-type) + 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) + 4 xn-component of xn+y argument ([+-]?\d*n|) + 5 sign of xn-component + 6 x of xn-component + 7 sign of y-component + 8 y of y-component + */ + match[1] = match[1].toLowerCase(); + + if ( match[1].slice( 0, 3 ) === "nth" ) { + // nth-* requires argument + if ( !match[3] ) { + Sizzle.error( match[0] ); + } + + // numeric x and y parameters for Expr.filter.CHILD + // remember that false/true cast respectively to 0/1 + match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) ); + match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" ); + + // other types prohibit arguments + } else if ( match[3] ) { + Sizzle.error( match[0] ); + } + + return match; + }, + + "PSEUDO": function( match ) { + var excess, + unquoted = !match[6] && match[2]; + + if ( matchExpr["CHILD"].test( match[0] ) ) { + return null; + } + + // Accept quoted arguments as-is + if ( match[3] ) { + match[2] = match[4] || match[5] || ""; + + // Strip excess characters from unquoted arguments + } else if ( unquoted && rpseudo.test( unquoted ) && + // Get excess from tokenize (recursively) + (excess = tokenize( unquoted, true )) && + // advance to the next closing parenthesis + (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) { + + // excess is a negative index + match[0] = match[0].slice( 0, excess ); + match[2] = unquoted.slice( 0, excess ); + } + + // Return only captures needed by the pseudo filter method (type and argument) + return match.slice( 0, 3 ); + } + }, + + filter: { + + "TAG": function( nodeNameSelector ) { + var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); + return nodeNameSelector === "*" ? + function() { return true; } : + function( elem ) { + return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; + }; + }, + + "CLASS": function( className ) { + var pattern = classCache[ className + " " ]; + + return pattern || + (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) && + classCache( className, function( elem ) { + return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== "undefined" && elem.getAttribute("class") || "" ); + }); + }, + + "ATTR": function( name, operator, check ) { + return function( elem ) { + var result = Sizzle.attr( elem, name ); + + if ( result == null ) { + return operator === "!="; + } + if ( !operator ) { + return true; + } + + result += ""; + + return operator === "=" ? result === check : + operator === "!=" ? result !== check : + operator === "^=" ? check && result.indexOf( check ) === 0 : + operator === "*=" ? check && result.indexOf( check ) > -1 : + operator === "$=" ? check && result.slice( -check.length ) === check : + operator === "~=" ? ( " " + result.replace( rwhitespace, " " ) + " " ).indexOf( check ) > -1 : + operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : + false; + }; + }, + + "CHILD": function( type, what, argument, first, last ) { + var simple = type.slice( 0, 3 ) !== "nth", + forward = type.slice( -4 ) !== "last", + ofType = what === "of-type"; + + return first === 1 && last === 0 ? + + // Shortcut for :nth-*(n) + function( elem ) { + return !!elem.parentNode; + } : + + function( elem, context, xml ) { + var cache, uniqueCache, outerCache, node, nodeIndex, start, + dir = simple !== forward ? "nextSibling" : "previousSibling", + parent = elem.parentNode, + name = ofType && elem.nodeName.toLowerCase(), + useCache = !xml && !ofType, + diff = false; + + if ( parent ) { + + // :(first|last|only)-(child|of-type) + if ( simple ) { + while ( dir ) { + node = elem; + while ( (node = node[ dir ]) ) { + if ( ofType ? + node.nodeName.toLowerCase() === name : + node.nodeType === 1 ) { + + return false; + } + } + // Reverse direction for :only-* (if we haven't yet done so) + start = dir = type === "only" && !start && "nextSibling"; + } + return true; + } + + start = [ forward ? parent.firstChild : parent.lastChild ]; + + // non-xml :nth-child(...) stores cache data on `parent` + if ( forward && useCache ) { + + // Seek `elem` from a previously-cached index + + // ...in a gzip-friendly way + node = parent; + outerCache = node[ expando ] || (node[ expando ] = {}); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + (outerCache[ node.uniqueID ] = {}); + + cache = uniqueCache[ type ] || []; + nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; + diff = nodeIndex && cache[ 2 ]; + node = nodeIndex && parent.childNodes[ nodeIndex ]; + + while ( (node = ++nodeIndex && node && node[ dir ] || + + // Fallback to seeking `elem` from the start + (diff = nodeIndex = 0) || start.pop()) ) { + + // When found, cache indexes on `parent` and break + if ( node.nodeType === 1 && ++diff && node === elem ) { + uniqueCache[ type ] = [ dirruns, nodeIndex, diff ]; + break; + } + } + + } else { + // Use previously-cached element index if available + if ( useCache ) { + // ...in a gzip-friendly way + node = elem; + outerCache = node[ expando ] || (node[ expando ] = {}); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + (outerCache[ node.uniqueID ] = {}); + + cache = uniqueCache[ type ] || []; + nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; + diff = nodeIndex; + } + + // xml :nth-child(...) + // or :nth-last-child(...) or :nth(-last)?-of-type(...) + if ( diff === false ) { + // Use the same loop as above to seek `elem` from the start + while ( (node = ++nodeIndex && node && node[ dir ] || + (diff = nodeIndex = 0) || start.pop()) ) { + + if ( ( ofType ? + node.nodeName.toLowerCase() === name : + node.nodeType === 1 ) && + ++diff ) { + + // Cache the index of each encountered element + if ( useCache ) { + outerCache = node[ expando ] || (node[ expando ] = {}); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + (outerCache[ node.uniqueID ] = {}); + + uniqueCache[ type ] = [ dirruns, diff ]; + } + + if ( node === elem ) { + break; + } + } + } + } + } + + // Incorporate the offset, then check against cycle size + diff -= last; + return diff === first || ( diff % first === 0 && diff / first >= 0 ); + } + }; + }, + + "PSEUDO": function( pseudo, argument ) { + // pseudo-class names are case-insensitive + // http://www.w3.org/TR/selectors/#pseudo-classes + // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters + // Remember that setFilters inherits from pseudos + var args, + fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || + Sizzle.error( "unsupported pseudo: " + pseudo ); + + // The user may use createPseudo to indicate that + // arguments are needed to create the filter function + // just as Sizzle does + if ( fn[ expando ] ) { + return fn( argument ); + } + + // But maintain support for old signatures + if ( fn.length > 1 ) { + args = [ pseudo, pseudo, "", argument ]; + return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? + markFunction(function( seed, matches ) { + var idx, + matched = fn( seed, argument ), + i = matched.length; + while ( i-- ) { + idx = indexOf( seed, matched[i] ); + seed[ idx ] = !( matches[ idx ] = matched[i] ); + } + }) : + function( elem ) { + return fn( elem, 0, args ); + }; + } + + return fn; + } + }, + + pseudos: { + // Potentially complex pseudos + "not": markFunction(function( selector ) { + // Trim the selector passed to compile + // to avoid treating leading and trailing + // spaces as combinators + var input = [], + results = [], + matcher = compile( selector.replace( rtrim, "$1" ) ); + + return matcher[ expando ] ? + markFunction(function( seed, matches, context, xml ) { + var elem, + unmatched = matcher( seed, null, xml, [] ), + i = seed.length; + + // Match elements unmatched by `matcher` + while ( i-- ) { + if ( (elem = unmatched[i]) ) { + seed[i] = !(matches[i] = elem); + } + } + }) : + function( elem, context, xml ) { + input[0] = elem; + matcher( input, null, xml, results ); + // Don't keep the element (issue #299) + input[0] = null; + return !results.pop(); + }; + }), + + "has": markFunction(function( selector ) { + return function( elem ) { + return Sizzle( selector, elem ).length > 0; + }; + }), + + "contains": markFunction(function( text ) { + text = text.replace( runescape, funescape ); + return function( elem ) { + return ( elem.textContent || getText( elem ) ).indexOf( text ) > -1; + }; + }), + + // "Whether an element is represented by a :lang() selector + // is based solely on the element's language value + // being equal to the identifier C, + // or beginning with the identifier C immediately followed by "-". + // The matching of C against the element's language value is performed case-insensitively. + // The identifier C does not have to be a valid language name." + // http://www.w3.org/TR/selectors/#lang-pseudo + "lang": markFunction( function( lang ) { + // lang value must be a valid identifier + if ( !ridentifier.test(lang || "") ) { + Sizzle.error( "unsupported lang: " + lang ); + } + lang = lang.replace( runescape, funescape ).toLowerCase(); + return function( elem ) { + var elemLang; + do { + if ( (elemLang = documentIsHTML ? + elem.lang : + elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) { + + elemLang = elemLang.toLowerCase(); + return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; + } + } while ( (elem = elem.parentNode) && elem.nodeType === 1 ); + return false; + }; + }), + + // Miscellaneous + "target": function( elem ) { + var hash = window.location && window.location.hash; + return hash && hash.slice( 1 ) === elem.id; + }, + + "root": function( elem ) { + return elem === docElem; + }, + + "focus": function( elem ) { + return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex); + }, + + // Boolean properties + "enabled": createDisabledPseudo( false ), + "disabled": createDisabledPseudo( true ), + + "checked": function( elem ) { + // In CSS3, :checked should return both checked and selected elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + var nodeName = elem.nodeName.toLowerCase(); + return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected); + }, + + "selected": function( elem ) { + // Accessing this property makes selected-by-default + // options in Safari work properly + if ( elem.parentNode ) { + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + // Contents + "empty": function( elem ) { + // http://www.w3.org/TR/selectors/#empty-pseudo + // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5), + // but not by others (comment: 8; processing instruction: 7; etc.) + // nodeType < 6 works because attributes (2) do not appear as children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + if ( elem.nodeType < 6 ) { + return false; + } + } + return true; + }, + + "parent": function( elem ) { + return !Expr.pseudos["empty"]( elem ); + }, + + // Element/input types + "header": function( elem ) { + return rheader.test( elem.nodeName ); + }, + + "input": function( elem ) { + return rinputs.test( elem.nodeName ); + }, + + "button": function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === "button" || name === "button"; + }, + + "text": function( elem ) { + var attr; + return elem.nodeName.toLowerCase() === "input" && + elem.type === "text" && + + // Support: IE<8 + // New HTML5 attribute values (e.g., "search") appear with elem.type === "text" + ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text" ); + }, + + // Position-in-collection + "first": createPositionalPseudo(function() { + return [ 0 ]; + }), + + "last": createPositionalPseudo(function( matchIndexes, length ) { + return [ length - 1 ]; + }), + + "eq": createPositionalPseudo(function( matchIndexes, length, argument ) { + return [ argument < 0 ? argument + length : argument ]; + }), + + "even": createPositionalPseudo(function( matchIndexes, length ) { + var i = 0; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "odd": createPositionalPseudo(function( matchIndexes, length ) { + var i = 1; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "lt": createPositionalPseudo(function( matchIndexes, length, argument ) { + var i = argument < 0 ? + argument + length : + argument > length ? + length : + argument; + for ( ; --i >= 0; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "gt": createPositionalPseudo(function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; ++i < length; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }) + } +}; + +Expr.pseudos["nth"] = Expr.pseudos["eq"]; + +// Add button/input type pseudos +for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { + Expr.pseudos[ i ] = createInputPseudo( i ); +} +for ( i in { submit: true, reset: true } ) { + Expr.pseudos[ i ] = createButtonPseudo( i ); +} + +// Easy API for creating new setFilters +function setFilters() {} +setFilters.prototype = Expr.filters = Expr.pseudos; +Expr.setFilters = new setFilters(); + +tokenize = Sizzle.tokenize = function( selector, parseOnly ) { + var matched, match, tokens, type, + soFar, groups, preFilters, + cached = tokenCache[ selector + " " ]; + + if ( cached ) { + return parseOnly ? 0 : cached.slice( 0 ); + } + + soFar = selector; + groups = []; + preFilters = Expr.preFilter; + + while ( soFar ) { + + // Comma and first run + if ( !matched || (match = rcomma.exec( soFar )) ) { + if ( match ) { + // Don't consume trailing commas as valid + soFar = soFar.slice( match[0].length ) || soFar; + } + groups.push( (tokens = []) ); + } + + matched = false; + + // Combinators + if ( (match = rcombinators.exec( soFar )) ) { + matched = match.shift(); + tokens.push({ + value: matched, + // Cast descendant combinators to space + type: match[0].replace( rtrim, " " ) + }); + soFar = soFar.slice( matched.length ); + } + + // Filters + for ( type in Expr.filter ) { + if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || + (match = preFilters[ type ]( match ))) ) { + matched = match.shift(); + tokens.push({ + value: matched, + type: type, + matches: match + }); + soFar = soFar.slice( matched.length ); + } + } + + if ( !matched ) { + break; + } + } + + // Return the length of the invalid excess + // if we're just parsing + // Otherwise, throw an error or return tokens + return parseOnly ? + soFar.length : + soFar ? + Sizzle.error( selector ) : + // Cache the tokens + tokenCache( selector, groups ).slice( 0 ); +}; + +function toSelector( tokens ) { + var i = 0, + len = tokens.length, + selector = ""; + for ( ; i < len; i++ ) { + selector += tokens[i].value; + } + return selector; +} + +function addCombinator( matcher, combinator, base ) { + var dir = combinator.dir, + skip = combinator.next, + key = skip || dir, + checkNonElements = base && key === "parentNode", + doneName = done++; + + return combinator.first ? + // Check against closest ancestor/preceding element + function( elem, context, xml ) { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + return matcher( elem, context, xml ); + } + } + return false; + } : + + // Check against all ancestor/preceding elements + function( elem, context, xml ) { + var oldCache, uniqueCache, outerCache, + newCache = [ dirruns, doneName ]; + + // We can't set arbitrary data on XML nodes, so they don't benefit from combinator caching + if ( xml ) { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + if ( matcher( elem, context, xml ) ) { + return true; + } + } + } + } else { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + outerCache = elem[ expando ] || (elem[ expando ] = {}); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ elem.uniqueID ] || (outerCache[ elem.uniqueID ] = {}); + + if ( skip && skip === elem.nodeName.toLowerCase() ) { + elem = elem[ dir ] || elem; + } else if ( (oldCache = uniqueCache[ key ]) && + oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) { + + // Assign to newCache so results back-propagate to previous elements + return (newCache[ 2 ] = oldCache[ 2 ]); + } else { + // Reuse newcache so results back-propagate to previous elements + uniqueCache[ key ] = newCache; + + // A match means we're done; a fail means we have to keep checking + if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) { + return true; + } + } + } + } + } + return false; + }; +} + +function elementMatcher( matchers ) { + return matchers.length > 1 ? + function( elem, context, xml ) { + var i = matchers.length; + while ( i-- ) { + if ( !matchers[i]( elem, context, xml ) ) { + return false; + } + } + return true; + } : + matchers[0]; +} + +function multipleContexts( selector, contexts, results ) { + var i = 0, + len = contexts.length; + for ( ; i < len; i++ ) { + Sizzle( selector, contexts[i], results ); + } + return results; +} + +function condense( unmatched, map, filter, context, xml ) { + var elem, + newUnmatched = [], + i = 0, + len = unmatched.length, + mapped = map != null; + + for ( ; i < len; i++ ) { + if ( (elem = unmatched[i]) ) { + if ( !filter || filter( elem, context, xml ) ) { + newUnmatched.push( elem ); + if ( mapped ) { + map.push( i ); + } + } + } + } + + return newUnmatched; +} + +function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { + if ( postFilter && !postFilter[ expando ] ) { + postFilter = setMatcher( postFilter ); + } + if ( postFinder && !postFinder[ expando ] ) { + postFinder = setMatcher( postFinder, postSelector ); + } + return markFunction(function( seed, results, context, xml ) { + var temp, i, elem, + preMap = [], + postMap = [], + preexisting = results.length, + + // Get initial elements from seed or context + elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ), + + // Prefilter to get matcher input, preserving a map for seed-results synchronization + matcherIn = preFilter && ( seed || !selector ) ? + condense( elems, preMap, preFilter, context, xml ) : + elems, + + matcherOut = matcher ? + // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, + postFinder || ( seed ? preFilter : preexisting || postFilter ) ? + + // ...intermediate processing is necessary + [] : + + // ...otherwise use results directly + results : + matcherIn; + + // Find primary matches + if ( matcher ) { + matcher( matcherIn, matcherOut, context, xml ); + } + + // Apply postFilter + if ( postFilter ) { + temp = condense( matcherOut, postMap ); + postFilter( temp, [], context, xml ); + + // Un-match failing elements by moving them back to matcherIn + i = temp.length; + while ( i-- ) { + if ( (elem = temp[i]) ) { + matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem); + } + } + } + + if ( seed ) { + if ( postFinder || preFilter ) { + if ( postFinder ) { + // Get the final matcherOut by condensing this intermediate into postFinder contexts + temp = []; + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) ) { + // Restore matcherIn since elem is not yet a final match + temp.push( (matcherIn[i] = elem) ); + } + } + postFinder( null, (matcherOut = []), temp, xml ); + } + + // Move matched elements from seed to results to keep them synchronized + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) && + (temp = postFinder ? indexOf( seed, elem ) : preMap[i]) > -1 ) { + + seed[temp] = !(results[temp] = elem); + } + } + } + + // Add elements to results, through postFinder if defined + } else { + matcherOut = condense( + matcherOut === results ? + matcherOut.splice( preexisting, matcherOut.length ) : + matcherOut + ); + if ( postFinder ) { + postFinder( null, results, matcherOut, xml ); + } else { + push.apply( results, matcherOut ); + } + } + }); +} + +function matcherFromTokens( tokens ) { + var checkContext, matcher, j, + len = tokens.length, + leadingRelative = Expr.relative[ tokens[0].type ], + implicitRelative = leadingRelative || Expr.relative[" "], + i = leadingRelative ? 1 : 0, + + // The foundational matcher ensures that elements are reachable from top-level context(s) + matchContext = addCombinator( function( elem ) { + return elem === checkContext; + }, implicitRelative, true ), + matchAnyContext = addCombinator( function( elem ) { + return indexOf( checkContext, elem ) > -1; + }, implicitRelative, true ), + matchers = [ function( elem, context, xml ) { + var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( + (checkContext = context).nodeType ? + matchContext( elem, context, xml ) : + matchAnyContext( elem, context, xml ) ); + // Avoid hanging onto element (issue #299) + checkContext = null; + return ret; + } ]; + + for ( ; i < len; i++ ) { + if ( (matcher = Expr.relative[ tokens[i].type ]) ) { + matchers = [ addCombinator(elementMatcher( matchers ), matcher) ]; + } else { + matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches ); + + // Return special upon seeing a positional matcher + if ( matcher[ expando ] ) { + // Find the next relative operator (if any) for proper handling + j = ++i; + for ( ; j < len; j++ ) { + if ( Expr.relative[ tokens[j].type ] ) { + break; + } + } + return setMatcher( + i > 1 && elementMatcher( matchers ), + i > 1 && toSelector( + // If the preceding token was a descendant combinator, insert an implicit any-element `*` + tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" }) + ).replace( rtrim, "$1" ), + matcher, + i < j && matcherFromTokens( tokens.slice( i, j ) ), + j < len && matcherFromTokens( (tokens = tokens.slice( j )) ), + j < len && toSelector( tokens ) + ); + } + matchers.push( matcher ); + } + } + + return elementMatcher( matchers ); +} + +function matcherFromGroupMatchers( elementMatchers, setMatchers ) { + var bySet = setMatchers.length > 0, + byElement = elementMatchers.length > 0, + superMatcher = function( seed, context, xml, results, outermost ) { + var elem, j, matcher, + matchedCount = 0, + i = "0", + unmatched = seed && [], + setMatched = [], + contextBackup = outermostContext, + // We must always have either seed elements or outermost context + elems = seed || byElement && Expr.find["TAG"]( "*", outermost ), + // Use integer dirruns iff this is the outermost matcher + dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1), + len = elems.length; + + if ( outermost ) { + outermostContext = context === document || context || outermost; + } + + // Add elements passing elementMatchers directly to results + // Support: IE<9, Safari + // Tolerate NodeList properties (IE: "length"; Safari: ) matching elements by id + for ( ; i !== len && (elem = elems[i]) != null; i++ ) { + if ( byElement && elem ) { + j = 0; + if ( !context && elem.ownerDocument !== document ) { + setDocument( elem ); + xml = !documentIsHTML; + } + while ( (matcher = elementMatchers[j++]) ) { + if ( matcher( elem, context || document, xml) ) { + results.push( elem ); + break; + } + } + if ( outermost ) { + dirruns = dirrunsUnique; + } + } + + // Track unmatched elements for set filters + if ( bySet ) { + // They will have gone through all possible matchers + if ( (elem = !matcher && elem) ) { + matchedCount--; + } + + // Lengthen the array for every element, matched or not + if ( seed ) { + unmatched.push( elem ); + } + } + } + + // `i` is now the count of elements visited above, and adding it to `matchedCount` + // makes the latter nonnegative. + matchedCount += i; + + // Apply set filters to unmatched elements + // NOTE: This can be skipped if there are no unmatched elements (i.e., `matchedCount` + // equals `i`), unless we didn't visit _any_ elements in the above loop because we have + // no element matchers and no seed. + // Incrementing an initially-string "0" `i` allows `i` to remain a string only in that + // case, which will result in a "00" `matchedCount` that differs from `i` but is also + // numerically zero. + if ( bySet && i !== matchedCount ) { + j = 0; + while ( (matcher = setMatchers[j++]) ) { + matcher( unmatched, setMatched, context, xml ); + } + + if ( seed ) { + // Reintegrate element matches to eliminate the need for sorting + if ( matchedCount > 0 ) { + while ( i-- ) { + if ( !(unmatched[i] || setMatched[i]) ) { + setMatched[i] = pop.call( results ); + } + } + } + + // Discard index placeholder values to get only actual matches + setMatched = condense( setMatched ); + } + + // Add matches to results + push.apply( results, setMatched ); + + // Seedless set matches succeeding multiple successful matchers stipulate sorting + if ( outermost && !seed && setMatched.length > 0 && + ( matchedCount + setMatchers.length ) > 1 ) { + + Sizzle.uniqueSort( results ); + } + } + + // Override manipulation of globals by nested matchers + if ( outermost ) { + dirruns = dirrunsUnique; + outermostContext = contextBackup; + } + + return unmatched; + }; + + return bySet ? + markFunction( superMatcher ) : + superMatcher; +} + +compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) { + var i, + setMatchers = [], + elementMatchers = [], + cached = compilerCache[ selector + " " ]; + + if ( !cached ) { + // Generate a function of recursive functions that can be used to check each element + if ( !match ) { + match = tokenize( selector ); + } + i = match.length; + while ( i-- ) { + cached = matcherFromTokens( match[i] ); + if ( cached[ expando ] ) { + setMatchers.push( cached ); + } else { + elementMatchers.push( cached ); + } + } + + // Cache the compiled function + cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) ); + + // Save selector and tokenization + cached.selector = selector; + } + return cached; +}; + +/** + * A low-level selection function that works with Sizzle's compiled + * selector functions + * @param {String|Function} selector A selector or a pre-compiled + * selector function built with Sizzle.compile + * @param {Element} context + * @param {Array} [results] + * @param {Array} [seed] A set of elements to match against + */ +select = Sizzle.select = function( selector, context, results, seed ) { + var i, tokens, token, type, find, + compiled = typeof selector === "function" && selector, + match = !seed && tokenize( (selector = compiled.selector || selector) ); + + results = results || []; + + // Try to minimize operations if there is only one selector in the list and no seed + // (the latter of which guarantees us context) + if ( match.length === 1 ) { + + // Reduce context if the leading compound selector is an ID + tokens = match[0] = match[0].slice( 0 ); + if ( tokens.length > 2 && (token = tokens[0]).type === "ID" && + context.nodeType === 9 && documentIsHTML && Expr.relative[ tokens[1].type ] ) { + + context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0]; + if ( !context ) { + return results; + + // Precompiled matchers will still verify ancestry, so step up a level + } else if ( compiled ) { + context = context.parentNode; + } + + selector = selector.slice( tokens.shift().value.length ); + } + + // Fetch a seed set for right-to-left matching + i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length; + while ( i-- ) { + token = tokens[i]; + + // Abort if we hit a combinator + if ( Expr.relative[ (type = token.type) ] ) { + break; + } + if ( (find = Expr.find[ type ]) ) { + // Search, expanding context for leading sibling combinators + if ( (seed = find( + token.matches[0].replace( runescape, funescape ), + rsibling.test( tokens[0].type ) && testContext( context.parentNode ) || context + )) ) { + + // If seed is empty or no tokens remain, we can return early + tokens.splice( i, 1 ); + selector = seed.length && toSelector( tokens ); + if ( !selector ) { + push.apply( results, seed ); + return results; + } + + break; + } + } + } + } + + // Compile and execute a filtering function if one is not provided + // Provide `match` to avoid retokenization if we modified the selector above + ( compiled || compile( selector, match ) )( + seed, + context, + !documentIsHTML, + results, + !context || rsibling.test( selector ) && testContext( context.parentNode ) || context + ); + return results; +}; + +// One-time assignments + +// Sort stability +support.sortStable = expando.split("").sort( sortOrder ).join("") === expando; + +// Support: Chrome 14-35+ +// Always assume duplicates if they aren't passed to the comparison function +support.detectDuplicates = !!hasDuplicate; + +// Initialize against the default document +setDocument(); + +// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27) +// Detached nodes confoundingly follow *each other* +support.sortDetached = assert(function( el ) { + // Should return 1, but returns 4 (following) + return el.compareDocumentPosition( document.createElement("fieldset") ) & 1; +}); + +// Support: IE<8 +// Prevent attribute/property "interpolation" +// https://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx +if ( !assert(function( el ) { + el.innerHTML = ""; + return el.firstChild.getAttribute("href") === "#" ; +}) ) { + addHandle( "type|href|height|width", function( elem, name, isXML ) { + if ( !isXML ) { + return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 ); + } + }); +} + +// Support: IE<9 +// Use defaultValue in place of getAttribute("value") +if ( !support.attributes || !assert(function( el ) { + el.innerHTML = ""; + el.firstChild.setAttribute( "value", "" ); + return el.firstChild.getAttribute( "value" ) === ""; +}) ) { + addHandle( "value", function( elem, name, isXML ) { + if ( !isXML && elem.nodeName.toLowerCase() === "input" ) { + return elem.defaultValue; + } + }); +} + +// Support: IE<9 +// Use getAttributeNode to fetch booleans when getAttribute lies +if ( !assert(function( el ) { + return el.getAttribute("disabled") == null; +}) ) { + addHandle( booleans, function( elem, name, isXML ) { + var val; + if ( !isXML ) { + return elem[ name ] === true ? name.toLowerCase() : + (val = elem.getAttributeNode( name )) && val.specified ? + val.value : + null; + } + }); +} + +return Sizzle; + +})( window ); + + + +jQuery.find = Sizzle; +jQuery.expr = Sizzle.selectors; + +// Deprecated +jQuery.expr[ ":" ] = jQuery.expr.pseudos; +jQuery.uniqueSort = jQuery.unique = Sizzle.uniqueSort; +jQuery.text = Sizzle.getText; +jQuery.isXMLDoc = Sizzle.isXML; +jQuery.contains = Sizzle.contains; +jQuery.escapeSelector = Sizzle.escape; + + + + +var dir = function( elem, dir, until ) { + var matched = [], + truncate = until !== undefined; + + while ( ( elem = elem[ dir ] ) && elem.nodeType !== 9 ) { + if ( elem.nodeType === 1 ) { + if ( truncate && jQuery( elem ).is( until ) ) { + break; + } + matched.push( elem ); + } + } + return matched; +}; + + +var siblings = function( n, elem ) { + var matched = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + matched.push( n ); + } + } + + return matched; +}; + + +var rneedsContext = jQuery.expr.match.needsContext; + + + +function nodeName( elem, name ) { + + return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); + +}; +var rsingleTag = ( /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i ); + + + +// Implement the identical functionality for filter and not +function winnow( elements, qualifier, not ) { + if ( isFunction( qualifier ) ) { + return jQuery.grep( elements, function( elem, i ) { + return !!qualifier.call( elem, i, elem ) !== not; + } ); + } + + // Single element + if ( qualifier.nodeType ) { + return jQuery.grep( elements, function( elem ) { + return ( elem === qualifier ) !== not; + } ); + } + + // Arraylike of elements (jQuery, arguments, Array) + if ( typeof qualifier !== "string" ) { + return jQuery.grep( elements, function( elem ) { + return ( indexOf.call( qualifier, elem ) > -1 ) !== not; + } ); + } + + // Filtered directly for both simple and complex selectors + return jQuery.filter( qualifier, elements, not ); +} + +jQuery.filter = function( expr, elems, not ) { + var elem = elems[ 0 ]; + + if ( not ) { + expr = ":not(" + expr + ")"; + } + + if ( elems.length === 1 && elem.nodeType === 1 ) { + return jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : []; + } + + return jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { + return elem.nodeType === 1; + } ) ); +}; + +jQuery.fn.extend( { + find: function( selector ) { + var i, ret, + len = this.length, + self = this; + + if ( typeof selector !== "string" ) { + return this.pushStack( jQuery( selector ).filter( function() { + for ( i = 0; i < len; i++ ) { + if ( jQuery.contains( self[ i ], this ) ) { + return true; + } + } + } ) ); + } + + ret = this.pushStack( [] ); + + for ( i = 0; i < len; i++ ) { + jQuery.find( selector, self[ i ], ret ); + } + + return len > 1 ? jQuery.uniqueSort( ret ) : ret; + }, + filter: function( selector ) { + return this.pushStack( winnow( this, selector || [], false ) ); + }, + not: function( selector ) { + return this.pushStack( winnow( this, selector || [], true ) ); + }, + is: function( selector ) { + return !!winnow( + this, + + // If this is a positional/relative selector, check membership in the returned set + // so $("p:first").is("p:last") won't return true for a doc with two "p". + typeof selector === "string" && rneedsContext.test( selector ) ? + jQuery( selector ) : + selector || [], + false + ).length; + } +} ); + + +// Initialize a jQuery object + + +// A central reference to the root jQuery(document) +var rootjQuery, + + // A simple way to check for HTML strings + // Prioritize #id over to avoid XSS via location.hash (#9521) + // Strict HTML recognition (#11290: must start with <) + // Shortcut simple #id case for speed + rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/, + + init = jQuery.fn.init = function( selector, context, root ) { + var match, elem; + + // HANDLE: $(""), $(null), $(undefined), $(false) + if ( !selector ) { + return this; + } + + // Method init() accepts an alternate rootjQuery + // so migrate can support jQuery.sub (gh-2101) + root = root || rootjQuery; + + // Handle HTML strings + if ( typeof selector === "string" ) { + if ( selector[ 0 ] === "<" && + selector[ selector.length - 1 ] === ">" && + selector.length >= 3 ) { + + // Assume that strings that start and end with <> are HTML and skip the regex check + match = [ null, selector, null ]; + + } else { + match = rquickExpr.exec( selector ); + } + + // Match html or make sure no context is specified for #id + if ( match && ( match[ 1 ] || !context ) ) { + + // HANDLE: $(html) -> $(array) + if ( match[ 1 ] ) { + context = context instanceof jQuery ? context[ 0 ] : context; + + // Option to run scripts is true for back-compat + // Intentionally let the error be thrown if parseHTML is not present + jQuery.merge( this, jQuery.parseHTML( + match[ 1 ], + context && context.nodeType ? context.ownerDocument || context : document, + true + ) ); + + // HANDLE: $(html, props) + if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) { + for ( match in context ) { + + // Properties of context are called as methods if possible + if ( isFunction( this[ match ] ) ) { + this[ match ]( context[ match ] ); + + // ...and otherwise set as attributes + } else { + this.attr( match, context[ match ] ); + } + } + } + + return this; + + // HANDLE: $(#id) + } else { + elem = document.getElementById( match[ 2 ] ); + + if ( elem ) { + + // Inject the element directly into the jQuery object + this[ 0 ] = elem; + this.length = 1; + } + return this; + } + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return ( context || root ).find( selector ); + + // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return this.constructor( context ).find( selector ); + } + + // HANDLE: $(DOMElement) + } else if ( selector.nodeType ) { + this[ 0 ] = selector; + this.length = 1; + return this; + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( isFunction( selector ) ) { + return root.ready !== undefined ? + root.ready( selector ) : + + // Execute immediately if ready is not present + selector( jQuery ); + } + + return jQuery.makeArray( selector, this ); + }; + +// Give the init function the jQuery prototype for later instantiation +init.prototype = jQuery.fn; + +// Initialize central reference +rootjQuery = jQuery( document ); + + +var rparentsprev = /^(?:parents|prev(?:Until|All))/, + + // Methods guaranteed to produce a unique set when starting from a unique set + guaranteedUnique = { + children: true, + contents: true, + next: true, + prev: true + }; + +jQuery.fn.extend( { + has: function( target ) { + var targets = jQuery( target, this ), + l = targets.length; + + return this.filter( function() { + var i = 0; + for ( ; i < l; i++ ) { + if ( jQuery.contains( this, targets[ i ] ) ) { + return true; + } + } + } ); + }, + + closest: function( selectors, context ) { + var cur, + i = 0, + l = this.length, + matched = [], + targets = typeof selectors !== "string" && jQuery( selectors ); + + // Positional selectors never match, since there's no _selection_ context + if ( !rneedsContext.test( selectors ) ) { + for ( ; i < l; i++ ) { + for ( cur = this[ i ]; cur && cur !== context; cur = cur.parentNode ) { + + // Always skip document fragments + if ( cur.nodeType < 11 && ( targets ? + targets.index( cur ) > -1 : + + // Don't pass non-elements to Sizzle + cur.nodeType === 1 && + jQuery.find.matchesSelector( cur, selectors ) ) ) { + + matched.push( cur ); + break; + } + } + } + } + + return this.pushStack( matched.length > 1 ? jQuery.uniqueSort( matched ) : matched ); + }, + + // Determine the position of an element within the set + index: function( elem ) { + + // No argument, return index in parent + if ( !elem ) { + return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1; + } + + // Index in selector + if ( typeof elem === "string" ) { + return indexOf.call( jQuery( elem ), this[ 0 ] ); + } + + // Locate the position of the desired element + return indexOf.call( this, + + // If it receives a jQuery object, the first element is used + elem.jquery ? elem[ 0 ] : elem + ); + }, + + add: function( selector, context ) { + return this.pushStack( + jQuery.uniqueSort( + jQuery.merge( this.get(), jQuery( selector, context ) ) + ) + ); + }, + + addBack: function( selector ) { + return this.add( selector == null ? + this.prevObject : this.prevObject.filter( selector ) + ); + } +} ); + +function sibling( cur, dir ) { + while ( ( cur = cur[ dir ] ) && cur.nodeType !== 1 ) {} + return cur; +} + +jQuery.each( { + parent: function( elem ) { + var parent = elem.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + parents: function( elem ) { + return dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, i, until ) { + return dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return sibling( elem, "nextSibling" ); + }, + prev: function( elem ) { + return sibling( elem, "previousSibling" ); + }, + nextAll: function( elem ) { + return dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, i, until ) { + return dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, i, until ) { + return dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return siblings( ( elem.parentNode || {} ).firstChild, elem ); + }, + children: function( elem ) { + return siblings( elem.firstChild ); + }, + contents: function( elem ) { + if ( typeof elem.contentDocument !== "undefined" ) { + return elem.contentDocument; + } + + // Support: IE 9 - 11 only, iOS 7 only, Android Browser <=4.3 only + // Treat the template element as a regular one in browsers that + // don't support it. + if ( nodeName( elem, "template" ) ) { + elem = elem.content || elem; + } + + return jQuery.merge( [], elem.childNodes ); + } +}, function( name, fn ) { + jQuery.fn[ name ] = function( until, selector ) { + var matched = jQuery.map( this, fn, until ); + + if ( name.slice( -5 ) !== "Until" ) { + selector = until; + } + + if ( selector && typeof selector === "string" ) { + matched = jQuery.filter( selector, matched ); + } + + if ( this.length > 1 ) { + + // Remove duplicates + if ( !guaranteedUnique[ name ] ) { + jQuery.uniqueSort( matched ); + } + + // Reverse order for parents* and prev-derivatives + if ( rparentsprev.test( name ) ) { + matched.reverse(); + } + } + + return this.pushStack( matched ); + }; +} ); +var rnothtmlwhite = ( /[^\x20\t\r\n\f]+/g ); + + + +// Convert String-formatted options into Object-formatted ones +function createOptions( options ) { + var object = {}; + jQuery.each( options.match( rnothtmlwhite ) || [], function( _, flag ) { + object[ flag ] = true; + } ); + return object; +} + +/* + * Create a callback list using the following parameters: + * + * options: an optional list of space-separated options that will change how + * the callback list behaves or a more traditional option object + * + * By default a callback list will act like an event callback list and can be + * "fired" multiple times. + * + * Possible options: + * + * once: will ensure the callback list can only be fired once (like a Deferred) + * + * memory: will keep track of previous values and will call any callback added + * after the list has been fired right away with the latest "memorized" + * values (like a Deferred) + * + * unique: will ensure a callback can only be added once (no duplicate in the list) + * + * stopOnFalse: interrupt callings when a callback returns false + * + */ +jQuery.Callbacks = function( options ) { + + // Convert options from String-formatted to Object-formatted if needed + // (we check in cache first) + options = typeof options === "string" ? + createOptions( options ) : + jQuery.extend( {}, options ); + + var // Flag to know if list is currently firing + firing, + + // Last fire value for non-forgettable lists + memory, + + // Flag to know if list was already fired + fired, + + // Flag to prevent firing + locked, + + // Actual callback list + list = [], + + // Queue of execution data for repeatable lists + queue = [], + + // Index of currently firing callback (modified by add/remove as needed) + firingIndex = -1, + + // Fire callbacks + fire = function() { + + // Enforce single-firing + locked = locked || options.once; + + // Execute callbacks for all pending executions, + // respecting firingIndex overrides and runtime changes + fired = firing = true; + for ( ; queue.length; firingIndex = -1 ) { + memory = queue.shift(); + while ( ++firingIndex < list.length ) { + + // Run callback and check for early termination + if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false && + options.stopOnFalse ) { + + // Jump to end and forget the data so .add doesn't re-fire + firingIndex = list.length; + memory = false; + } + } + } + + // Forget the data if we're done with it + if ( !options.memory ) { + memory = false; + } + + firing = false; + + // Clean up if we're done firing for good + if ( locked ) { + + // Keep an empty list if we have data for future add calls + if ( memory ) { + list = []; + + // Otherwise, this object is spent + } else { + list = ""; + } + } + }, + + // Actual Callbacks object + self = { + + // Add a callback or a collection of callbacks to the list + add: function() { + if ( list ) { + + // If we have memory from a past run, we should fire after adding + if ( memory && !firing ) { + firingIndex = list.length - 1; + queue.push( memory ); + } + + ( function add( args ) { + jQuery.each( args, function( _, arg ) { + if ( isFunction( arg ) ) { + if ( !options.unique || !self.has( arg ) ) { + list.push( arg ); + } + } else if ( arg && arg.length && toType( arg ) !== "string" ) { + + // Inspect recursively + add( arg ); + } + } ); + } )( arguments ); + + if ( memory && !firing ) { + fire(); + } + } + return this; + }, + + // Remove a callback from the list + remove: function() { + jQuery.each( arguments, function( _, arg ) { + var index; + while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { + list.splice( index, 1 ); + + // Handle firing indexes + if ( index <= firingIndex ) { + firingIndex--; + } + } + } ); + return this; + }, + + // Check if a given callback is in the list. + // If no argument is given, return whether or not list has callbacks attached. + has: function( fn ) { + return fn ? + jQuery.inArray( fn, list ) > -1 : + list.length > 0; + }, + + // Remove all callbacks from the list + empty: function() { + if ( list ) { + list = []; + } + return this; + }, + + // Disable .fire and .add + // Abort any current/pending executions + // Clear all callbacks and values + disable: function() { + locked = queue = []; + list = memory = ""; + return this; + }, + disabled: function() { + return !list; + }, + + // Disable .fire + // Also disable .add unless we have memory (since it would have no effect) + // Abort any pending executions + lock: function() { + locked = queue = []; + if ( !memory && !firing ) { + list = memory = ""; + } + return this; + }, + locked: function() { + return !!locked; + }, + + // Call all callbacks with the given context and arguments + fireWith: function( context, args ) { + if ( !locked ) { + args = args || []; + args = [ context, args.slice ? args.slice() : args ]; + queue.push( args ); + if ( !firing ) { + fire(); + } + } + return this; + }, + + // Call all the callbacks with the given arguments + fire: function() { + self.fireWith( this, arguments ); + return this; + }, + + // To know if the callbacks have already been called at least once + fired: function() { + return !!fired; + } + }; + + return self; +}; + + +function Identity( v ) { + return v; +} +function Thrower( ex ) { + throw ex; +} + +function adoptValue( value, resolve, reject, noValue ) { + var method; + + try { + + // Check for promise aspect first to privilege synchronous behavior + if ( value && isFunction( ( method = value.promise ) ) ) { + method.call( value ).done( resolve ).fail( reject ); + + // Other thenables + } else if ( value && isFunction( ( method = value.then ) ) ) { + method.call( value, resolve, reject ); + + // Other non-thenables + } else { + + // Control `resolve` arguments by letting Array#slice cast boolean `noValue` to integer: + // * false: [ value ].slice( 0 ) => resolve( value ) + // * true: [ value ].slice( 1 ) => resolve() + resolve.apply( undefined, [ value ].slice( noValue ) ); + } + + // For Promises/A+, convert exceptions into rejections + // Since jQuery.when doesn't unwrap thenables, we can skip the extra checks appearing in + // Deferred#then to conditionally suppress rejection. + } catch ( value ) { + + // Support: Android 4.0 only + // Strict mode functions invoked without .call/.apply get global-object context + reject.apply( undefined, [ value ] ); + } +} + +jQuery.extend( { + + Deferred: function( func ) { + var tuples = [ + + // action, add listener, callbacks, + // ... .then handlers, argument index, [final state] + [ "notify", "progress", jQuery.Callbacks( "memory" ), + jQuery.Callbacks( "memory" ), 2 ], + [ "resolve", "done", jQuery.Callbacks( "once memory" ), + jQuery.Callbacks( "once memory" ), 0, "resolved" ], + [ "reject", "fail", jQuery.Callbacks( "once memory" ), + jQuery.Callbacks( "once memory" ), 1, "rejected" ] + ], + state = "pending", + promise = { + state: function() { + return state; + }, + always: function() { + deferred.done( arguments ).fail( arguments ); + return this; + }, + "catch": function( fn ) { + return promise.then( null, fn ); + }, + + // Keep pipe for back-compat + pipe: function( /* fnDone, fnFail, fnProgress */ ) { + var fns = arguments; + + return jQuery.Deferred( function( newDefer ) { + jQuery.each( tuples, function( i, tuple ) { + + // Map tuples (progress, done, fail) to arguments (done, fail, progress) + var fn = isFunction( fns[ tuple[ 4 ] ] ) && fns[ tuple[ 4 ] ]; + + // deferred.progress(function() { bind to newDefer or newDefer.notify }) + // deferred.done(function() { bind to newDefer or newDefer.resolve }) + // deferred.fail(function() { bind to newDefer or newDefer.reject }) + deferred[ tuple[ 1 ] ]( function() { + var returned = fn && fn.apply( this, arguments ); + if ( returned && isFunction( returned.promise ) ) { + returned.promise() + .progress( newDefer.notify ) + .done( newDefer.resolve ) + .fail( newDefer.reject ); + } else { + newDefer[ tuple[ 0 ] + "With" ]( + this, + fn ? [ returned ] : arguments + ); + } + } ); + } ); + fns = null; + } ).promise(); + }, + then: function( onFulfilled, onRejected, onProgress ) { + var maxDepth = 0; + function resolve( depth, deferred, handler, special ) { + return function() { + var that = this, + args = arguments, + mightThrow = function() { + var returned, then; + + // Support: Promises/A+ section 2.3.3.3.3 + // https://promisesaplus.com/#point-59 + // Ignore double-resolution attempts + if ( depth < maxDepth ) { + return; + } + + returned = handler.apply( that, args ); + + // Support: Promises/A+ section 2.3.1 + // https://promisesaplus.com/#point-48 + if ( returned === deferred.promise() ) { + throw new TypeError( "Thenable self-resolution" ); + } + + // Support: Promises/A+ sections 2.3.3.1, 3.5 + // https://promisesaplus.com/#point-54 + // https://promisesaplus.com/#point-75 + // Retrieve `then` only once + then = returned && + + // Support: Promises/A+ section 2.3.4 + // https://promisesaplus.com/#point-64 + // Only check objects and functions for thenability + ( typeof returned === "object" || + typeof returned === "function" ) && + returned.then; + + // Handle a returned thenable + if ( isFunction( then ) ) { + + // Special processors (notify) just wait for resolution + if ( special ) { + then.call( + returned, + resolve( maxDepth, deferred, Identity, special ), + resolve( maxDepth, deferred, Thrower, special ) + ); + + // Normal processors (resolve) also hook into progress + } else { + + // ...and disregard older resolution values + maxDepth++; + + then.call( + returned, + resolve( maxDepth, deferred, Identity, special ), + resolve( maxDepth, deferred, Thrower, special ), + resolve( maxDepth, deferred, Identity, + deferred.notifyWith ) + ); + } + + // Handle all other returned values + } else { + + // Only substitute handlers pass on context + // and multiple values (non-spec behavior) + if ( handler !== Identity ) { + that = undefined; + args = [ returned ]; + } + + // Process the value(s) + // Default process is resolve + ( special || deferred.resolveWith )( that, args ); + } + }, + + // Only normal processors (resolve) catch and reject exceptions + process = special ? + mightThrow : + function() { + try { + mightThrow(); + } catch ( e ) { + + if ( jQuery.Deferred.exceptionHook ) { + jQuery.Deferred.exceptionHook( e, + process.stackTrace ); + } + + // Support: Promises/A+ section 2.3.3.3.4.1 + // https://promisesaplus.com/#point-61 + // Ignore post-resolution exceptions + if ( depth + 1 >= maxDepth ) { + + // Only substitute handlers pass on context + // and multiple values (non-spec behavior) + if ( handler !== Thrower ) { + that = undefined; + args = [ e ]; + } + + deferred.rejectWith( that, args ); + } + } + }; + + // Support: Promises/A+ section 2.3.3.3.1 + // https://promisesaplus.com/#point-57 + // Re-resolve promises immediately to dodge false rejection from + // subsequent errors + if ( depth ) { + process(); + } else { + + // Call an optional hook to record the stack, in case of exception + // since it's otherwise lost when execution goes async + if ( jQuery.Deferred.getStackHook ) { + process.stackTrace = jQuery.Deferred.getStackHook(); + } + window.setTimeout( process ); + } + }; + } + + return jQuery.Deferred( function( newDefer ) { + + // progress_handlers.add( ... ) + tuples[ 0 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onProgress ) ? + onProgress : + Identity, + newDefer.notifyWith + ) + ); + + // fulfilled_handlers.add( ... ) + tuples[ 1 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onFulfilled ) ? + onFulfilled : + Identity + ) + ); + + // rejected_handlers.add( ... ) + tuples[ 2 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onRejected ) ? + onRejected : + Thrower + ) + ); + } ).promise(); + }, + + // Get a promise for this deferred + // If obj is provided, the promise aspect is added to the object + promise: function( obj ) { + return obj != null ? jQuery.extend( obj, promise ) : promise; + } + }, + deferred = {}; + + // Add list-specific methods + jQuery.each( tuples, function( i, tuple ) { + var list = tuple[ 2 ], + stateString = tuple[ 5 ]; + + // promise.progress = list.add + // promise.done = list.add + // promise.fail = list.add + promise[ tuple[ 1 ] ] = list.add; + + // Handle state + if ( stateString ) { + list.add( + function() { + + // state = "resolved" (i.e., fulfilled) + // state = "rejected" + state = stateString; + }, + + // rejected_callbacks.disable + // fulfilled_callbacks.disable + tuples[ 3 - i ][ 2 ].disable, + + // rejected_handlers.disable + // fulfilled_handlers.disable + tuples[ 3 - i ][ 3 ].disable, + + // progress_callbacks.lock + tuples[ 0 ][ 2 ].lock, + + // progress_handlers.lock + tuples[ 0 ][ 3 ].lock + ); + } + + // progress_handlers.fire + // fulfilled_handlers.fire + // rejected_handlers.fire + list.add( tuple[ 3 ].fire ); + + // deferred.notify = function() { deferred.notifyWith(...) } + // deferred.resolve = function() { deferred.resolveWith(...) } + // deferred.reject = function() { deferred.rejectWith(...) } + deferred[ tuple[ 0 ] ] = function() { + deferred[ tuple[ 0 ] + "With" ]( this === deferred ? undefined : this, arguments ); + return this; + }; + + // deferred.notifyWith = list.fireWith + // deferred.resolveWith = list.fireWith + // deferred.rejectWith = list.fireWith + deferred[ tuple[ 0 ] + "With" ] = list.fireWith; + } ); + + // Make the deferred a promise + promise.promise( deferred ); + + // Call given func if any + if ( func ) { + func.call( deferred, deferred ); + } + + // All done! + return deferred; + }, + + // Deferred helper + when: function( singleValue ) { + var + + // count of uncompleted subordinates + remaining = arguments.length, + + // count of unprocessed arguments + i = remaining, + + // subordinate fulfillment data + resolveContexts = Array( i ), + resolveValues = slice.call( arguments ), + + // the master Deferred + master = jQuery.Deferred(), + + // subordinate callback factory + updateFunc = function( i ) { + return function( value ) { + resolveContexts[ i ] = this; + resolveValues[ i ] = arguments.length > 1 ? slice.call( arguments ) : value; + if ( !( --remaining ) ) { + master.resolveWith( resolveContexts, resolveValues ); + } + }; + }; + + // Single- and empty arguments are adopted like Promise.resolve + if ( remaining <= 1 ) { + adoptValue( singleValue, master.done( updateFunc( i ) ).resolve, master.reject, + !remaining ); + + // Use .then() to unwrap secondary thenables (cf. gh-3000) + if ( master.state() === "pending" || + isFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) { + + return master.then(); + } + } + + // Multiple arguments are aggregated like Promise.all array elements + while ( i-- ) { + adoptValue( resolveValues[ i ], updateFunc( i ), master.reject ); + } + + return master.promise(); + } +} ); + + +// These usually indicate a programmer mistake during development, +// warn about them ASAP rather than swallowing them by default. +var rerrorNames = /^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/; + +jQuery.Deferred.exceptionHook = function( error, stack ) { + + // Support: IE 8 - 9 only + // Console exists when dev tools are open, which can happen at any time + if ( window.console && window.console.warn && error && rerrorNames.test( error.name ) ) { + window.console.warn( "jQuery.Deferred exception: " + error.message, error.stack, stack ); + } +}; + + + + +jQuery.readyException = function( error ) { + window.setTimeout( function() { + throw error; + } ); +}; + + + + +// The deferred used on DOM ready +var readyList = jQuery.Deferred(); + +jQuery.fn.ready = function( fn ) { + + readyList + .then( fn ) + + // Wrap jQuery.readyException in a function so that the lookup + // happens at the time of error handling instead of callback + // registration. + .catch( function( error ) { + jQuery.readyException( error ); + } ); + + return this; +}; + +jQuery.extend( { + + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, + + // A counter to track how many items to wait for before + // the ready event fires. See #6781 + readyWait: 1, + + // Handle when the DOM is ready + ready: function( wait ) { + + // Abort if there are pending holds or we're already ready + if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { + return; + } + + // Remember that the DOM is ready + jQuery.isReady = true; + + // If a normal DOM Ready event fired, decrement, and wait if need be + if ( wait !== true && --jQuery.readyWait > 0 ) { + return; + } + + // If there are functions bound, to execute + readyList.resolveWith( document, [ jQuery ] ); + } +} ); + +jQuery.ready.then = readyList.then; + +// The ready event handler and self cleanup method +function completed() { + document.removeEventListener( "DOMContentLoaded", completed ); + window.removeEventListener( "load", completed ); + jQuery.ready(); +} + +// Catch cases where $(document).ready() is called +// after the browser event has already occurred. +// Support: IE <=9 - 10 only +// Older IE sometimes signals "interactive" too soon +if ( document.readyState === "complete" || + ( document.readyState !== "loading" && !document.documentElement.doScroll ) ) { + + // Handle it asynchronously to allow scripts the opportunity to delay ready + window.setTimeout( jQuery.ready ); + +} else { + + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", completed ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", completed ); +} + + + + +// Multifunctional method to get and set values of a collection +// The value/s can optionally be executed if it's a function +var access = function( elems, fn, key, value, chainable, emptyGet, raw ) { + var i = 0, + len = elems.length, + bulk = key == null; + + // Sets many values + if ( toType( key ) === "object" ) { + chainable = true; + for ( i in key ) { + access( elems, fn, i, key[ i ], true, emptyGet, raw ); + } + + // Sets one value + } else if ( value !== undefined ) { + chainable = true; + + if ( !isFunction( value ) ) { + raw = true; + } + + if ( bulk ) { + + // Bulk operations run against the entire set + if ( raw ) { + fn.call( elems, value ); + fn = null; + + // ...except when executing function values + } else { + bulk = fn; + fn = function( elem, key, value ) { + return bulk.call( jQuery( elem ), value ); + }; + } + } + + if ( fn ) { + for ( ; i < len; i++ ) { + fn( + elems[ i ], key, raw ? + value : + value.call( elems[ i ], i, fn( elems[ i ], key ) ) + ); + } + } + } + + if ( chainable ) { + return elems; + } + + // Gets + if ( bulk ) { + return fn.call( elems ); + } + + return len ? fn( elems[ 0 ], key ) : emptyGet; +}; + + +// Matches dashed string for camelizing +var rmsPrefix = /^-ms-/, + rdashAlpha = /-([a-z])/g; + +// Used by camelCase as callback to replace() +function fcamelCase( all, letter ) { + return letter.toUpperCase(); +} + +// Convert dashed to camelCase; used by the css and data modules +// Support: IE <=9 - 11, Edge 12 - 15 +// Microsoft forgot to hump their vendor prefix (#9572) +function camelCase( string ) { + return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); +} +var acceptData = function( owner ) { + + // Accepts only: + // - Node + // - Node.ELEMENT_NODE + // - Node.DOCUMENT_NODE + // - Object + // - Any + return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType ); +}; + + + + +function Data() { + this.expando = jQuery.expando + Data.uid++; +} + +Data.uid = 1; + +Data.prototype = { + + cache: function( owner ) { + + // Check if the owner object already has a cache + var value = owner[ this.expando ]; + + // If not, create one + if ( !value ) { + value = {}; + + // We can accept data for non-element nodes in modern browsers, + // but we should not, see #8335. + // Always return an empty object. + if ( acceptData( owner ) ) { + + // If it is a node unlikely to be stringify-ed or looped over + // use plain assignment + if ( owner.nodeType ) { + owner[ this.expando ] = value; + + // Otherwise secure it in a non-enumerable property + // configurable must be true to allow the property to be + // deleted when data is removed + } else { + Object.defineProperty( owner, this.expando, { + value: value, + configurable: true + } ); + } + } + } + + return value; + }, + set: function( owner, data, value ) { + var prop, + cache = this.cache( owner ); + + // Handle: [ owner, key, value ] args + // Always use camelCase key (gh-2257) + if ( typeof data === "string" ) { + cache[ camelCase( data ) ] = value; + + // Handle: [ owner, { properties } ] args + } else { + + // Copy the properties one-by-one to the cache object + for ( prop in data ) { + cache[ camelCase( prop ) ] = data[ prop ]; + } + } + return cache; + }, + get: function( owner, key ) { + return key === undefined ? + this.cache( owner ) : + + // Always use camelCase key (gh-2257) + owner[ this.expando ] && owner[ this.expando ][ camelCase( key ) ]; + }, + access: function( owner, key, value ) { + + // In cases where either: + // + // 1. No key was specified + // 2. A string key was specified, but no value provided + // + // Take the "read" path and allow the get method to determine + // which value to return, respectively either: + // + // 1. The entire cache object + // 2. The data stored at the key + // + if ( key === undefined || + ( ( key && typeof key === "string" ) && value === undefined ) ) { + + return this.get( owner, key ); + } + + // When the key is not a string, or both a key and value + // are specified, set or extend (existing objects) with either: + // + // 1. An object of properties + // 2. A key and value + // + this.set( owner, key, value ); + + // Since the "set" path can have two possible entry points + // return the expected data based on which path was taken[*] + return value !== undefined ? value : key; + }, + remove: function( owner, key ) { + var i, + cache = owner[ this.expando ]; + + if ( cache === undefined ) { + return; + } + + if ( key !== undefined ) { + + // Support array or space separated string of keys + if ( Array.isArray( key ) ) { + + // If key is an array of keys... + // We always set camelCase keys, so remove that. + key = key.map( camelCase ); + } else { + key = camelCase( key ); + + // If a key with the spaces exists, use it. + // Otherwise, create an array by matching non-whitespace + key = key in cache ? + [ key ] : + ( key.match( rnothtmlwhite ) || [] ); + } + + i = key.length; + + while ( i-- ) { + delete cache[ key[ i ] ]; + } + } + + // Remove the expando if there's no more data + if ( key === undefined || jQuery.isEmptyObject( cache ) ) { + + // Support: Chrome <=35 - 45 + // Webkit & Blink performance suffers when deleting properties + // from DOM nodes, so set to undefined instead + // https://bugs.chromium.org/p/chromium/issues/detail?id=378607 (bug restricted) + if ( owner.nodeType ) { + owner[ this.expando ] = undefined; + } else { + delete owner[ this.expando ]; + } + } + }, + hasData: function( owner ) { + var cache = owner[ this.expando ]; + return cache !== undefined && !jQuery.isEmptyObject( cache ); + } +}; +var dataPriv = new Data(); + +var dataUser = new Data(); + + + +// Implementation Summary +// +// 1. Enforce API surface and semantic compatibility with 1.9.x branch +// 2. Improve the module's maintainability by reducing the storage +// paths to a single mechanism. +// 3. Use the same single mechanism to support "private" and "user" data. +// 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData) +// 5. Avoid exposing implementation details on user objects (eg. expando properties) +// 6. Provide a clear path for implementation upgrade to WeakMap in 2014 + +var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, + rmultiDash = /[A-Z]/g; + +function getData( data ) { + if ( data === "true" ) { + return true; + } + + if ( data === "false" ) { + return false; + } + + if ( data === "null" ) { + return null; + } + + // Only convert to a number if it doesn't change the string + if ( data === +data + "" ) { + return +data; + } + + if ( rbrace.test( data ) ) { + return JSON.parse( data ); + } + + return data; +} + +function dataAttr( elem, key, data ) { + var name; + + // If nothing was found internally, try to fetch any + // data from the HTML5 data-* attribute + if ( data === undefined && elem.nodeType === 1 ) { + name = "data-" + key.replace( rmultiDash, "-$&" ).toLowerCase(); + data = elem.getAttribute( name ); + + if ( typeof data === "string" ) { + try { + data = getData( data ); + } catch ( e ) {} + + // Make sure we set the data so it isn't changed later + dataUser.set( elem, key, data ); + } else { + data = undefined; + } + } + return data; +} + +jQuery.extend( { + hasData: function( elem ) { + return dataUser.hasData( elem ) || dataPriv.hasData( elem ); + }, + + data: function( elem, name, data ) { + return dataUser.access( elem, name, data ); + }, + + removeData: function( elem, name ) { + dataUser.remove( elem, name ); + }, + + // TODO: Now that all calls to _data and _removeData have been replaced + // with direct calls to dataPriv methods, these can be deprecated. + _data: function( elem, name, data ) { + return dataPriv.access( elem, name, data ); + }, + + _removeData: function( elem, name ) { + dataPriv.remove( elem, name ); + } +} ); + +jQuery.fn.extend( { + data: function( key, value ) { + var i, name, data, + elem = this[ 0 ], + attrs = elem && elem.attributes; + + // Gets all values + if ( key === undefined ) { + if ( this.length ) { + data = dataUser.get( elem ); + + if ( elem.nodeType === 1 && !dataPriv.get( elem, "hasDataAttrs" ) ) { + i = attrs.length; + while ( i-- ) { + + // Support: IE 11 only + // The attrs elements can be null (#14894) + if ( attrs[ i ] ) { + name = attrs[ i ].name; + if ( name.indexOf( "data-" ) === 0 ) { + name = camelCase( name.slice( 5 ) ); + dataAttr( elem, name, data[ name ] ); + } + } + } + dataPriv.set( elem, "hasDataAttrs", true ); + } + } + + return data; + } + + // Sets multiple values + if ( typeof key === "object" ) { + return this.each( function() { + dataUser.set( this, key ); + } ); + } + + return access( this, function( value ) { + var data; + + // The calling jQuery object (element matches) is not empty + // (and therefore has an element appears at this[ 0 ]) and the + // `value` parameter was not undefined. An empty jQuery object + // will result in `undefined` for elem = this[ 0 ] which will + // throw an exception if an attempt to read a data cache is made. + if ( elem && value === undefined ) { + + // Attempt to get data from the cache + // The key will always be camelCased in Data + data = dataUser.get( elem, key ); + if ( data !== undefined ) { + return data; + } + + // Attempt to "discover" the data in + // HTML5 custom data-* attrs + data = dataAttr( elem, key ); + if ( data !== undefined ) { + return data; + } + + // We tried really hard, but the data doesn't exist. + return; + } + + // Set the data... + this.each( function() { + + // We always store the camelCased key + dataUser.set( this, key, value ); + } ); + }, null, value, arguments.length > 1, null, true ); + }, + + removeData: function( key ) { + return this.each( function() { + dataUser.remove( this, key ); + } ); + } +} ); + + +jQuery.extend( { + queue: function( elem, type, data ) { + var queue; + + if ( elem ) { + type = ( type || "fx" ) + "queue"; + queue = dataPriv.get( elem, type ); + + // Speed up dequeue by getting out quickly if this is just a lookup + if ( data ) { + if ( !queue || Array.isArray( data ) ) { + queue = dataPriv.access( elem, type, jQuery.makeArray( data ) ); + } else { + queue.push( data ); + } + } + return queue || []; + } + }, + + dequeue: function( elem, type ) { + type = type || "fx"; + + var queue = jQuery.queue( elem, type ), + startLength = queue.length, + fn = queue.shift(), + hooks = jQuery._queueHooks( elem, type ), + next = function() { + jQuery.dequeue( elem, type ); + }; + + // If the fx queue is dequeued, always remove the progress sentinel + if ( fn === "inprogress" ) { + fn = queue.shift(); + startLength--; + } + + if ( fn ) { + + // Add a progress sentinel to prevent the fx queue from being + // automatically dequeued + if ( type === "fx" ) { + queue.unshift( "inprogress" ); + } + + // Clear up the last queue stop function + delete hooks.stop; + fn.call( elem, next, hooks ); + } + + if ( !startLength && hooks ) { + hooks.empty.fire(); + } + }, + + // Not public - generate a queueHooks object, or return the current one + _queueHooks: function( elem, type ) { + var key = type + "queueHooks"; + return dataPriv.get( elem, key ) || dataPriv.access( elem, key, { + empty: jQuery.Callbacks( "once memory" ).add( function() { + dataPriv.remove( elem, [ type + "queue", key ] ); + } ) + } ); + } +} ); + +jQuery.fn.extend( { + queue: function( type, data ) { + var setter = 2; + + if ( typeof type !== "string" ) { + data = type; + type = "fx"; + setter--; + } + + if ( arguments.length < setter ) { + return jQuery.queue( this[ 0 ], type ); + } + + return data === undefined ? + this : + this.each( function() { + var queue = jQuery.queue( this, type, data ); + + // Ensure a hooks for this queue + jQuery._queueHooks( this, type ); + + if ( type === "fx" && queue[ 0 ] !== "inprogress" ) { + jQuery.dequeue( this, type ); + } + } ); + }, + dequeue: function( type ) { + return this.each( function() { + jQuery.dequeue( this, type ); + } ); + }, + clearQueue: function( type ) { + return this.queue( type || "fx", [] ); + }, + + // Get a promise resolved when queues of a certain type + // are emptied (fx is the type by default) + promise: function( type, obj ) { + var tmp, + count = 1, + defer = jQuery.Deferred(), + elements = this, + i = this.length, + resolve = function() { + if ( !( --count ) ) { + defer.resolveWith( elements, [ elements ] ); + } + }; + + if ( typeof type !== "string" ) { + obj = type; + type = undefined; + } + type = type || "fx"; + + while ( i-- ) { + tmp = dataPriv.get( elements[ i ], type + "queueHooks" ); + if ( tmp && tmp.empty ) { + count++; + tmp.empty.add( resolve ); + } + } + resolve(); + return defer.promise( obj ); + } +} ); +var pnum = ( /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/ ).source; + +var rcssNum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" ); + + +var cssExpand = [ "Top", "Right", "Bottom", "Left" ]; + +var documentElement = document.documentElement; + + + + var isAttached = function( elem ) { + return jQuery.contains( elem.ownerDocument, elem ); + }, + composed = { composed: true }; + + // Support: IE 9 - 11+, Edge 12 - 18+, iOS 10.0 - 10.2 only + // Check attachment across shadow DOM boundaries when possible (gh-3504) + // Support: iOS 10.0-10.2 only + // Early iOS 10 versions support `attachShadow` but not `getRootNode`, + // leading to errors. We need to check for `getRootNode`. + if ( documentElement.getRootNode ) { + isAttached = function( elem ) { + return jQuery.contains( elem.ownerDocument, elem ) || + elem.getRootNode( composed ) === elem.ownerDocument; + }; + } +var isHiddenWithinTree = function( elem, el ) { + + // isHiddenWithinTree might be called from jQuery#filter function; + // in that case, element will be second argument + elem = el || elem; + + // Inline style trumps all + return elem.style.display === "none" || + elem.style.display === "" && + + // Otherwise, check computed style + // Support: Firefox <=43 - 45 + // Disconnected elements can have computed display: none, so first confirm that elem is + // in the document. + isAttached( elem ) && + + jQuery.css( elem, "display" ) === "none"; + }; + +var swap = function( elem, options, callback, args ) { + var ret, name, + old = {}; + + // Remember the old values, and insert the new ones + for ( name in options ) { + old[ name ] = elem.style[ name ]; + elem.style[ name ] = options[ name ]; + } + + ret = callback.apply( elem, args || [] ); + + // Revert the old values + for ( name in options ) { + elem.style[ name ] = old[ name ]; + } + + return ret; +}; + + + + +function adjustCSS( elem, prop, valueParts, tween ) { + var adjusted, scale, + maxIterations = 20, + currentValue = tween ? + function() { + return tween.cur(); + } : + function() { + return jQuery.css( elem, prop, "" ); + }, + initial = currentValue(), + unit = valueParts && valueParts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ), + + // Starting value computation is required for potential unit mismatches + initialInUnit = elem.nodeType && + ( jQuery.cssNumber[ prop ] || unit !== "px" && +initial ) && + rcssNum.exec( jQuery.css( elem, prop ) ); + + if ( initialInUnit && initialInUnit[ 3 ] !== unit ) { + + // Support: Firefox <=54 + // Halve the iteration target value to prevent interference from CSS upper bounds (gh-2144) + initial = initial / 2; + + // Trust units reported by jQuery.css + unit = unit || initialInUnit[ 3 ]; + + // Iteratively approximate from a nonzero starting point + initialInUnit = +initial || 1; + + while ( maxIterations-- ) { + + // Evaluate and update our best guess (doubling guesses that zero out). + // Finish if the scale equals or crosses 1 (making the old*new product non-positive). + jQuery.style( elem, prop, initialInUnit + unit ); + if ( ( 1 - scale ) * ( 1 - ( scale = currentValue() / initial || 0.5 ) ) <= 0 ) { + maxIterations = 0; + } + initialInUnit = initialInUnit / scale; + + } + + initialInUnit = initialInUnit * 2; + jQuery.style( elem, prop, initialInUnit + unit ); + + // Make sure we update the tween properties later on + valueParts = valueParts || []; + } + + if ( valueParts ) { + initialInUnit = +initialInUnit || +initial || 0; + + // Apply relative offset (+=/-=) if specified + adjusted = valueParts[ 1 ] ? + initialInUnit + ( valueParts[ 1 ] + 1 ) * valueParts[ 2 ] : + +valueParts[ 2 ]; + if ( tween ) { + tween.unit = unit; + tween.start = initialInUnit; + tween.end = adjusted; + } + } + return adjusted; +} + + +var defaultDisplayMap = {}; + +function getDefaultDisplay( elem ) { + var temp, + doc = elem.ownerDocument, + nodeName = elem.nodeName, + display = defaultDisplayMap[ nodeName ]; + + if ( display ) { + return display; + } + + temp = doc.body.appendChild( doc.createElement( nodeName ) ); + display = jQuery.css( temp, "display" ); + + temp.parentNode.removeChild( temp ); + + if ( display === "none" ) { + display = "block"; + } + defaultDisplayMap[ nodeName ] = display; + + return display; +} + +function showHide( elements, show ) { + var display, elem, + values = [], + index = 0, + length = elements.length; + + // Determine new display value for elements that need to change + for ( ; index < length; index++ ) { + elem = elements[ index ]; + if ( !elem.style ) { + continue; + } + + display = elem.style.display; + if ( show ) { + + // Since we force visibility upon cascade-hidden elements, an immediate (and slow) + // check is required in this first loop unless we have a nonempty display value (either + // inline or about-to-be-restored) + if ( display === "none" ) { + values[ index ] = dataPriv.get( elem, "display" ) || null; + if ( !values[ index ] ) { + elem.style.display = ""; + } + } + if ( elem.style.display === "" && isHiddenWithinTree( elem ) ) { + values[ index ] = getDefaultDisplay( elem ); + } + } else { + if ( display !== "none" ) { + values[ index ] = "none"; + + // Remember what we're overwriting + dataPriv.set( elem, "display", display ); + } + } + } + + // Set the display of the elements in a second loop to avoid constant reflow + for ( index = 0; index < length; index++ ) { + if ( values[ index ] != null ) { + elements[ index ].style.display = values[ index ]; + } + } + + return elements; +} + +jQuery.fn.extend( { + show: function() { + return showHide( this, true ); + }, + hide: function() { + return showHide( this ); + }, + toggle: function( state ) { + if ( typeof state === "boolean" ) { + return state ? this.show() : this.hide(); + } + + return this.each( function() { + if ( isHiddenWithinTree( this ) ) { + jQuery( this ).show(); + } else { + jQuery( this ).hide(); + } + } ); + } +} ); +var rcheckableType = ( /^(?:checkbox|radio)$/i ); + +var rtagName = ( /<([a-z][^\/\0>\x20\t\r\n\f]*)/i ); + +var rscriptType = ( /^$|^module$|\/(?:java|ecma)script/i ); + + + +// We have to close these tags to support XHTML (#13200) +var wrapMap = { + + // Support: IE <=9 only + option: [ 1, "" ], + + // XHTML parsers do not magically insert elements in the + // same way that tag soup parsers do. So we cannot shorten + // this by omitting or other required elements. + thead: [ 1, "", "
" ], + col: [ 2, "", "
" ], + tr: [ 2, "", "
" ], + td: [ 3, "", "
" ], + + _default: [ 0, "", "" ] +}; + +// Support: IE <=9 only +wrapMap.optgroup = wrapMap.option; + +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + + +function getAll( context, tag ) { + + // Support: IE <=9 - 11 only + // Use typeof to avoid zero-argument method invocation on host objects (#15151) + var ret; + + if ( typeof context.getElementsByTagName !== "undefined" ) { + ret = context.getElementsByTagName( tag || "*" ); + + } else if ( typeof context.querySelectorAll !== "undefined" ) { + ret = context.querySelectorAll( tag || "*" ); + + } else { + ret = []; + } + + if ( tag === undefined || tag && nodeName( context, tag ) ) { + return jQuery.merge( [ context ], ret ); + } + + return ret; +} + + +// Mark scripts as having already been evaluated +function setGlobalEval( elems, refElements ) { + var i = 0, + l = elems.length; + + for ( ; i < l; i++ ) { + dataPriv.set( + elems[ i ], + "globalEval", + !refElements || dataPriv.get( refElements[ i ], "globalEval" ) + ); + } +} + + +var rhtml = /<|&#?\w+;/; + +function buildFragment( elems, context, scripts, selection, ignored ) { + var elem, tmp, tag, wrap, attached, j, + fragment = context.createDocumentFragment(), + nodes = [], + i = 0, + l = elems.length; + + for ( ; i < l; i++ ) { + elem = elems[ i ]; + + if ( elem || elem === 0 ) { + + // Add nodes directly + if ( toType( elem ) === "object" ) { + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); + + // Convert non-html into a text node + } else if ( !rhtml.test( elem ) ) { + nodes.push( context.createTextNode( elem ) ); + + // Convert html into DOM nodes + } else { + tmp = tmp || fragment.appendChild( context.createElement( "div" ) ); + + // Deserialize a standard representation + tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase(); + wrap = wrapMap[ tag ] || wrapMap._default; + tmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ]; + + // Descend through wrappers to the right content + j = wrap[ 0 ]; + while ( j-- ) { + tmp = tmp.lastChild; + } + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( nodes, tmp.childNodes ); + + // Remember the top-level container + tmp = fragment.firstChild; + + // Ensure the created nodes are orphaned (#12392) + tmp.textContent = ""; + } + } + } + + // Remove wrapper from fragment + fragment.textContent = ""; + + i = 0; + while ( ( elem = nodes[ i++ ] ) ) { + + // Skip elements already in the context collection (trac-4087) + if ( selection && jQuery.inArray( elem, selection ) > -1 ) { + if ( ignored ) { + ignored.push( elem ); + } + continue; + } + + attached = isAttached( elem ); + + // Append to fragment + tmp = getAll( fragment.appendChild( elem ), "script" ); + + // Preserve script evaluation history + if ( attached ) { + setGlobalEval( tmp ); + } + + // Capture executables + if ( scripts ) { + j = 0; + while ( ( elem = tmp[ j++ ] ) ) { + if ( rscriptType.test( elem.type || "" ) ) { + scripts.push( elem ); + } + } + } + } + + return fragment; +} + + +( function() { + var fragment = document.createDocumentFragment(), + div = fragment.appendChild( document.createElement( "div" ) ), + input = document.createElement( "input" ); + + // Support: Android 4.0 - 4.3 only + // Check state lost if the name is set (#11217) + // Support: Windows Web Apps (WWA) + // `name` and `type` must use .setAttribute for WWA (#14901) + input.setAttribute( "type", "radio" ); + input.setAttribute( "checked", "checked" ); + input.setAttribute( "name", "t" ); + + div.appendChild( input ); + + // Support: Android <=4.1 only + // Older WebKit doesn't clone checked state correctly in fragments + support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked; + + // Support: IE <=11 only + // Make sure textarea (and checkbox) defaultValue is properly cloned + div.innerHTML = ""; + support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; +} )(); + + +var + rkeyEvent = /^key/, + rmouseEvent = /^(?:mouse|pointer|contextmenu|drag|drop)|click/, + rtypenamespace = /^([^.]*)(?:\.(.+)|)/; + +function returnTrue() { + return true; +} + +function returnFalse() { + return false; +} + +// Support: IE <=9 - 11+ +// focus() and blur() are asynchronous, except when they are no-op. +// So expect focus to be synchronous when the element is already active, +// and blur to be synchronous when the element is not already active. +// (focus and blur are always synchronous in other supported browsers, +// this just defines when we can count on it). +function expectSync( elem, type ) { + return ( elem === safeActiveElement() ) === ( type === "focus" ); +} + +// Support: IE <=9 only +// Accessing document.activeElement can throw unexpectedly +// https://bugs.jquery.com/ticket/13393 +function safeActiveElement() { + try { + return document.activeElement; + } catch ( err ) { } +} + +function on( elem, types, selector, data, fn, one ) { + var origFn, type; + + // Types can be a map of types/handlers + if ( typeof types === "object" ) { + + // ( types-Object, selector, data ) + if ( typeof selector !== "string" ) { + + // ( types-Object, data ) + data = data || selector; + selector = undefined; + } + for ( type in types ) { + on( elem, type, selector, data, types[ type ], one ); + } + return elem; + } + + if ( data == null && fn == null ) { + + // ( types, fn ) + fn = selector; + data = selector = undefined; + } else if ( fn == null ) { + if ( typeof selector === "string" ) { + + // ( types, selector, fn ) + fn = data; + data = undefined; + } else { + + // ( types, data, fn ) + fn = data; + data = selector; + selector = undefined; + } + } + if ( fn === false ) { + fn = returnFalse; + } else if ( !fn ) { + return elem; + } + + if ( one === 1 ) { + origFn = fn; + fn = function( event ) { + + // Can use an empty set, since event contains the info + jQuery().off( event ); + return origFn.apply( this, arguments ); + }; + + // Use same guid so caller can remove using origFn + fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); + } + return elem.each( function() { + jQuery.event.add( this, types, fn, data, selector ); + } ); +} + +/* + * Helper functions for managing events -- not part of the public interface. + * Props to Dean Edwards' addEvent library for many of the ideas. + */ +jQuery.event = { + + global: {}, + + add: function( elem, types, handler, data, selector ) { + + var handleObjIn, eventHandle, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = dataPriv.get( elem ); + + // Don't attach events to noData or text/comment nodes (but allow plain objects) + if ( !elemData ) { + return; + } + + // Caller can pass in an object of custom data in lieu of the handler + if ( handler.handler ) { + handleObjIn = handler; + handler = handleObjIn.handler; + selector = handleObjIn.selector; + } + + // Ensure that invalid selectors throw exceptions at attach time + // Evaluate against documentElement in case elem is a non-element node (e.g., document) + if ( selector ) { + jQuery.find.matchesSelector( documentElement, selector ); + } + + // Make sure that the handler has a unique ID, used to find/remove it later + if ( !handler.guid ) { + handler.guid = jQuery.guid++; + } + + // Init the element's event structure and main handler, if this is the first + if ( !( events = elemData.events ) ) { + events = elemData.events = {}; + } + if ( !( eventHandle = elemData.handle ) ) { + eventHandle = elemData.handle = function( e ) { + + // Discard the second event of a jQuery.event.trigger() and + // when an event is called after a page has unloaded + return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ? + jQuery.event.dispatch.apply( elem, arguments ) : undefined; + }; + } + + // Handle multiple events separated by a space + types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[ t ] ) || []; + type = origType = tmp[ 1 ]; + namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); + + // There *must* be a type, no attaching namespace-only handlers + if ( !type ) { + continue; + } + + // If event changes its type, use the special event handlers for the changed type + special = jQuery.event.special[ type ] || {}; + + // If selector defined, determine special event api type, otherwise given type + type = ( selector ? special.delegateType : special.bindType ) || type; + + // Update special based on newly reset type + special = jQuery.event.special[ type ] || {}; + + // handleObj is passed to all event handlers + handleObj = jQuery.extend( { + type: type, + origType: origType, + data: data, + handler: handler, + guid: handler.guid, + selector: selector, + needsContext: selector && jQuery.expr.match.needsContext.test( selector ), + namespace: namespaces.join( "." ) + }, handleObjIn ); + + // Init the event handler queue if we're the first + if ( !( handlers = events[ type ] ) ) { + handlers = events[ type ] = []; + handlers.delegateCount = 0; + + // Only use addEventListener if the special events handler returns false + if ( !special.setup || + special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle ); + } + } + } + + if ( special.add ) { + special.add.call( elem, handleObj ); + + if ( !handleObj.handler.guid ) { + handleObj.handler.guid = handler.guid; + } + } + + // Add to the element's handler list, delegates in front + if ( selector ) { + handlers.splice( handlers.delegateCount++, 0, handleObj ); + } else { + handlers.push( handleObj ); + } + + // Keep track of which events have ever been used, for event optimization + jQuery.event.global[ type ] = true; + } + + }, + + // Detach an event or set of events from an element + remove: function( elem, types, handler, selector, mappedTypes ) { + + var j, origCount, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = dataPriv.hasData( elem ) && dataPriv.get( elem ); + + if ( !elemData || !( events = elemData.events ) ) { + return; + } + + // Once for each type.namespace in types; type may be omitted + types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[ t ] ) || []; + type = origType = tmp[ 1 ]; + namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); + + // Unbind all events (on this namespace, if provided) for the element + if ( !type ) { + for ( type in events ) { + jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); + } + continue; + } + + special = jQuery.event.special[ type ] || {}; + type = ( selector ? special.delegateType : special.bindType ) || type; + handlers = events[ type ] || []; + tmp = tmp[ 2 ] && + new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ); + + // Remove matching events + origCount = j = handlers.length; + while ( j-- ) { + handleObj = handlers[ j ]; + + if ( ( mappedTypes || origType === handleObj.origType ) && + ( !handler || handler.guid === handleObj.guid ) && + ( !tmp || tmp.test( handleObj.namespace ) ) && + ( !selector || selector === handleObj.selector || + selector === "**" && handleObj.selector ) ) { + handlers.splice( j, 1 ); + + if ( handleObj.selector ) { + handlers.delegateCount--; + } + if ( special.remove ) { + special.remove.call( elem, handleObj ); + } + } + } + + // Remove generic event handler if we removed something and no more handlers exist + // (avoids potential for endless recursion during removal of special event handlers) + if ( origCount && !handlers.length ) { + if ( !special.teardown || + special.teardown.call( elem, namespaces, elemData.handle ) === false ) { + + jQuery.removeEvent( elem, type, elemData.handle ); + } + + delete events[ type ]; + } + } + + // Remove data and the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + dataPriv.remove( elem, "handle events" ); + } + }, + + dispatch: function( nativeEvent ) { + + // Make a writable jQuery.Event from the native event object + var event = jQuery.event.fix( nativeEvent ); + + var i, j, ret, matched, handleObj, handlerQueue, + args = new Array( arguments.length ), + handlers = ( dataPriv.get( this, "events" ) || {} )[ event.type ] || [], + special = jQuery.event.special[ event.type ] || {}; + + // Use the fix-ed jQuery.Event rather than the (read-only) native event + args[ 0 ] = event; + + for ( i = 1; i < arguments.length; i++ ) { + args[ i ] = arguments[ i ]; + } + + event.delegateTarget = this; + + // Call the preDispatch hook for the mapped type, and let it bail if desired + if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { + return; + } + + // Determine handlers + handlerQueue = jQuery.event.handlers.call( this, event, handlers ); + + // Run delegates first; they may want to stop propagation beneath us + i = 0; + while ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) { + event.currentTarget = matched.elem; + + j = 0; + while ( ( handleObj = matched.handlers[ j++ ] ) && + !event.isImmediatePropagationStopped() ) { + + // If the event is namespaced, then each handler is only invoked if it is + // specially universal or its namespaces are a superset of the event's. + if ( !event.rnamespace || handleObj.namespace === false || + event.rnamespace.test( handleObj.namespace ) ) { + + event.handleObj = handleObj; + event.data = handleObj.data; + + ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle || + handleObj.handler ).apply( matched.elem, args ); + + if ( ret !== undefined ) { + if ( ( event.result = ret ) === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + } + } + } + + // Call the postDispatch hook for the mapped type + if ( special.postDispatch ) { + special.postDispatch.call( this, event ); + } + + return event.result; + }, + + handlers: function( event, handlers ) { + var i, handleObj, sel, matchedHandlers, matchedSelectors, + handlerQueue = [], + delegateCount = handlers.delegateCount, + cur = event.target; + + // Find delegate handlers + if ( delegateCount && + + // Support: IE <=9 + // Black-hole SVG instance trees (trac-13180) + cur.nodeType && + + // Support: Firefox <=42 + // Suppress spec-violating clicks indicating a non-primary pointer button (trac-3861) + // https://www.w3.org/TR/DOM-Level-3-Events/#event-type-click + // Support: IE 11 only + // ...but not arrow key "clicks" of radio inputs, which can have `button` -1 (gh-2343) + !( event.type === "click" && event.button >= 1 ) ) { + + for ( ; cur !== this; cur = cur.parentNode || this ) { + + // Don't check non-elements (#13208) + // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) + if ( cur.nodeType === 1 && !( event.type === "click" && cur.disabled === true ) ) { + matchedHandlers = []; + matchedSelectors = {}; + for ( i = 0; i < delegateCount; i++ ) { + handleObj = handlers[ i ]; + + // Don't conflict with Object.prototype properties (#13203) + sel = handleObj.selector + " "; + + if ( matchedSelectors[ sel ] === undefined ) { + matchedSelectors[ sel ] = handleObj.needsContext ? + jQuery( sel, this ).index( cur ) > -1 : + jQuery.find( sel, this, null, [ cur ] ).length; + } + if ( matchedSelectors[ sel ] ) { + matchedHandlers.push( handleObj ); + } + } + if ( matchedHandlers.length ) { + handlerQueue.push( { elem: cur, handlers: matchedHandlers } ); + } + } + } + } + + // Add the remaining (directly-bound) handlers + cur = this; + if ( delegateCount < handlers.length ) { + handlerQueue.push( { elem: cur, handlers: handlers.slice( delegateCount ) } ); + } + + return handlerQueue; + }, + + addProp: function( name, hook ) { + Object.defineProperty( jQuery.Event.prototype, name, { + enumerable: true, + configurable: true, + + get: isFunction( hook ) ? + function() { + if ( this.originalEvent ) { + return hook( this.originalEvent ); + } + } : + function() { + if ( this.originalEvent ) { + return this.originalEvent[ name ]; + } + }, + + set: function( value ) { + Object.defineProperty( this, name, { + enumerable: true, + configurable: true, + writable: true, + value: value + } ); + } + } ); + }, + + fix: function( originalEvent ) { + return originalEvent[ jQuery.expando ] ? + originalEvent : + new jQuery.Event( originalEvent ); + }, + + special: { + load: { + + // Prevent triggered image.load events from bubbling to window.load + noBubble: true + }, + click: { + + // Utilize native event to ensure correct state for checkable inputs + setup: function( data ) { + + // For mutual compressibility with _default, replace `this` access with a local var. + // `|| data` is dead code meant only to preserve the variable through minification. + var el = this || data; + + // Claim the first handler + if ( rcheckableType.test( el.type ) && + el.click && nodeName( el, "input" ) ) { + + // dataPriv.set( el, "click", ... ) + leverageNative( el, "click", returnTrue ); + } + + // Return false to allow normal processing in the caller + return false; + }, + trigger: function( data ) { + + // For mutual compressibility with _default, replace `this` access with a local var. + // `|| data` is dead code meant only to preserve the variable through minification. + var el = this || data; + + // Force setup before triggering a click + if ( rcheckableType.test( el.type ) && + el.click && nodeName( el, "input" ) ) { + + leverageNative( el, "click" ); + } + + // Return non-false to allow normal event-path propagation + return true; + }, + + // For cross-browser consistency, suppress native .click() on links + // Also prevent it if we're currently inside a leveraged native-event stack + _default: function( event ) { + var target = event.target; + return rcheckableType.test( target.type ) && + target.click && nodeName( target, "input" ) && + dataPriv.get( target, "click" ) || + nodeName( target, "a" ); + } + }, + + beforeunload: { + postDispatch: function( event ) { + + // Support: Firefox 20+ + // Firefox doesn't alert if the returnValue field is not set. + if ( event.result !== undefined && event.originalEvent ) { + event.originalEvent.returnValue = event.result; + } + } + } + } +}; + +// Ensure the presence of an event listener that handles manually-triggered +// synthetic events by interrupting progress until reinvoked in response to +// *native* events that it fires directly, ensuring that state changes have +// already occurred before other listeners are invoked. +function leverageNative( el, type, expectSync ) { + + // Missing expectSync indicates a trigger call, which must force setup through jQuery.event.add + if ( !expectSync ) { + if ( dataPriv.get( el, type ) === undefined ) { + jQuery.event.add( el, type, returnTrue ); + } + return; + } + + // Register the controller as a special universal handler for all event namespaces + dataPriv.set( el, type, false ); + jQuery.event.add( el, type, { + namespace: false, + handler: function( event ) { + var notAsync, result, + saved = dataPriv.get( this, type ); + + if ( ( event.isTrigger & 1 ) && this[ type ] ) { + + // Interrupt processing of the outer synthetic .trigger()ed event + // Saved data should be false in such cases, but might be a leftover capture object + // from an async native handler (gh-4350) + if ( !saved.length ) { + + // Store arguments for use when handling the inner native event + // There will always be at least one argument (an event object), so this array + // will not be confused with a leftover capture object. + saved = slice.call( arguments ); + dataPriv.set( this, type, saved ); + + // Trigger the native event and capture its result + // Support: IE <=9 - 11+ + // focus() and blur() are asynchronous + notAsync = expectSync( this, type ); + this[ type ](); + result = dataPriv.get( this, type ); + if ( saved !== result || notAsync ) { + dataPriv.set( this, type, false ); + } else { + result = {}; + } + if ( saved !== result ) { + + // Cancel the outer synthetic event + event.stopImmediatePropagation(); + event.preventDefault(); + return result.value; + } + + // If this is an inner synthetic event for an event with a bubbling surrogate + // (focus or blur), assume that the surrogate already propagated from triggering the + // native event and prevent that from happening again here. + // This technically gets the ordering wrong w.r.t. to `.trigger()` (in which the + // bubbling surrogate propagates *after* the non-bubbling base), but that seems + // less bad than duplication. + } else if ( ( jQuery.event.special[ type ] || {} ).delegateType ) { + event.stopPropagation(); + } + + // If this is a native event triggered above, everything is now in order + // Fire an inner synthetic event with the original arguments + } else if ( saved.length ) { + + // ...and capture the result + dataPriv.set( this, type, { + value: jQuery.event.trigger( + + // Support: IE <=9 - 11+ + // Extend with the prototype to reset the above stopImmediatePropagation() + jQuery.extend( saved[ 0 ], jQuery.Event.prototype ), + saved.slice( 1 ), + this + ) + } ); + + // Abort handling of the native event + event.stopImmediatePropagation(); + } + } + } ); +} + +jQuery.removeEvent = function( elem, type, handle ) { + + // This "if" is needed for plain objects + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle ); + } +}; + +jQuery.Event = function( src, props ) { + + // Allow instantiation without the 'new' keyword + if ( !( this instanceof jQuery.Event ) ) { + return new jQuery.Event( src, props ); + } + + // Event object + if ( src && src.type ) { + this.originalEvent = src; + this.type = src.type; + + // Events bubbling up the document may have been marked as prevented + // by a handler lower down the tree; reflect the correct value. + this.isDefaultPrevented = src.defaultPrevented || + src.defaultPrevented === undefined && + + // Support: Android <=2.3 only + src.returnValue === false ? + returnTrue : + returnFalse; + + // Create target properties + // Support: Safari <=6 - 7 only + // Target should not be a text node (#504, #13143) + this.target = ( src.target && src.target.nodeType === 3 ) ? + src.target.parentNode : + src.target; + + this.currentTarget = src.currentTarget; + this.relatedTarget = src.relatedTarget; + + // Event type + } else { + this.type = src; + } + + // Put explicitly provided properties onto the event object + if ( props ) { + jQuery.extend( this, props ); + } + + // Create a timestamp if incoming event doesn't have one + this.timeStamp = src && src.timeStamp || Date.now(); + + // Mark it as fixed + this[ jQuery.expando ] = true; +}; + +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// https://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +jQuery.Event.prototype = { + constructor: jQuery.Event, + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse, + isSimulated: false, + + preventDefault: function() { + var e = this.originalEvent; + + this.isDefaultPrevented = returnTrue; + + if ( e && !this.isSimulated ) { + e.preventDefault(); + } + }, + stopPropagation: function() { + var e = this.originalEvent; + + this.isPropagationStopped = returnTrue; + + if ( e && !this.isSimulated ) { + e.stopPropagation(); + } + }, + stopImmediatePropagation: function() { + var e = this.originalEvent; + + this.isImmediatePropagationStopped = returnTrue; + + if ( e && !this.isSimulated ) { + e.stopImmediatePropagation(); + } + + this.stopPropagation(); + } +}; + +// Includes all common event props including KeyEvent and MouseEvent specific props +jQuery.each( { + altKey: true, + bubbles: true, + cancelable: true, + changedTouches: true, + ctrlKey: true, + detail: true, + eventPhase: true, + metaKey: true, + pageX: true, + pageY: true, + shiftKey: true, + view: true, + "char": true, + code: true, + charCode: true, + key: true, + keyCode: true, + button: true, + buttons: true, + clientX: true, + clientY: true, + offsetX: true, + offsetY: true, + pointerId: true, + pointerType: true, + screenX: true, + screenY: true, + targetTouches: true, + toElement: true, + touches: true, + + which: function( event ) { + var button = event.button; + + // Add which for key events + if ( event.which == null && rkeyEvent.test( event.type ) ) { + return event.charCode != null ? event.charCode : event.keyCode; + } + + // Add which for click: 1 === left; 2 === middle; 3 === right + if ( !event.which && button !== undefined && rmouseEvent.test( event.type ) ) { + if ( button & 1 ) { + return 1; + } + + if ( button & 2 ) { + return 3; + } + + if ( button & 4 ) { + return 2; + } + + return 0; + } + + return event.which; + } +}, jQuery.event.addProp ); + +jQuery.each( { focus: "focusin", blur: "focusout" }, function( type, delegateType ) { + jQuery.event.special[ type ] = { + + // Utilize native event if possible so blur/focus sequence is correct + setup: function() { + + // Claim the first handler + // dataPriv.set( this, "focus", ... ) + // dataPriv.set( this, "blur", ... ) + leverageNative( this, type, expectSync ); + + // Return false to allow normal processing in the caller + return false; + }, + trigger: function() { + + // Force setup before trigger + leverageNative( this, type ); + + // Return non-false to allow normal event-path propagation + return true; + }, + + delegateType: delegateType + }; +} ); + +// Create mouseenter/leave events using mouseover/out and event-time checks +// so that event delegation works in jQuery. +// Do the same for pointerenter/pointerleave and pointerover/pointerout +// +// Support: Safari 7 only +// Safari sends mouseenter too often; see: +// https://bugs.chromium.org/p/chromium/issues/detail?id=470258 +// for the description of the bug (it existed in older Chrome versions as well). +jQuery.each( { + mouseenter: "mouseover", + mouseleave: "mouseout", + pointerenter: "pointerover", + pointerleave: "pointerout" +}, function( orig, fix ) { + jQuery.event.special[ orig ] = { + delegateType: fix, + bindType: fix, + + handle: function( event ) { + var ret, + target = this, + related = event.relatedTarget, + handleObj = event.handleObj; + + // For mouseenter/leave call the handler if related is outside the target. + // NB: No relatedTarget if the mouse left/entered the browser window + if ( !related || ( related !== target && !jQuery.contains( target, related ) ) ) { + event.type = handleObj.origType; + ret = handleObj.handler.apply( this, arguments ); + event.type = fix; + } + return ret; + } + }; +} ); + +jQuery.fn.extend( { + + on: function( types, selector, data, fn ) { + return on( this, types, selector, data, fn ); + }, + one: function( types, selector, data, fn ) { + return on( this, types, selector, data, fn, 1 ); + }, + off: function( types, selector, fn ) { + var handleObj, type; + if ( types && types.preventDefault && types.handleObj ) { + + // ( event ) dispatched jQuery.Event + handleObj = types.handleObj; + jQuery( types.delegateTarget ).off( + handleObj.namespace ? + handleObj.origType + "." + handleObj.namespace : + handleObj.origType, + handleObj.selector, + handleObj.handler + ); + return this; + } + if ( typeof types === "object" ) { + + // ( types-object [, selector] ) + for ( type in types ) { + this.off( type, selector, types[ type ] ); + } + return this; + } + if ( selector === false || typeof selector === "function" ) { + + // ( types [, fn] ) + fn = selector; + selector = undefined; + } + if ( fn === false ) { + fn = returnFalse; + } + return this.each( function() { + jQuery.event.remove( this, types, fn, selector ); + } ); + } +} ); + + +var + + /* eslint-disable max-len */ + + // See https://github.com/eslint/eslint/issues/3229 + rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([a-z][^\/\0>\x20\t\r\n\f]*)[^>]*)\/>/gi, + + /* eslint-enable */ + + // Support: IE <=10 - 11, Edge 12 - 13 only + // In IE/Edge using regex groups here causes severe slowdowns. + // See https://connect.microsoft.com/IE/feedback/details/1736512/ + rnoInnerhtml = /\s*$/g; + +// Prefer a tbody over its parent table for containing new rows +function manipulationTarget( elem, content ) { + if ( nodeName( elem, "table" ) && + nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ) { + + return jQuery( elem ).children( "tbody" )[ 0 ] || elem; + } + + return elem; +} + +// Replace/restore the type attribute of script elements for safe DOM manipulation +function disableScript( elem ) { + elem.type = ( elem.getAttribute( "type" ) !== null ) + "/" + elem.type; + return elem; +} +function restoreScript( elem ) { + if ( ( elem.type || "" ).slice( 0, 5 ) === "true/" ) { + elem.type = elem.type.slice( 5 ); + } else { + elem.removeAttribute( "type" ); + } + + return elem; +} + +function cloneCopyEvent( src, dest ) { + var i, l, type, pdataOld, pdataCur, udataOld, udataCur, events; + + if ( dest.nodeType !== 1 ) { + return; + } + + // 1. Copy private data: events, handlers, etc. + if ( dataPriv.hasData( src ) ) { + pdataOld = dataPriv.access( src ); + pdataCur = dataPriv.set( dest, pdataOld ); + events = pdataOld.events; + + if ( events ) { + delete pdataCur.handle; + pdataCur.events = {}; + + for ( type in events ) { + for ( i = 0, l = events[ type ].length; i < l; i++ ) { + jQuery.event.add( dest, type, events[ type ][ i ] ); + } + } + } + } + + // 2. Copy user data + if ( dataUser.hasData( src ) ) { + udataOld = dataUser.access( src ); + udataCur = jQuery.extend( {}, udataOld ); + + dataUser.set( dest, udataCur ); + } +} + +// Fix IE bugs, see support tests +function fixInput( src, dest ) { + var nodeName = dest.nodeName.toLowerCase(); + + // Fails to persist the checked state of a cloned checkbox or radio button. + if ( nodeName === "input" && rcheckableType.test( src.type ) ) { + dest.checked = src.checked; + + // Fails to return the selected option to the default selected state when cloning options + } else if ( nodeName === "input" || nodeName === "textarea" ) { + dest.defaultValue = src.defaultValue; + } +} + +function domManip( collection, args, callback, ignored ) { + + // Flatten any nested arrays + args = concat.apply( [], args ); + + var fragment, first, scripts, hasScripts, node, doc, + i = 0, + l = collection.length, + iNoClone = l - 1, + value = args[ 0 ], + valueIsFunction = isFunction( value ); + + // We can't cloneNode fragments that contain checked, in WebKit + if ( valueIsFunction || + ( l > 1 && typeof value === "string" && + !support.checkClone && rchecked.test( value ) ) ) { + return collection.each( function( index ) { + var self = collection.eq( index ); + if ( valueIsFunction ) { + args[ 0 ] = value.call( this, index, self.html() ); + } + domManip( self, args, callback, ignored ); + } ); + } + + if ( l ) { + fragment = buildFragment( args, collection[ 0 ].ownerDocument, false, collection, ignored ); + first = fragment.firstChild; + + if ( fragment.childNodes.length === 1 ) { + fragment = first; + } + + // Require either new content or an interest in ignored elements to invoke the callback + if ( first || ignored ) { + scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); + hasScripts = scripts.length; + + // Use the original fragment for the last item + // instead of the first because it can end up + // being emptied incorrectly in certain situations (#8070). + for ( ; i < l; i++ ) { + node = fragment; + + if ( i !== iNoClone ) { + node = jQuery.clone( node, true, true ); + + // Keep references to cloned scripts for later restoration + if ( hasScripts ) { + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( scripts, getAll( node, "script" ) ); + } + } + + callback.call( collection[ i ], node, i ); + } + + if ( hasScripts ) { + doc = scripts[ scripts.length - 1 ].ownerDocument; + + // Reenable scripts + jQuery.map( scripts, restoreScript ); + + // Evaluate executable scripts on first document insertion + for ( i = 0; i < hasScripts; i++ ) { + node = scripts[ i ]; + if ( rscriptType.test( node.type || "" ) && + !dataPriv.access( node, "globalEval" ) && + jQuery.contains( doc, node ) ) { + + if ( node.src && ( node.type || "" ).toLowerCase() !== "module" ) { + + // Optional AJAX dependency, but won't run scripts if not present + if ( jQuery._evalUrl && !node.noModule ) { + jQuery._evalUrl( node.src, { + nonce: node.nonce || node.getAttribute( "nonce" ) + } ); + } + } else { + DOMEval( node.textContent.replace( rcleanScript, "" ), node, doc ); + } + } + } + } + } + } + + return collection; +} + +function remove( elem, selector, keepData ) { + var node, + nodes = selector ? jQuery.filter( selector, elem ) : elem, + i = 0; + + for ( ; ( node = nodes[ i ] ) != null; i++ ) { + if ( !keepData && node.nodeType === 1 ) { + jQuery.cleanData( getAll( node ) ); + } + + if ( node.parentNode ) { + if ( keepData && isAttached( node ) ) { + setGlobalEval( getAll( node, "script" ) ); + } + node.parentNode.removeChild( node ); + } + } + + return elem; +} + +jQuery.extend( { + htmlPrefilter: function( html ) { + return html.replace( rxhtmlTag, "<$1>" ); + }, + + clone: function( elem, dataAndEvents, deepDataAndEvents ) { + var i, l, srcElements, destElements, + clone = elem.cloneNode( true ), + inPage = isAttached( elem ); + + // Fix IE cloning issues + if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) && + !jQuery.isXMLDoc( elem ) ) { + + // We eschew Sizzle here for performance reasons: https://jsperf.com/getall-vs-sizzle/2 + destElements = getAll( clone ); + srcElements = getAll( elem ); + + for ( i = 0, l = srcElements.length; i < l; i++ ) { + fixInput( srcElements[ i ], destElements[ i ] ); + } + } + + // Copy the events from the original to the clone + if ( dataAndEvents ) { + if ( deepDataAndEvents ) { + srcElements = srcElements || getAll( elem ); + destElements = destElements || getAll( clone ); + + for ( i = 0, l = srcElements.length; i < l; i++ ) { + cloneCopyEvent( srcElements[ i ], destElements[ i ] ); + } + } else { + cloneCopyEvent( elem, clone ); + } + } + + // Preserve script evaluation history + destElements = getAll( clone, "script" ); + if ( destElements.length > 0 ) { + setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); + } + + // Return the cloned set + return clone; + }, + + cleanData: function( elems ) { + var data, elem, type, + special = jQuery.event.special, + i = 0; + + for ( ; ( elem = elems[ i ] ) !== undefined; i++ ) { + if ( acceptData( elem ) ) { + if ( ( data = elem[ dataPriv.expando ] ) ) { + if ( data.events ) { + for ( type in data.events ) { + if ( special[ type ] ) { + jQuery.event.remove( elem, type ); + + // This is a shortcut to avoid jQuery.event.remove's overhead + } else { + jQuery.removeEvent( elem, type, data.handle ); + } + } + } + + // Support: Chrome <=35 - 45+ + // Assign undefined instead of using delete, see Data#remove + elem[ dataPriv.expando ] = undefined; + } + if ( elem[ dataUser.expando ] ) { + + // Support: Chrome <=35 - 45+ + // Assign undefined instead of using delete, see Data#remove + elem[ dataUser.expando ] = undefined; + } + } + } + } +} ); + +jQuery.fn.extend( { + detach: function( selector ) { + return remove( this, selector, true ); + }, + + remove: function( selector ) { + return remove( this, selector ); + }, + + text: function( value ) { + return access( this, function( value ) { + return value === undefined ? + jQuery.text( this ) : + this.empty().each( function() { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + this.textContent = value; + } + } ); + }, null, value, arguments.length ); + }, + + append: function() { + return domManip( this, arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.appendChild( elem ); + } + } ); + }, + + prepend: function() { + return domManip( this, arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.insertBefore( elem, target.firstChild ); + } + } ); + }, + + before: function() { + return domManip( this, arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this ); + } + } ); + }, + + after: function() { + return domManip( this, arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this.nextSibling ); + } + } ); + }, + + empty: function() { + var elem, + i = 0; + + for ( ; ( elem = this[ i ] ) != null; i++ ) { + if ( elem.nodeType === 1 ) { + + // Prevent memory leaks + jQuery.cleanData( getAll( elem, false ) ); + + // Remove any remaining nodes + elem.textContent = ""; + } + } + + return this; + }, + + clone: function( dataAndEvents, deepDataAndEvents ) { + dataAndEvents = dataAndEvents == null ? false : dataAndEvents; + deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; + + return this.map( function() { + return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); + } ); + }, + + html: function( value ) { + return access( this, function( value ) { + var elem = this[ 0 ] || {}, + i = 0, + l = this.length; + + if ( value === undefined && elem.nodeType === 1 ) { + return elem.innerHTML; + } + + // See if we can take a shortcut and just use innerHTML + if ( typeof value === "string" && !rnoInnerhtml.test( value ) && + !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) { + + value = jQuery.htmlPrefilter( value ); + + try { + for ( ; i < l; i++ ) { + elem = this[ i ] || {}; + + // Remove element nodes and prevent memory leaks + if ( elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem, false ) ); + elem.innerHTML = value; + } + } + + elem = 0; + + // If using innerHTML throws an exception, use the fallback method + } catch ( e ) {} + } + + if ( elem ) { + this.empty().append( value ); + } + }, null, value, arguments.length ); + }, + + replaceWith: function() { + var ignored = []; + + // Make the changes, replacing each non-ignored context element with the new content + return domManip( this, arguments, function( elem ) { + var parent = this.parentNode; + + if ( jQuery.inArray( this, ignored ) < 0 ) { + jQuery.cleanData( getAll( this ) ); + if ( parent ) { + parent.replaceChild( elem, this ); + } + } + + // Force callback invocation + }, ignored ); + } +} ); + +jQuery.each( { + appendTo: "append", + prependTo: "prepend", + insertBefore: "before", + insertAfter: "after", + replaceAll: "replaceWith" +}, function( name, original ) { + jQuery.fn[ name ] = function( selector ) { + var elems, + ret = [], + insert = jQuery( selector ), + last = insert.length - 1, + i = 0; + + for ( ; i <= last; i++ ) { + elems = i === last ? this : this.clone( true ); + jQuery( insert[ i ] )[ original ]( elems ); + + // Support: Android <=4.0 only, PhantomJS 1 only + // .get() because push.apply(_, arraylike) throws on ancient WebKit + push.apply( ret, elems.get() ); + } + + return this.pushStack( ret ); + }; +} ); +var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" ); + +var getStyles = function( elem ) { + + // Support: IE <=11 only, Firefox <=30 (#15098, #14150) + // IE throws on elements created in popups + // FF meanwhile throws on frame elements through "defaultView.getComputedStyle" + var view = elem.ownerDocument.defaultView; + + if ( !view || !view.opener ) { + view = window; + } + + return view.getComputedStyle( elem ); + }; + +var rboxStyle = new RegExp( cssExpand.join( "|" ), "i" ); + + + +( function() { + + // Executing both pixelPosition & boxSizingReliable tests require only one layout + // so they're executed at the same time to save the second computation. + function computeStyleTests() { + + // This is a singleton, we need to execute it only once + if ( !div ) { + return; + } + + container.style.cssText = "position:absolute;left:-11111px;width:60px;" + + "margin-top:1px;padding:0;border:0"; + div.style.cssText = + "position:relative;display:block;box-sizing:border-box;overflow:scroll;" + + "margin:auto;border:1px;padding:1px;" + + "width:60%;top:1%"; + documentElement.appendChild( container ).appendChild( div ); + + var divStyle = window.getComputedStyle( div ); + pixelPositionVal = divStyle.top !== "1%"; + + // Support: Android 4.0 - 4.3 only, Firefox <=3 - 44 + reliableMarginLeftVal = roundPixelMeasures( divStyle.marginLeft ) === 12; + + // Support: Android 4.0 - 4.3 only, Safari <=9.1 - 10.1, iOS <=7.0 - 9.3 + // Some styles come back with percentage values, even though they shouldn't + div.style.right = "60%"; + pixelBoxStylesVal = roundPixelMeasures( divStyle.right ) === 36; + + // Support: IE 9 - 11 only + // Detect misreporting of content dimensions for box-sizing:border-box elements + boxSizingReliableVal = roundPixelMeasures( divStyle.width ) === 36; + + // Support: IE 9 only + // Detect overflow:scroll screwiness (gh-3699) + // Support: Chrome <=64 + // Don't get tricked when zoom affects offsetWidth (gh-4029) + div.style.position = "absolute"; + scrollboxSizeVal = roundPixelMeasures( div.offsetWidth / 3 ) === 12; + + documentElement.removeChild( container ); + + // Nullify the div so it wouldn't be stored in the memory and + // it will also be a sign that checks already performed + div = null; + } + + function roundPixelMeasures( measure ) { + return Math.round( parseFloat( measure ) ); + } + + var pixelPositionVal, boxSizingReliableVal, scrollboxSizeVal, pixelBoxStylesVal, + reliableMarginLeftVal, + container = document.createElement( "div" ), + div = document.createElement( "div" ); + + // Finish early in limited (non-browser) environments + if ( !div.style ) { + return; + } + + // Support: IE <=9 - 11 only + // Style of cloned element affects source element cloned (#8908) + div.style.backgroundClip = "content-box"; + div.cloneNode( true ).style.backgroundClip = ""; + support.clearCloneStyle = div.style.backgroundClip === "content-box"; + + jQuery.extend( support, { + boxSizingReliable: function() { + computeStyleTests(); + return boxSizingReliableVal; + }, + pixelBoxStyles: function() { + computeStyleTests(); + return pixelBoxStylesVal; + }, + pixelPosition: function() { + computeStyleTests(); + return pixelPositionVal; + }, + reliableMarginLeft: function() { + computeStyleTests(); + return reliableMarginLeftVal; + }, + scrollboxSize: function() { + computeStyleTests(); + return scrollboxSizeVal; + } + } ); +} )(); + + +function curCSS( elem, name, computed ) { + var width, minWidth, maxWidth, ret, + + // Support: Firefox 51+ + // Retrieving style before computed somehow + // fixes an issue with getting wrong values + // on detached elements + style = elem.style; + + computed = computed || getStyles( elem ); + + // getPropertyValue is needed for: + // .css('filter') (IE 9 only, #12537) + // .css('--customProperty) (#3144) + if ( computed ) { + ret = computed.getPropertyValue( name ) || computed[ name ]; + + if ( ret === "" && !isAttached( elem ) ) { + ret = jQuery.style( elem, name ); + } + + // A tribute to the "awesome hack by Dean Edwards" + // Android Browser returns percentage for some values, + // but width seems to be reliably pixels. + // This is against the CSSOM draft spec: + // https://drafts.csswg.org/cssom/#resolved-values + if ( !support.pixelBoxStyles() && rnumnonpx.test( ret ) && rboxStyle.test( name ) ) { + + // Remember the original values + width = style.width; + minWidth = style.minWidth; + maxWidth = style.maxWidth; + + // Put in the new values to get a computed value out + style.minWidth = style.maxWidth = style.width = ret; + ret = computed.width; + + // Revert the changed values + style.width = width; + style.minWidth = minWidth; + style.maxWidth = maxWidth; + } + } + + return ret !== undefined ? + + // Support: IE <=9 - 11 only + // IE returns zIndex value as an integer. + ret + "" : + ret; +} + + +function addGetHookIf( conditionFn, hookFn ) { + + // Define the hook, we'll check on the first run if it's really needed. + return { + get: function() { + if ( conditionFn() ) { + + // Hook not needed (or it's not possible to use it due + // to missing dependency), remove it. + delete this.get; + return; + } + + // Hook needed; redefine it so that the support test is not executed again. + return ( this.get = hookFn ).apply( this, arguments ); + } + }; +} + + +var cssPrefixes = [ "Webkit", "Moz", "ms" ], + emptyStyle = document.createElement( "div" ).style, + vendorProps = {}; + +// Return a vendor-prefixed property or undefined +function vendorPropName( name ) { + + // Check for vendor prefixed names + var capName = name[ 0 ].toUpperCase() + name.slice( 1 ), + i = cssPrefixes.length; + + while ( i-- ) { + name = cssPrefixes[ i ] + capName; + if ( name in emptyStyle ) { + return name; + } + } +} + +// Return a potentially-mapped jQuery.cssProps or vendor prefixed property +function finalPropName( name ) { + var final = jQuery.cssProps[ name ] || vendorProps[ name ]; + + if ( final ) { + return final; + } + if ( name in emptyStyle ) { + return name; + } + return vendorProps[ name ] = vendorPropName( name ) || name; +} + + +var + + // Swappable if display is none or starts with table + // except "table", "table-cell", or "table-caption" + // See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display + rdisplayswap = /^(none|table(?!-c[ea]).+)/, + rcustomProp = /^--/, + cssShow = { position: "absolute", visibility: "hidden", display: "block" }, + cssNormalTransform = { + letterSpacing: "0", + fontWeight: "400" + }; + +function setPositiveNumber( elem, value, subtract ) { + + // Any relative (+/-) values have already been + // normalized at this point + var matches = rcssNum.exec( value ); + return matches ? + + // Guard against undefined "subtract", e.g., when used as in cssHooks + Math.max( 0, matches[ 2 ] - ( subtract || 0 ) ) + ( matches[ 3 ] || "px" ) : + value; +} + +function boxModelAdjustment( elem, dimension, box, isBorderBox, styles, computedVal ) { + var i = dimension === "width" ? 1 : 0, + extra = 0, + delta = 0; + + // Adjustment may not be necessary + if ( box === ( isBorderBox ? "border" : "content" ) ) { + return 0; + } + + for ( ; i < 4; i += 2 ) { + + // Both box models exclude margin + if ( box === "margin" ) { + delta += jQuery.css( elem, box + cssExpand[ i ], true, styles ); + } + + // If we get here with a content-box, we're seeking "padding" or "border" or "margin" + if ( !isBorderBox ) { + + // Add padding + delta += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + + // For "border" or "margin", add border + if ( box !== "padding" ) { + delta += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + + // But still keep track of it otherwise + } else { + extra += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + + // If we get here with a border-box (content + padding + border), we're seeking "content" or + // "padding" or "margin" + } else { + + // For "content", subtract padding + if ( box === "content" ) { + delta -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + } + + // For "content" or "padding", subtract border + if ( box !== "margin" ) { + delta -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + } + } + + // Account for positive content-box scroll gutter when requested by providing computedVal + if ( !isBorderBox && computedVal >= 0 ) { + + // offsetWidth/offsetHeight is a rounded sum of content, padding, scroll gutter, and border + // Assuming integer scroll gutter, subtract the rest and round down + delta += Math.max( 0, Math.ceil( + elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] - + computedVal - + delta - + extra - + 0.5 + + // If offsetWidth/offsetHeight is unknown, then we can't determine content-box scroll gutter + // Use an explicit zero to avoid NaN (gh-3964) + ) ) || 0; + } + + return delta; +} + +function getWidthOrHeight( elem, dimension, extra ) { + + // Start with computed style + var styles = getStyles( elem ), + + // To avoid forcing a reflow, only fetch boxSizing if we need it (gh-4322). + // Fake content-box until we know it's needed to know the true value. + boxSizingNeeded = !support.boxSizingReliable() || extra, + isBorderBox = boxSizingNeeded && + jQuery.css( elem, "boxSizing", false, styles ) === "border-box", + valueIsBorderBox = isBorderBox, + + val = curCSS( elem, dimension, styles ), + offsetProp = "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ); + + // Support: Firefox <=54 + // Return a confounding non-pixel value or feign ignorance, as appropriate. + if ( rnumnonpx.test( val ) ) { + if ( !extra ) { + return val; + } + val = "auto"; + } + + + // Fall back to offsetWidth/offsetHeight when value is "auto" + // This happens for inline elements with no explicit setting (gh-3571) + // Support: Android <=4.1 - 4.3 only + // Also use offsetWidth/offsetHeight for misreported inline dimensions (gh-3602) + // Support: IE 9-11 only + // Also use offsetWidth/offsetHeight for when box sizing is unreliable + // We use getClientRects() to check for hidden/disconnected. + // In those cases, the computed value can be trusted to be border-box + if ( ( !support.boxSizingReliable() && isBorderBox || + val === "auto" || + !parseFloat( val ) && jQuery.css( elem, "display", false, styles ) === "inline" ) && + elem.getClientRects().length ) { + + isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box"; + + // Where available, offsetWidth/offsetHeight approximate border box dimensions. + // Where not available (e.g., SVG), assume unreliable box-sizing and interpret the + // retrieved value as a content box dimension. + valueIsBorderBox = offsetProp in elem; + if ( valueIsBorderBox ) { + val = elem[ offsetProp ]; + } + } + + // Normalize "" and auto + val = parseFloat( val ) || 0; + + // Adjust for the element's box model + return ( val + + boxModelAdjustment( + elem, + dimension, + extra || ( isBorderBox ? "border" : "content" ), + valueIsBorderBox, + styles, + + // Provide the current computed size to request scroll gutter calculation (gh-3589) + val + ) + ) + "px"; +} + +jQuery.extend( { + + // Add in style property hooks for overriding the default + // behavior of getting and setting a style property + cssHooks: { + opacity: { + get: function( elem, computed ) { + if ( computed ) { + + // We should always get a number back from opacity + var ret = curCSS( elem, "opacity" ); + return ret === "" ? "1" : ret; + } + } + } + }, + + // Don't automatically add "px" to these possibly-unitless properties + cssNumber: { + "animationIterationCount": true, + "columnCount": true, + "fillOpacity": true, + "flexGrow": true, + "flexShrink": true, + "fontWeight": true, + "gridArea": true, + "gridColumn": true, + "gridColumnEnd": true, + "gridColumnStart": true, + "gridRow": true, + "gridRowEnd": true, + "gridRowStart": true, + "lineHeight": true, + "opacity": true, + "order": true, + "orphans": true, + "widows": true, + "zIndex": true, + "zoom": true + }, + + // Add in properties whose names you wish to fix before + // setting or getting the value + cssProps: {}, + + // Get and set the style property on a DOM Node + style: function( elem, name, value, extra ) { + + // Don't set styles on text and comment nodes + if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) { + return; + } + + // Make sure that we're working with the right name + var ret, type, hooks, + origName = camelCase( name ), + isCustomProp = rcustomProp.test( name ), + style = elem.style; + + // Make sure that we're working with the right name. We don't + // want to query the value if it is a CSS custom property + // since they are user-defined. + if ( !isCustomProp ) { + name = finalPropName( origName ); + } + + // Gets hook for the prefixed version, then unprefixed version + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // Check if we're setting a value + if ( value !== undefined ) { + type = typeof value; + + // Convert "+=" or "-=" to relative numbers (#7345) + if ( type === "string" && ( ret = rcssNum.exec( value ) ) && ret[ 1 ] ) { + value = adjustCSS( elem, name, ret ); + + // Fixes bug #9237 + type = "number"; + } + + // Make sure that null and NaN values aren't set (#7116) + if ( value == null || value !== value ) { + return; + } + + // If a number was passed in, add the unit (except for certain CSS properties) + // The isCustomProp check can be removed in jQuery 4.0 when we only auto-append + // "px" to a few hardcoded values. + if ( type === "number" && !isCustomProp ) { + value += ret && ret[ 3 ] || ( jQuery.cssNumber[ origName ] ? "" : "px" ); + } + + // background-* props affect original clone's values + if ( !support.clearCloneStyle && value === "" && name.indexOf( "background" ) === 0 ) { + style[ name ] = "inherit"; + } + + // If a hook was provided, use that value, otherwise just set the specified value + if ( !hooks || !( "set" in hooks ) || + ( value = hooks.set( elem, value, extra ) ) !== undefined ) { + + if ( isCustomProp ) { + style.setProperty( name, value ); + } else { + style[ name ] = value; + } + } + + } else { + + // If a hook was provided get the non-computed value from there + if ( hooks && "get" in hooks && + ( ret = hooks.get( elem, false, extra ) ) !== undefined ) { + + return ret; + } + + // Otherwise just get the value from the style object + return style[ name ]; + } + }, + + css: function( elem, name, extra, styles ) { + var val, num, hooks, + origName = camelCase( name ), + isCustomProp = rcustomProp.test( name ); + + // Make sure that we're working with the right name. We don't + // want to modify the value if it is a CSS custom property + // since they are user-defined. + if ( !isCustomProp ) { + name = finalPropName( origName ); + } + + // Try prefixed name followed by the unprefixed name + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // If a hook was provided get the computed value from there + if ( hooks && "get" in hooks ) { + val = hooks.get( elem, true, extra ); + } + + // Otherwise, if a way to get the computed value exists, use that + if ( val === undefined ) { + val = curCSS( elem, name, styles ); + } + + // Convert "normal" to computed value + if ( val === "normal" && name in cssNormalTransform ) { + val = cssNormalTransform[ name ]; + } + + // Make numeric if forced or a qualifier was provided and val looks numeric + if ( extra === "" || extra ) { + num = parseFloat( val ); + return extra === true || isFinite( num ) ? num || 0 : val; + } + + return val; + } +} ); + +jQuery.each( [ "height", "width" ], function( i, dimension ) { + jQuery.cssHooks[ dimension ] = { + get: function( elem, computed, extra ) { + if ( computed ) { + + // Certain elements can have dimension info if we invisibly show them + // but it must have a current display style that would benefit + return rdisplayswap.test( jQuery.css( elem, "display" ) ) && + + // Support: Safari 8+ + // Table columns in Safari have non-zero offsetWidth & zero + // getBoundingClientRect().width unless display is changed. + // Support: IE <=11 only + // Running getBoundingClientRect on a disconnected node + // in IE throws an error. + ( !elem.getClientRects().length || !elem.getBoundingClientRect().width ) ? + swap( elem, cssShow, function() { + return getWidthOrHeight( elem, dimension, extra ); + } ) : + getWidthOrHeight( elem, dimension, extra ); + } + }, + + set: function( elem, value, extra ) { + var matches, + styles = getStyles( elem ), + + // Only read styles.position if the test has a chance to fail + // to avoid forcing a reflow. + scrollboxSizeBuggy = !support.scrollboxSize() && + styles.position === "absolute", + + // To avoid forcing a reflow, only fetch boxSizing if we need it (gh-3991) + boxSizingNeeded = scrollboxSizeBuggy || extra, + isBorderBox = boxSizingNeeded && + jQuery.css( elem, "boxSizing", false, styles ) === "border-box", + subtract = extra ? + boxModelAdjustment( + elem, + dimension, + extra, + isBorderBox, + styles + ) : + 0; + + // Account for unreliable border-box dimensions by comparing offset* to computed and + // faking a content-box to get border and padding (gh-3699) + if ( isBorderBox && scrollboxSizeBuggy ) { + subtract -= Math.ceil( + elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] - + parseFloat( styles[ dimension ] ) - + boxModelAdjustment( elem, dimension, "border", false, styles ) - + 0.5 + ); + } + + // Convert to pixels if value adjustment is needed + if ( subtract && ( matches = rcssNum.exec( value ) ) && + ( matches[ 3 ] || "px" ) !== "px" ) { + + elem.style[ dimension ] = value; + value = jQuery.css( elem, dimension ); + } + + return setPositiveNumber( elem, value, subtract ); + } + }; +} ); + +jQuery.cssHooks.marginLeft = addGetHookIf( support.reliableMarginLeft, + function( elem, computed ) { + if ( computed ) { + return ( parseFloat( curCSS( elem, "marginLeft" ) ) || + elem.getBoundingClientRect().left - + swap( elem, { marginLeft: 0 }, function() { + return elem.getBoundingClientRect().left; + } ) + ) + "px"; + } + } +); + +// These hooks are used by animate to expand properties +jQuery.each( { + margin: "", + padding: "", + border: "Width" +}, function( prefix, suffix ) { + jQuery.cssHooks[ prefix + suffix ] = { + expand: function( value ) { + var i = 0, + expanded = {}, + + // Assumes a single number if not a string + parts = typeof value === "string" ? value.split( " " ) : [ value ]; + + for ( ; i < 4; i++ ) { + expanded[ prefix + cssExpand[ i ] + suffix ] = + parts[ i ] || parts[ i - 2 ] || parts[ 0 ]; + } + + return expanded; + } + }; + + if ( prefix !== "margin" ) { + jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber; + } +} ); + +jQuery.fn.extend( { + css: function( name, value ) { + return access( this, function( elem, name, value ) { + var styles, len, + map = {}, + i = 0; + + if ( Array.isArray( name ) ) { + styles = getStyles( elem ); + len = name.length; + + for ( ; i < len; i++ ) { + map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles ); + } + + return map; + } + + return value !== undefined ? + jQuery.style( elem, name, value ) : + jQuery.css( elem, name ); + }, name, value, arguments.length > 1 ); + } +} ); + + +function Tween( elem, options, prop, end, easing ) { + return new Tween.prototype.init( elem, options, prop, end, easing ); +} +jQuery.Tween = Tween; + +Tween.prototype = { + constructor: Tween, + init: function( elem, options, prop, end, easing, unit ) { + this.elem = elem; + this.prop = prop; + this.easing = easing || jQuery.easing._default; + this.options = options; + this.start = this.now = this.cur(); + this.end = end; + this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" ); + }, + cur: function() { + var hooks = Tween.propHooks[ this.prop ]; + + return hooks && hooks.get ? + hooks.get( this ) : + Tween.propHooks._default.get( this ); + }, + run: function( percent ) { + var eased, + hooks = Tween.propHooks[ this.prop ]; + + if ( this.options.duration ) { + this.pos = eased = jQuery.easing[ this.easing ]( + percent, this.options.duration * percent, 0, 1, this.options.duration + ); + } else { + this.pos = eased = percent; + } + this.now = ( this.end - this.start ) * eased + this.start; + + if ( this.options.step ) { + this.options.step.call( this.elem, this.now, this ); + } + + if ( hooks && hooks.set ) { + hooks.set( this ); + } else { + Tween.propHooks._default.set( this ); + } + return this; + } +}; + +Tween.prototype.init.prototype = Tween.prototype; + +Tween.propHooks = { + _default: { + get: function( tween ) { + var result; + + // Use a property on the element directly when it is not a DOM element, + // or when there is no matching style property that exists. + if ( tween.elem.nodeType !== 1 || + tween.elem[ tween.prop ] != null && tween.elem.style[ tween.prop ] == null ) { + return tween.elem[ tween.prop ]; + } + + // Passing an empty string as a 3rd parameter to .css will automatically + // attempt a parseFloat and fallback to a string if the parse fails. + // Simple values such as "10px" are parsed to Float; + // complex values such as "rotate(1rad)" are returned as-is. + result = jQuery.css( tween.elem, tween.prop, "" ); + + // Empty strings, null, undefined and "auto" are converted to 0. + return !result || result === "auto" ? 0 : result; + }, + set: function( tween ) { + + // Use step hook for back compat. + // Use cssHook if its there. + // Use .style if available and use plain properties where available. + if ( jQuery.fx.step[ tween.prop ] ) { + jQuery.fx.step[ tween.prop ]( tween ); + } else if ( tween.elem.nodeType === 1 && ( + jQuery.cssHooks[ tween.prop ] || + tween.elem.style[ finalPropName( tween.prop ) ] != null ) ) { + jQuery.style( tween.elem, tween.prop, tween.now + tween.unit ); + } else { + tween.elem[ tween.prop ] = tween.now; + } + } + } +}; + +// Support: IE <=9 only +// Panic based approach to setting things on disconnected nodes +Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = { + set: function( tween ) { + if ( tween.elem.nodeType && tween.elem.parentNode ) { + tween.elem[ tween.prop ] = tween.now; + } + } +}; + +jQuery.easing = { + linear: function( p ) { + return p; + }, + swing: function( p ) { + return 0.5 - Math.cos( p * Math.PI ) / 2; + }, + _default: "swing" +}; + +jQuery.fx = Tween.prototype.init; + +// Back compat <1.8 extension point +jQuery.fx.step = {}; + + + + +var + fxNow, inProgress, + rfxtypes = /^(?:toggle|show|hide)$/, + rrun = /queueHooks$/; + +function schedule() { + if ( inProgress ) { + if ( document.hidden === false && window.requestAnimationFrame ) { + window.requestAnimationFrame( schedule ); + } else { + window.setTimeout( schedule, jQuery.fx.interval ); + } + + jQuery.fx.tick(); + } +} + +// Animations created synchronously will run synchronously +function createFxNow() { + window.setTimeout( function() { + fxNow = undefined; + } ); + return ( fxNow = Date.now() ); +} + +// Generate parameters to create a standard animation +function genFx( type, includeWidth ) { + var which, + i = 0, + attrs = { height: type }; + + // If we include width, step value is 1 to do all cssExpand values, + // otherwise step value is 2 to skip over Left and Right + includeWidth = includeWidth ? 1 : 0; + for ( ; i < 4; i += 2 - includeWidth ) { + which = cssExpand[ i ]; + attrs[ "margin" + which ] = attrs[ "padding" + which ] = type; + } + + if ( includeWidth ) { + attrs.opacity = attrs.width = type; + } + + return attrs; +} + +function createTween( value, prop, animation ) { + var tween, + collection = ( Animation.tweeners[ prop ] || [] ).concat( Animation.tweeners[ "*" ] ), + index = 0, + length = collection.length; + for ( ; index < length; index++ ) { + if ( ( tween = collection[ index ].call( animation, prop, value ) ) ) { + + // We're done with this property + return tween; + } + } +} + +function defaultPrefilter( elem, props, opts ) { + var prop, value, toggle, hooks, oldfire, propTween, restoreDisplay, display, + isBox = "width" in props || "height" in props, + anim = this, + orig = {}, + style = elem.style, + hidden = elem.nodeType && isHiddenWithinTree( elem ), + dataShow = dataPriv.get( elem, "fxshow" ); + + // Queue-skipping animations hijack the fx hooks + if ( !opts.queue ) { + hooks = jQuery._queueHooks( elem, "fx" ); + if ( hooks.unqueued == null ) { + hooks.unqueued = 0; + oldfire = hooks.empty.fire; + hooks.empty.fire = function() { + if ( !hooks.unqueued ) { + oldfire(); + } + }; + } + hooks.unqueued++; + + anim.always( function() { + + // Ensure the complete handler is called before this completes + anim.always( function() { + hooks.unqueued--; + if ( !jQuery.queue( elem, "fx" ).length ) { + hooks.empty.fire(); + } + } ); + } ); + } + + // Detect show/hide animations + for ( prop in props ) { + value = props[ prop ]; + if ( rfxtypes.test( value ) ) { + delete props[ prop ]; + toggle = toggle || value === "toggle"; + if ( value === ( hidden ? "hide" : "show" ) ) { + + // Pretend to be hidden if this is a "show" and + // there is still data from a stopped show/hide + if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) { + hidden = true; + + // Ignore all other no-op show/hide data + } else { + continue; + } + } + orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop ); + } + } + + // Bail out if this is a no-op like .hide().hide() + propTween = !jQuery.isEmptyObject( props ); + if ( !propTween && jQuery.isEmptyObject( orig ) ) { + return; + } + + // Restrict "overflow" and "display" styles during box animations + if ( isBox && elem.nodeType === 1 ) { + + // Support: IE <=9 - 11, Edge 12 - 15 + // Record all 3 overflow attributes because IE does not infer the shorthand + // from identically-valued overflowX and overflowY and Edge just mirrors + // the overflowX value there. + opts.overflow = [ style.overflow, style.overflowX, style.overflowY ]; + + // Identify a display type, preferring old show/hide data over the CSS cascade + restoreDisplay = dataShow && dataShow.display; + if ( restoreDisplay == null ) { + restoreDisplay = dataPriv.get( elem, "display" ); + } + display = jQuery.css( elem, "display" ); + if ( display === "none" ) { + if ( restoreDisplay ) { + display = restoreDisplay; + } else { + + // Get nonempty value(s) by temporarily forcing visibility + showHide( [ elem ], true ); + restoreDisplay = elem.style.display || restoreDisplay; + display = jQuery.css( elem, "display" ); + showHide( [ elem ] ); + } + } + + // Animate inline elements as inline-block + if ( display === "inline" || display === "inline-block" && restoreDisplay != null ) { + if ( jQuery.css( elem, "float" ) === "none" ) { + + // Restore the original display value at the end of pure show/hide animations + if ( !propTween ) { + anim.done( function() { + style.display = restoreDisplay; + } ); + if ( restoreDisplay == null ) { + display = style.display; + restoreDisplay = display === "none" ? "" : display; + } + } + style.display = "inline-block"; + } + } + } + + if ( opts.overflow ) { + style.overflow = "hidden"; + anim.always( function() { + style.overflow = opts.overflow[ 0 ]; + style.overflowX = opts.overflow[ 1 ]; + style.overflowY = opts.overflow[ 2 ]; + } ); + } + + // Implement show/hide animations + propTween = false; + for ( prop in orig ) { + + // General show/hide setup for this element animation + if ( !propTween ) { + if ( dataShow ) { + if ( "hidden" in dataShow ) { + hidden = dataShow.hidden; + } + } else { + dataShow = dataPriv.access( elem, "fxshow", { display: restoreDisplay } ); + } + + // Store hidden/visible for toggle so `.stop().toggle()` "reverses" + if ( toggle ) { + dataShow.hidden = !hidden; + } + + // Show elements before animating them + if ( hidden ) { + showHide( [ elem ], true ); + } + + /* eslint-disable no-loop-func */ + + anim.done( function() { + + /* eslint-enable no-loop-func */ + + // The final step of a "hide" animation is actually hiding the element + if ( !hidden ) { + showHide( [ elem ] ); + } + dataPriv.remove( elem, "fxshow" ); + for ( prop in orig ) { + jQuery.style( elem, prop, orig[ prop ] ); + } + } ); + } + + // Per-property setup + propTween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim ); + if ( !( prop in dataShow ) ) { + dataShow[ prop ] = propTween.start; + if ( hidden ) { + propTween.end = propTween.start; + propTween.start = 0; + } + } + } +} + +function propFilter( props, specialEasing ) { + var index, name, easing, value, hooks; + + // camelCase, specialEasing and expand cssHook pass + for ( index in props ) { + name = camelCase( index ); + easing = specialEasing[ name ]; + value = props[ index ]; + if ( Array.isArray( value ) ) { + easing = value[ 1 ]; + value = props[ index ] = value[ 0 ]; + } + + if ( index !== name ) { + props[ name ] = value; + delete props[ index ]; + } + + hooks = jQuery.cssHooks[ name ]; + if ( hooks && "expand" in hooks ) { + value = hooks.expand( value ); + delete props[ name ]; + + // Not quite $.extend, this won't overwrite existing keys. + // Reusing 'index' because we have the correct "name" + for ( index in value ) { + if ( !( index in props ) ) { + props[ index ] = value[ index ]; + specialEasing[ index ] = easing; + } + } + } else { + specialEasing[ name ] = easing; + } + } +} + +function Animation( elem, properties, options ) { + var result, + stopped, + index = 0, + length = Animation.prefilters.length, + deferred = jQuery.Deferred().always( function() { + + // Don't match elem in the :animated selector + delete tick.elem; + } ), + tick = function() { + if ( stopped ) { + return false; + } + var currentTime = fxNow || createFxNow(), + remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ), + + // Support: Android 2.3 only + // Archaic crash bug won't allow us to use `1 - ( 0.5 || 0 )` (#12497) + temp = remaining / animation.duration || 0, + percent = 1 - temp, + index = 0, + length = animation.tweens.length; + + for ( ; index < length; index++ ) { + animation.tweens[ index ].run( percent ); + } + + deferred.notifyWith( elem, [ animation, percent, remaining ] ); + + // If there's more to do, yield + if ( percent < 1 && length ) { + return remaining; + } + + // If this was an empty animation, synthesize a final progress notification + if ( !length ) { + deferred.notifyWith( elem, [ animation, 1, 0 ] ); + } + + // Resolve the animation and report its conclusion + deferred.resolveWith( elem, [ animation ] ); + return false; + }, + animation = deferred.promise( { + elem: elem, + props: jQuery.extend( {}, properties ), + opts: jQuery.extend( true, { + specialEasing: {}, + easing: jQuery.easing._default + }, options ), + originalProperties: properties, + originalOptions: options, + startTime: fxNow || createFxNow(), + duration: options.duration, + tweens: [], + createTween: function( prop, end ) { + var tween = jQuery.Tween( elem, animation.opts, prop, end, + animation.opts.specialEasing[ prop ] || animation.opts.easing ); + animation.tweens.push( tween ); + return tween; + }, + stop: function( gotoEnd ) { + var index = 0, + + // If we are going to the end, we want to run all the tweens + // otherwise we skip this part + length = gotoEnd ? animation.tweens.length : 0; + if ( stopped ) { + return this; + } + stopped = true; + for ( ; index < length; index++ ) { + animation.tweens[ index ].run( 1 ); + } + + // Resolve when we played the last frame; otherwise, reject + if ( gotoEnd ) { + deferred.notifyWith( elem, [ animation, 1, 0 ] ); + deferred.resolveWith( elem, [ animation, gotoEnd ] ); + } else { + deferred.rejectWith( elem, [ animation, gotoEnd ] ); + } + return this; + } + } ), + props = animation.props; + + propFilter( props, animation.opts.specialEasing ); + + for ( ; index < length; index++ ) { + result = Animation.prefilters[ index ].call( animation, elem, props, animation.opts ); + if ( result ) { + if ( isFunction( result.stop ) ) { + jQuery._queueHooks( animation.elem, animation.opts.queue ).stop = + result.stop.bind( result ); + } + return result; + } + } + + jQuery.map( props, createTween, animation ); + + if ( isFunction( animation.opts.start ) ) { + animation.opts.start.call( elem, animation ); + } + + // Attach callbacks from options + animation + .progress( animation.opts.progress ) + .done( animation.opts.done, animation.opts.complete ) + .fail( animation.opts.fail ) + .always( animation.opts.always ); + + jQuery.fx.timer( + jQuery.extend( tick, { + elem: elem, + anim: animation, + queue: animation.opts.queue + } ) + ); + + return animation; +} + +jQuery.Animation = jQuery.extend( Animation, { + + tweeners: { + "*": [ function( prop, value ) { + var tween = this.createTween( prop, value ); + adjustCSS( tween.elem, prop, rcssNum.exec( value ), tween ); + return tween; + } ] + }, + + tweener: function( props, callback ) { + if ( isFunction( props ) ) { + callback = props; + props = [ "*" ]; + } else { + props = props.match( rnothtmlwhite ); + } + + var prop, + index = 0, + length = props.length; + + for ( ; index < length; index++ ) { + prop = props[ index ]; + Animation.tweeners[ prop ] = Animation.tweeners[ prop ] || []; + Animation.tweeners[ prop ].unshift( callback ); + } + }, + + prefilters: [ defaultPrefilter ], + + prefilter: function( callback, prepend ) { + if ( prepend ) { + Animation.prefilters.unshift( callback ); + } else { + Animation.prefilters.push( callback ); + } + } +} ); + +jQuery.speed = function( speed, easing, fn ) { + var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : { + complete: fn || !fn && easing || + isFunction( speed ) && speed, + duration: speed, + easing: fn && easing || easing && !isFunction( easing ) && easing + }; + + // Go to the end state if fx are off + if ( jQuery.fx.off ) { + opt.duration = 0; + + } else { + if ( typeof opt.duration !== "number" ) { + if ( opt.duration in jQuery.fx.speeds ) { + opt.duration = jQuery.fx.speeds[ opt.duration ]; + + } else { + opt.duration = jQuery.fx.speeds._default; + } + } + } + + // Normalize opt.queue - true/undefined/null -> "fx" + if ( opt.queue == null || opt.queue === true ) { + opt.queue = "fx"; + } + + // Queueing + opt.old = opt.complete; + + opt.complete = function() { + if ( isFunction( opt.old ) ) { + opt.old.call( this ); + } + + if ( opt.queue ) { + jQuery.dequeue( this, opt.queue ); + } + }; + + return opt; +}; + +jQuery.fn.extend( { + fadeTo: function( speed, to, easing, callback ) { + + // Show any hidden elements after setting opacity to 0 + return this.filter( isHiddenWithinTree ).css( "opacity", 0 ).show() + + // Animate to the value specified + .end().animate( { opacity: to }, speed, easing, callback ); + }, + animate: function( prop, speed, easing, callback ) { + var empty = jQuery.isEmptyObject( prop ), + optall = jQuery.speed( speed, easing, callback ), + doAnimation = function() { + + // Operate on a copy of prop so per-property easing won't be lost + var anim = Animation( this, jQuery.extend( {}, prop ), optall ); + + // Empty animations, or finishing resolves immediately + if ( empty || dataPriv.get( this, "finish" ) ) { + anim.stop( true ); + } + }; + doAnimation.finish = doAnimation; + + return empty || optall.queue === false ? + this.each( doAnimation ) : + this.queue( optall.queue, doAnimation ); + }, + stop: function( type, clearQueue, gotoEnd ) { + var stopQueue = function( hooks ) { + var stop = hooks.stop; + delete hooks.stop; + stop( gotoEnd ); + }; + + if ( typeof type !== "string" ) { + gotoEnd = clearQueue; + clearQueue = type; + type = undefined; + } + if ( clearQueue && type !== false ) { + this.queue( type || "fx", [] ); + } + + return this.each( function() { + var dequeue = true, + index = type != null && type + "queueHooks", + timers = jQuery.timers, + data = dataPriv.get( this ); + + if ( index ) { + if ( data[ index ] && data[ index ].stop ) { + stopQueue( data[ index ] ); + } + } else { + for ( index in data ) { + if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) { + stopQueue( data[ index ] ); + } + } + } + + for ( index = timers.length; index--; ) { + if ( timers[ index ].elem === this && + ( type == null || timers[ index ].queue === type ) ) { + + timers[ index ].anim.stop( gotoEnd ); + dequeue = false; + timers.splice( index, 1 ); + } + } + + // Start the next in the queue if the last step wasn't forced. + // Timers currently will call their complete callbacks, which + // will dequeue but only if they were gotoEnd. + if ( dequeue || !gotoEnd ) { + jQuery.dequeue( this, type ); + } + } ); + }, + finish: function( type ) { + if ( type !== false ) { + type = type || "fx"; + } + return this.each( function() { + var index, + data = dataPriv.get( this ), + queue = data[ type + "queue" ], + hooks = data[ type + "queueHooks" ], + timers = jQuery.timers, + length = queue ? queue.length : 0; + + // Enable finishing flag on private data + data.finish = true; + + // Empty the queue first + jQuery.queue( this, type, [] ); + + if ( hooks && hooks.stop ) { + hooks.stop.call( this, true ); + } + + // Look for any active animations, and finish them + for ( index = timers.length; index--; ) { + if ( timers[ index ].elem === this && timers[ index ].queue === type ) { + timers[ index ].anim.stop( true ); + timers.splice( index, 1 ); + } + } + + // Look for any animations in the old queue and finish them + for ( index = 0; index < length; index++ ) { + if ( queue[ index ] && queue[ index ].finish ) { + queue[ index ].finish.call( this ); + } + } + + // Turn off finishing flag + delete data.finish; + } ); + } +} ); + +jQuery.each( [ "toggle", "show", "hide" ], function( i, name ) { + var cssFn = jQuery.fn[ name ]; + jQuery.fn[ name ] = function( speed, easing, callback ) { + return speed == null || typeof speed === "boolean" ? + cssFn.apply( this, arguments ) : + this.animate( genFx( name, true ), speed, easing, callback ); + }; +} ); + +// Generate shortcuts for custom animations +jQuery.each( { + slideDown: genFx( "show" ), + slideUp: genFx( "hide" ), + slideToggle: genFx( "toggle" ), + fadeIn: { opacity: "show" }, + fadeOut: { opacity: "hide" }, + fadeToggle: { opacity: "toggle" } +}, function( name, props ) { + jQuery.fn[ name ] = function( speed, easing, callback ) { + return this.animate( props, speed, easing, callback ); + }; +} ); + +jQuery.timers = []; +jQuery.fx.tick = function() { + var timer, + i = 0, + timers = jQuery.timers; + + fxNow = Date.now(); + + for ( ; i < timers.length; i++ ) { + timer = timers[ i ]; + + // Run the timer and safely remove it when done (allowing for external removal) + if ( !timer() && timers[ i ] === timer ) { + timers.splice( i--, 1 ); + } + } + + if ( !timers.length ) { + jQuery.fx.stop(); + } + fxNow = undefined; +}; + +jQuery.fx.timer = function( timer ) { + jQuery.timers.push( timer ); + jQuery.fx.start(); +}; + +jQuery.fx.interval = 13; +jQuery.fx.start = function() { + if ( inProgress ) { + return; + } + + inProgress = true; + schedule(); +}; + +jQuery.fx.stop = function() { + inProgress = null; +}; + +jQuery.fx.speeds = { + slow: 600, + fast: 200, + + // Default speed + _default: 400 +}; + + +// Based off of the plugin by Clint Helfers, with permission. +// https://web.archive.org/web/20100324014747/http://blindsignals.com/index.php/2009/07/jquery-delay/ +jQuery.fn.delay = function( time, type ) { + time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; + type = type || "fx"; + + return this.queue( type, function( next, hooks ) { + var timeout = window.setTimeout( next, time ); + hooks.stop = function() { + window.clearTimeout( timeout ); + }; + } ); +}; + + +( function() { + var input = document.createElement( "input" ), + select = document.createElement( "select" ), + opt = select.appendChild( document.createElement( "option" ) ); + + input.type = "checkbox"; + + // Support: Android <=4.3 only + // Default value for a checkbox should be "on" + support.checkOn = input.value !== ""; + + // Support: IE <=11 only + // Must access selectedIndex to make default options select + support.optSelected = opt.selected; + + // Support: IE <=11 only + // An input loses its value after becoming a radio + input = document.createElement( "input" ); + input.value = "t"; + input.type = "radio"; + support.radioValue = input.value === "t"; +} )(); + + +var boolHook, + attrHandle = jQuery.expr.attrHandle; + +jQuery.fn.extend( { + attr: function( name, value ) { + return access( this, jQuery.attr, name, value, arguments.length > 1 ); + }, + + removeAttr: function( name ) { + return this.each( function() { + jQuery.removeAttr( this, name ); + } ); + } +} ); + +jQuery.extend( { + attr: function( elem, name, value ) { + var ret, hooks, + nType = elem.nodeType; + + // Don't get/set attributes on text, comment and attribute nodes + if ( nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + // Fallback to prop when attributes are not supported + if ( typeof elem.getAttribute === "undefined" ) { + return jQuery.prop( elem, name, value ); + } + + // Attribute hooks are determined by the lowercase version + // Grab necessary hook if one is defined + if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { + hooks = jQuery.attrHooks[ name.toLowerCase() ] || + ( jQuery.expr.match.bool.test( name ) ? boolHook : undefined ); + } + + if ( value !== undefined ) { + if ( value === null ) { + jQuery.removeAttr( elem, name ); + return; + } + + if ( hooks && "set" in hooks && + ( ret = hooks.set( elem, value, name ) ) !== undefined ) { + return ret; + } + + elem.setAttribute( name, value + "" ); + return value; + } + + if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { + return ret; + } + + ret = jQuery.find.attr( elem, name ); + + // Non-existent attributes return null, we normalize to undefined + return ret == null ? undefined : ret; + }, + + attrHooks: { + type: { + set: function( elem, value ) { + if ( !support.radioValue && value === "radio" && + nodeName( elem, "input" ) ) { + var val = elem.value; + elem.setAttribute( "type", value ); + if ( val ) { + elem.value = val; + } + return value; + } + } + } + }, + + removeAttr: function( elem, value ) { + var name, + i = 0, + + // Attribute names can contain non-HTML whitespace characters + // https://html.spec.whatwg.org/multipage/syntax.html#attributes-2 + attrNames = value && value.match( rnothtmlwhite ); + + if ( attrNames && elem.nodeType === 1 ) { + while ( ( name = attrNames[ i++ ] ) ) { + elem.removeAttribute( name ); + } + } + } +} ); + +// Hooks for boolean attributes +boolHook = { + set: function( elem, value, name ) { + if ( value === false ) { + + // Remove boolean attributes when set to false + jQuery.removeAttr( elem, name ); + } else { + elem.setAttribute( name, name ); + } + return name; + } +}; + +jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( i, name ) { + var getter = attrHandle[ name ] || jQuery.find.attr; + + attrHandle[ name ] = function( elem, name, isXML ) { + var ret, handle, + lowercaseName = name.toLowerCase(); + + if ( !isXML ) { + + // Avoid an infinite loop by temporarily removing this function from the getter + handle = attrHandle[ lowercaseName ]; + attrHandle[ lowercaseName ] = ret; + ret = getter( elem, name, isXML ) != null ? + lowercaseName : + null; + attrHandle[ lowercaseName ] = handle; + } + return ret; + }; +} ); + + + + +var rfocusable = /^(?:input|select|textarea|button)$/i, + rclickable = /^(?:a|area)$/i; + +jQuery.fn.extend( { + prop: function( name, value ) { + return access( this, jQuery.prop, name, value, arguments.length > 1 ); + }, + + removeProp: function( name ) { + return this.each( function() { + delete this[ jQuery.propFix[ name ] || name ]; + } ); + } +} ); + +jQuery.extend( { + prop: function( elem, name, value ) { + var ret, hooks, + nType = elem.nodeType; + + // Don't get/set properties on text, comment and attribute nodes + if ( nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { + + // Fix name and attach hooks + name = jQuery.propFix[ name ] || name; + hooks = jQuery.propHooks[ name ]; + } + + if ( value !== undefined ) { + if ( hooks && "set" in hooks && + ( ret = hooks.set( elem, value, name ) ) !== undefined ) { + return ret; + } + + return ( elem[ name ] = value ); + } + + if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { + return ret; + } + + return elem[ name ]; + }, + + propHooks: { + tabIndex: { + get: function( elem ) { + + // Support: IE <=9 - 11 only + // elem.tabIndex doesn't always return the + // correct value when it hasn't been explicitly set + // https://web.archive.org/web/20141116233347/http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ + // Use proper attribute retrieval(#12072) + var tabindex = jQuery.find.attr( elem, "tabindex" ); + + if ( tabindex ) { + return parseInt( tabindex, 10 ); + } + + if ( + rfocusable.test( elem.nodeName ) || + rclickable.test( elem.nodeName ) && + elem.href + ) { + return 0; + } + + return -1; + } + } + }, + + propFix: { + "for": "htmlFor", + "class": "className" + } +} ); + +// Support: IE <=11 only +// Accessing the selectedIndex property +// forces the browser to respect setting selected +// on the option +// The getter ensures a default option is selected +// when in an optgroup +// eslint rule "no-unused-expressions" is disabled for this code +// since it considers such accessions noop +if ( !support.optSelected ) { + jQuery.propHooks.selected = { + get: function( elem ) { + + /* eslint no-unused-expressions: "off" */ + + var parent = elem.parentNode; + if ( parent && parent.parentNode ) { + parent.parentNode.selectedIndex; + } + return null; + }, + set: function( elem ) { + + /* eslint no-unused-expressions: "off" */ + + var parent = elem.parentNode; + if ( parent ) { + parent.selectedIndex; + + if ( parent.parentNode ) { + parent.parentNode.selectedIndex; + } + } + } + }; +} + +jQuery.each( [ + "tabIndex", + "readOnly", + "maxLength", + "cellSpacing", + "cellPadding", + "rowSpan", + "colSpan", + "useMap", + "frameBorder", + "contentEditable" +], function() { + jQuery.propFix[ this.toLowerCase() ] = this; +} ); + + + + + // Strip and collapse whitespace according to HTML spec + // https://infra.spec.whatwg.org/#strip-and-collapse-ascii-whitespace + function stripAndCollapse( value ) { + var tokens = value.match( rnothtmlwhite ) || []; + return tokens.join( " " ); + } + + +function getClass( elem ) { + return elem.getAttribute && elem.getAttribute( "class" ) || ""; +} + +function classesToArray( value ) { + if ( Array.isArray( value ) ) { + return value; + } + if ( typeof value === "string" ) { + return value.match( rnothtmlwhite ) || []; + } + return []; +} + +jQuery.fn.extend( { + addClass: function( value ) { + var classes, elem, cur, curValue, clazz, j, finalValue, + i = 0; + + if ( isFunction( value ) ) { + return this.each( function( j ) { + jQuery( this ).addClass( value.call( this, j, getClass( this ) ) ); + } ); + } + + classes = classesToArray( value ); + + if ( classes.length ) { + while ( ( elem = this[ i++ ] ) ) { + curValue = getClass( elem ); + cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); + + if ( cur ) { + j = 0; + while ( ( clazz = classes[ j++ ] ) ) { + if ( cur.indexOf( " " + clazz + " " ) < 0 ) { + cur += clazz + " "; + } + } + + // Only assign if different to avoid unneeded rendering. + finalValue = stripAndCollapse( cur ); + if ( curValue !== finalValue ) { + elem.setAttribute( "class", finalValue ); + } + } + } + } + + return this; + }, + + removeClass: function( value ) { + var classes, elem, cur, curValue, clazz, j, finalValue, + i = 0; + + if ( isFunction( value ) ) { + return this.each( function( j ) { + jQuery( this ).removeClass( value.call( this, j, getClass( this ) ) ); + } ); + } + + if ( !arguments.length ) { + return this.attr( "class", "" ); + } + + classes = classesToArray( value ); + + if ( classes.length ) { + while ( ( elem = this[ i++ ] ) ) { + curValue = getClass( elem ); + + // This expression is here for better compressibility (see addClass) + cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); + + if ( cur ) { + j = 0; + while ( ( clazz = classes[ j++ ] ) ) { + + // Remove *all* instances + while ( cur.indexOf( " " + clazz + " " ) > -1 ) { + cur = cur.replace( " " + clazz + " ", " " ); + } + } + + // Only assign if different to avoid unneeded rendering. + finalValue = stripAndCollapse( cur ); + if ( curValue !== finalValue ) { + elem.setAttribute( "class", finalValue ); + } + } + } + } + + return this; + }, + + toggleClass: function( value, stateVal ) { + var type = typeof value, + isValidValue = type === "string" || Array.isArray( value ); + + if ( typeof stateVal === "boolean" && isValidValue ) { + return stateVal ? this.addClass( value ) : this.removeClass( value ); + } + + if ( isFunction( value ) ) { + return this.each( function( i ) { + jQuery( this ).toggleClass( + value.call( this, i, getClass( this ), stateVal ), + stateVal + ); + } ); + } + + return this.each( function() { + var className, i, self, classNames; + + if ( isValidValue ) { + + // Toggle individual class names + i = 0; + self = jQuery( this ); + classNames = classesToArray( value ); + + while ( ( className = classNames[ i++ ] ) ) { + + // Check each className given, space separated list + if ( self.hasClass( className ) ) { + self.removeClass( className ); + } else { + self.addClass( className ); + } + } + + // Toggle whole class name + } else if ( value === undefined || type === "boolean" ) { + className = getClass( this ); + if ( className ) { + + // Store className if set + dataPriv.set( this, "__className__", className ); + } + + // If the element has a class name or if we're passed `false`, + // then remove the whole classname (if there was one, the above saved it). + // Otherwise bring back whatever was previously saved (if anything), + // falling back to the empty string if nothing was stored. + if ( this.setAttribute ) { + this.setAttribute( "class", + className || value === false ? + "" : + dataPriv.get( this, "__className__" ) || "" + ); + } + } + } ); + }, + + hasClass: function( selector ) { + var className, elem, + i = 0; + + className = " " + selector + " "; + while ( ( elem = this[ i++ ] ) ) { + if ( elem.nodeType === 1 && + ( " " + stripAndCollapse( getClass( elem ) ) + " " ).indexOf( className ) > -1 ) { + return true; + } + } + + return false; + } +} ); + + + + +var rreturn = /\r/g; + +jQuery.fn.extend( { + val: function( value ) { + var hooks, ret, valueIsFunction, + elem = this[ 0 ]; + + if ( !arguments.length ) { + if ( elem ) { + hooks = jQuery.valHooks[ elem.type ] || + jQuery.valHooks[ elem.nodeName.toLowerCase() ]; + + if ( hooks && + "get" in hooks && + ( ret = hooks.get( elem, "value" ) ) !== undefined + ) { + return ret; + } + + ret = elem.value; + + // Handle most common string cases + if ( typeof ret === "string" ) { + return ret.replace( rreturn, "" ); + } + + // Handle cases where value is null/undef or number + return ret == null ? "" : ret; + } + + return; + } + + valueIsFunction = isFunction( value ); + + return this.each( function( i ) { + var val; + + if ( this.nodeType !== 1 ) { + return; + } + + if ( valueIsFunction ) { + val = value.call( this, i, jQuery( this ).val() ); + } else { + val = value; + } + + // Treat null/undefined as ""; convert numbers to string + if ( val == null ) { + val = ""; + + } else if ( typeof val === "number" ) { + val += ""; + + } else if ( Array.isArray( val ) ) { + val = jQuery.map( val, function( value ) { + return value == null ? "" : value + ""; + } ); + } + + hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; + + // If set returns undefined, fall back to normal setting + if ( !hooks || !( "set" in hooks ) || hooks.set( this, val, "value" ) === undefined ) { + this.value = val; + } + } ); + } +} ); + +jQuery.extend( { + valHooks: { + option: { + get: function( elem ) { + + var val = jQuery.find.attr( elem, "value" ); + return val != null ? + val : + + // Support: IE <=10 - 11 only + // option.text throws exceptions (#14686, #14858) + // Strip and collapse whitespace + // https://html.spec.whatwg.org/#strip-and-collapse-whitespace + stripAndCollapse( jQuery.text( elem ) ); + } + }, + select: { + get: function( elem ) { + var value, option, i, + options = elem.options, + index = elem.selectedIndex, + one = elem.type === "select-one", + values = one ? null : [], + max = one ? index + 1 : options.length; + + if ( index < 0 ) { + i = max; + + } else { + i = one ? index : 0; + } + + // Loop through all the selected options + for ( ; i < max; i++ ) { + option = options[ i ]; + + // Support: IE <=9 only + // IE8-9 doesn't update selected after form reset (#2551) + if ( ( option.selected || i === index ) && + + // Don't return options that are disabled or in a disabled optgroup + !option.disabled && + ( !option.parentNode.disabled || + !nodeName( option.parentNode, "optgroup" ) ) ) { + + // Get the specific value for the option + value = jQuery( option ).val(); + + // We don't need an array for one selects + if ( one ) { + return value; + } + + // Multi-Selects return an array + values.push( value ); + } + } + + return values; + }, + + set: function( elem, value ) { + var optionSet, option, + options = elem.options, + values = jQuery.makeArray( value ), + i = options.length; + + while ( i-- ) { + option = options[ i ]; + + /* eslint-disable no-cond-assign */ + + if ( option.selected = + jQuery.inArray( jQuery.valHooks.option.get( option ), values ) > -1 + ) { + optionSet = true; + } + + /* eslint-enable no-cond-assign */ + } + + // Force browsers to behave consistently when non-matching value is set + if ( !optionSet ) { + elem.selectedIndex = -1; + } + return values; + } + } + } +} ); + +// Radios and checkboxes getter/setter +jQuery.each( [ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = { + set: function( elem, value ) { + if ( Array.isArray( value ) ) { + return ( elem.checked = jQuery.inArray( jQuery( elem ).val(), value ) > -1 ); + } + } + }; + if ( !support.checkOn ) { + jQuery.valHooks[ this ].get = function( elem ) { + return elem.getAttribute( "value" ) === null ? "on" : elem.value; + }; + } +} ); + + + + +// Return jQuery for attributes-only inclusion + + +support.focusin = "onfocusin" in window; + + +var rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, + stopPropagationCallback = function( e ) { + e.stopPropagation(); + }; + +jQuery.extend( jQuery.event, { + + trigger: function( event, data, elem, onlyHandlers ) { + + var i, cur, tmp, bubbleType, ontype, handle, special, lastElement, + eventPath = [ elem || document ], + type = hasOwn.call( event, "type" ) ? event.type : event, + namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split( "." ) : []; + + cur = lastElement = tmp = elem = elem || document; + + // Don't do events on text and comment nodes + if ( elem.nodeType === 3 || elem.nodeType === 8 ) { + return; + } + + // focus/blur morphs to focusin/out; ensure we're not firing them right now + if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { + return; + } + + if ( type.indexOf( "." ) > -1 ) { + + // Namespaced trigger; create a regexp to match event type in handle() + namespaces = type.split( "." ); + type = namespaces.shift(); + namespaces.sort(); + } + ontype = type.indexOf( ":" ) < 0 && "on" + type; + + // Caller can pass in a jQuery.Event object, Object, or just an event type string + event = event[ jQuery.expando ] ? + event : + new jQuery.Event( type, typeof event === "object" && event ); + + // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) + event.isTrigger = onlyHandlers ? 2 : 3; + event.namespace = namespaces.join( "." ); + event.rnamespace = event.namespace ? + new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ) : + null; + + // Clean up the event in case it is being reused + event.result = undefined; + if ( !event.target ) { + event.target = elem; + } + + // Clone any incoming data and prepend the event, creating the handler arg list + data = data == null ? + [ event ] : + jQuery.makeArray( data, [ event ] ); + + // Allow special events to draw outside the lines + special = jQuery.event.special[ type ] || {}; + if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { + return; + } + + // Determine event propagation path in advance, per W3C events spec (#9951) + // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) + if ( !onlyHandlers && !special.noBubble && !isWindow( elem ) ) { + + bubbleType = special.delegateType || type; + if ( !rfocusMorph.test( bubbleType + type ) ) { + cur = cur.parentNode; + } + for ( ; cur; cur = cur.parentNode ) { + eventPath.push( cur ); + tmp = cur; + } + + // Only add window if we got to document (e.g., not plain obj or detached DOM) + if ( tmp === ( elem.ownerDocument || document ) ) { + eventPath.push( tmp.defaultView || tmp.parentWindow || window ); + } + } + + // Fire handlers on the event path + i = 0; + while ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) { + lastElement = cur; + event.type = i > 1 ? + bubbleType : + special.bindType || type; + + // jQuery handler + handle = ( dataPriv.get( cur, "events" ) || {} )[ event.type ] && + dataPriv.get( cur, "handle" ); + if ( handle ) { + handle.apply( cur, data ); + } + + // Native handler + handle = ontype && cur[ ontype ]; + if ( handle && handle.apply && acceptData( cur ) ) { + event.result = handle.apply( cur, data ); + if ( event.result === false ) { + event.preventDefault(); + } + } + } + event.type = type; + + // If nobody prevented the default action, do it now + if ( !onlyHandlers && !event.isDefaultPrevented() ) { + + if ( ( !special._default || + special._default.apply( eventPath.pop(), data ) === false ) && + acceptData( elem ) ) { + + // Call a native DOM method on the target with the same name as the event. + // Don't do default actions on window, that's where global variables be (#6170) + if ( ontype && isFunction( elem[ type ] ) && !isWindow( elem ) ) { + + // Don't re-trigger an onFOO event when we call its FOO() method + tmp = elem[ ontype ]; + + if ( tmp ) { + elem[ ontype ] = null; + } + + // Prevent re-triggering of the same event, since we already bubbled it above + jQuery.event.triggered = type; + + if ( event.isPropagationStopped() ) { + lastElement.addEventListener( type, stopPropagationCallback ); + } + + elem[ type ](); + + if ( event.isPropagationStopped() ) { + lastElement.removeEventListener( type, stopPropagationCallback ); + } + + jQuery.event.triggered = undefined; + + if ( tmp ) { + elem[ ontype ] = tmp; + } + } + } + } + + return event.result; + }, + + // Piggyback on a donor event to simulate a different one + // Used only for `focus(in | out)` events + simulate: function( type, elem, event ) { + var e = jQuery.extend( + new jQuery.Event(), + event, + { + type: type, + isSimulated: true + } + ); + + jQuery.event.trigger( e, null, elem ); + } + +} ); + +jQuery.fn.extend( { + + trigger: function( type, data ) { + return this.each( function() { + jQuery.event.trigger( type, data, this ); + } ); + }, + triggerHandler: function( type, data ) { + var elem = this[ 0 ]; + if ( elem ) { + return jQuery.event.trigger( type, data, elem, true ); + } + } +} ); + + +// Support: Firefox <=44 +// Firefox doesn't have focus(in | out) events +// Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787 +// +// Support: Chrome <=48 - 49, Safari <=9.0 - 9.1 +// focus(in | out) events fire after focus & blur events, +// which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order +// Related ticket - https://bugs.chromium.org/p/chromium/issues/detail?id=449857 +if ( !support.focusin ) { + jQuery.each( { focus: "focusin", blur: "focusout" }, function( orig, fix ) { + + // Attach a single capturing handler on the document while someone wants focusin/focusout + var handler = function( event ) { + jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ) ); + }; + + jQuery.event.special[ fix ] = { + setup: function() { + var doc = this.ownerDocument || this, + attaches = dataPriv.access( doc, fix ); + + if ( !attaches ) { + doc.addEventListener( orig, handler, true ); + } + dataPriv.access( doc, fix, ( attaches || 0 ) + 1 ); + }, + teardown: function() { + var doc = this.ownerDocument || this, + attaches = dataPriv.access( doc, fix ) - 1; + + if ( !attaches ) { + doc.removeEventListener( orig, handler, true ); + dataPriv.remove( doc, fix ); + + } else { + dataPriv.access( doc, fix, attaches ); + } + } + }; + } ); +} +var location = window.location; + +var nonce = Date.now(); + +var rquery = ( /\?/ ); + + + +// Cross-browser xml parsing +jQuery.parseXML = function( data ) { + var xml; + if ( !data || typeof data !== "string" ) { + return null; + } + + // Support: IE 9 - 11 only + // IE throws on parseFromString with invalid input. + try { + xml = ( new window.DOMParser() ).parseFromString( data, "text/xml" ); + } catch ( e ) { + xml = undefined; + } + + if ( !xml || xml.getElementsByTagName( "parsererror" ).length ) { + jQuery.error( "Invalid XML: " + data ); + } + return xml; +}; + + +var + rbracket = /\[\]$/, + rCRLF = /\r?\n/g, + rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i, + rsubmittable = /^(?:input|select|textarea|keygen)/i; + +function buildParams( prefix, obj, traditional, add ) { + var name; + + if ( Array.isArray( obj ) ) { + + // Serialize array item. + jQuery.each( obj, function( i, v ) { + if ( traditional || rbracket.test( prefix ) ) { + + // Treat each array item as a scalar. + add( prefix, v ); + + } else { + + // Item is non-scalar (array or object), encode its numeric index. + buildParams( + prefix + "[" + ( typeof v === "object" && v != null ? i : "" ) + "]", + v, + traditional, + add + ); + } + } ); + + } else if ( !traditional && toType( obj ) === "object" ) { + + // Serialize object item. + for ( name in obj ) { + buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add ); + } + + } else { + + // Serialize scalar item. + add( prefix, obj ); + } +} + +// Serialize an array of form elements or a set of +// key/values into a query string +jQuery.param = function( a, traditional ) { + var prefix, + s = [], + add = function( key, valueOrFunction ) { + + // If value is a function, invoke it and use its return value + var value = isFunction( valueOrFunction ) ? + valueOrFunction() : + valueOrFunction; + + s[ s.length ] = encodeURIComponent( key ) + "=" + + encodeURIComponent( value == null ? "" : value ); + }; + + if ( a == null ) { + return ""; + } + + // If an array was passed in, assume that it is an array of form elements. + if ( Array.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) { + + // Serialize the form elements + jQuery.each( a, function() { + add( this.name, this.value ); + } ); + + } else { + + // If traditional, encode the "old" way (the way 1.3.2 or older + // did it), otherwise encode params recursively. + for ( prefix in a ) { + buildParams( prefix, a[ prefix ], traditional, add ); + } + } + + // Return the resulting serialization + return s.join( "&" ); +}; + +jQuery.fn.extend( { + serialize: function() { + return jQuery.param( this.serializeArray() ); + }, + serializeArray: function() { + return this.map( function() { + + // Can add propHook for "elements" to filter or add form elements + var elements = jQuery.prop( this, "elements" ); + return elements ? jQuery.makeArray( elements ) : this; + } ) + .filter( function() { + var type = this.type; + + // Use .is( ":disabled" ) so that fieldset[disabled] works + return this.name && !jQuery( this ).is( ":disabled" ) && + rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) && + ( this.checked || !rcheckableType.test( type ) ); + } ) + .map( function( i, elem ) { + var val = jQuery( this ).val(); + + if ( val == null ) { + return null; + } + + if ( Array.isArray( val ) ) { + return jQuery.map( val, function( val ) { + return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + } ); + } + + return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + } ).get(); + } +} ); + + +var + r20 = /%20/g, + rhash = /#.*$/, + rantiCache = /([?&])_=[^&]*/, + rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg, + + // #7653, #8125, #8152: local protocol detection + rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/, + rnoContent = /^(?:GET|HEAD)$/, + rprotocol = /^\/\//, + + /* Prefilters + * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example) + * 2) These are called: + * - BEFORE asking for a transport + * - AFTER param serialization (s.data is a string if s.processData is true) + * 3) key is the dataType + * 4) the catchall symbol "*" can be used + * 5) execution will start with transport dataType and THEN continue down to "*" if needed + */ + prefilters = {}, + + /* Transports bindings + * 1) key is the dataType + * 2) the catchall symbol "*" can be used + * 3) selection will start with transport dataType and THEN go to "*" if needed + */ + transports = {}, + + // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression + allTypes = "*/".concat( "*" ), + + // Anchor tag for parsing the document origin + originAnchor = document.createElement( "a" ); + originAnchor.href = location.href; + +// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport +function addToPrefiltersOrTransports( structure ) { + + // dataTypeExpression is optional and defaults to "*" + return function( dataTypeExpression, func ) { + + if ( typeof dataTypeExpression !== "string" ) { + func = dataTypeExpression; + dataTypeExpression = "*"; + } + + var dataType, + i = 0, + dataTypes = dataTypeExpression.toLowerCase().match( rnothtmlwhite ) || []; + + if ( isFunction( func ) ) { + + // For each dataType in the dataTypeExpression + while ( ( dataType = dataTypes[ i++ ] ) ) { + + // Prepend if requested + if ( dataType[ 0 ] === "+" ) { + dataType = dataType.slice( 1 ) || "*"; + ( structure[ dataType ] = structure[ dataType ] || [] ).unshift( func ); + + // Otherwise append + } else { + ( structure[ dataType ] = structure[ dataType ] || [] ).push( func ); + } + } + } + }; +} + +// Base inspection function for prefilters and transports +function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) { + + var inspected = {}, + seekingTransport = ( structure === transports ); + + function inspect( dataType ) { + var selected; + inspected[ dataType ] = true; + jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) { + var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR ); + if ( typeof dataTypeOrTransport === "string" && + !seekingTransport && !inspected[ dataTypeOrTransport ] ) { + + options.dataTypes.unshift( dataTypeOrTransport ); + inspect( dataTypeOrTransport ); + return false; + } else if ( seekingTransport ) { + return !( selected = dataTypeOrTransport ); + } + } ); + return selected; + } + + return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" ); +} + +// A special extend for ajax options +// that takes "flat" options (not to be deep extended) +// Fixes #9887 +function ajaxExtend( target, src ) { + var key, deep, + flatOptions = jQuery.ajaxSettings.flatOptions || {}; + + for ( key in src ) { + if ( src[ key ] !== undefined ) { + ( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ]; + } + } + if ( deep ) { + jQuery.extend( true, target, deep ); + } + + return target; +} + +/* Handles responses to an ajax request: + * - finds the right dataType (mediates between content-type and expected dataType) + * - returns the corresponding response + */ +function ajaxHandleResponses( s, jqXHR, responses ) { + + var ct, type, finalDataType, firstDataType, + contents = s.contents, + dataTypes = s.dataTypes; + + // Remove auto dataType and get content-type in the process + while ( dataTypes[ 0 ] === "*" ) { + dataTypes.shift(); + if ( ct === undefined ) { + ct = s.mimeType || jqXHR.getResponseHeader( "Content-Type" ); + } + } + + // Check if we're dealing with a known content-type + if ( ct ) { + for ( type in contents ) { + if ( contents[ type ] && contents[ type ].test( ct ) ) { + dataTypes.unshift( type ); + break; + } + } + } + + // Check to see if we have a response for the expected dataType + if ( dataTypes[ 0 ] in responses ) { + finalDataType = dataTypes[ 0 ]; + } else { + + // Try convertible dataTypes + for ( type in responses ) { + if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[ 0 ] ] ) { + finalDataType = type; + break; + } + if ( !firstDataType ) { + firstDataType = type; + } + } + + // Or just use first one + finalDataType = finalDataType || firstDataType; + } + + // If we found a dataType + // We add the dataType to the list if needed + // and return the corresponding response + if ( finalDataType ) { + if ( finalDataType !== dataTypes[ 0 ] ) { + dataTypes.unshift( finalDataType ); + } + return responses[ finalDataType ]; + } +} + +/* Chain conversions given the request and the original response + * Also sets the responseXXX fields on the jqXHR instance + */ +function ajaxConvert( s, response, jqXHR, isSuccess ) { + var conv2, current, conv, tmp, prev, + converters = {}, + + // Work with a copy of dataTypes in case we need to modify it for conversion + dataTypes = s.dataTypes.slice(); + + // Create converters map with lowercased keys + if ( dataTypes[ 1 ] ) { + for ( conv in s.converters ) { + converters[ conv.toLowerCase() ] = s.converters[ conv ]; + } + } + + current = dataTypes.shift(); + + // Convert to each sequential dataType + while ( current ) { + + if ( s.responseFields[ current ] ) { + jqXHR[ s.responseFields[ current ] ] = response; + } + + // Apply the dataFilter if provided + if ( !prev && isSuccess && s.dataFilter ) { + response = s.dataFilter( response, s.dataType ); + } + + prev = current; + current = dataTypes.shift(); + + if ( current ) { + + // There's only work to do if current dataType is non-auto + if ( current === "*" ) { + + current = prev; + + // Convert response if prev dataType is non-auto and differs from current + } else if ( prev !== "*" && prev !== current ) { + + // Seek a direct converter + conv = converters[ prev + " " + current ] || converters[ "* " + current ]; + + // If none found, seek a pair + if ( !conv ) { + for ( conv2 in converters ) { + + // If conv2 outputs current + tmp = conv2.split( " " ); + if ( tmp[ 1 ] === current ) { + + // If prev can be converted to accepted input + conv = converters[ prev + " " + tmp[ 0 ] ] || + converters[ "* " + tmp[ 0 ] ]; + if ( conv ) { + + // Condense equivalence converters + if ( conv === true ) { + conv = converters[ conv2 ]; + + // Otherwise, insert the intermediate dataType + } else if ( converters[ conv2 ] !== true ) { + current = tmp[ 0 ]; + dataTypes.unshift( tmp[ 1 ] ); + } + break; + } + } + } + } + + // Apply converter (if not an equivalence) + if ( conv !== true ) { + + // Unless errors are allowed to bubble, catch and return them + if ( conv && s.throws ) { + response = conv( response ); + } else { + try { + response = conv( response ); + } catch ( e ) { + return { + state: "parsererror", + error: conv ? e : "No conversion from " + prev + " to " + current + }; + } + } + } + } + } + } + + return { state: "success", data: response }; +} + +jQuery.extend( { + + // Counter for holding the number of active queries + active: 0, + + // Last-Modified header cache for next request + lastModified: {}, + etag: {}, + + ajaxSettings: { + url: location.href, + type: "GET", + isLocal: rlocalProtocol.test( location.protocol ), + global: true, + processData: true, + async: true, + contentType: "application/x-www-form-urlencoded; charset=UTF-8", + + /* + timeout: 0, + data: null, + dataType: null, + username: null, + password: null, + cache: null, + throws: false, + traditional: false, + headers: {}, + */ + + accepts: { + "*": allTypes, + text: "text/plain", + html: "text/html", + xml: "application/xml, text/xml", + json: "application/json, text/javascript" + }, + + contents: { + xml: /\bxml\b/, + html: /\bhtml/, + json: /\bjson\b/ + }, + + responseFields: { + xml: "responseXML", + text: "responseText", + json: "responseJSON" + }, + + // Data converters + // Keys separate source (or catchall "*") and destination types with a single space + converters: { + + // Convert anything to text + "* text": String, + + // Text to html (true = no transformation) + "text html": true, + + // Evaluate text as a json expression + "text json": JSON.parse, + + // Parse text as xml + "text xml": jQuery.parseXML + }, + + // For options that shouldn't be deep extended: + // you can add your own custom options here if + // and when you create one that shouldn't be + // deep extended (see ajaxExtend) + flatOptions: { + url: true, + context: true + } + }, + + // Creates a full fledged settings object into target + // with both ajaxSettings and settings fields. + // If target is omitted, writes into ajaxSettings. + ajaxSetup: function( target, settings ) { + return settings ? + + // Building a settings object + ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) : + + // Extending ajaxSettings + ajaxExtend( jQuery.ajaxSettings, target ); + }, + + ajaxPrefilter: addToPrefiltersOrTransports( prefilters ), + ajaxTransport: addToPrefiltersOrTransports( transports ), + + // Main method + ajax: function( url, options ) { + + // If url is an object, simulate pre-1.5 signature + if ( typeof url === "object" ) { + options = url; + url = undefined; + } + + // Force options to be an object + options = options || {}; + + var transport, + + // URL without anti-cache param + cacheURL, + + // Response headers + responseHeadersString, + responseHeaders, + + // timeout handle + timeoutTimer, + + // Url cleanup var + urlAnchor, + + // Request state (becomes false upon send and true upon completion) + completed, + + // To know if global events are to be dispatched + fireGlobals, + + // Loop variable + i, + + // uncached part of the url + uncached, + + // Create the final options object + s = jQuery.ajaxSetup( {}, options ), + + // Callbacks context + callbackContext = s.context || s, + + // Context for global events is callbackContext if it is a DOM node or jQuery collection + globalEventContext = s.context && + ( callbackContext.nodeType || callbackContext.jquery ) ? + jQuery( callbackContext ) : + jQuery.event, + + // Deferreds + deferred = jQuery.Deferred(), + completeDeferred = jQuery.Callbacks( "once memory" ), + + // Status-dependent callbacks + statusCode = s.statusCode || {}, + + // Headers (they are sent all at once) + requestHeaders = {}, + requestHeadersNames = {}, + + // Default abort message + strAbort = "canceled", + + // Fake xhr + jqXHR = { + readyState: 0, + + // Builds headers hashtable if needed + getResponseHeader: function( key ) { + var match; + if ( completed ) { + if ( !responseHeaders ) { + responseHeaders = {}; + while ( ( match = rheaders.exec( responseHeadersString ) ) ) { + responseHeaders[ match[ 1 ].toLowerCase() + " " ] = + ( responseHeaders[ match[ 1 ].toLowerCase() + " " ] || [] ) + .concat( match[ 2 ] ); + } + } + match = responseHeaders[ key.toLowerCase() + " " ]; + } + return match == null ? null : match.join( ", " ); + }, + + // Raw string + getAllResponseHeaders: function() { + return completed ? responseHeadersString : null; + }, + + // Caches the header + setRequestHeader: function( name, value ) { + if ( completed == null ) { + name = requestHeadersNames[ name.toLowerCase() ] = + requestHeadersNames[ name.toLowerCase() ] || name; + requestHeaders[ name ] = value; + } + return this; + }, + + // Overrides response content-type header + overrideMimeType: function( type ) { + if ( completed == null ) { + s.mimeType = type; + } + return this; + }, + + // Status-dependent callbacks + statusCode: function( map ) { + var code; + if ( map ) { + if ( completed ) { + + // Execute the appropriate callbacks + jqXHR.always( map[ jqXHR.status ] ); + } else { + + // Lazy-add the new callbacks in a way that preserves old ones + for ( code in map ) { + statusCode[ code ] = [ statusCode[ code ], map[ code ] ]; + } + } + } + return this; + }, + + // Cancel the request + abort: function( statusText ) { + var finalText = statusText || strAbort; + if ( transport ) { + transport.abort( finalText ); + } + done( 0, finalText ); + return this; + } + }; + + // Attach deferreds + deferred.promise( jqXHR ); + + // Add protocol if not provided (prefilters might expect it) + // Handle falsy url in the settings object (#10093: consistency with old signature) + // We also use the url parameter if available + s.url = ( ( url || s.url || location.href ) + "" ) + .replace( rprotocol, location.protocol + "//" ); + + // Alias method option to type as per ticket #12004 + s.type = options.method || options.type || s.method || s.type; + + // Extract dataTypes list + s.dataTypes = ( s.dataType || "*" ).toLowerCase().match( rnothtmlwhite ) || [ "" ]; + + // A cross-domain request is in order when the origin doesn't match the current origin. + if ( s.crossDomain == null ) { + urlAnchor = document.createElement( "a" ); + + // Support: IE <=8 - 11, Edge 12 - 15 + // IE throws exception on accessing the href property if url is malformed, + // e.g. http://example.com:80x/ + try { + urlAnchor.href = s.url; + + // Support: IE <=8 - 11 only + // Anchor's host property isn't correctly set when s.url is relative + urlAnchor.href = urlAnchor.href; + s.crossDomain = originAnchor.protocol + "//" + originAnchor.host !== + urlAnchor.protocol + "//" + urlAnchor.host; + } catch ( e ) { + + // If there is an error parsing the URL, assume it is crossDomain, + // it can be rejected by the transport if it is invalid + s.crossDomain = true; + } + } + + // Convert data if not already a string + if ( s.data && s.processData && typeof s.data !== "string" ) { + s.data = jQuery.param( s.data, s.traditional ); + } + + // Apply prefilters + inspectPrefiltersOrTransports( prefilters, s, options, jqXHR ); + + // If request was aborted inside a prefilter, stop there + if ( completed ) { + return jqXHR; + } + + // We can fire global events as of now if asked to + // Don't fire events if jQuery.event is undefined in an AMD-usage scenario (#15118) + fireGlobals = jQuery.event && s.global; + + // Watch for a new set of requests + if ( fireGlobals && jQuery.active++ === 0 ) { + jQuery.event.trigger( "ajaxStart" ); + } + + // Uppercase the type + s.type = s.type.toUpperCase(); + + // Determine if request has content + s.hasContent = !rnoContent.test( s.type ); + + // Save the URL in case we're toying with the If-Modified-Since + // and/or If-None-Match header later on + // Remove hash to simplify url manipulation + cacheURL = s.url.replace( rhash, "" ); + + // More options handling for requests with no content + if ( !s.hasContent ) { + + // Remember the hash so we can put it back + uncached = s.url.slice( cacheURL.length ); + + // If data is available and should be processed, append data to url + if ( s.data && ( s.processData || typeof s.data === "string" ) ) { + cacheURL += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data; + + // #9682: remove data so that it's not used in an eventual retry + delete s.data; + } + + // Add or update anti-cache param if needed + if ( s.cache === false ) { + cacheURL = cacheURL.replace( rantiCache, "$1" ); + uncached = ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ( nonce++ ) + uncached; + } + + // Put hash and anti-cache on the URL that will be requested (gh-1732) + s.url = cacheURL + uncached; + + // Change '%20' to '+' if this is encoded form body content (gh-2658) + } else if ( s.data && s.processData && + ( s.contentType || "" ).indexOf( "application/x-www-form-urlencoded" ) === 0 ) { + s.data = s.data.replace( r20, "+" ); + } + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + if ( jQuery.lastModified[ cacheURL ] ) { + jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] ); + } + if ( jQuery.etag[ cacheURL ] ) { + jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] ); + } + } + + // Set the correct header, if data is being sent + if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) { + jqXHR.setRequestHeader( "Content-Type", s.contentType ); + } + + // Set the Accepts header for the server, depending on the dataType + jqXHR.setRequestHeader( + "Accept", + s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[ 0 ] ] ? + s.accepts[ s.dataTypes[ 0 ] ] + + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) : + s.accepts[ "*" ] + ); + + // Check for headers option + for ( i in s.headers ) { + jqXHR.setRequestHeader( i, s.headers[ i ] ); + } + + // Allow custom headers/mimetypes and early abort + if ( s.beforeSend && + ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || completed ) ) { + + // Abort if not done already and return + return jqXHR.abort(); + } + + // Aborting is no longer a cancellation + strAbort = "abort"; + + // Install callbacks on deferreds + completeDeferred.add( s.complete ); + jqXHR.done( s.success ); + jqXHR.fail( s.error ); + + // Get transport + transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR ); + + // If no transport, we auto-abort + if ( !transport ) { + done( -1, "No Transport" ); + } else { + jqXHR.readyState = 1; + + // Send global event + if ( fireGlobals ) { + globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] ); + } + + // If request was aborted inside ajaxSend, stop there + if ( completed ) { + return jqXHR; + } + + // Timeout + if ( s.async && s.timeout > 0 ) { + timeoutTimer = window.setTimeout( function() { + jqXHR.abort( "timeout" ); + }, s.timeout ); + } + + try { + completed = false; + transport.send( requestHeaders, done ); + } catch ( e ) { + + // Rethrow post-completion exceptions + if ( completed ) { + throw e; + } + + // Propagate others as results + done( -1, e ); + } + } + + // Callback for when everything is done + function done( status, nativeStatusText, responses, headers ) { + var isSuccess, success, error, response, modified, + statusText = nativeStatusText; + + // Ignore repeat invocations + if ( completed ) { + return; + } + + completed = true; + + // Clear timeout if it exists + if ( timeoutTimer ) { + window.clearTimeout( timeoutTimer ); + } + + // Dereference transport for early garbage collection + // (no matter how long the jqXHR object will be used) + transport = undefined; + + // Cache response headers + responseHeadersString = headers || ""; + + // Set readyState + jqXHR.readyState = status > 0 ? 4 : 0; + + // Determine if successful + isSuccess = status >= 200 && status < 300 || status === 304; + + // Get response data + if ( responses ) { + response = ajaxHandleResponses( s, jqXHR, responses ); + } + + // Convert no matter what (that way responseXXX fields are always set) + response = ajaxConvert( s, response, jqXHR, isSuccess ); + + // If successful, handle type chaining + if ( isSuccess ) { + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + modified = jqXHR.getResponseHeader( "Last-Modified" ); + if ( modified ) { + jQuery.lastModified[ cacheURL ] = modified; + } + modified = jqXHR.getResponseHeader( "etag" ); + if ( modified ) { + jQuery.etag[ cacheURL ] = modified; + } + } + + // if no content + if ( status === 204 || s.type === "HEAD" ) { + statusText = "nocontent"; + + // if not modified + } else if ( status === 304 ) { + statusText = "notmodified"; + + // If we have data, let's convert it + } else { + statusText = response.state; + success = response.data; + error = response.error; + isSuccess = !error; + } + } else { + + // Extract error from statusText and normalize for non-aborts + error = statusText; + if ( status || !statusText ) { + statusText = "error"; + if ( status < 0 ) { + status = 0; + } + } + } + + // Set data for the fake xhr object + jqXHR.status = status; + jqXHR.statusText = ( nativeStatusText || statusText ) + ""; + + // Success/Error + if ( isSuccess ) { + deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] ); + } else { + deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] ); + } + + // Status-dependent callbacks + jqXHR.statusCode( statusCode ); + statusCode = undefined; + + if ( fireGlobals ) { + globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError", + [ jqXHR, s, isSuccess ? success : error ] ); + } + + // Complete + completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] ); + + if ( fireGlobals ) { + globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] ); + + // Handle the global AJAX counter + if ( !( --jQuery.active ) ) { + jQuery.event.trigger( "ajaxStop" ); + } + } + } + + return jqXHR; + }, + + getJSON: function( url, data, callback ) { + return jQuery.get( url, data, callback, "json" ); + }, + + getScript: function( url, callback ) { + return jQuery.get( url, undefined, callback, "script" ); + } +} ); + +jQuery.each( [ "get", "post" ], function( i, method ) { + jQuery[ method ] = function( url, data, callback, type ) { + + // Shift arguments if data argument was omitted + if ( isFunction( data ) ) { + type = type || callback; + callback = data; + data = undefined; + } + + // The url can be an options object (which then must have .url) + return jQuery.ajax( jQuery.extend( { + url: url, + type: method, + dataType: type, + data: data, + success: callback + }, jQuery.isPlainObject( url ) && url ) ); + }; +} ); + + +jQuery._evalUrl = function( url, options ) { + return jQuery.ajax( { + url: url, + + // Make this explicit, since user can override this through ajaxSetup (#11264) + type: "GET", + dataType: "script", + cache: true, + async: false, + global: false, + + // Only evaluate the response if it is successful (gh-4126) + // dataFilter is not invoked for failure responses, so using it instead + // of the default converter is kludgy but it works. + converters: { + "text script": function() {} + }, + dataFilter: function( response ) { + jQuery.globalEval( response, options ); + } + } ); +}; + + +jQuery.fn.extend( { + wrapAll: function( html ) { + var wrap; + + if ( this[ 0 ] ) { + if ( isFunction( html ) ) { + html = html.call( this[ 0 ] ); + } + + // The elements to wrap the target around + wrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true ); + + if ( this[ 0 ].parentNode ) { + wrap.insertBefore( this[ 0 ] ); + } + + wrap.map( function() { + var elem = this; + + while ( elem.firstElementChild ) { + elem = elem.firstElementChild; + } + + return elem; + } ).append( this ); + } + + return this; + }, + + wrapInner: function( html ) { + if ( isFunction( html ) ) { + return this.each( function( i ) { + jQuery( this ).wrapInner( html.call( this, i ) ); + } ); + } + + return this.each( function() { + var self = jQuery( this ), + contents = self.contents(); + + if ( contents.length ) { + contents.wrapAll( html ); + + } else { + self.append( html ); + } + } ); + }, + + wrap: function( html ) { + var htmlIsFunction = isFunction( html ); + + return this.each( function( i ) { + jQuery( this ).wrapAll( htmlIsFunction ? html.call( this, i ) : html ); + } ); + }, + + unwrap: function( selector ) { + this.parent( selector ).not( "body" ).each( function() { + jQuery( this ).replaceWith( this.childNodes ); + } ); + return this; + } +} ); + + +jQuery.expr.pseudos.hidden = function( elem ) { + return !jQuery.expr.pseudos.visible( elem ); +}; +jQuery.expr.pseudos.visible = function( elem ) { + return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length ); +}; + + + + +jQuery.ajaxSettings.xhr = function() { + try { + return new window.XMLHttpRequest(); + } catch ( e ) {} +}; + +var xhrSuccessStatus = { + + // File protocol always yields status code 0, assume 200 + 0: 200, + + // Support: IE <=9 only + // #1450: sometimes IE returns 1223 when it should be 204 + 1223: 204 + }, + xhrSupported = jQuery.ajaxSettings.xhr(); + +support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported ); +support.ajax = xhrSupported = !!xhrSupported; + +jQuery.ajaxTransport( function( options ) { + var callback, errorCallback; + + // Cross domain only allowed if supported through XMLHttpRequest + if ( support.cors || xhrSupported && !options.crossDomain ) { + return { + send: function( headers, complete ) { + var i, + xhr = options.xhr(); + + xhr.open( + options.type, + options.url, + options.async, + options.username, + options.password + ); + + // Apply custom fields if provided + if ( options.xhrFields ) { + for ( i in options.xhrFields ) { + xhr[ i ] = options.xhrFields[ i ]; + } + } + + // Override mime type if needed + if ( options.mimeType && xhr.overrideMimeType ) { + xhr.overrideMimeType( options.mimeType ); + } + + // X-Requested-With header + // For cross-domain requests, seeing as conditions for a preflight are + // akin to a jigsaw puzzle, we simply never set it to be sure. + // (it can always be set on a per-request basis or even using ajaxSetup) + // For same-domain requests, won't change header if already provided. + if ( !options.crossDomain && !headers[ "X-Requested-With" ] ) { + headers[ "X-Requested-With" ] = "XMLHttpRequest"; + } + + // Set headers + for ( i in headers ) { + xhr.setRequestHeader( i, headers[ i ] ); + } + + // Callback + callback = function( type ) { + return function() { + if ( callback ) { + callback = errorCallback = xhr.onload = + xhr.onerror = xhr.onabort = xhr.ontimeout = + xhr.onreadystatechange = null; + + if ( type === "abort" ) { + xhr.abort(); + } else if ( type === "error" ) { + + // Support: IE <=9 only + // On a manual native abort, IE9 throws + // errors on any property access that is not readyState + if ( typeof xhr.status !== "number" ) { + complete( 0, "error" ); + } else { + complete( + + // File: protocol always yields status 0; see #8605, #14207 + xhr.status, + xhr.statusText + ); + } + } else { + complete( + xhrSuccessStatus[ xhr.status ] || xhr.status, + xhr.statusText, + + // Support: IE <=9 only + // IE9 has no XHR2 but throws on binary (trac-11426) + // For XHR2 non-text, let the caller handle it (gh-2498) + ( xhr.responseType || "text" ) !== "text" || + typeof xhr.responseText !== "string" ? + { binary: xhr.response } : + { text: xhr.responseText }, + xhr.getAllResponseHeaders() + ); + } + } + }; + }; + + // Listen to events + xhr.onload = callback(); + errorCallback = xhr.onerror = xhr.ontimeout = callback( "error" ); + + // Support: IE 9 only + // Use onreadystatechange to replace onabort + // to handle uncaught aborts + if ( xhr.onabort !== undefined ) { + xhr.onabort = errorCallback; + } else { + xhr.onreadystatechange = function() { + + // Check readyState before timeout as it changes + if ( xhr.readyState === 4 ) { + + // Allow onerror to be called first, + // but that will not handle a native abort + // Also, save errorCallback to a variable + // as xhr.onerror cannot be accessed + window.setTimeout( function() { + if ( callback ) { + errorCallback(); + } + } ); + } + }; + } + + // Create the abort callback + callback = callback( "abort" ); + + try { + + // Do send the request (this may raise an exception) + xhr.send( options.hasContent && options.data || null ); + } catch ( e ) { + + // #14683: Only rethrow if this hasn't been notified as an error yet + if ( callback ) { + throw e; + } + } + }, + + abort: function() { + if ( callback ) { + callback(); + } + } + }; + } +} ); + + + + +// Prevent auto-execution of scripts when no explicit dataType was provided (See gh-2432) +jQuery.ajaxPrefilter( function( s ) { + if ( s.crossDomain ) { + s.contents.script = false; + } +} ); + +// Install script dataType +jQuery.ajaxSetup( { + accepts: { + script: "text/javascript, application/javascript, " + + "application/ecmascript, application/x-ecmascript" + }, + contents: { + script: /\b(?:java|ecma)script\b/ + }, + converters: { + "text script": function( text ) { + jQuery.globalEval( text ); + return text; + } + } +} ); + +// Handle cache's special case and crossDomain +jQuery.ajaxPrefilter( "script", function( s ) { + if ( s.cache === undefined ) { + s.cache = false; + } + if ( s.crossDomain ) { + s.type = "GET"; + } +} ); + +// Bind script tag hack transport +jQuery.ajaxTransport( "script", function( s ) { + + // This transport only deals with cross domain or forced-by-attrs requests + if ( s.crossDomain || s.scriptAttrs ) { + var script, callback; + return { + send: function( _, complete ) { + script = jQuery( " + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +
+

API documentation

+

This section contains auto-generated API documentation for AppTools.

+ +
+ + +
+
+
+
+
+ +

Previous topic

+

File I/O

+

Next topic

+

apptools

+

This Page

+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/api/apptools.html b/5.0/api/apptools.html new file mode 100644 index 000000000..1bf597fd6 --- /dev/null +++ b/5.0/api/apptools.html @@ -0,0 +1,367 @@ + + + + + + + apptools package — Apptools Documentation + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +
+

apptools package

+
+

Subpackages

+
+ +
+
+
+

Module contents

+
+
+ + +
+
+
+
+
+ +

Table of Contents

+ + +

Previous topic

+

apptools

+

Next topic

+

apptools.io package

+

This Page

+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/api/apptools.io.h5.html b/5.0/api/apptools.io.h5.html new file mode 100644 index 000000000..4a6f98d18 --- /dev/null +++ b/5.0/api/apptools.io.h5.html @@ -0,0 +1,705 @@ + + + + + + + apptools.io.h5 package — Apptools Documentation + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+ +
+ + +
+
+ +
+
+ +
+

apptools.io.h5 package

+
+

Submodules

+
+
+

apptools.io.h5.dict_node module

+
+
+apptools.io.h5.dict_node.ARRAY_PROXY_KEY = '__array__'
+

The key name which identifies array objects in the JSON dict.

+
+ +
+
+class apptools.io.h5.dict_node.H5DictNode(h5_group, auto_flush=True)[source]
+

Bases: object

+

Dictionary-like node interface.

+

Data for the dict is stored as a JSON file in a PyTables FileNode. This +allows easy storage of Python objects, such as dictionaries and lists of +different data types.

+

Note that this is implemented using a group-node assuming that arrays are +valid inputs and will be stored as H5 array nodes.

+
+
Parameters
+
    +
  • h5_group (H5Group instance) – Group node which will be used as a dictionary store.

  • +
  • auto_flush (bool) – If True, write data to disk whenever the dict data is altered. +Otherwise, call flush() explicitly to write data to disk.

  • +
+
+
+
+
+classmethod add_to_h5file(h5, node_path, data=None, **kwargs)[source]
+

Add dict node to an H5 file at the specified path.

+
+
Parameters
+
    +
  • h5 (H5File) – The H5 file where the dictionary data will be stored.

  • +
  • node_path (str) – Path to node where data is stored (e.g. ‘/path/to/my_dict’)

  • +
  • data (dict) – Data for initialization, if desired.

  • +
+
+
+
+ +
+
+property data
+
+ +
+
+flush()[source]
+

Write buffered data to disk.

+
+ +
+
+classmethod is_dict_node(pytables_node)[source]
+

Return True if PyTables node looks like an H5DictNode.

+

NOTE: That this returns False if the node is an H5DictNode instance, +since the input node should be a normal PyTables Group node.

+
+ +
+
+keys()[source]
+
+ +
+ +
+
+

apptools.io.h5.file module

+
+
+class apptools.io.h5.file.H5Attrs(node_attrs)[source]
+

Bases: collections.abc.MutableMapping

+

An attributes dictionary for an h5 node.

+

This intercepts __setitem__ so that python sequences can be converted to +numpy arrays. This helps preserve the readability of our HDF5 files by +other (non-python) programs.

+
+
+get(k[, d]) → D[k] if k in D, else d. d defaults to None.[source]
+
+ +
+
+items() → a set-like object providing a view on D's items[source]
+
+ +
+
+keys() → a set-like object providing a view on D's keys[source]
+
+ +
+
+values() → an object providing a view on D's values[source]
+
+ +
+ +
+
+class apptools.io.h5.file.H5File(filename, mode='r+', delete_existing=False, auto_groups=True, auto_open=True, h5filters=None)[source]
+

Bases: collections.abc.Mapping

+

File object for HDF5 files.

+

This class wraps PyTables to provide a cleaner, but only implements an +interface for accessing arrays.

+
+
Parameters
+
    +
  • filename (str or a tables.File instance) – Filename for an HDF5 file, or a PyTables File object.

  • +
  • mode (str) –

    Mode to open the file:

    +
    +

    ’r’ : Read-only +‘w’ : Write; create new file (an existing file would be deleted). +‘a’ : Read and write to file; create if not existing +‘r+’: Read and write to file; must already exist

    +
    +

  • +
  • delete_existing (bool) – If True, an existing node will be deleted when a create_* method is +called. Otherwise, a ValueError will be raise.

  • +
  • auto_groups (bool) – If True, create_array will automatically create parent groups.

  • +
  • auto_open (bool) – If True, open the file automatically on initialization. Otherwise, +you can call H5File.open() explicitly after initialization.

  • +
  • chunked (bool) – If True, the default behavior of create_array will be a chunked +array (see PyTables create_carray).

  • +
+
+
+
+
+close()[source]
+
+ +
+
+create_array(node_path, array_or_shape, dtype=None, chunked=False, extendable=False, **kwargs)[source]
+

Create node to store an array.

+
+
Parameters
+
    +
  • node_path (str) – PyTable node path; e.g. ‘/path/to/node’.

  • +
  • array_or_shape (array or shape tuple) – Array or shape tuple for an array. If given a shape tuple, the +dtype parameter must also specified.

  • +
  • dtype (str or numpy.dtype) – Data type of array. Only necessary if array_or_shape is a shape.

  • +
  • chunked (bool) – Controls whether the array is chunked.

  • +
  • extendable ({None | bool}) – Controls whether the array is extendable.

  • +
  • kwargs (key/value pairs) – Keyword args passed to PyTables File.create_(c|e)array.

  • +
+
+
+
+ +
+
+create_dict(node_path, data=None, **kwargs)[source]
+

Create dict node at the specified path.

+
+
Parameters
+
    +
  • node_path (str) – Path to node where data is stored (e.g. ‘/path/to/my_dict’)

  • +
  • data (dict) – Data for initialization, if desired.

  • +
+
+
+
+ +
+
+create_group(group_path, **kwargs)[source]
+

Create group.

+
+
Parameters
+
    +
  • group_path (str) – PyTable group path; e.g. ‘/path/to/group’.

  • +
  • kwargs (key/value pairs) – Keyword args passed to PyTables File.create_group.

  • +
+
+
+
+ +
+
+create_table(node_path, description, **kwargs)[source]
+

Create table node at the specified path.

+
+
Parameters
+
    +
  • node_path (str) – Path to node where data is stored (e.g. ‘/path/to/my_dict’)

  • +
  • description (dict or numpy dtype object) – The description of the columns in the table. This is either a dict +of column name -> dtype items or a numpy record array dtype. For +more information, see the documentation for Table in pytables.

  • +
+
+
+
+ +
+
+exists_error = "'{}' exists in '{}'; set `delete_existing` attribute to True to overwrite existing calculations."
+
+ +
+
+property is_open
+
+ +
+
+iteritems(path='/')[source]
+

Iterate over node paths and nodes of the h5 file.

+
+ +
+
+classmethod join_path(*args)[source]
+

Join parts of an h5 path.

+

For example, the 3 argmuments ‘path’, ‘to’, ‘node’ will return +‘/path/to/node’.

+
+
Parameters
+

args (str) – Parts of path to be joined.

+
+
+
+ +
+
+open()[source]
+
+ +
+
+remove_group(group_path, **kwargs)[source]
+

Remove group

+
+
Parameters
+

group_path (str) – PyTable group path; e.g. ‘/path/to/group’.

+
+
+
+ +
+
+remove_node(node_path)[source]
+

Remove node

+
+
Parameters
+

node_path (str) – PyTable node path; e.g. ‘/path/to/node’.

+
+
+
+ +
+
+property root
+
+ +
+
+classmethod split_path(node_path)[source]
+

Split node path returning the base path and node name.

+

For example: ‘/path/to/node’ will return ‘/path/to’ and ‘node’

+
+
Parameters
+

node_path (str) – PyTable node path; e.g. ‘/path/to/node’.

+
+
+
+ +
+ +
+
+class apptools.io.h5.file.H5Group(pytables_group)[source]
+

Bases: collections.abc.Mapping

+

A group node in an H5File.

+

This is a thin wrapper around PyTables’ Group object to expose attributes +and maintain the dict interface of H5File.

+
+
+property children_names
+
+ +
+
+create_array(node_subpath, array_or_shape, dtype=None, chunked=False, extendable=False, **kwargs)[source]
+

** H5Group wrapper for H5File.create_array: ** +Note that the first argument is a nodepath relative to the group, rather than +an absolute path. Below is the original docstring:

+

Create node to store an array.

+
+
Parameters
+
    +
  • node_path (str) – PyTable node path; e.g. ‘/path/to/node’.

  • +
  • array_or_shape (array or shape tuple) – Array or shape tuple for an array. If given a shape tuple, the +dtype parameter must also specified.

  • +
  • dtype (str or numpy.dtype) – Data type of array. Only necessary if array_or_shape is a shape.

  • +
  • chunked (bool) – Controls whether the array is chunked.

  • +
  • extendable ({None | bool}) – Controls whether the array is extendable.

  • +
  • kwargs (key/value pairs) – Keyword args passed to PyTables File.create_(c|e)array.

  • +
+
+
+
+ +
+
+create_dict(node_subpath, data=None, **kwargs)[source]
+

** H5Group wrapper for H5File.create_dict: ** +Note that the first argument is a nodepath relative to the group, rather than +an absolute path. Below is the original docstring:

+

Create dict node at the specified path.

+
+
Parameters
+
    +
  • node_path (str) – Path to node where data is stored (e.g. ‘/path/to/my_dict’)

  • +
  • data (dict) – Data for initialization, if desired.

  • +
+
+
+
+ +
+
+create_group(group_subpath, delete_existing=False, **kwargs)[source]
+

** H5Group wrapper for H5File.create_group: ** +Note that the first argument is a nodepath relative to the group, rather than +an absolute path. Below is the original docstring:

+

Create group.

+
+
Parameters
+
    +
  • group_path (str) – PyTable group path; e.g. ‘/path/to/group’.

  • +
  • kwargs (key/value pairs) – Keyword args passed to PyTables File.create_group.

  • +
+
+
+
+ +
+
+create_table(node_subpath, description, *args, **kwargs)[source]
+

** H5Group wrapper for H5File.create_table: ** +Note that the first argument is a nodepath relative to the group, rather than +an absolute path. Below is the original docstring:

+

Create table node at the specified path.

+
+
Parameters
+
    +
  • node_path (str) – Path to node where data is stored (e.g. ‘/path/to/my_dict’)

  • +
  • description (dict or numpy dtype object) – The description of the columns in the table. This is either a dict +of column name -> dtype items or a numpy record array dtype. For +more information, see the documentation for Table in pytables.

  • +
+
+
+
+ +
+
+property filename
+
+ +
+
+iter_groups()[source]
+

Iterate over H5Group nodes that are children of this group.

+
+ +
+
+property name
+
+ +
+
+property pathname
+
+ +
+
+remove_group(group_subpath, **kwargs)[source]
+

** H5Group wrapper for H5File.remove_group: ** +Note that the first argument is a nodepath relative to the group, rather than +an absolute path. Below is the original docstring:

+

Remove group

+
+
Parameters
+

group_path (str) – PyTable group path; e.g. ‘/path/to/group’.

+
+
+
+ +
+
+remove_node(node_subpath, **kwargs)[source]
+

** H5Group wrapper for H5File.remove_node: ** +Note that the first argument is a nodepath relative to the group, rather than +an absolute path. Below is the original docstring:

+

Remove node

+
+
Parameters
+

node_path (str) – PyTable node path; e.g. ‘/path/to/node’.

+
+
+
+ +
+
+property root
+
+ +
+
+property subgroup_names
+
+ +
+ +
+
+apptools.io.h5.file.get_atom(dtype)[source]
+

Return a PyTables Atom for the given dtype or dtype string.

+
+ +
+
+apptools.io.h5.file.h5_group_wrapper(original)[source]
+
+ +
+
+apptools.io.h5.file.iterator_length(iterator)[source]
+
+ +
+
+

apptools.io.h5.table_node module

+
+
+class apptools.io.h5.table_node.H5TableNode(node)[source]
+

Bases: object

+

A wrapper for PyTables Table nodes.

+
+
Parameters
+

node (tables.Table instance) – An H5 node which is a pytables.Table or H5TableNode instance

+
+
+
+
+classmethod add_to_h5file(h5, node_path, description, **kwargs)[source]
+

Add table node to an H5 file at the specified path.

+
+
Parameters
+
    +
  • h5 (H5File) – The H5 file where the table node will be stored.

  • +
  • node_path (str) – Path to node where data is stored (e.g. ‘/path/to/my_table’)

  • +
  • description (list of tuples or numpy dtype object) – The description of the columns in the table. This is either a list +of (column name, dtype, [, shape or itemsize]) tuples or a numpy +record array dtype. For more information, see the documentation for +Table in PyTables.

  • +
  • **kwargs (dict) – Additional keyword arguments to pass to pytables.File.create_table

  • +
+
+
+
+ +
+
+append(data)[source]
+

Add some data to the table.

+
+
Parameters
+

data (dict) – A dictionary of column name -> values items

+
+
+
+ +
+
+classmethod is_table_node(pytables_node)[source]
+

Return True if pytables_node is a pytables.Table or a H5TableNode.

+
+ +
+
+property ix
+

Return an object which provides access to row data.

+
+ +
+
+keys()[source]
+
+ +
+
+to_dataframe()[source]
+

Return table data as a pandas DataFrame.

+

XXX: This does not work if the table contains a multidimensional column

+

This method requires pandas to have been installed in the environment.

+
+ +
+ +
+
+

apptools.io.h5.utils module

+
+
+apptools.io.h5.utils.open_h5file(filename, mode='r+', **kwargs)[source]
+

Context manager for reading an HDF5 file as an H5File object.

+
+
Parameters
+
    +
  • filename (str) – HDF5 file name.

  • +
  • mode (str) –

    Mode to open the file:

    +

    ’r’ : Read-only +‘w’ : Write; create new file (an existing file would be deleted). +‘a’ : Read and write to file; create if not existing +‘r+’: Read and write to file; must already exist

    +

  • +
  • H5File for additional keyword arguments. (See) –

  • +
+
+
+
+ +
+
+

Module contents

+
+
+ + +
+
+
+ +
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/api/apptools.io.html b/5.0/api/apptools.io.html new file mode 100644 index 000000000..fa1514e92 --- /dev/null +++ b/5.0/api/apptools.io.html @@ -0,0 +1,256 @@ + + + + + + + apptools.io package — Apptools Documentation + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +
+

apptools.io package

+ +
+

Submodules

+
+
+

apptools.io.api module

+
+
+

apptools.io.file module

+

A representation of files and folders in a file system.

+
+
+class apptools.io.file.File(path, **traits)[source]
+

Bases: traits.has_traits.HasPrivateTraits

+

A representation of files and folders in a file system.

+
+
+copy(destination)[source]
+

Copies this file/folder.

+
+ +
+
+create_file(contents='')[source]
+

Creates a file at this path.

+
+ +
+
+create_folder()[source]
+

Creates a folder at this path.

+

All intermediate folders MUST already exist.

+
+ +
+
+create_folders()[source]
+

Creates a folder at this path.

+

This will attempt to create any missing intermediate folders.

+
+ +
+
+create_package()[source]
+

Creates a package at this path.

+

All intermediate folders/packages MUST already exist.

+
+ +
+
+delete()[source]
+

Deletes this file/folder.

+

Does nothing if the file/folder does not exist.

+
+ +
+
+make_writeable()[source]
+

Attempt to make the file/folder writeable.

+
+ +
+
+move(destination)[source]
+

Moves this file/folder.

+
+ +
+ +
+
+

Module contents

+

Provides an abstraction for files and folders in a file system. +Part of the AppTools project of the Enthought Tool Suite.

+
+
+ + +
+
+
+
+
+ +

Table of Contents

+ + +

Previous topic

+

apptools package

+

Next topic

+

apptools.io.h5 package

+

This Page

+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/api/apptools.logger.agent.html b/5.0/api/apptools.logger.agent.html new file mode 100644 index 000000000..109f12f4f --- /dev/null +++ b/5.0/api/apptools.logger.agent.html @@ -0,0 +1,286 @@ + + + + + + + apptools.logger.agent package — Apptools Documentation + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+ +
+ + +
+
+ +
+
+ +
+

apptools.logger.agent package

+
+

Submodules

+
+
+

apptools.logger.agent.attachments module

+

Attach relevant project files.

+

FIXME: there are no public project plugins for Envisage 3, yet. In any case, +this stuff should not be hard-coded, but extensible via extension points. The +code remains here because we can reuse the zip utility code in that extensible +rewrite.

+
+
+class apptools.logger.agent.attachments.Attachments(message, **traits)[source]
+

Bases: traits.has_traits.HasTraits

+
+
+package_any_relevant_files()[source]
+
+ +
+
+package_single_project()[source]
+
+ +
+
+package_workspace()[source]
+
+ +
+ +
+
+

apptools.logger.agent.quality_agent_mailer module

+
+
+apptools.logger.agent.quality_agent_mailer.create_email_message(fromaddr, toaddrs, ccaddrs, subject, priority, include_project=False, stack_trace='', comments='')[source]
+
+ +
+
+

apptools.logger.agent.quality_agent_view module

+
+
+class apptools.logger.agent.quality_agent_view.QualityAgentView(*args, **kwargs)[source]
+

Bases: pyface.base_toolkit.Unimplemented

+
+
+cc_address = <traits.trait_types.Str object>
+
+ +
+
+comments = <traits.trait_types.Str object>
+
+ +
+
+from_address = <traits.trait_types.Str object>
+
+ +
+
+help_id = 'enlib|HID_Quality_Agent_Dlg'
+
+ +
+
+include_userdata
+

alias of traits.trait_types.Any

+
+ +
+
+msg = <traits.trait_types.Str object>
+
+ +
+
+priority = <traits.trait_types.Str object>
+
+ +
+
+service = <traits.trait_types.Any object>
+
+ +
+
+size = <traits.trait_types.Tuple object>
+
+ +
+
+smtp_server = <traits.trait_types.Str object>
+
+ +
+
+subject = <traits.trait_types.Str object>
+
+ +
+
+title = <traits.trait_types.Str object>
+
+ +
+
+to_address = <traits.trait_types.Str object>
+
+ +
+ +
+
+

Module contents

+

lib.apptools.logger.agent

+
+
+ + +
+
+
+ +
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/api/apptools.logger.html b/5.0/api/apptools.logger.html new file mode 100644 index 000000000..454905560 --- /dev/null +++ b/5.0/api/apptools.logger.html @@ -0,0 +1,323 @@ + + + + + + + apptools.logger package — Apptools Documentation + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +
+

apptools.logger package

+ +
+

Submodules

+
+
+

apptools.logger.api module

+
+
+

apptools.logger.custom_excepthook module

+
+
+apptools.logger.custom_excepthook.custom_excepthook(type, value, traceback)[source]
+

Pass on the exception to the logging system.

+
+ +
+
+

apptools.logger.log_point module

+

Prints a stack trace every time it is called but does not halt execution +of the application.

+

Copied from Uche Ogbuji’s blog

+
+
+apptools.logger.log_point.log_point(msg='\n')[source]
+
+ +
+
+

apptools.logger.log_queue_handler module

+
+
+class apptools.logger.log_queue_handler.LogQueueHandler(size=1000)[source]
+

Bases: logging.Handler

+

Buffers up the log messages so that we can display them later. +This is important on startup when log messages are generated before +the ui has started. By putting them in this queue we can display +them once the ui is ready.

+
+
+emit(record)[source]
+

Actually this is more like an enqueue than an emit().

+
+ +
+
+get()[source]
+
+ +
+
+has_new_records()[source]
+
+ +
+
+reset()[source]
+
+ +
+ +
+
+

apptools.logger.logger module

+

Convenience functions for creating logging handlers etc.

+
+
+class apptools.logger.logger.LogFileHandler(path, maxBytes=1000000, backupCount=3, level=None, formatter=None)[source]
+

Bases: logging.handlers.RotatingFileHandler

+

The default log file handler.

+
+ +
+
+apptools.logger.logger.add_log_queue_handler(logger, level=None, formatter=None)[source]
+

Adds a queueing log handler to a logger.

+
+ +
+
+

apptools.logger.ring_buffer module

+

Copied from Python Cookbook.

+
+
+class apptools.logger.ring_buffer.RingBuffer(size_max)[source]
+

Bases: object

+
+
+append(x)[source]
+

append an element at the end of the buffer

+
+ +
+
+get()[source]
+

return a list of elements from the oldest to the newest

+
+ +
+ +
+
+class apptools.logger.ring_buffer.RingBufferFull(n)[source]
+

Bases: object

+
+
+append(x)[source]
+
+ +
+
+get()[source]
+
+ +
+ +
+
+

Module contents

+

Convenience functions for creating logging handlers. +Part of the EnthoughtBase project.

+
+
+ + +
+
+
+ +
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/api/apptools.logger.plugin.html b/5.0/api/apptools.logger.plugin.html new file mode 100644 index 000000000..0f53db074 --- /dev/null +++ b/5.0/api/apptools.logger.plugin.html @@ -0,0 +1,305 @@ + + + + + + + apptools.logger.plugin package — Apptools Documentation + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+ +
+ + +
+
+ +
+
+ +
+

apptools.logger.plugin package

+ +
+

Submodules

+
+
+

apptools.logger.plugin.logger_plugin module

+

Logger plugin.

+
+
+class apptools.logger.plugin.logger_plugin.LoggerPlugin(*args, **kwargs)[source]
+

Bases: envisage.api.Plugin

+

Logger plugin.

+
+
+MAIL_FILES = 'apptools.logger.plugin.mail_files'
+
+ +
+
+PREFERENCES = 'envisage.preferences'
+
+ +
+
+PREFERENCES_PAGES = 'envisage.ui.workbench.preferences_pages'
+
+ +
+
+VIEWS = 'envisage.ui.workbench.views'
+
+ +
+
+id = 'apptools.logger'
+
+ +
+
+mail_files
+
+ +
+
+name = 'Logger plugin'
+
+ +
+
+preferences = <traits.trait_types.List object>
+
+ +
+
+preferences_pages = <traits.trait_types.List object>
+
+ +
+
+start()[source]
+

Starts the plugin.

+
+ +
+
+stop()[source]
+

Stops the plugin.

+
+ +
+
+views = <traits.trait_types.List object>
+
+ +
+ +
+
+

apptools.logger.plugin.logger_preferences module

+
+
+class apptools.logger.plugin.logger_preferences.LoggerPreferences(**traits)[source]
+

Bases: apptools.preferences.preferences_helper.PreferencesHelper

+

The persistent service exposing the Logger plugin’s API.

+
+ +
+
+

apptools.logger.plugin.logger_service module

+
+
+class apptools.logger.plugin.logger_service.LoggerService[source]
+

Bases: traits.has_traits.HasTraits

+

The persistent service exposing the Logger plugin’s API.

+
+
+create_email_message(fromaddr, toaddrs, ccaddrs, subject, priority, include_userdata=False, stack_trace='', comments='', include_environment=True)[source]
+

Format a bug report email from the log files.

+
+ +
+
+save_preferences()[source]
+

Save the preferences.

+
+ +
+
+send_bug_report(smtp_server, fromaddr, toaddrs, ccaddrs, message)[source]
+

Send a bug report email.

+
+ +
+
+whole_log_text()[source]
+

Return all of the logged data as formatted text.

+
+ +
+ +
+
+

Module contents

+
+
+ + +
+
+
+ +
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/api/apptools.logger.plugin.view.html b/5.0/api/apptools.logger.plugin.view.html new file mode 100644 index 000000000..6cc16d720 --- /dev/null +++ b/5.0/api/apptools.logger.plugin.view.html @@ -0,0 +1,303 @@ + + + + + + + apptools.logger.plugin.view package — Apptools Documentation + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ + + + +
+
+ +
+
+ +
+

apptools.logger.plugin.view package

+
+

Submodules

+
+
+

apptools.logger.plugin.view.logger_preferences_page module

+
+
+class apptools.logger.plugin.view.logger_preferences_page.LoggerPreferencesPage(**traits)[source]
+

Bases: apptools.preferences.ui.preferences_page.PreferencesPage

+

A preference page for the logger plugin.

+
+ +
+
+

apptools.logger.plugin.view.logger_view module

+
+
+class apptools.logger.plugin.view.logger_view.LogRecordAdapter[source]
+

Bases: traitsui.tabular_adapter.TabularAdapter

+

A TabularEditor adapter for logging.LogRecord objects.

+
+
+column_widths = [80, 100, 120, -1]
+
+ +
+
+get_width(object, trait, column)[source]
+

Returns the width to use for a specified column.

+

If the value is <= 0, the column will have a default width, which is +the same as specifying a width of 0.1.

+

If the value is > 1.0, it is converted to an integer and the result is +the width of the column in pixels. This is referred to as a +fixed width column.

+

If the value is a float such that 0.0 < value <= 1.0, it is treated as +the unnormalized fraction of the available space that is to be +assigned to the column. What this means requires a little explanation.

+

To arrive at the size in pixels of the column at any given time, the +editor adds together all of the unnormalized fraction values +returned for all columns in the table to arrive at a total value. Each +unnormalized fraction is then divided by the total to create a +normalized fraction. Each column is then assigned an amount of space +in pixels equal to the maximum of 30 or its normalized fraction +multiplied by the available space. The available space is defined +as the actual width of the table minus the width of all fixed width +columns. Note that this calculation is performed each time the table is +resized in the user interface, thus allowing columns of this type to +increase or decrease their width dynamically, while leaving fixed +width columns unchanged.

+
+ +
+ +
+
+class apptools.logger.plugin.view.logger_view.LoggerView(*args, **kwargs)[source]
+

Bases: pyface.workbench.traits_ui_view.TraitsUIView

+

The Workbench View showing the list of log items.

+
+
+activated = <traits.trait_types.Instance object>
+
+ +
+
+activated_text = <traits.traits.ForwardProperty object>
+
+ +
+
+code_editor = <traitsui.editors.code_editor.ToolkitEditorFactory object>
+
+ +
+
+copy_button = <traits.trait_types.Button object>
+
+ +
+
+formatted_records = <traits.traits.ForwardProperty object>
+
+ +
+
+id = <traits.trait_types.Str object>
+
+ +
+
+log_records = <traits.trait_types.List object>
+
+ +
+
+log_records_editor = <traitsui.editors.tabular_editor.TabularEditor object>
+
+ +
+
+name = <traits.trait_types.Str object>
+
+ +
+
+reset_button = <traits.trait_types.Button object>
+
+ +
+
+service = <traits.trait_types.Instance object>
+
+ +
+
+show_button = <traits.trait_types.Button object>
+
+ +
+
+trait_view = ( Group( Item( 'log_records' object = 'object', style = 'simple', show_label = False ), Group( Item( 'reset_button' object = 'object', style = 'simple', show_label = False ), Item( 'trait_modified' object = 'object', style = 'simple' ), Item( 'show_button' object = 'object', style = 'simple', show_label = False ), Item( 'copy_button' object = 'object', style = 'simple', show_label = False ), orientation = 'horizontal', show_labels = False, object = 'object', style = 'simple' ), show_labels = False, object = 'object', style = 'simple' ) )
+
+ +
+
+update(force=False)[source]
+

Update ‘log_records’ if our handler has new records or ‘force’ is +set.

+
+ +
+ +
+
+

Module contents

+
+
+ + +
+
+
+ +
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/api/apptools.naming.html b/5.0/api/apptools.naming.html new file mode 100644 index 000000000..8879ceee8 --- /dev/null +++ b/5.0/api/apptools.naming.html @@ -0,0 +1,840 @@ + + + + + + + apptools.naming package — Apptools Documentation + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +
+

apptools.naming package

+ +
+

Submodules

+
+
+

apptools.naming.address module

+

The address of a commuications endpoint.

+
+
+class apptools.naming.address.Address[source]
+

Bases: traits.has_traits.HasTraits

+

The address of a communications end-point.

+

It contains a type that describes the communication mechanism, and the +actual address content.

+
+ +
+
+

apptools.naming.api module

+
+
+

apptools.naming.binding module

+

The representation of a name-to-object binding in a context.

+
+
+class apptools.naming.binding.Binding[source]
+

Bases: traits.has_traits.HasTraits

+

The representation of a name-to-object binding in a context.

+
+ +
+
+

apptools.naming.context module

+

The base class for all naming contexts.

+
+
+class apptools.naming.context.Context[source]
+

Bases: traits.has_traits.HasTraits

+

The base class for all naming contexts.

+
+
+INITIAL_CONTEXT_FACTORY = 'apptools.naming.factory.initial'
+
+ +
+
+OBJECT_FACTORIES = 'apptools.naming.factory.object'
+
+ +
+
+STATE_FACTORIES = 'apptools.naming.factory.state'
+
+ +
+
+bind(name, obj, make_contexts=False)[source]
+

Binds a name to an object.

+

If ‘make_contexts’ is True then any missing intermediate contexts are +created automatically.

+
+ +
+
+create_subcontext(name)[source]
+

Creates a sub-context.

+
+ +
+
+destroy_subcontext(name)[source]
+

Destroys a sub-context.

+
+ +
+
+get_unique_name(prefix)[source]
+

Returns a name that is unique within the context.

+

The name returned will start with the specified prefix.

+
+ +
+
+is_context(name)[source]
+

Returns True if the name is bound to a context.

+
+ +
+
+list_bindings(name='')[source]
+

Lists the bindings in a context.

+
+ +
+
+list_names(name='')[source]
+

Lists the names bound in a context.

+
+ +
+
+lookup(name)[source]
+

Resolves a name relative to this context.

+
+ +
+
+lookup_binding(name)[source]
+

Looks up the binding for a name relative to this context.

+
+ +
+
+lookup_context(name)[source]
+

Resolves a name relative to this context.

+

The name MUST resolve to a context.

+
+ +
+
+rebind(name, obj, make_contexts=False)[source]
+

Binds an object to a name that may already be bound.

+

If ‘make_contexts’ is True then any missing intermediate contexts are +created automatically.

+

The object may be a different object but may also be the same object +that is already bound to the specified name. The name may or may not be +already used. Think of this as a safer version of ‘bind’ since this +one will never raise an exception regarding a name being used.

+
+ +
+
+rename(old_name, new_name)[source]
+

Binds a new name to an object.

+
+ +
+
+search(obj)[source]
+

Returns a list of namespace names that are bound to obj.

+
+ +
+
+unbind(name)[source]
+

Unbinds a name.

+
+ +
+ +
+
+

apptools.naming.dir_context module

+

The base class for all directory contexts.

+
+
+class apptools.naming.dir_context.DirContext[source]
+

Bases: apptools.naming.context.Context

+

The base class for all directory contexts.

+
+
+find_bindings(visitor)[source]
+

Find bindings with attributes matching criteria in visitor.

+

Visitor is a function that is passed the bindings for each level of the +heirarchy and the attribute dictionary for those bindings. The visitor +examines the bindings and dictionary and returns the bindings it is +interested in.

+
+ +
+
+get_attributes(name)[source]
+

Returns the attributes associated with a named object.

+
+ +
+
+set_attributes(name, attributes)[source]
+

Sets the attributes associated with a named object.

+
+ +
+ +
+
+

apptools.naming.dynamic_context module

+

Provider of a framework that dynamically determines the contents of a +context at the time of interaction with the contents rather than at the +time a class is written.

+

This capability is particularly useful when the object acting as a context +is part of a plug-in application – such as Envisage. In general, this +capability allows the context to be:

+
    +
  • Extendable by contributions from somewhere other than the original +code writer

  • +
  • Dynamic in that the elements it is composed of can change each time +someone interacts with the contents of the context.

  • +
+

It should be noted that this capability is explicitly different from +contexts that look at another container to determine their contents, such +as a file system context!

+

Users of this framework contribute items to a dynamic context by adding +traits to the dynamic context instance. (This addition can happen +statically through the use of a Traits Category.) The trait value is the +context item’s value and the trait definition’s metadata determines how the +item is treated within the context. The support metadata is:

+
+
context_name: A non-empty string

Represents the name of the item within this context. This must be +present for the trait to show up as a context item though the value +may change over time as the item gets bound to different names.

+
+
context_order: A float value

Indicates the position for the item within this context. All +dynamically contributed context items are sorted by ascending order +of this value using the standard list sort function.

+
+
is_context: A boolean value

True if the item is itself a context.

+
+
+
+
+class apptools.naming.dynamic_context.DynamicContext[source]
+

Bases: apptools.naming.context.Context

+

A framework that dynamically determines the contents of a context at +the time of interaction with the contents rather than at the time a +context class is written.

+

It should be noted that this capability is explicitly different from +contexts that look at another container to determine their contents, +such as a file system context!

+
+ +
+
+

apptools.naming.exception module

+

Naming exceptions.

+
+
+exception apptools.naming.exception.InvalidNameError[source]
+

Bases: apptools.naming.exception.NamingError

+

Invalid name.

+

This exception is thrown when the name passed to a naming operation does +not conform to the syntax of the naming system (or is empty etc).

+
+ +
+
+exception apptools.naming.exception.NameAlreadyBoundError[source]
+

Bases: apptools.naming.exception.NamingError

+

Name already bound.

+

This exception is thrown when an attempt is made to bind a name that is +already bound in the current context.

+
+ +
+
+exception apptools.naming.exception.NameNotFoundError[source]
+

Bases: apptools.naming.exception.NamingError

+

Name not found.

+

This exception is thrown when a component of a name cannot be resolved +because it is not bound in the current context.

+
+ +
+
+exception apptools.naming.exception.NamingError[source]
+

Bases: Exception

+

Base class for all naming exceptions.

+
+ +
+
+exception apptools.naming.exception.NotContextError[source]
+

Bases: apptools.naming.exception.NamingError

+

Not a context.

+

This exception is thrown when a naming operation has reached a point where +a context is required to continue the operation, but the resolved object +is not a context.

+
+ +
+
+exception apptools.naming.exception.OperationNotSupportedError[source]
+

Bases: apptools.naming.exception.NamingError

+

The context does support the requested operation.

+
+ +
+
+

apptools.naming.initial_context module

+

The starting point for performing naming operations.

+
+
+apptools.naming.initial_context.InitialContext(environment)[source]
+

Creates an initial context for beginning name resolution.

+
+ +
+
+

apptools.naming.initial_context_factory module

+

The base class for all initial context factories.

+
+
+class apptools.naming.initial_context_factory.InitialContextFactory[source]
+

Bases: traits.has_traits.HasTraits

+

The base class for all initial context factories.

+
+
+get_initial_context(environment)[source]
+

Creates an initial context for beginning name resolution.

+
+ +
+ +
+
+

apptools.naming.naming_event module

+

The event fired by the tree model when it changes.

+
+
+class apptools.naming.naming_event.NamingEvent[source]
+

Bases: traits.has_traits.HasTraits

+

Information about tree model changes.

+
+ +
+
+

apptools.naming.naming_manager module

+

The naming manager.

+
+
+class apptools.naming.naming_manager.NamingManager[source]
+

Bases: traits.has_traits.HasTraits

+

The naming manager.

+
+
+get_object_instance(info, name, context)[source]
+

Creates an object using the specified state information.

+

The naming manager asks the context for its list of OBJECT factories +and calls them one by one until it gets a non-None result, indicating +that the factory recognised the information and created an object.

+

If none of the factories recognize the state information (or if the +context has no factories) then the state information itself is +returned.

+
+ +
+
+get_state_to_bind(obj, name, context)[source]
+

Returns the state of an object for binding.

+

The naming manager asks the context for its list of STATE factories +and then calls them one by one until it gets a non-None result +indicating that the factory recognised the object and created state +information for it.

+

If none of the factories recognize the object (or if the context +has no factories) then the object itself is returned.

+
+ +
+ +
+
+

apptools.naming.object_factory module

+

The base class for all object factories.

+
+
+class apptools.naming.object_factory.ObjectFactory[source]
+

Bases: traits.has_traits.HasTraits

+

The base class for all object factories.

+

An object factory accepts some information about how to create an object +(such as a reference) and returns an instance of that object.

+
+
+get_object_instance(state, name, context)[source]
+

Creates an object using the specified state information.

+

Returns None if the factory cannot create the object (ie. it does not +recognise the state passed to it).

+
+ +
+ +
+
+

apptools.naming.object_serializer module

+

The base class for all object serializers.

+
+
+class apptools.naming.object_serializer.ObjectSerializer[source]
+

Bases: traits.has_traits.HasTraits

+

The base class for all object serializers.

+
+
+can_load(path)[source]
+

Returns True if the serializer can load a file.

+
+ +
+
+can_save(obj)[source]
+

Returns True if the serializer can save an object.

+
+ +
+
+load(path)[source]
+

Loads an object from a file.

+
+ +
+
+save(path, obj)[source]
+

Saves an object to a file.

+
+ +
+ +
+
+

apptools.naming.py_context module

+

A naming context for a Python namespace.

+
+
+class apptools.naming.py_context.PyContext(**traits)[source]
+

Bases: apptools.naming.context.Context, apptools.naming.referenceable.Referenceable

+

A naming context for a Python namespace.

+
+ +
+
+

apptools.naming.py_object_factory module

+

Object factory for Python namespace contexts.

+
+
+class apptools.naming.py_object_factory.PyObjectFactory[source]
+

Bases: apptools.naming.object_factory.ObjectFactory

+

Object factory for Python namespace contexts.

+
+
+get_object_instance(state, name, context)[source]
+

Creates an object using the specified state information.

+
+ +
+ +
+
+

apptools.naming.pyfs_context module

+

A Python File System context.

+
+
+class apptools.naming.pyfs_context.PyFSContext(**traits)[source]
+

Bases: apptools.naming.dir_context.DirContext, apptools.naming.referenceable.Referenceable

+

A Python File System context.

+

This context represents a directory on a local file system.

+
+
+ATTRIBUTES_FILE = '__attributes__'
+
+ +
+
+FILTERS = 'apptools.naming.pyfs.filters'
+
+ +
+
+OBJECT_SERIALIZERS = 'apptools.naming.pyfs.object.serializers'
+
+ +
+
+get_unique_name(name)[source]
+

Returns a name that is unique within the context.

+

The name returned will start with the specified prefix.

+
+ +
+
+refresh()[source]
+

Refresh the context to reflect changes in the file system.

+
+ +
+ +
+
+

apptools.naming.pyfs_context_factory module

+

Object factory for Python File System contexts.

+
+
+class apptools.naming.pyfs_context_factory.PyFSContextFactory[source]
+

Bases: apptools.naming.object_factory.ObjectFactory

+

Object factory for Python File System contexts.

+
+
+get_object_instance(state, name, context)[source]
+

Creates an object using the specified state information.

+
+ +
+ +
+
+

apptools.naming.pyfs_initial_context_factory module

+

The initial context factory for Python file system contexts.

+
+
+class apptools.naming.pyfs_initial_context_factory.PyFSInitialContextFactory[source]
+

Bases: apptools.naming.initial_context_factory.InitialContextFactory

+

The initial context factory for Python file system contexts.

+
+
+get_initial_context(environment)[source]
+

Creates an initial context for beginning name resolution.

+
+ +
+ +
+
+

apptools.naming.pyfs_object_factory module

+

Object factory for Python File System contexts.

+
+
+class apptools.naming.pyfs_object_factory.PyFSObjectFactory[source]
+

Bases: apptools.naming.object_factory.ObjectFactory

+

Object factory for Python File System contexts.

+
+
+get_object_instance(state, name, context)[source]
+

Creates an object using the specified state information.

+
+ +
+ +
+
+

apptools.naming.pyfs_state_factory module

+

State factory for Python File System contexts.

+
+
+class apptools.naming.pyfs_state_factory.PyFSStateFactory[source]
+

Bases: apptools.naming.state_factory.StateFactory

+

State factory for Python File System contexts.

+
+
+get_state_to_bind(obj, name, context)[source]
+

Returns the state of an object for binding.

+
+ +
+ +
+
+

apptools.naming.reference module

+

A reference to an object that lives outside of the naming system.

+
+
+class apptools.naming.reference.Reference[source]
+

Bases: traits.has_traits.HasPrivateTraits

+

A reference to an object that lives outside of the naming system.

+

References provide a way to store the address(s) of objects that live +outside of the naming system. A reference consists of a list of +addresses that represent a communications endpoint for the object being +referenced.

+

A reference also contains information to assist in the creation of an +instance of the object to which it refers. It contains the name of +the class that will be created and the class name and location of a +factory that will be used to do the actual instance creation.

+
+ +
+
+

apptools.naming.referenceable module

+

Base class for classes that can produce a reference to themselves.

+
+
+class apptools.naming.referenceable.Referenceable[source]
+

Bases: traits.has_traits.HasPrivateTraits

+

Base class for classes that can produce a reference to themselves.

+
+ +
+
+

apptools.naming.referenceable_state_factory module

+

State factory for referenceable objects.

+
+
+class apptools.naming.referenceable_state_factory.ReferenceableStateFactory[source]
+

Bases: apptools.naming.state_factory.StateFactory

+

State factory for referenceable objects.

+
+
+get_state_to_bind(obj, name, context)[source]
+

Returns the state of an object for binding.

+
+ +
+ +
+
+

apptools.naming.state_factory module

+

The base class for all state factories.

+
+
+class apptools.naming.state_factory.StateFactory[source]
+

Bases: traits.has_traits.HasPrivateTraits

+

The base class for all state factories.

+

A state factory accepts an object and returns some data representing the +object that is suitable for storing in a particular context.

+
+
+get_state_to_bind(obj, name, context)[source]
+

Returns the state of an object for binding.

+

Returns None if the factory cannot create the state (ie. it does not +recognise the object passed to it).

+
+ +
+ +
+
+

apptools.naming.unique_name module

+

A re-usable method for calculating a unique name given a list of existing +names.

+
+
+apptools.naming.unique_name.make_unique_name(base, existing=[], format='%s_%s')[source]
+

Return a name, unique within a context, based on the specified name.

+

base: the desired base name of the generated unique name. +existing: a sequence of the existing names to avoid returning. +format: a formatting specification for how the name is made unique.

+
+ +
+
+

Module contents

+

Manages naming contexts. Supports non-string data types and scoped +preferences. Part of the AppTools project of the Enthought Tool Suite.

+
+
+ + +
+
+
+ +
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/api/apptools.naming.trait_defs.html b/5.0/api/apptools.naming.trait_defs.html new file mode 100644 index 000000000..6938f18d2 --- /dev/null +++ b/5.0/api/apptools.naming.trait_defs.html @@ -0,0 +1,271 @@ + + + + + + + apptools.naming.trait_defs package — Apptools Documentation + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+ +
+ + +
+
+ +
+
+ +
+

apptools.naming.trait_defs package

+
+

Submodules

+
+
+

apptools.naming.trait_defs.api module

+
+
+

apptools.naming.trait_defs.naming_traits module

+
+
+class apptools.naming.trait_defs.naming_traits.NamingTraitHandler(aClass, or_none, module)[source]
+

Bases: traits.trait_handler.TraitHandler

+
+
+find_class()[source]
+
+ +
+
+get_editor(trait)[source]
+

Returns a trait editor that allows the user to modify the trait +trait. +This method only needs to be specified if traits defined using this +trait handler require a non-default trait editor in trait user +interfaces. The default implementation of this method returns a trait +editor that allows the user to type an arbitrary string as the value.

+

For more information on trait user interfaces, refer to the Traits UI +User Guide.

+
+
Parameters
+

trait (Trait) – The trait to be edited.

+
+
+
+ +
+
+info()[source]
+

Must return a string describing the type of value accepted by the +trait handler.

+

The string should be a phrase describing the type defined by the +TraitHandler subclass, rather than a complete sentence. For example, +use the phrase, “a square sprocket” instead of the sentence, “The value +must be a square sprocket.” The value returned by info() is combined +with other information whenever an error occurs and therefore makes +more sense to the user if the result is a phrase. The info() method is +similar in purpose and use to the info attribute of a validator +function.

+

Note that the result can include information specific to the particular +trait handler instance. If the info() method is not overridden, the +default method returns the value of the ‘info_text’ attribute.

+
+ +
+
+post_setattr(object, name, value)[source]
+
+ +
+
+resolve_class(object, name, value)[source]
+
+ +
+
+validate(object, name, value)[source]
+

Verifies whether a new value assigned to a trait attribute is +valid.

+

This method must be implemented by subclasses of TraitHandler. It is +called whenever a new value is assigned to a trait attribute defined +using this trait handler.

+

If the value received by validate() is not valid for the trait +attribute, the method must called the predefined error() method to +raise a TraitError exception

+
+
Parameters
+
    +
  • object (HasTraits instance) – The object whose attribute is being assigned.

  • +
  • name (str) – The name of the attribute being assigned.

  • +
  • value (any) – The proposed new value for the attribute.

  • +
+
+
Returns
+

If the new value is valid, this method must return either the +original value passed to it, or an alternate value to be assigned +in place of the original value. Whatever value this method returns +is the actual value assigned to object.name.

+
+
Return type
+

any

+
+
+
+ +
+
+validate_failed(object, name, value)[source]
+
+ +
+ +
+
+

Module contents

+
+
+ + +
+
+
+ +
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/api/apptools.persistence.html b/5.0/api/apptools.persistence.html new file mode 100644 index 000000000..8640a7d0b --- /dev/null +++ b/5.0/api/apptools.persistence.html @@ -0,0 +1,794 @@ + + + + + + + apptools.persistence package — Apptools Documentation + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +
+

apptools.persistence package

+
+

Submodules

+
+
+

apptools.persistence.file_path module

+

Simple class to support file path objects that work well in the +context of persistent storage with the state_pickler.

+
+
+class apptools.persistence.file_path.FilePath(value='')[source]
+

Bases: object

+

This class stores two paths to the file. A relative path and +an absolute one. The absolute path is used by the end user. When +this object is pickled the state_pickler sets the relative path +relative to the file that is being generated. When unpickled, the +stored relative path is used to set the absolute path correctly +based on the path of the saved file.

+
+
+get()[source]
+

Get the path.

+
+ +
+
+set(value)[source]
+

Sets the value of the path.

+
+ +
+
+set_absolute(base_f_name)[source]
+

Sets the absolute file name for the current relative file +name with respect to the given base_f_name.

+
+ +
+
+set_relative(base_f_name)[source]
+

Sets the path relative to base_f_name. Note that +base_f_name and self.rel_pth should be valid file names +correct on the current os. The set name is a file name that +has a POSIX path.

+
+ +
+ +
+
+

apptools.persistence.project_loader module

+
+
+apptools.persistence.project_loader.load_project(pickle_filename, updater_path, application_version, protocol, max_pass=-1)[source]
+

Reads a project from a pickle file and if necessary will update it to +the latest version of the application.

+
+ +
+
+apptools.persistence.project_loader.upgrade_project(pickle_filename, updater_path, project_version, application_version, protocol, max_pass=-1)[source]
+

Repeatedly read and write the project to disk updating it one version +at a time.

+

Example the p5.project is at version 0 +The application is at version 3

+

p5.project — Update1 —> p5.project.v1 +p5.project.v1 — Update2 —> p5.project.v2 +p5.project.v2 — Update3 —> p5.project.v3 +p5.project.v3 —> loaded into app

+

The user then has the option to save the updated project as p5.project

+
+ +
+
+

apptools.persistence.state_pickler module

+

This module provides code that allows one to pickle the state of a +Python object to a dictionary.

+

The motivation for this is simple. The standard Python +pickler/unpickler is best used to pickle simple objects and does not +work too well for complex code. Specifically, there are two major +problems (1) the pickle file format is not easy to edit with a text +editor and (2) when a pickle is unpickled, it creates all the +necessary objects and sets the state of these objects.

+

Issue (2) might not appear to be a problem. However, often, the +determination of the entire ‘state’ of an application requires the +knowledge of the state of many objects that are not really in the +users concern. The user would ideally like to pickle just what he +thinks is relevant. Now, given that the user is not going to save the +entire state of the application, the use of pickle is insufficient +since the state is no longer completely known (or worth knowing). The +default Unpickler recreates the objects and the typical +implementation of __setstate__ is usually to simply update the +object’s __dict__ attribute. This is inadequate because the pickled +information is taken out of the real context when it was saved.

+

The StatePickler basically pickles the ‘state’ of an object into a +large dictionary. This pickled data may be easily unpickled and +modified on the interpreter or edited with a text editor +(pprint.saferepr is a friend). The second problem is also +eliminated. When this state is unpickled using StateUnpickler, what +you get is a special dictionary (a State instance). This allows one +to navigate the state just like the original object. Its up to the +user to create any new objects and set their states using this +information. This allows for a lot of flexibility while allowing one +to save and set the state of (almost) any Python object.

+

The StateSetter class helps set the state of a known instance. When +setting the state of an instance it checks to see if there is a +__set_pure_state__ method that in turn calls StateSetter.set +appropriately.

+

Additionally, there is support for versioning. The class’ version is +obtain from the __version__ class attribute. This version along +with the versions of the bases of a class is embedded into the +metadata of the state and stored. By using version_registry.py a +user may register a handler for a particular class and module. When +the state of an object is set using StateSetter.set_state, then +these handlers are called in reverse order of their MRO. This gives +the handler an opportunity to upgrade the state depending on its +version. Builtin classes are not scanned for versions. If a class +has no version, then by default it is assumed to be -1.

+

Example:

+
>>> class A:
+...    def __init__(self):
+...        self.a = 'a'
+...
+>>> a = A()
+>>> a.a = 100
+>>> import state_pickler
+>>> s = state_pickler.dumps(a)               # Dump the state of `a`.
+>>> state = state_pickler.loads_state(s)     # Get the state back.
+>>> b = state_pickler.create_instance(state) # Create the object.
+>>> state_pickler.set_state(b, state)        # Set the object's state.
+>>> assert b.a == 100
+
+
+
+

Features

+
+
    +
  • The output is a plain old dictionary so is easy to parse, edit etc.

  • +
  • Handles references to avoid duplication.

  • +
  • Gzips Numeric arrays when dumping them.

  • +
  • Support for versioning.

  • +
+
+
+
+

Caveats

+
+
    +
  • Does not pickle a whole bunch of stuff including code objects and +functions.

  • +
  • The output is a pure dictionary and does not contain instances. So +using this as it is in __setstate__ will not work. Instead +define a __set_pure_state__ and use the StateSetter class or +the set_state function provided by this module.

  • +
+
+

Notes

+

Browsing the code from XMarshaL and pickle.py proved useful for +ideas. None of the code is taken from there though.

+
+
+class apptools.persistence.state_pickler.State(**kw)[source]
+

Bases: dict

+

Used to encapsulate the state of an instance in a very +convenient form. The ‘__metadata__’ attribute/key is a dictionary +that has class specific details like the class name, module name +etc.

+
+ +
+
+class apptools.persistence.state_pickler.StateDict(**kw)[source]
+

Bases: dict

+

Used to encapsulate a dictionary stored in a State instance. +The has_instance attribute specifies if the dict has an instance +embedded in it.

+
+ +
+
+class apptools.persistence.state_pickler.StateList(seq=None)[source]
+

Bases: list

+

Used to encapsulate a list stored in a State instance. The +has_instance attribute specifies if the list has an instance +embedded in it.

+
+ +
+
+class apptools.persistence.state_pickler.StatePickler[source]
+

Bases: object

+

Pickles the state of an object into a dictionary. The +dictionary is itself either saved as a pickled file (dump) or +pickled string (dumps). Alternatively, the dump_state method +will return the dictionary that is pickled.

+

The format of the state dict is quite strightfoward. Basic types +(bool, int, long, float, complex, None, string) are +represented as they are. Everything else is stored as a +dictionary containing metadata information on the object’s type +etc. and also the actual object in the ‘data’ key. For example:

+
>>> p = StatePickler()
+>>> p.dump_state(1)
+1
+>>> l = [1,2.0, None, [1,2,3]]
+>>> p.dump_state(l)
+{'data': [1, 2.0, None, {'data': [1, 2, 3], 'type': 'list', 'id': 1}],
+ 'id': 0,
+ 'type': 'list'}
+
+
+

Classes are also represented similarly. The state in this case is +obtained from the __getstate__ method or from the __dict__. +Here is an example:

+
>>> class A:
+...     __version__ = 1  # State version
+...     def __init__(self):
+...         self.attribute = 1
+...
+>>> a = A()
+>>> p = StatePickler()
+>>> p.dump_state(a)
+{'class_name': 'A',
+ 'data': {'data': {'attribute': 1}, 'type': 'dict', 'id': 2},
+ 'id': 0,
+ 'initargs': {'data': (), 'type': 'tuple', 'id': 1},
+ 'module': '__main__',
+ 'type': 'instance',
+ 'version': [(('A', '__main__'), 1)]}
+
+
+

When pickling data, references are taken care of. Numeric arrays +can be pickled and are stored as a gzipped base64 encoded string.

+
+
+dump(value, file)[source]
+

Pickles the state of the object (value) into the passed +file.

+
+ +
+
+dump_state(value)[source]
+

Returns a dictionary or a basic type representing the +complete state of the object (value).

+

This value is pickled by the dump and dumps methods.

+
+ +
+
+dumps(value)[source]
+

Pickles the state of the object (value) and returns a +string.

+
+ +
+ +
+
+exception apptools.persistence.state_pickler.StatePicklerError[source]
+

Bases: Exception

+
+ +
+
+class apptools.persistence.state_pickler.StateSetter[source]
+

Bases: object

+

This is a convenience class that helps a user set the +attributes of an object given its saved state. For instances it +checks to see if a __set_pure_state__ method exists and calls +that when it sets the state.

+
+
+set(obj, state, ignore=None, first=None, last=None)[source]
+

Sets the state of the object.

+

This is to be used as a means to simplify loading the state of +an object from its __setstate__ method using the dictionary +describing its state. Note that before the state is set, the +registered handlers for the particular class are called in +order to upgrade the version of the state to the latest +version.

+
+
Parameters
+
    +
  • obj (-) – The object whose state is to be set. If this is None +(default) then the object is created.

  • +
  • state (-) – The dictionary representing the state of the object.

  • +
  • ignore (-) – The list of attributes specified in this list are ignored +and the state of these attributes are not set (this excludes +the ones specified in first and last). If one specifies +a ‘*’ then all attributes are ignored except the ones +specified in first and last.

  • +
  • first (-) – The list of attributes specified in this list are set first (in +order), before any other attributes are set.

  • +
  • last (-) – The list of attributes specified in this list are set last (in +order), after all other attributes are set.

  • +
+
+
+
+ +
+ +
+
+exception apptools.persistence.state_pickler.StateSetterError[source]
+

Bases: Exception

+
+ +
+
+class apptools.persistence.state_pickler.StateTuple[source]
+

Bases: tuple

+

Used to encapsulate a tuple stored in a State instance. The +has_instance attribute specifies if the tuple has an instance +embedded in it.

+
+ +
+
+class apptools.persistence.state_pickler.StateUnpickler[source]
+

Bases: object

+

Unpickles the state of an object saved using StatePickler.

+

Please note that unlike the standard Unpickler, no instances of +any user class are created. The data for the state is obtained +from the file or string, reference objects are setup to refer to +the same state value and this state is returned in the form +usually in the form of a dictionary. For example:

+
>>> class A:
+...     def __init__(self):
+...         self.attribute = 1
+...
+>>> a = A()
+>>> p = StatePickler()
+>>> s = p.dumps(a)
+>>> up = StateUnpickler()
+>>> state = up.loads_state(s)
+>>> state.__class__.__name__
+'State'
+>>> state.attribute
+1
+>>> state.__metadata__
+{'class_name': 'A',
+ 'has_instance': True,
+ 'id': 0,
+ 'initargs': (),
+ 'module': '__main__',
+ 'type': 'instance',
+ 'version': [(('A', '__main__'), -1)]}
+
+
+

Note that the state is actually a State instance and is +navigable just like the original object. The details of the +instance are stored in the __metadata__ attribute. This is +highly convenient since it is possible for someone to view and +modify the state very easily.

+
+
+load_state(file)[source]
+

Returns the state of an object loaded from the pickled data +in the given file.

+
+ +
+
+loads_state(string)[source]
+

Returns the state of an object loaded from the pickled data +in the given string.

+
+ +
+ +
+
+exception apptools.persistence.state_pickler.StateUnpicklerError[source]
+

Bases: Exception

+
+ +
+
+apptools.persistence.state_pickler.create_instance(state)[source]
+

Create an instance from the state if possible.

+
+ +
+
+apptools.persistence.state_pickler.dump(value, file)[source]
+

Pickles the state of the object (value) into the passed file +(or file name).

+
+ +
+
+apptools.persistence.state_pickler.dumps(value)[source]
+

Pickles the state of the object (value) and returns a string.

+
+ +
+
+apptools.persistence.state_pickler.get_state(obj)[source]
+

Returns the state of the object (usually as a dictionary). The +returned state may be used directy to set the state of the object +via set_state.

+
+ +
+
+apptools.persistence.state_pickler.gunzip_string(data)[source]
+

Given a gzipped string (data) this unzips the string and +returns it.

+
+ +
+
+apptools.persistence.state_pickler.gzip_string(data)[source]
+

Given a string (data) this gzips the string and returns it.

+
+ +
+
+apptools.persistence.state_pickler.load_state(file)[source]
+

Returns the state of an object loaded from the pickled data in +the given file (or file name).

+
+ +
+
+apptools.persistence.state_pickler.loads_state(string)[source]
+

Returns the state of an object loaded from the pickled data +in the given string.

+
+ +
+
+apptools.persistence.state_pickler.set_state(obj, state, ignore=None, first=None, last=None)[source]
+

Sets the state of the object.

+

This is to be used as a means to simplify loading the state of +an object from its __setstate__ method using the dictionary +describing its state. Note that before the state is set, the +registered handlers for the particular class are called in +order to upgrade the version of the state to the latest +version.

+
+
Parameters
+
    +
  • obj (-) – The object whose state is to be set. If this is None +(default) then the object is created.

  • +
  • state (-) – The dictionary representing the state of the object.

  • +
  • ignore (-) – The list of attributes specified in this list are ignored +and the state of these attributes are not set (this excludes +the ones specified in first and last). If one specifies +a ‘*’ then all attributes are ignored except the ones +specified in first and last.

  • +
  • first (-) – The list of attributes specified in this list are set first (in +order), before any other attributes are set.

  • +
  • last (-) – The list of attributes specified in this list are set last (in +order), after all other attributes are set.

  • +
+
+
+
+ +
+
+apptools.persistence.state_pickler.update_state(state)[source]
+

Given the state of an object, this updates the state to the +latest version using the handlers given in the version registry. +The state is modified in-place.

+
+ +
+
+
+

apptools.persistence.updater module

+
+
+class apptools.persistence.updater.Updater[source]
+

Bases: object

+

An abstract class to provide functionality common to the updaters.

+
+
+get_latest(module, name)[source]
+

The refactorings dictionary contains mappings between old and new +module names. Since we only bump the version number one increment +there is only one possible answer.

+
+ +
+
+strip(string)[source]
+
+ +
+ +
+
+

apptools.persistence.version_registry module

+

A version registry that manages handlers for different state +versions.

+
+
+class apptools.persistence.version_registry.HandlerRegistry[source]
+

Bases: object

+

A simple version conversion handler registry. Classes register +handlers in order to convert the state version to the latest +version. When an object’s state is about to be set, the update +method of the registy is called. This in turn calls any handlers +registered for the class/module and this handler is then called +with the state and the version of the state. The state is +modified in-place by the handlers.

+
+
+register(class_name, module, handler)[source]
+

Register handler that handles versioning for class having +class name (class_name) and module name (module). The +handler function will be passed the state and its version to fix.

+
+ +
+
+unregister(class_name, module)[source]
+

Unregisters any handlers for a class and module.

+
+ +
+
+update(state)[source]
+

Updates the given state using the handlers. Note that the +state is modified in-place.

+
+ +
+ +
+
+apptools.persistence.version_registry.get_version(obj)[source]
+

Walks the class hierarchy and obtains the versions of the +various classes and returns a list of tuples of the form +((class_name, module), version) in reverse order of the MRO.

+
+ +
+
+

apptools.persistence.versioned_unpickler module

+
+
+class apptools.persistence.versioned_unpickler.NewUnpickler(file, *, fix_imports=True, encoding='ASCII', errors='strict')[source]
+

Bases: pickle._Unpickler

+

An unpickler that implements a two-stage pickling process to make it +possible to unpickle complicated Python object hierarchies where the +unserialized state of an object depends on the state of other objects in +the same pickle.

+
+
+initialize(max_pass)[source]
+
+ +
+
+load(max_pass=-1)[source]
+

Read a pickled object representation from the open file.

+

Return the reconstituted object hierarchy specified in the file.

+
+ +
+
+classmethod load_build(obj)[source]
+
+ +
+ +
+
+class apptools.persistence.versioned_unpickler.VersionedUnpickler(file, updater=None)[source]
+

Bases: apptools.persistence.versioned_unpickler.NewUnpickler

+

This class reads in a pickled file created at revision version ‘n’ +and then applies the transforms specified in the updater class to +generate a new set of objects which are at revision version ‘n+1’.

+

I decided to keep the loading of the updater out of this generic class +because we will want updaters to be generated for each plugin’s type +of project.

+

This ensures that the VersionedUnpickler can remain ignorant about the +actual version numbers - all it needs to do is upgrade one release.

+
+
+add_updater(module, name, klass)[source]
+

If there is an updater defined for this class we will add it to the +class as the __setstate__ method.

+
+ +
+
+backup_setstate(module, klass)[source]
+

If the class has a user defined __setstate__ we back it up.

+
+ +
+
+find_class(module, name)[source]
+

Overridden method from Unpickler.

+

NB __setstate__ is not called until later.

+
+ +
+
+import_name(module, name)[source]
+

If the class is needed for the latest version of the application then +it should presumably exist.

+

If the class no longer exists then we should perhaps return +a proxy of the class.

+

If the persisted file is at v1 say and the application is at v3 then +objects that are required for v1 and v2 do not have to exist they only +need to be placeholders for the state during an upgrade.

+
+ +
+ +
+
+

Module contents

+

Supports flexible pickling and unpickling of the state of a Python object +to a dictionary. Part of the AppTools project of the Enthought Tool Suite.

+
+
+ + +
+
+
+ +
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/api/apptools.preferences.html b/5.0/api/apptools.preferences.html new file mode 100644 index 000000000..e7f3cde28 --- /dev/null +++ b/5.0/api/apptools.preferences.html @@ -0,0 +1,653 @@ + + + + + + + apptools.preferences package — Apptools Documentation + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +
+

apptools.preferences package

+ +
+

Submodules

+
+
+

apptools.preferences.api module

+
+
+

apptools.preferences.i_preferences module

+

The interface for a node in a preferences hierarchy.

+
+
+class apptools.preferences.i_preferences.IPreferences[source]
+

Bases: traits.has_traits.Interface

+

The interface for a node in a preferences hierarchy.

+
+
+clear(path='')[source]
+

Remove all preference from the node at the specified path.

+

If the path is the empty string (the default) then remove the +preferences in this node.

+

This does not affect any of the node’s children.

+

e.g. To clear the preferences out of a node directly:

+
preferences.clear()
+
+
+

Or to clear the preferences of a node at a given path:

+
preferences.clear('acme.ui')
+
+
+
+ +
+
+flush()[source]
+

Force any changes in the node to the backing store.

+

This includes any changes to the node’s descendants.

+
+ +
+
+get(path, default=None, inherit=False)[source]
+

Get the value of the preference at the specified path.

+

If no value exists for the path (or any part of the path does not +exist) then return the default value.

+

Preference values are always returned as strings.

+

e.g:

+
preferences.set('acme.ui.bgcolor', 'blue')
+preferences.get('acme.ui.bgcolor') -> 'blue'
+
+preferences.set('acme.ui.width', 100)
+preferences.get('acme.ui.width') -> '100'
+
+preferences.set('acme.ui.visible', True)
+preferences.get('acme.ui.visible') -> 'True'
+
+
+

If ‘inherit’ is True then we allow ‘inherited’ preference values.

+

e.g. If we are looking up:

+
'acme.ui.widget.bgcolor'
+
+
+

and it does not exist then we will also try:

+
'acme.ui.bgcolor'
+'acme.bgcolor'
+'bgcolor'
+
+
+

Raise a ‘ValueError’ exception if the path is the empty string.

+
+ +
+
+keys(path='')[source]
+

Return the preference keys of the node at the specified path.

+

If the path is the empty string (the default) then return the +preference keys of this node.

+

e.g:

+
keys = preferences.keys('acme.ui')
+
+
+
+ +
+
+node(path='')[source]
+

Return the node at the specified path.

+

If the path is the empty string (the default) then return this node.

+

Any missing nodes are created automatically.

+

e.g:

+
node = preferences.node('acme.ui')
+bgcolor = node.get('bgcolor')
+
+
+
+ +
+
+node_exists(path='')[source]
+

Return True if the node at the specified path exists

+

If the path is the empty string (the default) then return True.

+

e.g:

+
exists = preferences.exists('acme.ui')
+
+
+
+ +
+
+node_names(path='')[source]
+

Return the names of the children of the node at the specified path.

+

If the path is the empty string (the default) then return the names of +the children of this node.

+

e.g:

+
names = preferences.node_names('acme.ui')
+
+
+
+ +
+
+remove(path)[source]
+

Remove the preference at the specified path.

+

Does nothing if no value exists for the path (or any part of the path +does not exist.

+

Raise a ‘ValueError’ exception if the path is the empty string.

+

e.g.:

+
preferences.remove('acme.ui.bgcolor')
+
+
+
+ +
+
+set(path, value)[source]
+

Set the value of the preference at the specified path.

+

Any missing nodes are created automatically.

+

Primitive Python types can be set, but preferences are always +stored and returned as strings.

+

e.g:

+
preferences.set('acme.ui.bgcolor', 'blue')
+preferences.get('acme.ui.bgcolor') -> 'blue'
+
+preferences.set('acme.ui.width', 100)
+preferences.get('acme.ui.width') -> '100'
+
+preferences.set('acme.ui.visible', True)
+preferences.get('acme.ui.visible') -> 'True'
+
+
+

Raise a ‘ValueError’ exception if the path is the empty string.

+
+ +
+ +
+
+

apptools.preferences.package_globals module

+

Package-scope globals.

+

The default preferences node is currently used by ‘PreferencesHelper’ and +‘PreferencesBinding’ instances if no specific preferences node is set. This +makes it easy for them to access the root node of an application-wide +preferences hierarchy.

+
+
+apptools.preferences.package_globals.get_default_preferences()[source]
+

Get the default preferences node.

+
+ +
+
+apptools.preferences.package_globals.set_default_preferences(default_preferences)[source]
+

Set the default preferences node.

+
+ +
+
+

apptools.preferences.preference_binding module

+

A binding between a trait on an object and a preference value.

+
+
+class apptools.preferences.preference_binding.PreferenceBinding(**traits)[source]
+

Bases: traits.has_traits.HasTraits

+

A binding between a trait on an object and a preference value.

+
+ +
+
+apptools.preferences.preference_binding.bind_preference(obj, trait_name, preference_path, preferences=None)[source]
+

Create a new preference binding.

+
+ +
+
+

apptools.preferences.preferences module

+

The default implementation of a node in a preferences hierarchy.

+
+
+class apptools.preferences.preferences.Preferences(**traits)[source]
+

Bases: traits.has_traits.HasTraits

+

The default implementation of a node in a preferences hierarchy.

+
+
+add_preferences_listener(listener, path='')[source]
+

Add a listener for changes to a node’s preferences.

+
+ +
+
+clear(path='')[source]
+

Remove all preferences from the node at the specified path.

+
+ +
+
+dump(indent='')[source]
+

Dump the preferences hierarchy to stdout.

+
+ +
+
+flush()[source]
+

Force any changes in the node to the backing store.

+

This includes any changes to the node’s descendants.

+
+ +
+
+get(path, default=None, inherit=False)[source]
+

Get the value of the preference at the specified path.

+
+ +
+
+keys(path='')[source]
+

Return the preference keys of the node at the specified path.

+
+ +
+
+load(file_or_filename=None)[source]
+

Load preferences from a file.

+

This is a merge operation i.e. the contents of the file are added to +the node.

+

This implementation uses ‘ConfigObj’ files.

+
+ +
+
+node(path='')[source]
+

Return the node at the specified path.

+
+ +
+
+node_exists(path='')[source]
+

Return True if the node at the specified path exists.

+
+ +
+
+node_names(path='')[source]
+

Return the names of the children of the node at the specified path.

+
+ +
+
+remove(path)[source]
+

Remove the preference at the specified path.

+
+ +
+
+remove_preferences_listener(listener, path='')[source]
+

Remove a listener for changes to a node’s preferences.

+
+ +
+
+save(file_or_filename=None)[source]
+

Save the node’s preferences to a file.

+

This implementation uses ‘ConfigObj’ files.

+
+ +
+
+set(path, value)[source]
+

Set the value of the preference at the specified path.

+
+ +
+ +
+
+

apptools.preferences.preferences_helper module

+

An object that can be initialized from a preferences node.

+
+
+class apptools.preferences.preferences_helper.PreferencesHelper(**traits)[source]
+

Bases: traits.has_traits.HasTraits

+

A base class for objects that can be initialized from a preferences +node.

+

Additional traits defined on subclasses will be listened to. Changes +are then synchronized with the preferences. Note that mutations on nested +containers e.g. List(List(Str)) cannot be synchronized and should be +avoided.

+
+ +
+
+

apptools.preferences.scoped_preferences module

+

A preferences node that adds the notion of preferences scopes.

+
+
+class apptools.preferences.scoped_preferences.ScopedPreferences(**traits)[source]
+

Bases: apptools.preferences.preferences.Preferences

+

A preferences node that adds the notion of preferences scopes.

+

Scopes provide a way to access preferences in a precedence order, usually +depending on where they came from, for example from the command-line, +or set by the user in a preferences file, or the defaults (set by the +developer).

+

By default, this class provides two scopes - ‘application’ which is +persistent and ‘default’ which is not.

+

Path names passed to ‘ScopedPreferences’ nodes can be either:

+
a) a preference path as used in a standard 'Preferences' node, e.g::
+
+'acme.widget.bgcolor'.
+
+In this case the operation either takes place in the primary scope
+(for operations such as 'set' etc), or on all scopes in precedence
+order (for operations such as 'get' etc).
+
+or
+
+b) a preference path that refers to a specific scope e.g::
+
+'default/acme.widget.bgcolor'
+
+In this case the operation takes place *only* in the specified scope.
+
+
+

There is one drawback to this scheme. If you want to access a scope node +itself via the ‘clear’, ‘keys’, ‘node’, ‘node_exists’ or ‘node_names’ +methods then you have to append a trailing ‘/’ to the path. Without that, +the node would try to perform the operation in the primary scope.

+

e.g. To get the names of the children of the ‘application’ scope, use:

+
scoped.node_names('application/')
+
+
+

If you did this:

+
scoped.node_names('application')
+
+
+

Then the node would get the primary scope and try to find its child node +called ‘application’.

+

Of course you can just get the scope via:

+
application_scope = scoped.get_scope('application')
+
+
+

and then call whatever methods you like on it - which is definitely more +intentional and is highly recommended:

+
application_scope.node_names()
+
+
+
+
+add_preferences_listener(listener, path='')[source]
+

Add a listener for changes to a node’s preferences.

+
+ +
+
+clear(path='')[source]
+

Remove all preference from the node at the specified path.

+
+ +
+
+dump(indent='')[source]
+

Dump the preferences hierarchy to stdout.

+
+ +
+
+get(path, default=None, inherit=False)[source]
+

Get the value of the preference at the specified path.

+
+ +
+
+get_scope(scope_name)[source]
+

Return the scope with the specified name.

+

Return None if no such scope exists.

+
+ +
+
+keys(path='')[source]
+

Return the preference keys of the node at the specified path.

+
+ +
+
+load(file_or_filename=None)[source]
+

Load preferences from a file.

+

This loads the preferences into the primary scope.

+

fixme: I’m not sure it is worth providing an implentation here. I +think it would be better to encourage people to explicitly reference +a particular scope.

+
+ +
+
+node(path='')[source]
+

Return the node at the specified path.

+
+ +
+
+node_exists(path='')[source]
+

Return True if the node at the specified path exists.

+
+ +
+
+node_names(path='')[source]
+

Return the names of the children of the node at the specified path.

+
+ +
+
+remove(path)[source]
+

Remove the preference at the specified path.

+
+ +
+
+remove_preferences_listener(listener, path='')[source]
+

Remove a listener for changes to a node’s preferences.

+
+ +
+
+save(file_or_filename=None)[source]
+

Save the node’s preferences to a file.

+

This asks each scope in turn to save its preferences.

+

If a file or filename is specified then it is only passed to the +primary scope.

+
+ +
+
+set(path, value)[source]
+

Set the value of the preference at the specified path.

+
+ +
+ +
+
+

Module contents

+

Manages application preferences. +Part of the AppTools project of the Enthought Tool Suite

+
+
+ + +
+
+
+ +
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/api/apptools.preferences.ui.html b/5.0/api/apptools.preferences.ui.html new file mode 100644 index 000000000..fa72bce2b --- /dev/null +++ b/5.0/api/apptools.preferences.ui.html @@ -0,0 +1,405 @@ + + + + + + + apptools.preferences.ui package — Apptools Documentation + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ + + + +
+
+ +
+
+ +
+

apptools.preferences.ui package

+
+

Submodules

+
+
+

apptools.preferences.ui.api module

+
+
+

apptools.preferences.ui.i_preferences_page module

+

The interface for pages in a preferences dialog.

+
+
+class apptools.preferences.ui.i_preferences_page.IPreferencesPage[source]
+

Bases: traits.has_traits.Interface

+

The interface for pages in a preferences dialog.

+
+
+apply()[source]
+

Apply the page’s preferences.

+
+ +
+ +
+
+

apptools.preferences.ui.preferences_manager module

+

The preferences manager.

+
+
+class apptools.preferences.ui.preferences_manager.PreferencesHelpWindow[source]
+

Bases: traits.has_traits.HasTraits

+

Container class to present a view with string info.

+
+
+traits_view()[source]
+

Default view to show for this class.

+
+ +
+ +
+
+class apptools.preferences.ui.preferences_manager.PreferencesManager[source]
+

Bases: traits.has_traits.HasTraits

+

The preferences manager.

+
+
+apply()[source]
+

Apply all changes made in the manager.

+
+ +
+
+traits_view()[source]
+

Default traits view for this class.

+
+ +
+ +
+
+class apptools.preferences.ui.preferences_manager.PreferencesManagerHandler[source]
+

Bases: traitsui.handler.Handler

+

The traits UI handler for the preferences manager.

+
+
+apply(info)[source]
+

Handle the Apply button being clicked.

+
+ +
+
+close(info, is_ok)[source]
+

Close a dialog-based user interface.

+
+ +
+
+init(info)[source]
+

Initialize the controls of a user interface.

+
+ +
+
+preferences_help(info)[source]
+

Custom preferences help panel. The Traits help doesn’t work.

+
+ +
+ +
+
+

apptools.preferences.ui.preferences_node module

+

Abstract base class for a node in a preferences dialog.

+
+
+class apptools.preferences.ui.preferences_node.PreferencesNode[source]
+

Bases: apptools.preferences.ui.tree_item.TreeItem

+

Abstract base class for a node in a preferences dialog.

+

A preferences node has a name and an image which are used to represent the +node in a preferences dialog (usually in the form of a tree).

+
+
+create_page(parent)[source]
+

Creates the preference page for this node.

+
+ +
+
+dump(indent='')[source]
+

Pretty-print the node to stdout.

+
+ +
+
+lookup(name)[source]
+

Returns the child of this node with the specified Id.

+

Returns None if no such child exists.

+
+ +
+ +
+
+

apptools.preferences.ui.preferences_page module

+

A page in a preferences dialog.

+
+
+class apptools.preferences.ui.preferences_page.PreferencesPage(**traits)[source]
+

Bases: apptools.preferences.preferences_helper.PreferencesHelper

+

A page in a preferences dialog.

+
+
+apply()[source]
+

Apply the page’s preferences.

+
+ +
+ +
+
+

apptools.preferences.ui.tree_item module

+

A generic base-class for items in a tree data structure.

+

An example:-

+

root = TreeItem(data=’Root’)

+

fruit = TreeItem(data=’Fruit’) +fruit.append(TreeItem(data=’Apple’, allows_children=False)) +fruit.append(TreeItem(data=’Orange’, allows_children=False)) +fruit.append(TreeItem(data=’Pear’, allows_children=False)) +root.append(fruit)

+

veg = TreeItem(data=’Veg’) +veg.append(TreeItem(data=’Carrot’, allows_children=False)) +veg.append(TreeItem(data=’Cauliflower’, allows_children=False)) +veg.append(TreeItem(data=’Sprout’, allows_children=False)) +root.append(veg)

+
+
+class apptools.preferences.ui.tree_item.TreeItem[source]
+

Bases: traits.has_traits.HasTraits

+

A generic base-class for items in a tree data structure.

+
+
+append(child)[source]
+

Appends a child to this item.

+

This removes the child from its current parent (if it has one).

+
+ +
+
+insert(index, child)[source]
+

Inserts a child into this item at the specified index.

+

This removes the child from its current parent (if it has one).

+
+ +
+
+insert_after(after, child)[source]
+

Inserts a child into this item after the specified item.

+

This removes the child from its current parent (if it has one).

+
+ +
+
+insert_before(before, child)[source]
+

Inserts a child into this item before the specified item.

+

This removes the child from its current parent (if it has one).

+
+ +
+
+remove(child)[source]
+

Removes a child from this item.

+
+ +
+ +
+
+

apptools.preferences.ui.widget_editor module

+

An instance editor that allows total control over widget creation.

+
+
+class apptools.preferences.ui.widget_editor.WidgetEditor(*args, **traits)[source]
+

Bases: traitsui.editor_factory.EditorFactory

+

A factory widget editors.

+
+
+custom_editor(ui, object, name, description, parent)
+

Create a simple editor.

+
+ +
+
+readonly_editor(ui, object, name, description, parent)
+

Create a simple editor.

+
+ +
+
+simple_editor(ui, object, name, description, parent)[source]
+

Create a simple editor.

+
+ +
+
+text_editor(ui, object, name, description, parent)
+

Create a simple editor.

+
+ +
+ +
+
+

Module contents

+
+
+ + +
+
+
+ +
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/api/apptools.scripting.html b/5.0/api/apptools.scripting.html new file mode 100644 index 000000000..8ec515512 --- /dev/null +++ b/5.0/api/apptools.scripting.html @@ -0,0 +1,414 @@ + + + + + + + apptools.scripting package — Apptools Documentation + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +
+

apptools.scripting package

+
+

Submodules

+
+
+

apptools.scripting.api module

+

Public API for the scripting package.

+
+
+

apptools.scripting.package_globals module

+

Globals for the scripting package.

+
+
+apptools.scripting.package_globals.get_recorder()[source]
+

Return the global recorder. Does not create a new one if none +exists.

+
+ +
+
+apptools.scripting.package_globals.set_recorder(rec)[source]
+

Set the global recorder instance.

+
+ +
+
+

apptools.scripting.recordable module

+

Decorator to mark functions and methods as recordable.

+
+
+apptools.scripting.recordable.recordable(func)[source]
+

A decorator that wraps a function into one that is recordable.

+

This will record the function only if the global recorder has been +set via a set_recorder function call.

+
+ +
+
+

apptools.scripting.recorder module

+

Code to support recording to a readable and executable Python script.

+
+
FIXME:
    +
  • Support for dictionaries?

  • +
+
+
+
+
+class apptools.scripting.recorder.Recorder[source]
+

Bases: traits.has_traits.HasTraits

+
+
+clear()[source]
+

Clears all previous recorded state and unregisters all +registered objects.

+
+ +
+
+get_code()[source]
+

Returns the recorded lines as a string of printable code.

+
+ +
+
+get_object_path(object)[source]
+

Returns the path in the object hierarchy of a registered +object. Useful for debugging.

+
+ +
+
+get_script_id(object)[source]
+

Returns the script_id of a registered object. Useful when +you want to manually add a record statement.

+
+ +
+
+is_registered(object)[source]
+

Returns True if the given object is registered with the +recorder.

+
+ +
+
+record(code)[source]
+

Record a string to be stored to the output file.

+
+
Parameters
+

code (str) – A string of text.

+
+
+
+ +
+
+record_function(func, args, kw)[source]
+

Record a function call given the function and its +arguments.

+
+ +
+
+register(object, parent=None, trait_name_on_parent='', ignore=None, known=False, script_id=None)[source]
+

Register an object with the recorder. This sets up the +object for recording.

+

By default all traits (except those starting and ending with +‘_’) are recorded. For attributes that are themselves +recordable, one may mark traits with a ‘record’ metadata as +follows:

+
    +
  • If metadata record=False is set, the nested object will not be +recorded.

  • +
  • If record=True, then that object is also recorded if it is +not None.

  • +
+

If the object is a list or dict that is marked with +record=True, the list is itself not listened to for changes +but all its contents are registered.

+

If the object has a trait named recorder then this recorder +instance will be set to it if possible.

+
+
Parameters
+
    +
  • object (Instance(HasTraits)) – The object to register in the registry.

  • +
  • parent (Instance(HasTraits)) – An optional parent object in which object is contained

  • +
  • trait_name_on_parent (str) – An optional trait name of the object in the parent.

  • +
  • ignore (list(str)) – An optional list of trait names on the object to be +ignored.

  • +
  • known (bool) – Optional specification if the object id is known on the +interpreter. This is needed if you are manually injecting +code to define/create an object.

  • +
  • script_id (str) – Optionally specify a script_id to use for this object. It +is not guaranteed that this ID will be used since it may +already be in use.

  • +
+
+
+
+ +
+
+save(file)[source]
+

Save the recorded lines to the given file. It does not close +the file.

+
+ +
+
+ui_save()[source]
+

Save recording to file, pop up a UI dialog to find out where +and close the file when done.

+
+ +
+
+unregister(object)[source]
+

Unregister the given object from the recorder. This inverts +the logic of the register(…) method.

+
+ +
+
+write_script_id_in_namespace(script_id)[source]
+

If a script_id is not known in the current script’s namespace, +this sets it using the path of the object or actually +instantiating it. If this is not possible (since the script_id +matches no existing object), nothing is recorded but the +framework is notified that the particular script_id is available +in the namespace. This is useful when you want to inject code +in the namespace to create a particular object.

+
+ +
+ +
+
+exception apptools.scripting.recorder.RecorderError[source]
+

Bases: Exception

+
+ +
+
+

apptools.scripting.recorder_with_ui module

+

A Recorder subclass that presents a simple user interface.

+
+
+class apptools.scripting.recorder_with_ui.CloseHandler[source]
+

Bases: traitsui.handler.Handler

+

This class cleans up after the UI for the recorder is closed.

+
+
+close(info, is_ok)[source]
+

This method is invoked when the user closes the UI.

+
+ +
+ +
+
+class apptools.scripting.recorder_with_ui.RecorderWithUI[source]
+

Bases: apptools.scripting.recorder.Recorder

+

This class represents a Recorder but with a simple user interface.

+
+
+on_ui_close()[source]
+

Called from the CloseHandler when the UI is closed. This +method basically stops the recording.

+
+ +
+ +
+
+

apptools.scripting.util module

+

Simple utility functions provided by the scripting API.

+
+
+apptools.scripting.util.start_recording(object, ui=True, **kw)[source]
+

Convenience function to start recording. Returns the recorder.

+
+
Parameters
+
    +
  • object (object to record.) –

  • +
  • ui (bool specifying if a UI is to be shown or not) –

  • +
  • kw (Keyword arguments to pass to the register function of the) –

  • +
  • recorder.

  • +
+
+
+
+ +
+
+apptools.scripting.util.stop_recording(object, save=True)[source]
+

Stop recording the object. If save is True, this will pop up +a UI to ask where to save the script.

+
+ +
+
+

Module contents

+

Automatic script recording framework, part of the AppTools project +of the Enthought Tool Suite.

+
+
+ + +
+
+
+ +
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/api/apptools.selection.html b/5.0/api/apptools.selection.html new file mode 100644 index 000000000..a8950c469 --- /dev/null +++ b/5.0/api/apptools.selection.html @@ -0,0 +1,482 @@ + + + + + + + apptools.selection package — Apptools Documentation + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +
+

apptools.selection package

+
+

Submodules

+
+
+

apptools.selection.api module

+
+
+

apptools.selection.errors module

+
+
+exception apptools.selection.errors.IDConflictError(provider_id)[source]
+

Bases: Exception

+

Raised when a provider is added and its ID is already registered.

+
+ +
+
+exception apptools.selection.errors.ListenerNotConnectedError(provider_id, listener)[source]
+

Bases: Exception

+

Raised when a listener that was never connected is disconnected.

+
+ +
+
+exception apptools.selection.errors.ProviderNotRegisteredError(provider_id)[source]
+

Bases: Exception

+

Raised when a provider is requested by ID and not found.

+
+ +
+
+

apptools.selection.i_selection module

+
+
+class apptools.selection.i_selection.IListSelection[source]
+

Bases: apptools.selection.i_selection.ISelection

+

Selection for ordered sequences of items.

+
+
+indices = List
+

Indices of the selected objects in the selection provider.

+
+ +
+
+items = List
+

Selected objects.

+
+ +
+ +
+
+class apptools.selection.i_selection.ISelection[source]
+

Bases: traits.has_traits.Interface

+

Collection of selected items.

+
+
+is_empty()[source]
+

Is the selection empty?

+
+ +
+
+provider_id = Str
+

ID of the selection provider that created this selection object.

+
+ +
+ +
+
+

apptools.selection.i_selection_provider module

+
+
+class apptools.selection.i_selection_provider.ISelectionProvider[source]
+

Bases: traits.has_traits.Interface

+

Source of selections.

+
+
+get_selection()[source]
+

Return the current selection.

+
+
Returns
+

+
selection – ISelection

Object representing the current selection.

+
+
+

+
+
+
+ +
+
+provider_id = Str()
+

Unique ID identifying the provider.

+
+ +
+
+selection = Event
+

Event triggered when the selection changes. +The content of the event is an ISelection instance.

+
+ +
+
+set_selection(items, ignore_missing=False)[source]
+

Set the current selection to the given items.

+

If ignore_missing is True, items that are not available in the +selection provider are silently ignored. If it is False (default), +an ValueError should be raised.

+
+
Parameters
+
    +
  • -- list (items) – List of items to be selected.

  • +
  • -- bool (ignore_missing) – If False (default), the provider raises an exception if any +of the items in items is not available to be selected. +Otherwise, missing elements are silently ignored, and the rest +is selected.

  • +
+
+
+
+ +
+ +
+
+

apptools.selection.list_selection module

+
+
+class apptools.selection.list_selection.ListSelection[source]
+

Bases: traits.has_traits.HasTraits

+

Selection for ordered sequences of items.

+

This is the default implementation of the IListSelection +interface.

+
+
+classmethod from_available_items(provider_id, selected, all_items)[source]
+

Create a list selection given a list of all available items.

+

Fills in the required information (in particular, the indices) based +on a list of selected items and a list of all available items.

+
+

Note

+
    +
  • The list of available items must not contain any duplicate items.

  • +
  • It is expected that selected is populated by items in +all_items.

  • +
+
+
+ +
+
+indices = List
+

Indices of the selected objects in the selection provider.

+
+ +
+
+is_empty()[source]
+

Is the selection empty?

+
+ +
+
+items = List
+

Selected objects.

+
+ +
+
+provider_id = Str
+

ID of the selection provider that created this selection object.

+
+ +
+ +
+
+

apptools.selection.selection_service module

+
+
+class apptools.selection.selection_service.SelectionService[source]
+

Bases: traits.has_traits.HasTraits

+

The selection service connects selection providers and listeners.

+

The selection service is a register of selection providers, i.e., objects +that publish their current selection.

+

Selections can be requested actively, by explicitly requesting the current +selection in a provider (get_selection(id)()), or passively by +connecting selection listeners.

+
+
+add_selection_provider(provider)[source]
+

Add a selection provider.

+

The provider is identified by its ID. If a provider with the same +ID has been already registered, an IDConflictError +is raised.

+
+
Parameters
+

-- ISelectionProvider (provider) – The selection provider added to the internal registry.

+
+
+
+ +
+
+connect_selection_listener(provider_id, func)[source]
+

Connect a listener to selection events from a specific provider.

+

The signature if the listener callback is func(i_selection). +The listener is called:

+
    +
  1. When a provider with the given ID is registered, with its initial +selection as argument, or

  2. +
  3. whenever the provider fires a selection event.

  4. +
+

It is perfectly valid to connect a listener before a provider with the +given ID is registered. The listener will remain connected even if +the provider is repeatedly connected and disconnected.

+
+
Parameters
+
    +
  • -- str (provider_id) – The selection provider ID.

  • +
  • -- callable (func) – A callable object that is notified when the selection changes.

  • +
+
+
+
+ +
+
+disconnect_selection_listener(provider_id, func)[source]
+

Disconnect a listener from a specific provider.

+
+
Parameters
+
    +
  • -- str (provider_id) – The selection provider ID.

  • +
  • -- callable (func) – A callable object that is notified when the selection changes.

  • +
+
+
+
+ +
+
+get_selection(provider_id)[source]
+

Return the current selection of the provider with the given ID.

+

If a provider with that ID has not been registered, a +ProviderNotRegisteredError is raised.

+
+
Parameters
+

-- str (provider_id) – The selection provider ID.

+
+
Returns
+

+
selection – ISelection

The current selection of the provider.

+
+
+

+
+
+
+ +
+
+has_selection_provider(provider_id)[source]
+

Has a provider with the given ID been registered?

+
+ +
+
+remove_selection_provider(provider)[source]
+

Remove a selection provider.

+

If the provider has not been registered, a +ProviderNotRegisteredError is raised.

+
+
Parameters
+

-- ISelectionProvider (provider) – The selection provider added to the internal registry.

+
+
+
+ +
+
+set_selection(provider_id, items, ignore_missing=False)[source]
+

Set the current selection in a provider to the given items.

+

If a provider with the given ID has not been registered, a +ProviderNotRegisteredError is raised.

+

If ignore_missing is True, items that are not available in the +selection provider are silently ignored. If it is False (default), +a ValueError should be raised.

+
+
Parameters
+
    +
  • -- str (provider_id) – The selection provider ID.

  • +
  • -- list (items) – List of items to be selected.

  • +
  • -- bool (ignore_missing) – If False (default), the provider raises an exception if any +of the items in items is not available to be selected. +Otherwise, missing elements are silently ignored, and the rest +is selected.

  • +
+
+
+
+ +
+ +
+
+

Module contents

+
+
+ + +
+
+
+ +
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/api/apptools.type_registry.html b/5.0/api/apptools.type_registry.html new file mode 100644 index 000000000..276d40e0b --- /dev/null +++ b/5.0/api/apptools.type_registry.html @@ -0,0 +1,344 @@ + + + + + + + apptools.type_registry package — Apptools Documentation + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +
+

apptools.type_registry package

+
+

Submodules

+
+
+

apptools.type_registry.api module

+
+
+

apptools.type_registry.type_registry module

+
+
+class apptools.type_registry.type_registry.LazyRegistry[source]
+

Bases: apptools.type_registry.type_registry.TypeRegistry

+

A type registry that will lazily import the registered objects.

+

Register ‘__module__:__name__’ strings for the lazily imported objects. +These will only be imported when the matching type is looked up. The module +name must be a fully-qualified absolute name with all of the parent +packages specified.

+
+
+lookup_by_type(typ)[source]
+

Look up the registered object for a type.

+
+ +
+ +
+
+class apptools.type_registry.type_registry.TypeRegistry[source]
+

Bases: object

+

Register objects for types.

+

Each type maintains a stack of registered objects that can be pushed and +popped.

+
+
+lookup(instance)[source]
+

Look up the registered object for the given instance.

+
+
Parameters
+

instance (object) – An instance of a possibly registered type.

+
+
Returns
+

obj – The registered object for the type of the instance, one of the +type’s superclasses, or else one of the ABCs the type implements.

+
+
Return type
+

object

+
+
Raises
+

KeyError if the instance's type has not been registered.

+
+
+
+ +
+
+lookup_all(instance)[source]
+

Look up all the registered objects for the given instance.

+
+
Parameters
+

instance (object) – An instance of a possibly registered type.

+
+
Returns
+

objs – The list of registered objects for the instance. If the given +instance is not registered, its superclasses are searched. If none +of the superclasses are registered, search the possible ABCs.

+
+
Return type
+

list of objects

+
+
Raises
+

KeyError if the instance's type has not been registered.

+
+
+
+ +
+
+lookup_all_by_type(typ)[source]
+

Look up all the registered objects for a type.

+
+
Parameters
+

typ (type) –

+
+
Returns
+

objs – The list of registered objects for the type. If the given type is +not registered, its superclasses are searched. If none of the +superclasses are registered, search the possible ABCs.

+
+
Return type
+

list of objects

+
+
Raises
+

KeyError if the type has not been registered.

+
+
+
+ +
+
+lookup_by_type(typ)[source]
+

Look up the registered object for a type.

+
+
Parameters
+

typ (type) –

+
+
Returns
+

obj – The registered object for the type, one of its superclasses, or +else one of the ABCs it implements.

+
+
Return type
+

object

+
+
Raises
+

KeyError if the type has not been registered.

+
+
+
+ +
+
+pop(typ)[source]
+

Pop a registered object for the given type.

+
+
Parameters
+

typ (type or '__module__:__name__' string for a type) –

+
+
Returns
+

obj – The last registered object for the type.

+
+
Return type
+

object

+
+
Raises
+

KeyError if the type is not registered.

+
+
+
+ +
+
+push(typ, obj)[source]
+

Push an object onto the stack for the given type.

+
+
Parameters
+
    +
  • typ (type or '__module__:__name__' string for a type) –

  • +
  • obj (object) – The object to register.

  • +
+
+
+
+ +
+
+push_abc(typ, obj)[source]
+

Push an object onto the stack for the given ABC.

+
+
Parameters
+
    +
  • typ (abc.ABCMeta) –

  • +
  • obj (object) –

  • +
+
+
+
+ +
+ +
+
+apptools.type_registry.type_registry.get_mro(obj_class)[source]
+

Get a reasonable method resolution order of a class and its +superclasses for both old-style and new-style classes.

+
+ +
+
+

Module contents

+
+
+ + +
+
+
+
+ +
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/api/apptools.undo.action.html b/5.0/api/apptools.undo.action.html new file mode 100644 index 000000000..375395026 --- /dev/null +++ b/5.0/api/apptools.undo.action.html @@ -0,0 +1,241 @@ + + + + + + + apptools.undo.action package — Apptools Documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ + + + +
+
+ +
+
+ +
+

apptools.undo.action package

+
+

Submodules

+
+
+

apptools.undo.action.abstract_command_stack_action module

+
+
+class apptools.undo.action.abstract_command_stack_action.AbstractCommandStackAction(**traits)[source]
+

Bases: pyface.action.action.Action

+

The abstract base class for all actions that operate on a command +stack.

+
+
+destroy()[source]
+

Called when the action is no longer required.

+

By default this method does nothing, but this would be a great place to +unhook trait listeners etc.

+
+ +
+ +
+
+

apptools.undo.action.api module

+
+
+

apptools.undo.action.command_action module

+
+
+class apptools.undo.action.command_action.CommandAction[source]
+

Bases: pyface.action.action.Action

+

The CommandAction class is an Action class that wraps undo/redo +commands. It is only useful for commands that do not take any arguments or +return any result.

+
+
+perform(event)[source]
+

This is reimplemented to push a new command instance onto the +command stack.

+
+ +
+ +
+
+

apptools.undo.action.redo_action module

+
+
+class apptools.undo.action.redo_action.RedoAction(**traits)[source]
+

Bases: apptools.undo.action.abstract_command_stack_action.AbstractCommandStackAction

+

An action that redos the last command undone of the active command +stack.

+
+
+perform(event)[source]
+

Perform the action.

+
+ +
+ +
+
+

apptools.undo.action.undo_action module

+
+
+class apptools.undo.action.undo_action.UndoAction(**traits)[source]
+

Bases: apptools.undo.action.abstract_command_stack_action.AbstractCommandStackAction

+

An action that undos the last command of the active command stack.

+
+
+perform(event)[source]
+

Perform the action.

+
+ +
+ +
+
+

Module contents

+
+
+ + +
+
+
+ +
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/api/apptools.undo.html b/5.0/api/apptools.undo.html new file mode 100644 index 000000000..e78c602c1 --- /dev/null +++ b/5.0/api/apptools.undo.html @@ -0,0 +1,468 @@ + + + + + + + apptools.undo package — Apptools Documentation + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +
+

apptools.undo package

+ +
+

Submodules

+
+
+

apptools.undo.abstract_command module

+
+
+class apptools.undo.abstract_command.AbstractCommand[source]
+

Bases: traits.has_traits.HasTraits

+

The AbstractCommand class is an abstract base class that implements the +ICommand interface.

+
+
+do()[source]
+

This is called by the command stack to do the command and to return +any value. The command must save any state necessary for the ‘redo()’ +and ‘undo()’ methods to work. The class’s __init__() must also ensure +that deep copies of any arguments are made if appropriate. It is +guaranteed that this will only ever be called once and that it will be +called before any call to ‘redo()’ or ‘undo()’.

+
+ +
+
+merge(other)[source]
+

This is called by the command stack to try and merge another +command with this one. True is returned if the commands were merged. +‘other’ is the command that is about to be executed. If the commands +are merged then ‘other’ will discarded and not placed on the command +stack. A subsequent undo or redo of this modified command must have +the same effect as the two original commands.

+
+ +
+
+redo()[source]
+

This is called by the command stack to redo the command. Any +returned value will replace the value that the command stack references +from the original call to ‘do()’ or previous call to ‘redo()’.

+
+ +
+
+undo()[source]
+

This is called by the command stack to undo the command.

+
+ +
+ +
+
+

apptools.undo.api module

+
+
+

apptools.undo.command_stack module

+
+
+class apptools.undo.command_stack.CommandStack[source]
+

Bases: traits.has_traits.HasTraits

+

The CommandStack class is the default implementation of the +ICommandStack interface.

+
+
+begin_macro(name)[source]
+

This begins a macro by creating an empty command with the given +‘name’. All subsequent calls to ‘push()’ create commands that will be +children of the empty command until the next call to ‘end_macro()’. +Macros may be nested. The stack is disabled (ie. nothing can be undone +or redone) while a macro is being created (ie. while there is an +outstanding ‘end_macro()’ call).

+
+ +
+
+clear()[source]
+

This clears the stack, without undoing or redoing any commands, and +leaves the stack in a clean state. It is typically used when all +changes to the data have been abandoned.

+
+ +
+
+end_macro()[source]
+

This ends a macro.

+
+ +
+
+push(command)[source]
+

This executes a command and saves it on the command stack so that +it can be subsequently undone and redone. ‘command’ is an instance +that implements the ICommand interface. Its ‘do()’ method is called +to execute the command. If any value is returned by ‘do()’ then it is +returned by ‘push()’.

+
+ +
+
+redo(sequence_nr=0)[source]
+

If ‘sequence_nr’ is 0 then the last command that was undone is +redone and any result returned. Otherwise commands are redone up to +and including the given ‘sequence_nr’ and any result of the last of +these is returned.

+
+ +
+
+undo(sequence_nr=0)[source]
+

If ‘sequence_nr’ is 0 then the last command is undone. Otherwise +commands are undone up to and including the given ‘sequence_nr’.

+
+ +
+ +
+
+

apptools.undo.i_command module

+
+
+class apptools.undo.i_command.ICommand[source]
+

Bases: traits.has_traits.Interface

+

The command interface. The state of the data can be changed by passing +an instance that implements this interface to the ‘push()’ method of a +command stack along with any arguments.

+
+
+do()[source]
+

This is called by the command stack to do the command and to return +any value. The command must save any state necessary for the ‘redo()’ +and ‘undo()’ methods to work. The class’s __init__() must also ensure +that deep copies of any arguments are made if appropriate. It is +guaranteed that this will only ever be called once and that it will be +called before any call to ‘redo()’ or ‘undo()’.

+
+ +
+
+merge(other)[source]
+

This is called by the command stack to try and merge another +command with this one. True is returned if the commands were merged. +‘other’ is the command that is about to be executed. If the commands +are merged then ‘other’ will discarded and not placed on the command +stack. A subsequent undo or redo of this modified command must have +the same effect as the two original commands.

+
+ +
+
+redo()[source]
+

This is called by the command stack to redo the command. Any +returned value will replace the value that the command stack references +from the original call to ‘do()’ or previous call to ‘redo()’.

+
+ +
+
+undo()[source]
+

This is called by the command stack to undo the command.

+
+ +
+ +
+
+

apptools.undo.i_command_stack module

+
+
+class apptools.undo.i_command_stack.ICommandStack[source]
+

Bases: traits.has_traits.Interface

+

The command stack interface. A command stack is responsible for +managing the changes to a data model and recording those changes so that +they can be undone or redone.

+
+
+begin_macro(name)[source]
+

This begins a macro by creating an empty command with the given +‘name’. The commands passed to all subsequent calls to ‘push()’ will +be contained in the macro until the next call to ‘end_macro()’. Macros +may be nested. The stack is disabled (ie. nothing can be undone or +redone) while a macro is being created (ie. while there is an +outstanding ‘end_macro()’ call).

+
+ +
+
+clear()[source]
+

This clears the stack, without undoing or redoing any commands, and +leaves the stack in a clean state. It is typically used when all +changes to the data have been abandoned.

+
+ +
+
+end_macro()[source]
+

This ends a macro.

+
+ +
+
+push(command)[source]
+

This executes a command and saves it on the command stack so that +it can be subsequently undone and redone. ‘command’ is an instance +that implements the ICommand interface. Its ‘do()’ method is called +to execute the command. If any value is returned by ‘do()’ then it is +returned by ‘push()’. The command stack will keep a reference to the +result so that it can recognise it as an argument to a subsequent +command (which allows a script to properly save a result needed later).

+
+ +
+
+redo(sequence_nr=0)[source]
+

If ‘sequence_nr’ is 0 then the last command that was undone is +redone and any result returned. Otherwise commands are redone up to +and including the given ‘sequence_nr’ and any result of the last of +these is returned.

+
+ +
+
+undo(sequence_nr=0)[source]
+

If ‘sequence_nr’ is 0 then the last command is undone. Otherwise +commands are undone up to and including the given ‘sequence_nr’.

+
+ +
+ +
+
+

apptools.undo.i_undo_manager module

+
+
+class apptools.undo.i_undo_manager.IUndoManager[source]
+

Bases: traits.has_traits.Interface

+

The undo manager interface. An undo manager is responsible for one or +more command stacks. Typically an application would have a single undo +manager.

+
+
+redo()[source]
+

Redo the last undone command of the active command stack.

+
+ +
+
+undo()[source]
+

Undo the last command of the active command stack.

+
+ +
+ +
+
+

apptools.undo.undo_manager module

+
+
+class apptools.undo.undo_manager.UndoManager[source]
+

Bases: traits.has_traits.HasTraits

+

The UndoManager class is the default implementation of the +IUndoManager interface.

+
+
+redo()[source]
+

Redo the last undone command of the active command stack.

+
+ +
+
+undo()[source]
+

Undo the last command of the active command stack.

+
+ +
+ +
+
+

Module contents

+

Supports undoing and scripting application commands. +Part of the AppTools project of the Enthought Tool Suite.

+
+
+ + +
+
+
+ +
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/api/modules.html b/5.0/api/modules.html new file mode 100644 index 000000000..2cef3b579 --- /dev/null +++ b/5.0/api/modules.html @@ -0,0 +1,280 @@ + + + + + + + apptools — Apptools Documentation + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +
+

apptools

+
+ +
+
+ + +
+
+
+
+
+ +

Previous topic

+

API documentation

+

Next topic

+

apptools package

+

This Page

+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/genindex.html b/5.0/genindex.html new file mode 100644 index 000000000..abcc35441 --- /dev/null +++ b/5.0/genindex.html @@ -0,0 +1,1536 @@ + + + + + + + + Index — Apptools Documentation + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ + +

Index

+ +
+ A + | B + | C + | D + | E + | F + | G + | H + | I + | J + | K + | L + | M + | N + | O + | P + | Q + | R + | S + | T + | U + | V + | W + +
+

A

+ + + +
+ +

B

+ + + +
+ +

C

+ + + +
+ +

D

+ + + +
+ +

E

+ + + +
+ +

F

+ + + +
+ +

G

+ + + +
+ +

H

+ + + +
+ +

I

+ + + +
+ +

J

+ + +
+ +

K

+ + +
+ +

L

+ + + +
+ +

M

+ + + +
+ +

N

+ + + +
+ +

O

+ + + +
+ +

P

+ + + +
+ +

Q

+ + +
+ +

R

+ + + +
+ +

S

+ + + +
+ +

T

+ + + +
+ +

U

+ + + +
+ +

V

+ + + +
+ +

W

+ + + +
+ + + +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/index.html b/5.0/index.html new file mode 100644 index 000000000..53e7b1311 --- /dev/null +++ b/5.0/index.html @@ -0,0 +1,179 @@ + + + + + + + AppTools Documentation — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + + +
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/io/introduction.html b/5.0/io/introduction.html new file mode 100644 index 000000000..1f4d10d3d --- /dev/null +++ b/5.0/io/introduction.html @@ -0,0 +1,169 @@ + + + + + + + File I/O — Apptools Documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +
+

File I/O

+

The apptools.io package provides a traited File object provides +properties and methods for common file path manipulation operations. Much of +this functionality was implemented before Python 3 pathlib standard library +became available to provide similar support. For new code we encourage users +to investigate if pathlib can satisfy their use cases before they turn to +the apptools.io File object

+
+

HDF5 File Support

+

The apptools.io.h5 sub-package provides a wrapper around PyTables +with a dictionary-style mapping.

+
+
+ + +
+
+
+
+
+ +

Table of Contents

+ + +

Previous topic

+

Naming

+

Next topic

+

API documentation

+

This Page

+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/naming/Introduction.html b/5.0/naming/Introduction.html new file mode 100644 index 000000000..6c5f08423 --- /dev/null +++ b/5.0/naming/Introduction.html @@ -0,0 +1,154 @@ + + + + + + + Naming — Apptools Documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +
+

Naming

+

apptools.naming package is a Python implementation of the Naming portion of the Java +Naming and Directory Interface, +including specific implementations for a heirarchy of Python objects. You can +also find the Java JNDI tutorial here.

+
+ + +
+
+
+
+
+ +

Previous topic

+

The selection service

+

Next topic

+

File I/O

+

This Page

+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/objects.inv b/5.0/objects.inv new file mode 100644 index 0000000000000000000000000000000000000000..784d3243de5585acd54760ce4354ddbbff8cf933 GIT binary patch literal 4807 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 + + + + + + Preferences — Apptools Documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +
+

Preferences

+

The preferences package provides a simple API for managing application +preferences. The classes in the package are implemented using a layered +approach where the lowest layer provides access to the raw preferences +mechanism and each layer on top providing more convenient ways to get and set +preference values.

+
+

The Basic Preferences Mechanism

+

Lets start by taking a look at the lowest layer which consists of the +IPreferences interface and its default implementation in the Preferences +class. This layer implements the basic preferences system which is a +hierarchical arrangement of preferences ‘nodes’ (where each node is simply an +object that implements the IPreferences interface). Nodes in the hierarchy can +contain preference settings and/or child nodes. This layer also provides a +default way to read and write preferences from the filesystem using the +excellent ConfigObj package.

+

This all sounds a bit complicated but, believe me, it isn’t! To prove it +(hopefully) lets look at an example. Say I have the following preferences in +a file ‘example.ini’:

+
[acme.ui]
+bgcolor = blue
+width = 50
+ratio = 1.0
+visible = True
+
+[acme.ui.splash_screen]
+image = splash
+fgcolor = red
+
+
+

I can create a preferences hierarchy from this file by:

+
>>> from apptools.preferences.api import Preferences
+>>> preferences = Preferences(filename='example.ini')
+>>> preferences.dump()
+
+ Node() {}
+   Node(acme) {}
+     Node(ui) {'bgcolor': 'blue', 'width': '50', 'ratio': '1.0', 'visible': 'True'}
+       Node(splash_screen) {'image': 'splash', 'fgcolor': 'red'}
+
+
+

The ‘dump’ method (useful for debugging etc) simply ‘pretty prints’ a +preferences hierarchy. The dictionary next to each node contains the node’s +actual preferences. In this case, the root node (the node with no name) is the +preferences object that we created. This node now has one child node ‘acme’, +which contains no preferences. The ‘acme’ node has one child, ‘ui’, which +contains some preferences (e.g. ‘bgcolor’) and also a child node +‘splash_screen’ which also contains preferences (e.g. ‘image’).

+

To look up a preference we use:

+
>>> preferences.get('acme.ui.bgcolor')
+'blue'
+
+
+

If no such preferences exists then, by default, None is returned:

+
>>> preferences.get('acme.ui.bogus') is None
+True
+
+
+

You can also specify an explicit default value:

+
>>> preferences.get('acme.ui.bogus', 'fred')
+'fred'
+
+
+

To set a preference we use:

+
>>> preferences.set('acme.ui.bgcolor', 'red')
+>>> preferences.get('acme.ui.bgcolor')
+'red'
+
+
+

And to make sure the preferences are saved back to disk:

+
>>> preferences.flush()
+
+
+

To add a new preference value we simply set it:

+
>>> preferences.set('acme.ui.fgcolor', 'black')
+>>> preferences.get('acme.ui.fgcolor')
+'black'
+
+
+

Any missing nodes in a call to ‘set’ are created automatically, hence:

+
>>> preferences.set('acme.ui.button.fgcolor', 'white')
+>>> preferences.get('acme.ui.button.fgcolor')
+'white'
+
+
+

Preferences can also be ‘inherited’. e.g. Notice that the ‘splash_screen’ +node does not contain a ‘bgcolor’ preference, and hence:

+
>>> preferences.get('acme.ui.splash_screen.bgcolor') is None
+True
+
+
+

But if we allow the ‘inheritance’ of preference values then:

+
>>> preferences.get('acme.ui.splash_screen.bgcolor', inherit=True)
+'red'
+
+
+

By using ‘inheritance’ here the preferences system will try the following +preferences:

+
'acme.ui.splash_screen.bgcolor'
+'acme.ui.bgcolor'
+'acme.bgcolor'
+'bgcolor'
+
+
+
+

Strings, Glorious Strings

+

At this point it is worth mentioning that preferences are always stored and +returned as strings. This is because of the limitations of the traditional +‘.ini’ file format i.e. they don’t contain any type information! Now before you +start panicking, this doesn’t mean that all of your preferences have to be +strings! Currently the preferences system allows, strings(!), booleans, ints, +longs, floats and complex numbers. When you store a non-string value it gets +converted to a string for you, but you always get a string back:

+
>>> preferences.get('acme.ui.width')
+'50'
+>>> preferences.set('acme.ui.width', 100)
+>>> preferences.get('acme.ui.width')
+'100'
+
+>>> preferences.get('acme.ui.visible')
+'True'
+>>> preferences.set('acme.ui.visible', False)
+>>> preferences.get('acme.ui.visible')
+'False'
+
+
+

This is obviously not terribly convenient, and so the following section +discusses how we associate type information with our preferences to make +getting and setting them more natural.

+
+
+
+

Preferences and Types

+

As mentioned previously, we would like to be able to get and set non-string +preferences in a more convenient way. This is where the PreferencesHelper +class comes in.

+

Let’s take another look at ‘example.ini’:

+
[acme.ui]
+bgcolor = blue
+width = 50
+ratio = 1.0
+visible = True
+
+[acme.ui.splash_screen]
+image = splash
+fgcolor = red
+
+
+

Say, I am interested in the preferences in the ‘acme.ui’ section. I can use a +preferences helper as follows:

+
from apptools.preferences.api import PreferencesHelper
+
+class SplashScreenPreferences(PreferencesHelper):
+    """ A preferences helper for the splash screen. """
+
+    preferences_path = 'acme.ui'
+
+    bgcolor = Str
+    width   = Int
+    ratio   = Float
+    visible = Bool
+
+>>> preferences = Preferences(filename='example.ini')
+>>> helper = SplashScreenPreferences(preferences=preferences)
+>>> helper.bgcolor
+'blue'
+>>> helper.width
+50
+>>> helper.ratio
+1.0
+>>> helper.visible
+True
+
+
+

And, obviously, I can set the value of the preferences via the helper too:

+
>>> helper.ratio = 0.5
+
+
+

And if you want to prove to yourself it really did set the preference:

+
>>> preferences.get('acme.ui.ratio')
+'0.5'
+
+
+

Using a preferences helper you also get notified via the usual trait +mechanism when the preferences are changed (either via the helper or via the +preferences node directly:

+
def listener(obj, trait_name, old, new):
+    print(trait_name, old, new)
+
+>>> helper.on_trait_change(listener)
+>>> helper.ratio = 0.75
+ratio 0.5 0.75
+>>> preferences.set('acme.ui.ratio', 0.33)
+ratio 0.75 0.33
+
+
+
+
+

Scoped Preferences

+

In many applications the idea of preferences scopes is useful. In a scoped +system, an actual preference value can be stored in any scope and when a call +is made to the ‘get’ method the scopes are searched in order of precedence.

+

The default implementation (in the ScopedPreferences class) provides two +scopes by default:

+
    +
  1. The application scope

  2. +
+

This scope stores itself in the ‘ETSConfig.application_home’ directory. This +scope is generally used when setting any user preferences.

+
    +
  1. The default scope

  2. +
+

This scope is transient (i.e. it does not store itself anywhere). This scope +is generally used to load any predefined default values into the preferences +system.

+

If you are happy with the default arrangement, then using the scoped +preferences is just like using the plain old non-scoped version:

+
>>> from apptools.preferences.api import ScopedPreferences
+>>> preferences = ScopedPreferences(filename='example.ini')
+>>> preferences.load('example.ini')
+>>> preferences.dump()
+
+ Node() {}
+   Node(application) {}
+     Node(acme) {}
+       Node(ui) {'bgcolor': 'blue', 'width': '50', 'ratio': '1.0', 'visible': 'True'}
+         Node(splash_screen) {'image': 'splash', 'fgcolor': 'red'}
+   Node(default) {}
+
+
+

Here you can see that the root node now has a child node representing each +scope.

+

When we are getting and setting preferences using scopes we generally want the +following behaviour:

+

a) When we get a preference we want to look it up in each scope in order. The +first scope that contains a value ‘wins’.

+

b) When we set a preference, we want to set it in the first scope. By default +this means that when we set a preference it will be set in the application +scope. This is exactly what we want as the application scope is the scope that +is persistent.

+

So usually, we just use the scoped preferences as before:

+
>>> preferences.get('acme.ui.bgcolor')
+'blue'
+>>> preferences.set('acme.ui.bgcolor', 'red')
+>>> preferences.dump()
+
+ Node() {}
+   Node(application) {}
+     Node(acme) {}
+       Node(ui) {'bgcolor': 'red', 'width': '50', 'ratio': '1.0', 'visible': 'True'}
+         Node(splash_screen) {'image': 'splash', 'fgcolor': 'red'}
+   Node(default) {}
+
+
+

And, conveniently, preference helpers work just the same with scoped +preferences too:

+
>>> helper = SplashScreenPreferences(preferences=preferences)
+>>> helper.bgcolor
+'red'
+>>> helper.width
+50
+>>> helper.ratio
+1.0
+>>> helper.visible
+True
+
+
+
+

Accessing a particular scope

+

Should you care about getting or setting a preference in a particular scope +then you use the following syntax:

+
>>> preferences.set('default/acme.ui.bgcolor', 'red')
+>>> preferences.get('default/acme.ui.bgcolor')
+'red'
+>>> preferences.dump()
+
+ Node() {}
+   Node(application) {}
+     Node(acme) {}
+       Node(ui) {'bgcolor': 'red', 'width': '50', 'ratio': '1.0', 'visible': 'True'}
+         Node(splash_screen) {'image': 'splash', 'fgcolor': 'red'}
+   Node(default) {}
+     Node(acme) {}
+       Node(ui) {'bgcolor': 'red'}
+
+
+

You can also get hold of a scope via:

+
>>> default = preferences.get_scope('default')
+
+
+

And then perform any of the usual operations on it.

+
+
+
+

Further Reading

+

So that’s a quick tour around the basic useage of the preferences API. For more +information about what is provided take a look at the API documentation.

+

If you are using Envisage to build your applications then you might also be +interested in the Preferences in Envisage section.

+
+
+ + +
+
+
+
+ +
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/preferences/PreferencesInEnvisage.html b/5.0/preferences/PreferencesInEnvisage.html new file mode 100644 index 000000000..cba25dc9f --- /dev/null +++ b/5.0/preferences/PreferencesInEnvisage.html @@ -0,0 +1,189 @@ + + + + + + + Preferences in Envisage — Apptools Documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +
+

Preferences in Envisage

+

This section discusses how an Envisage application uses the preferences +mechanism. Envisage tries not to dictate too much, and so this describes the +default behaviour, but you are free to override it as desired.

+

Envisage uses the default implementation of the ScopedPreferences class which +is made available via the application’s ‘preferences’ trait:

+
>>> application = Application(id='myapplication')
+>>> application.preferences.set('acme.ui.bgcolor', 'yellow')
+>>> application.preferences.get('acme.ui.bgcolor')
+'yellow'
+
+
+

Hence, you use the Envisage preferences just like you would any other scoped +preferences.

+

It also registers itself as the default preferences node used by the +PreferencesHelper class. Hence you don’t need to provide a preferences node +explicitly to your helper:

+
>>> helper = SplashScreenPreferences()
+>>> helper.bgcolor
+'blue'
+>>> helper.width
+100
+>>> helper.ratio
+1.0
+>>> helper.visible
+True
+
+
+

The only extra thing that Envisage does for you is to provide an extension +point that allows you to contribute any number of ‘.ini’ files that are +loaded into the default scope when the application is started.

+

e.g. To contribute a preference file for my plugin I might use:

+
class MyPlugin(Plugin):
+    ...
+
+    @contributes_to('envisage.preferences')
+    def get_preferences(self, application):
+        return ['pkgfile://mypackage:preferences.ini']
+
+
+
+ + +
+
+
+
+
+ +

Previous topic

+

Preferences

+

Next topic

+

Automatic script recording

+

This Page

+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/py-modindex.html b/5.0/py-modindex.html new file mode 100644 index 000000000..7b3cbde79 --- /dev/null +++ b/5.0/py-modindex.html @@ -0,0 +1,692 @@ + + + + + + + Python Module Index — Apptools Documentation + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ + +

Python Module Index

+ +
+ a +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
+ a
+ apptools +
    + apptools.io +
    + apptools.io.api +
    + apptools.io.file +
    + apptools.io.h5 +
    + apptools.io.h5.dict_node +
    + apptools.io.h5.file +
    + apptools.io.h5.table_node +
    + apptools.io.h5.utils +
    + apptools.logger +
    + apptools.logger.agent +
    + apptools.logger.agent.attachments +
    + apptools.logger.agent.quality_agent_mailer +
    + apptools.logger.agent.quality_agent_view +
    + apptools.logger.api +
    + apptools.logger.custom_excepthook +
    + apptools.logger.log_point +
    + apptools.logger.log_queue_handler +
    + apptools.logger.logger +
    + apptools.logger.plugin +
    + apptools.logger.plugin.logger_plugin +
    + apptools.logger.plugin.logger_preferences +
    + apptools.logger.plugin.logger_service +
    + apptools.logger.plugin.view +
    + apptools.logger.plugin.view.logger_preferences_page +
    + apptools.logger.plugin.view.logger_view +
    + apptools.logger.ring_buffer +
    + apptools.naming +
    + apptools.naming.address +
    + apptools.naming.api +
    + apptools.naming.binding +
    + apptools.naming.context +
    + apptools.naming.dir_context +
    + apptools.naming.dynamic_context +
    + apptools.naming.exception +
    + apptools.naming.initial_context +
    + apptools.naming.initial_context_factory +
    + apptools.naming.naming_event +
    + apptools.naming.naming_manager +
    + apptools.naming.object_factory +
    + apptools.naming.object_serializer +
    + apptools.naming.py_context +
    + apptools.naming.py_object_factory +
    + apptools.naming.pyfs_context +
    + apptools.naming.pyfs_context_factory +
    + apptools.naming.pyfs_initial_context_factory +
    + apptools.naming.pyfs_object_factory +
    + apptools.naming.pyfs_state_factory +
    + apptools.naming.reference +
    + apptools.naming.referenceable +
    + apptools.naming.referenceable_state_factory +
    + apptools.naming.state_factory +
    + apptools.naming.trait_defs +
    + apptools.naming.trait_defs.api +
    + apptools.naming.trait_defs.naming_traits +
    + apptools.naming.unique_name +
    + apptools.persistence +
    + apptools.persistence.file_path +
    + apptools.persistence.project_loader +
    + apptools.persistence.state_pickler +
    + apptools.persistence.updater +
    + apptools.persistence.version_registry +
    + apptools.persistence.versioned_unpickler +
    + apptools.preferences +
    + apptools.preferences.api +
    + apptools.preferences.i_preferences +
    + apptools.preferences.package_globals +
    + apptools.preferences.preference_binding +
    + apptools.preferences.preferences +
    + apptools.preferences.preferences_helper +
    + apptools.preferences.scoped_preferences +
    + apptools.preferences.ui +
    + apptools.preferences.ui.api +
    + apptools.preferences.ui.i_preferences_page +
    + apptools.preferences.ui.preferences_manager +
    + apptools.preferences.ui.preferences_node +
    + apptools.preferences.ui.preferences_page +
    + apptools.preferences.ui.tree_item +
    + apptools.preferences.ui.widget_editor +
    + apptools.scripting +
    + apptools.scripting.api +
    + apptools.scripting.package_globals +
    + apptools.scripting.recordable +
    + apptools.scripting.recorder +
    + apptools.scripting.recorder_with_ui +
    + apptools.scripting.util +
    + apptools.selection +
    + apptools.selection.api +
    + apptools.selection.errors +
    + apptools.selection.i_selection +
    + apptools.selection.i_selection_provider +
    + apptools.selection.list_selection +
    + apptools.selection.selection_service +
    + apptools.type_registry +
    + apptools.type_registry.api +
    + apptools.type_registry.type_registry +
    + apptools.undo +
    + apptools.undo.abstract_command +
    + apptools.undo.action +
    + apptools.undo.action.abstract_command_stack_action +
    + apptools.undo.action.api +
    + apptools.undo.action.command_action +
    + apptools.undo.action.redo_action +
    + apptools.undo.action.undo_action +
    + apptools.undo.api +
    + apptools.undo.command_stack +
    + apptools.undo.i_command +
    + apptools.undo.i_command_stack +
    + apptools.undo.i_undo_manager +
    + apptools.undo.undo_manager +
+ + +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/scripting/introduction.html b/5.0/scripting/introduction.html new file mode 100644 index 000000000..add6b06ce --- /dev/null +++ b/5.0/scripting/introduction.html @@ -0,0 +1,336 @@ + + + + + + + Automatic script recording — Apptools Documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +
+

Automatic script recording

+

This package provides a very handy and powerful Python script recording +facility. This can be used to:

+
+
    +
  • record all actions performed on a traits based UI into a human +readable, Python script that should be able to recreate your UI +actions.

  • +
  • easily learn the scripting API of an application.

  • +
+
+

This package is not just a toy framework and is powerful enough to +provide full script recording to the Mayavi application. Mayavi is a +powerful 3D visualization tool that is part of ETS.

+
+

The scripting API

+

The scripting API primarily allows you to record UI actions for objects +that have Traits. Technically the framework listens to all trait +changes so will work outside a UI. We do not document the full API +here, the best place to look for that is the +apptools.scripting.recorder module which is reasonably well +documented. We provide a high level overview of the library.

+

The quickest way to get started is to look at a small example.

+
+

A tour by example

+

The following example is taken from the test suite. Consider a set of +simple objects organized in a hierarchy:

+
from traits.api import (HasTraits, Float, Instance,
+        Str, List, Bool, HasStrictTraits, Tuple, PrefixMap, Range,
+        Trait)
+from apptools.scripting.api import (Recorder, recordable,
+    set_recorder)
+
+class Property(HasStrictTraits):
+    color = Tuple(Range(0.0, 1.0), Range(0.0, 1.0), Range(0.0, 1.0))
+    opacity = Range(0.0, 1.0, 1.0)
+    representation = PrefixMap(
+        {"surface": 2, "wireframe": 1, "points": 0},
+        default_value="surface"
+    )
+
+class Toy(HasTraits):
+    color = Str
+    type = Str
+    # Note the use of the trait metadata to ignore this trait.
+    ignore = Bool(False, record=False)
+
+class Child(HasTraits):
+    name = Str('child')
+    age = Float(10.0)
+    # The recorder walks through sub-instances if they are marked
+    # with record=True
+    property = Instance(Property, (), record=True)
+    toy = Instance(Toy, record=True)
+    friends = List(Str)
+
+    # The decorator records the method.
+    @recordable
+    def grow(self, x):
+        """Increase age by x years."""
+        self.age += x
+
+class Parent(HasTraits):
+    children = List(Child, record=True)
+    recorder = Instance(Recorder, record=False)
+
+
+

Using these simple classes we first create a simple object hierarchy as +follows:

+
p = Parent()
+c = Child()
+t = Toy()
+c.toy = t
+p.children.append(c)
+
+
+

Given this hierarchy, we’d like to be able to record a script. To do +this we setup the recording infrastructure:

+
from mayavi.core.recorder import Recorder, set_recorder
+# Create a recorder.
+r = Recorder()
+# Set the global recorder so the decorator works.
+set_recorder(r)
+r.register(p)
+r.recording = True
+
+
+

The key method here is the r.register(p) call above. It looks at +the traits of p and finds all traits and nested objects that specify +a record=True in their trait metadata (all methods starting and +ending with _ are ignored). All sub-objects are in turn registered +with the recorder and so on. Callbacks are attached to traits changes +and these are wired up to produce readable and executable code. The +set_recorder(r) call is also very important and sets the global +recorder so the framework listens to any functions that are decorated +with the recordable decorator.

+

Now lets test this out like so:

+
# The following will be recorded.
+c.name = 'Shiva'
+c.property.representation = 'w'
+c.property.opacity = 0.4
+c.grow(1)
+
+
+

To see what’s been recorded do this:

+
print(r.script)
+
+
+

This prints:

+
child = parent.children[0]
+child.name = 'Shiva'
+child.property.representation = 'wireframe'
+child.property.opacity = 0.40000000000000002
+child.grow(1)
+
+
+

The recorder internally maintains a mapping between objects and unique +names for each object. It also stores the information about the +location of a particular object in the object hierarchy. For example, +the path to the Toy instance in the hierarchy above is +parent.children[0].toy. Since scripting with lists this way can be +tedious, the recorder first instantiates the child:

+
child = parent.children[0]
+
+
+

Subsequent lines use the child attribute. The recorder always tries +to instantiate the object referred to using its path information in this +manner.

+

To record a function or method call one must simply decorate the +function/method with the recordable decorator. Nested recordable +functions are not recorded and trait changes are also not recorded if +done inside a recordable function.

+
+

Note

+
    +
  1. It is very important to note that the global recorder must be set +via the set_recorder method. The recordable decorator +relies on this being set to work.

  2. +
  3. The recordable decorator will work with plain Python classes +and with functions too.

  4. +
+
+

To stop recording do this:

+
r.unregister(p)
+r.recording = False
+
+
+

The r.unregister(p) reverses the r.register(p) call and +unregisters all nested objects as well.

+
+
+

Advanced use cases

+

Here are a few advanced use cases.

+
+
    +
  • The API also provides a RecorderWithUI class that provides a +simple user interface that prints the recorded script and allows the +user to save the script.

  • +
  • Sometimes it is not enough to just record trait changes, one may want +to pass an arbitrary string or command when recording is occurring. +To allow for this, if one defines a recorder trait on the object, +it is set to the current recorder. One can then use this recorder to +do whatever one wants. This is very convenient.

  • +
  • To ignore specific traits one must specify either a record=False +metadata to the trait definition or specify a list of strings to the +register method in the ignore keyword argument.

  • +
  • If you want to use a specific name for an object on the script you +can pass the script_id parameter to the register function.

  • +
+
+

For more details on the recorder itself we suggest reading the module +source code. It is fairly well documented and with the above background +should be enough to get you going.

+
+
+
+ + +
+
+
+
+
+ +

Table of Contents

+ + +

Previous topic

+

Preferences in Envisage

+

Next topic

+

Undo Framework

+

This Page

+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/search.html b/5.0/search.html new file mode 100644 index 000000000..fb3726636 --- /dev/null +++ b/5.0/search.html @@ -0,0 +1,143 @@ + + + + + + + Search — Apptools Documentation + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Search

+
+ +

+ Please activate JavaScript to enable the search + functionality. +

+
+

+ From here you can search these documents. Enter your search + words into the box below and click "search". Note that the search + function will automatically search for all of the words. Pages + containing fewer words won't appear in the result list. +

+
+ + + +
+ +
+ +
+ +
+
+
+
+
+ +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/searchindex.js b/5.0/searchindex.js new file mode 100644 index 000000000..2f5fef6a4 --- /dev/null +++ b/5.0/searchindex.js @@ -0,0 +1 @@ +Search.setIndex({docnames:["api","api/apptools","api/apptools.io","api/apptools.io.h5","api/apptools.logger","api/apptools.logger.agent","api/apptools.logger.plugin","api/apptools.logger.plugin.view","api/apptools.naming","api/apptools.naming.trait_defs","api/apptools.persistence","api/apptools.preferences","api/apptools.preferences.ui","api/apptools.scripting","api/apptools.selection","api/apptools.type_registry","api/apptools.undo","api/apptools.undo.action","api/modules","index","io/introduction","naming/Introduction","preferences/Preferences","preferences/PreferencesInEnvisage","scripting/introduction","selection/selection","undo/Introduction"],envversion:{"sphinx.domains.c":1,"sphinx.domains.changeset":1,"sphinx.domains.citation":1,"sphinx.domains.cpp":1,"sphinx.domains.javascript":1,"sphinx.domains.math":2,"sphinx.domains.python":1,"sphinx.domains.rst":1,"sphinx.domains.std":1,"sphinx.ext.viewcode":1,sphinx:56},filenames:["api.rst","api/apptools.rst","api/apptools.io.rst","api/apptools.io.h5.rst","api/apptools.logger.rst","api/apptools.logger.agent.rst","api/apptools.logger.plugin.rst","api/apptools.logger.plugin.view.rst","api/apptools.naming.rst","api/apptools.naming.trait_defs.rst","api/apptools.persistence.rst","api/apptools.preferences.rst","api/apptools.preferences.ui.rst","api/apptools.scripting.rst","api/apptools.selection.rst","api/apptools.type_registry.rst","api/apptools.undo.rst","api/apptools.undo.action.rst","api/modules.rst","index.rst","io/introduction.rst","naming/Introduction.rst","preferences/Preferences.rst","preferences/PreferencesInEnvisage.rst","scripting/introduction.rst","selection/selection.rst","undo/Introduction.rst"],objects:{"":{apptools:[1,0,0,"-"]},"apptools.io":{api:[2,0,0,"-"],file:[2,0,0,"-"],h5:[3,0,0,"-"]},"apptools.io.file":{File:[2,1,1,""]},"apptools.io.file.File":{"delete":[2,2,1,""],copy:[2,2,1,""],create_file:[2,2,1,""],create_folder:[2,2,1,""],create_folders:[2,2,1,""],create_package:[2,2,1,""],make_writeable:[2,2,1,""],move:[2,2,1,""]},"apptools.io.h5":{dict_node:[3,0,0,"-"],file:[3,0,0,"-"],table_node:[3,0,0,"-"],utils:[3,0,0,"-"]},"apptools.io.h5.dict_node":{ARRAY_PROXY_KEY:[3,3,1,""],H5DictNode:[3,1,1,""]},"apptools.io.h5.dict_node.H5DictNode":{add_to_h5file:[3,2,1,""],data:[3,2,1,""],flush:[3,2,1,""],is_dict_node:[3,2,1,""],keys:[3,2,1,""]},"apptools.io.h5.file":{H5Attrs:[3,1,1,""],H5File:[3,1,1,""],H5Group:[3,1,1,""],get_atom:[3,5,1,""],h5_group_wrapper:[3,5,1,""],iterator_length:[3,5,1,""]},"apptools.io.h5.file.H5Attrs":{get:[3,2,1,""],items:[3,2,1,""],keys:[3,2,1,""],values:[3,2,1,""]},"apptools.io.h5.file.H5File":{close:[3,2,1,""],create_array:[3,2,1,""],create_dict:[3,2,1,""],create_group:[3,2,1,""],create_table:[3,2,1,""],exists_error:[3,4,1,""],is_open:[3,2,1,""],iteritems:[3,2,1,""],join_path:[3,2,1,""],open:[3,2,1,""],remove_group:[3,2,1,""],remove_node:[3,2,1,""],root:[3,2,1,""],split_path:[3,2,1,""]},"apptools.io.h5.file.H5Group":{children_names:[3,2,1,""],create_array:[3,2,1,""],create_dict:[3,2,1,""],create_group:[3,2,1,""],create_table:[3,2,1,""],filename:[3,2,1,""],iter_groups:[3,2,1,""],name:[3,2,1,""],pathname:[3,2,1,""],remove_group:[3,2,1,""],remove_node:[3,2,1,""],root:[3,2,1,""],subgroup_names:[3,2,1,""]},"apptools.io.h5.table_node":{H5TableNode:[3,1,1,""]},"apptools.io.h5.table_node.H5TableNode":{add_to_h5file:[3,2,1,""],append:[3,2,1,""],is_table_node:[3,2,1,""],ix:[3,2,1,""],keys:[3,2,1,""],to_dataframe:[3,2,1,""]},"apptools.io.h5.utils":{open_h5file:[3,5,1,""]},"apptools.logger":{agent:[5,0,0,"-"],api:[4,0,0,"-"],custom_excepthook:[4,0,0,"-"],log_point:[4,0,0,"-"],log_queue_handler:[4,0,0,"-"],logger:[4,0,0,"-"],plugin:[6,0,0,"-"],ring_buffer:[4,0,0,"-"]},"apptools.logger.agent":{attachments:[5,0,0,"-"],quality_agent_mailer:[5,0,0,"-"],quality_agent_view:[5,0,0,"-"]},"apptools.logger.agent.attachments":{Attachments:[5,1,1,""]},"apptools.logger.agent.attachments.Attachments":{package_any_relevant_files:[5,2,1,""],package_single_project:[5,2,1,""],package_workspace:[5,2,1,""]},"apptools.logger.agent.quality_agent_mailer":{create_email_message:[5,5,1,""]},"apptools.logger.agent.quality_agent_view":{QualityAgentView:[5,1,1,""]},"apptools.logger.agent.quality_agent_view.QualityAgentView":{cc_address:[5,4,1,""],comments:[5,4,1,""],from_address:[5,4,1,""],help_id:[5,4,1,""],include_userdata:[5,4,1,""],msg:[5,4,1,""],priority:[5,4,1,""],service:[5,4,1,""],size:[5,4,1,""],smtp_server:[5,4,1,""],subject:[5,4,1,""],title:[5,4,1,""],to_address:[5,4,1,""]},"apptools.logger.custom_excepthook":{custom_excepthook:[4,5,1,""]},"apptools.logger.log_point":{log_point:[4,5,1,""]},"apptools.logger.log_queue_handler":{LogQueueHandler:[4,1,1,""]},"apptools.logger.log_queue_handler.LogQueueHandler":{emit:[4,2,1,""],get:[4,2,1,""],has_new_records:[4,2,1,""],reset:[4,2,1,""]},"apptools.logger.logger":{LogFileHandler:[4,1,1,""],add_log_queue_handler:[4,5,1,""]},"apptools.logger.plugin":{logger_plugin:[6,0,0,"-"],logger_preferences:[6,0,0,"-"],logger_service:[6,0,0,"-"],view:[7,0,0,"-"]},"apptools.logger.plugin.logger_plugin":{LoggerPlugin:[6,1,1,""]},"apptools.logger.plugin.logger_plugin.LoggerPlugin":{MAIL_FILES:[6,4,1,""],PREFERENCES:[6,4,1,""],PREFERENCES_PAGES:[6,4,1,""],VIEWS:[6,4,1,""],id:[6,4,1,""],mail_files:[6,4,1,""],name:[6,4,1,""],preferences:[6,4,1,""],preferences_pages:[6,4,1,""],start:[6,2,1,""],stop:[6,2,1,""],views:[6,4,1,""]},"apptools.logger.plugin.logger_preferences":{LoggerPreferences:[6,1,1,""]},"apptools.logger.plugin.logger_service":{LoggerService:[6,1,1,""]},"apptools.logger.plugin.logger_service.LoggerService":{create_email_message:[6,2,1,""],save_preferences:[6,2,1,""],send_bug_report:[6,2,1,""],whole_log_text:[6,2,1,""]},"apptools.logger.plugin.view":{logger_preferences_page:[7,0,0,"-"],logger_view:[7,0,0,"-"]},"apptools.logger.plugin.view.logger_preferences_page":{LoggerPreferencesPage:[7,1,1,""]},"apptools.logger.plugin.view.logger_view":{LogRecordAdapter:[7,1,1,""],LoggerView:[7,1,1,""]},"apptools.logger.plugin.view.logger_view.LogRecordAdapter":{column_widths:[7,4,1,""],get_width:[7,2,1,""]},"apptools.logger.plugin.view.logger_view.LoggerView":{activated:[7,4,1,""],activated_text:[7,4,1,""],code_editor:[7,4,1,""],copy_button:[7,4,1,""],formatted_records:[7,4,1,""],id:[7,4,1,""],log_records:[7,4,1,""],log_records_editor:[7,4,1,""],name:[7,4,1,""],reset_button:[7,4,1,""],service:[7,4,1,""],show_button:[7,4,1,""],trait_view:[7,4,1,""],update:[7,2,1,""]},"apptools.logger.ring_buffer":{RingBuffer:[4,1,1,""],RingBufferFull:[4,1,1,""]},"apptools.logger.ring_buffer.RingBuffer":{append:[4,2,1,""],get:[4,2,1,""]},"apptools.logger.ring_buffer.RingBufferFull":{append:[4,2,1,""],get:[4,2,1,""]},"apptools.naming":{address:[8,0,0,"-"],api:[8,0,0,"-"],binding:[8,0,0,"-"],context:[8,0,0,"-"],dir_context:[8,0,0,"-"],dynamic_context:[8,0,0,"-"],exception:[8,0,0,"-"],initial_context:[8,0,0,"-"],initial_context_factory:[8,0,0,"-"],naming_event:[8,0,0,"-"],naming_manager:[8,0,0,"-"],object_factory:[8,0,0,"-"],object_serializer:[8,0,0,"-"],py_context:[8,0,0,"-"],py_object_factory:[8,0,0,"-"],pyfs_context:[8,0,0,"-"],pyfs_context_factory:[8,0,0,"-"],pyfs_initial_context_factory:[8,0,0,"-"],pyfs_object_factory:[8,0,0,"-"],pyfs_state_factory:[8,0,0,"-"],reference:[8,0,0,"-"],referenceable:[8,0,0,"-"],referenceable_state_factory:[8,0,0,"-"],state_factory:[8,0,0,"-"],trait_defs:[9,0,0,"-"],unique_name:[8,0,0,"-"]},"apptools.naming.address":{Address:[8,1,1,""]},"apptools.naming.binding":{Binding:[8,1,1,""]},"apptools.naming.context":{Context:[8,1,1,""]},"apptools.naming.context.Context":{INITIAL_CONTEXT_FACTORY:[8,4,1,""],OBJECT_FACTORIES:[8,4,1,""],STATE_FACTORIES:[8,4,1,""],bind:[8,2,1,""],create_subcontext:[8,2,1,""],destroy_subcontext:[8,2,1,""],get_unique_name:[8,2,1,""],is_context:[8,2,1,""],list_bindings:[8,2,1,""],list_names:[8,2,1,""],lookup:[8,2,1,""],lookup_binding:[8,2,1,""],lookup_context:[8,2,1,""],rebind:[8,2,1,""],rename:[8,2,1,""],search:[8,2,1,""],unbind:[8,2,1,""]},"apptools.naming.dir_context":{DirContext:[8,1,1,""]},"apptools.naming.dir_context.DirContext":{find_bindings:[8,2,1,""],get_attributes:[8,2,1,""],set_attributes:[8,2,1,""]},"apptools.naming.dynamic_context":{DynamicContext:[8,1,1,""]},"apptools.naming.exception":{InvalidNameError:[8,6,1,""],NameAlreadyBoundError:[8,6,1,""],NameNotFoundError:[8,6,1,""],NamingError:[8,6,1,""],NotContextError:[8,6,1,""],OperationNotSupportedError:[8,6,1,""]},"apptools.naming.initial_context":{InitialContext:[8,5,1,""]},"apptools.naming.initial_context_factory":{InitialContextFactory:[8,1,1,""]},"apptools.naming.initial_context_factory.InitialContextFactory":{get_initial_context:[8,2,1,""]},"apptools.naming.naming_event":{NamingEvent:[8,1,1,""]},"apptools.naming.naming_manager":{NamingManager:[8,1,1,""]},"apptools.naming.naming_manager.NamingManager":{get_object_instance:[8,2,1,""],get_state_to_bind:[8,2,1,""]},"apptools.naming.object_factory":{ObjectFactory:[8,1,1,""]},"apptools.naming.object_factory.ObjectFactory":{get_object_instance:[8,2,1,""]},"apptools.naming.object_serializer":{ObjectSerializer:[8,1,1,""]},"apptools.naming.object_serializer.ObjectSerializer":{can_load:[8,2,1,""],can_save:[8,2,1,""],load:[8,2,1,""],save:[8,2,1,""]},"apptools.naming.py_context":{PyContext:[8,1,1,""]},"apptools.naming.py_object_factory":{PyObjectFactory:[8,1,1,""]},"apptools.naming.py_object_factory.PyObjectFactory":{get_object_instance:[8,2,1,""]},"apptools.naming.pyfs_context":{PyFSContext:[8,1,1,""]},"apptools.naming.pyfs_context.PyFSContext":{ATTRIBUTES_FILE:[8,4,1,""],FILTERS:[8,4,1,""],OBJECT_SERIALIZERS:[8,4,1,""],get_unique_name:[8,2,1,""],refresh:[8,2,1,""]},"apptools.naming.pyfs_context_factory":{PyFSContextFactory:[8,1,1,""]},"apptools.naming.pyfs_context_factory.PyFSContextFactory":{get_object_instance:[8,2,1,""]},"apptools.naming.pyfs_initial_context_factory":{PyFSInitialContextFactory:[8,1,1,""]},"apptools.naming.pyfs_initial_context_factory.PyFSInitialContextFactory":{get_initial_context:[8,2,1,""]},"apptools.naming.pyfs_object_factory":{PyFSObjectFactory:[8,1,1,""]},"apptools.naming.pyfs_object_factory.PyFSObjectFactory":{get_object_instance:[8,2,1,""]},"apptools.naming.pyfs_state_factory":{PyFSStateFactory:[8,1,1,""]},"apptools.naming.pyfs_state_factory.PyFSStateFactory":{get_state_to_bind:[8,2,1,""]},"apptools.naming.reference":{Reference:[8,1,1,""]},"apptools.naming.referenceable":{Referenceable:[8,1,1,""]},"apptools.naming.referenceable_state_factory":{ReferenceableStateFactory:[8,1,1,""]},"apptools.naming.referenceable_state_factory.ReferenceableStateFactory":{get_state_to_bind:[8,2,1,""]},"apptools.naming.state_factory":{StateFactory:[8,1,1,""]},"apptools.naming.state_factory.StateFactory":{get_state_to_bind:[8,2,1,""]},"apptools.naming.trait_defs":{api:[9,0,0,"-"],naming_traits:[9,0,0,"-"]},"apptools.naming.trait_defs.naming_traits":{NamingTraitHandler:[9,1,1,""]},"apptools.naming.trait_defs.naming_traits.NamingTraitHandler":{find_class:[9,2,1,""],get_editor:[9,2,1,""],info:[9,2,1,""],post_setattr:[9,2,1,""],resolve_class:[9,2,1,""],validate:[9,2,1,""],validate_failed:[9,2,1,""]},"apptools.naming.unique_name":{make_unique_name:[8,5,1,""]},"apptools.persistence":{file_path:[10,0,0,"-"],project_loader:[10,0,0,"-"],state_pickler:[10,0,0,"-"],updater:[10,0,0,"-"],version_registry:[10,0,0,"-"],versioned_unpickler:[10,0,0,"-"]},"apptools.persistence.file_path":{FilePath:[10,1,1,""]},"apptools.persistence.file_path.FilePath":{get:[10,2,1,""],set:[10,2,1,""],set_absolute:[10,2,1,""],set_relative:[10,2,1,""]},"apptools.persistence.project_loader":{load_project:[10,5,1,""],upgrade_project:[10,5,1,""]},"apptools.persistence.state_pickler":{State:[10,1,1,""],StateDict:[10,1,1,""],StateList:[10,1,1,""],StatePickler:[10,1,1,""],StatePicklerError:[10,6,1,""],StateSetter:[10,1,1,""],StateSetterError:[10,6,1,""],StateTuple:[10,1,1,""],StateUnpickler:[10,1,1,""],StateUnpicklerError:[10,6,1,""],create_instance:[10,5,1,""],dump:[10,5,1,""],dumps:[10,5,1,""],get_state:[10,5,1,""],gunzip_string:[10,5,1,""],gzip_string:[10,5,1,""],load_state:[10,5,1,""],loads_state:[10,5,1,""],set_state:[10,5,1,""],update_state:[10,5,1,""]},"apptools.persistence.state_pickler.StatePickler":{dump:[10,2,1,""],dump_state:[10,2,1,""],dumps:[10,2,1,""]},"apptools.persistence.state_pickler.StateSetter":{set:[10,2,1,""]},"apptools.persistence.state_pickler.StateUnpickler":{load_state:[10,2,1,""],loads_state:[10,2,1,""]},"apptools.persistence.updater":{Updater:[10,1,1,""]},"apptools.persistence.updater.Updater":{get_latest:[10,2,1,""],strip:[10,2,1,""]},"apptools.persistence.version_registry":{HandlerRegistry:[10,1,1,""],get_version:[10,5,1,""]},"apptools.persistence.version_registry.HandlerRegistry":{register:[10,2,1,""],unregister:[10,2,1,""],update:[10,2,1,""]},"apptools.persistence.versioned_unpickler":{NewUnpickler:[10,1,1,""],VersionedUnpickler:[10,1,1,""]},"apptools.persistence.versioned_unpickler.NewUnpickler":{initialize:[10,2,1,""],load:[10,2,1,""],load_build:[10,2,1,""]},"apptools.persistence.versioned_unpickler.VersionedUnpickler":{add_updater:[10,2,1,""],backup_setstate:[10,2,1,""],find_class:[10,2,1,""],import_name:[10,2,1,""]},"apptools.preferences":{api:[11,0,0,"-"],i_preferences:[11,0,0,"-"],package_globals:[11,0,0,"-"],preference_binding:[11,0,0,"-"],preferences:[11,0,0,"-"],preferences_helper:[11,0,0,"-"],scoped_preferences:[11,0,0,"-"],ui:[12,0,0,"-"]},"apptools.preferences.i_preferences":{IPreferences:[11,1,1,""]},"apptools.preferences.i_preferences.IPreferences":{clear:[11,2,1,""],flush:[11,2,1,""],get:[11,2,1,""],keys:[11,2,1,""],node:[11,2,1,""],node_exists:[11,2,1,""],node_names:[11,2,1,""],remove:[11,2,1,""],set:[11,2,1,""]},"apptools.preferences.package_globals":{get_default_preferences:[11,5,1,""],set_default_preferences:[11,5,1,""]},"apptools.preferences.preference_binding":{PreferenceBinding:[11,1,1,""],bind_preference:[11,5,1,""]},"apptools.preferences.preferences":{Preferences:[11,1,1,""]},"apptools.preferences.preferences.Preferences":{add_preferences_listener:[11,2,1,""],clear:[11,2,1,""],dump:[11,2,1,""],flush:[11,2,1,""],get:[11,2,1,""],keys:[11,2,1,""],load:[11,2,1,""],node:[11,2,1,""],node_exists:[11,2,1,""],node_names:[11,2,1,""],remove:[11,2,1,""],remove_preferences_listener:[11,2,1,""],save:[11,2,1,""],set:[11,2,1,""]},"apptools.preferences.preferences_helper":{PreferencesHelper:[11,1,1,""]},"apptools.preferences.scoped_preferences":{ScopedPreferences:[11,1,1,""]},"apptools.preferences.scoped_preferences.ScopedPreferences":{add_preferences_listener:[11,2,1,""],clear:[11,2,1,""],dump:[11,2,1,""],get:[11,2,1,""],get_scope:[11,2,1,""],keys:[11,2,1,""],load:[11,2,1,""],node:[11,2,1,""],node_exists:[11,2,1,""],node_names:[11,2,1,""],remove:[11,2,1,""],remove_preferences_listener:[11,2,1,""],save:[11,2,1,""],set:[11,2,1,""]},"apptools.preferences.ui":{api:[12,0,0,"-"],i_preferences_page:[12,0,0,"-"],preferences_manager:[12,0,0,"-"],preferences_node:[12,0,0,"-"],preferences_page:[12,0,0,"-"],tree_item:[12,0,0,"-"],widget_editor:[12,0,0,"-"]},"apptools.preferences.ui.i_preferences_page":{IPreferencesPage:[12,1,1,""]},"apptools.preferences.ui.i_preferences_page.IPreferencesPage":{apply:[12,2,1,""]},"apptools.preferences.ui.preferences_manager":{PreferencesHelpWindow:[12,1,1,""],PreferencesManager:[12,1,1,""],PreferencesManagerHandler:[12,1,1,""]},"apptools.preferences.ui.preferences_manager.PreferencesHelpWindow":{traits_view:[12,2,1,""]},"apptools.preferences.ui.preferences_manager.PreferencesManager":{apply:[12,2,1,""],traits_view:[12,2,1,""]},"apptools.preferences.ui.preferences_manager.PreferencesManagerHandler":{apply:[12,2,1,""],close:[12,2,1,""],init:[12,2,1,""],preferences_help:[12,2,1,""]},"apptools.preferences.ui.preferences_node":{PreferencesNode:[12,1,1,""]},"apptools.preferences.ui.preferences_node.PreferencesNode":{create_page:[12,2,1,""],dump:[12,2,1,""],lookup:[12,2,1,""]},"apptools.preferences.ui.preferences_page":{PreferencesPage:[12,1,1,""]},"apptools.preferences.ui.preferences_page.PreferencesPage":{apply:[12,2,1,""]},"apptools.preferences.ui.tree_item":{TreeItem:[12,1,1,""]},"apptools.preferences.ui.tree_item.TreeItem":{append:[12,2,1,""],insert:[12,2,1,""],insert_after:[12,2,1,""],insert_before:[12,2,1,""],remove:[12,2,1,""]},"apptools.preferences.ui.widget_editor":{WidgetEditor:[12,1,1,""]},"apptools.preferences.ui.widget_editor.WidgetEditor":{custom_editor:[12,2,1,""],readonly_editor:[12,2,1,""],simple_editor:[12,2,1,""],text_editor:[12,2,1,""]},"apptools.scripting":{api:[13,0,0,"-"],package_globals:[13,0,0,"-"],recordable:[13,0,0,"-"],recorder:[13,0,0,"-"],recorder_with_ui:[13,0,0,"-"],util:[13,0,0,"-"]},"apptools.scripting.package_globals":{get_recorder:[13,5,1,""],set_recorder:[13,5,1,""]},"apptools.scripting.recordable":{recordable:[13,5,1,""]},"apptools.scripting.recorder":{Recorder:[13,1,1,""],RecorderError:[13,6,1,""]},"apptools.scripting.recorder.Recorder":{clear:[13,2,1,""],get_code:[13,2,1,""],get_object_path:[13,2,1,""],get_script_id:[13,2,1,""],is_registered:[13,2,1,""],record:[13,2,1,""],record_function:[13,2,1,""],register:[13,2,1,""],save:[13,2,1,""],ui_save:[13,2,1,""],unregister:[13,2,1,""],write_script_id_in_namespace:[13,2,1,""]},"apptools.scripting.recorder_with_ui":{CloseHandler:[13,1,1,""],RecorderWithUI:[13,1,1,""]},"apptools.scripting.recorder_with_ui.CloseHandler":{close:[13,2,1,""]},"apptools.scripting.recorder_with_ui.RecorderWithUI":{on_ui_close:[13,2,1,""]},"apptools.scripting.util":{start_recording:[13,5,1,""],stop_recording:[13,5,1,""]},"apptools.selection":{api:[14,0,0,"-"],errors:[14,0,0,"-"],i_selection:[14,0,0,"-"],i_selection_provider:[14,0,0,"-"],list_selection:[14,0,0,"-"],selection_service:[14,0,0,"-"]},"apptools.selection.errors":{IDConflictError:[14,6,1,""],ListenerNotConnectedError:[14,6,1,""],ProviderNotRegisteredError:[14,6,1,""]},"apptools.selection.i_selection":{IListSelection:[14,1,1,""],ISelection:[14,1,1,""]},"apptools.selection.i_selection.IListSelection":{indices:[14,4,1,""],items:[14,4,1,""]},"apptools.selection.i_selection.ISelection":{is_empty:[14,2,1,""],provider_id:[14,4,1,""]},"apptools.selection.i_selection_provider":{ISelectionProvider:[14,1,1,""]},"apptools.selection.i_selection_provider.ISelectionProvider":{get_selection:[14,2,1,""],provider_id:[14,4,1,""],selection:[14,4,1,""],set_selection:[14,2,1,""]},"apptools.selection.list_selection":{ListSelection:[14,1,1,""]},"apptools.selection.list_selection.ListSelection":{from_available_items:[14,2,1,""],indices:[14,4,1,""],is_empty:[14,2,1,""],items:[14,4,1,""],provider_id:[14,4,1,""]},"apptools.selection.selection_service":{SelectionService:[14,1,1,""]},"apptools.selection.selection_service.SelectionService":{add_selection_provider:[14,2,1,""],connect_selection_listener:[14,2,1,""],disconnect_selection_listener:[14,2,1,""],get_selection:[14,2,1,""],has_selection_provider:[14,2,1,""],remove_selection_provider:[14,2,1,""],set_selection:[14,2,1,""]},"apptools.type_registry":{api:[15,0,0,"-"],type_registry:[15,0,0,"-"]},"apptools.type_registry.type_registry":{LazyRegistry:[15,1,1,""],TypeRegistry:[15,1,1,""],get_mro:[15,5,1,""]},"apptools.type_registry.type_registry.LazyRegistry":{lookup_by_type:[15,2,1,""]},"apptools.type_registry.type_registry.TypeRegistry":{lookup:[15,2,1,""],lookup_all:[15,2,1,""],lookup_all_by_type:[15,2,1,""],lookup_by_type:[15,2,1,""],pop:[15,2,1,""],push:[15,2,1,""],push_abc:[15,2,1,""]},"apptools.undo":{abstract_command:[16,0,0,"-"],action:[17,0,0,"-"],api:[16,0,0,"-"],command_stack:[16,0,0,"-"],i_command:[16,0,0,"-"],i_command_stack:[16,0,0,"-"],i_undo_manager:[16,0,0,"-"],undo_manager:[16,0,0,"-"]},"apptools.undo.abstract_command":{AbstractCommand:[16,1,1,""]},"apptools.undo.abstract_command.AbstractCommand":{"do":[16,2,1,""],merge:[16,2,1,""],redo:[16,2,1,""],undo:[16,2,1,""]},"apptools.undo.action":{abstract_command_stack_action:[17,0,0,"-"],api:[17,0,0,"-"],command_action:[17,0,0,"-"],redo_action:[17,0,0,"-"],undo_action:[17,0,0,"-"]},"apptools.undo.action.abstract_command_stack_action":{AbstractCommandStackAction:[17,1,1,""]},"apptools.undo.action.abstract_command_stack_action.AbstractCommandStackAction":{destroy:[17,2,1,""]},"apptools.undo.action.command_action":{CommandAction:[17,1,1,""]},"apptools.undo.action.command_action.CommandAction":{perform:[17,2,1,""]},"apptools.undo.action.redo_action":{RedoAction:[17,1,1,""]},"apptools.undo.action.redo_action.RedoAction":{perform:[17,2,1,""]},"apptools.undo.action.undo_action":{UndoAction:[17,1,1,""]},"apptools.undo.action.undo_action.UndoAction":{perform:[17,2,1,""]},"apptools.undo.command_stack":{CommandStack:[16,1,1,""]},"apptools.undo.command_stack.CommandStack":{begin_macro:[16,2,1,""],clear:[16,2,1,""],end_macro:[16,2,1,""],push:[16,2,1,""],redo:[16,2,1,""],undo:[16,2,1,""]},"apptools.undo.i_command":{ICommand:[16,1,1,""]},"apptools.undo.i_command.ICommand":{"do":[16,2,1,""],merge:[16,2,1,""],redo:[16,2,1,""],undo:[16,2,1,""]},"apptools.undo.i_command_stack":{ICommandStack:[16,1,1,""]},"apptools.undo.i_command_stack.ICommandStack":{begin_macro:[16,2,1,""],clear:[16,2,1,""],end_macro:[16,2,1,""],push:[16,2,1,""],redo:[16,2,1,""],undo:[16,2,1,""]},"apptools.undo.i_undo_manager":{IUndoManager:[16,1,1,""]},"apptools.undo.i_undo_manager.IUndoManager":{redo:[16,2,1,""],undo:[16,2,1,""]},"apptools.undo.undo_manager":{UndoManager:[16,1,1,""]},"apptools.undo.undo_manager.UndoManager":{redo:[16,2,1,""],undo:[16,2,1,""]},apptools:{io:[2,0,0,"-"],logger:[4,0,0,"-"],naming:[8,0,0,"-"],persistence:[10,0,0,"-"],preferences:[11,0,0,"-"],scripting:[13,0,0,"-"],selection:[14,0,0,"-"],type_registry:[15,0,0,"-"],undo:[16,0,0,"-"]}},objnames:{"0":["py","module","Python module"],"1":["py","class","Python class"],"2":["py","method","Python method"],"3":["py","data","Python data"],"4":["py","attribute","Python attribute"],"5":["py","function","Python function"],"6":["py","exception","Python exception"]},objtypes:{"0":"py:module","1":"py:class","2":"py:method","3":"py:data","4":"py:attribute","5":"py:function","6":"py:exception"},terms:{"abstract":[2,10,12,16,17,26],"boolean":[8,22,26],"case":[5,10,11,20,22,25],"class":[2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,22,23,24,26],"default":[3,4,7,9,10,11,12,13,14,16,17,22,23,26],"final":25,"float":[7,8,10,22,24],"function":[4,8,9,10,13,20,24,25],"import":[4,10,15,22,24,25],"int":[10,22],"long":[10,22,26],"new":[3,7,8,9,10,11,13,15,17,20,22,25],"public":[5,13,25],"return":[3,4,6,7,8,9,10,11,12,13,14,15,16,17,22,23,25,26],"static":8,"transient":22,"true":[3,6,8,10,11,13,14,16,22,23,24,25,26],"try":[11,16,22,26],"while":[7,10,16,25,26],And:22,But:22,ETS:24,For:[3,9,10,13,20,22,24,25],Has:14,Its:[10,16,26],Not:8,One:24,That:3,The:[3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,19,20,23,26],Then:11,There:11,These:15,Used:10,Useful:13,Using:[22,24,26],__array__:3,__attributes__:8,__class__:10,__dict__:10,__getstate__:10,__init__:[10,16,26],__main__:10,__metadata__:10,__module__:15,__name__:[10,15],__set_pure_state__:10,__setitem__:3,__setstate__:10,__version__:10,_unpickl:10,abandon:[16,26],abc:[3,15],abcmeta:15,abl:[22,24],about:[8,10,16,22,24,25],abov:24,absolut:[3,10,15],abstract_command:[1,18],abstract_command_stack_act:[1,16],abstractcommand:16,abstractcommandstackact:17,accept:[8,9],access:[3,11],aclass:9,acm:[11,22,23],act:[8,25],action:[1,16,24,25,26],activ:[7,14,16,17,26],activated_text:7,active_stack:26,active_stack_clean:26,actual:[4,7,8,9,10,13,22],adapt:7,add:[3,4,7,10,11,13,14,22,26],add_log_queue_handl:4,add_preferences_listen:11,add_selection_provid:[14,25],add_to_h5fil:3,add_updat:10,added:[11,14],adding:8,addit:[3,8,11],addition:10,address:[1,18],affect:11,after:[3,10,12,13],age:24,agent:[1,4],alia:5,all:[2,6,7,8,10,11,12,13,14,15,16,17,22,24,26],all_item:14,alloc:26,allow:[3,7,8,9,10,11,12,16,22,23,24,26],allows_children:12,almost:10,along:[10,16],alreadi:[2,3,8,13,14],also:[3,8,10,11,13,16,21,22,23,24,25,26],alter:3,altern:[9,10,26],alwai:[11,22,24],amount:7,ani:[2,5,7,8,9,10,11,14,16,17,22,23,24,25,26],anoth:[8,16,22],answer:10,anymor:25,anywher:22,api:[1,6,18,19,22],app:10,appear:[10,26],append:[3,4,11,12,24],appl:12,appli:[10,12],applic:[4,8,10,11,16,22,23,24,25,26],application_hom:22,application_scop:11,application_vers:10,approach:22,appropri:[10,16,26],apptool:[0,20,21,22,24],arbitrari:[9,24],arg:[3,5,6,7,12,13,26],argmument:3,argument:[3,13,14,16,17,24,25,26],around:[3,20,22,26],arrai:[3,10],arrang:22,array_or_shap:3,array_proxy_kei:3,arriv:7,ascend:8,ascii:10,ask:[8,11,13],assert:10,assign:[7,9],assist:8,associ:[8,22],assum:[3,10],atom:3,attach:[1,4,24],attempt:[2,8],attribut:[3,8,9,10,13,24],attributes_fil:8,auto:0,auto_flush:3,auto_group:3,auto_open:3,automat:[3,8,11,13,19,22,26],avail:[7,13,14,20,23,25,26],avoid:[8,10,11],back:[10,11,22],background:24,backup_setst:10,backupcount:4,base64:10,base:[2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,24,25,26],base_f_nam:10,base_toolkit:5,basic:[10,13,19],becam:20,becaus:[5,8,10,22,25],becom:[25,26],been:[3,13,14,15,16,24,25,26],befor:[4,10,12,14,16,20,22,25,26],begin:[8,16,26],begin_macro:[16,26],behavior:3,behaviour:[22,23],being:[8,9,10,12,16,24,26],believ:22,below:3,best:[10,24],better:11,between:[10,11,24,25],bgcolor:[11,22,23],bind:[1,11,18],bind_prefer:11,bit:22,black:22,blog:4,blue:[11,22,23],bogu:22,bool:[3,10,13,14,22,24],both:[15,26],bound:8,brief:26,broker:25,brows:10,buffer:[3,4],bug:6,build:22,builtin:10,bump:10,bunch:10,button:[7,12,22,25],calcul:[3,7,8],call:[3,4,8,9,10,11,13,14,16,17,22,24,25,26],callabl:[14,25,26],callback:[14,24,25],came:11,can:[3,4,5,8,9,10,11,14,15,16,20,21,22,24,25,26],can_load:8,can_sav:8,cannot:[8,11],capabl:8,care:[10,22,26],carrot:12,categori:8,cauliflow:12,caveat:1,cc_address:5,ccaddr:[5,6],central:25,chang:[8,11,12,13,14,16,22,24,25,26],charact:26,check:10,child:[11,12,22,24],children:[3,11,16,24],children_nam:3,chunk:3,class_nam:10,classmethod:[3,10,14],clean:[13,16,26],cleaner:3,clear:[11,13,16,26],click:12,close:[3,12,13,25],closehandl:13,code:[5,8,10,13,20,24],code_editor:7,collaps:26,collect:[3,14,25],color:24,column:[3,7],column_width:7,combin:9,come:22,command:[11,16,17,24,26],command_act:[1,16],command_stack:[1,18,26],commandact:17,commandstack:16,comment:[5,6],common:[10,20,25,26],commuic:8,commun:[8,25],complet:[9,10,26],complex:[10,22,25],complic:[10,22],compon:[8,25,26],compos:8,concept:19,concern:10,configobj:[11,22],configur:26,conform:8,confus:26,connect:[14,25],connect_selection_listen:[14,25],consid:24,consist:[8,22],consumpt:25,contain:[0,3,8,10,11,12,13,14,16,22,25,26],content:[18,25],context:[1,3,10,18],context_nam:8,context_ord:8,continu:8,contribut:[8,23],contributes_to:23,control:[3,12,25],conveni:[4,10,13,22,24,26],convers:10,convert:[3,7,10,22],cookbook:4,copi:[2,4,16,26],copy_button:7,core:24,correct:10,correctli:10,correspond:25,could:25,couldn:26,cours:11,creat:[2,3,4,7,8,10,11,12,13,14,16,22,24,26],create_:3,create_arrai:3,create_carrai:3,create_dict:3,create_email_messag:[5,6],create_fil:2,create_fold:2,create_group:3,create_inst:10,create_packag:2,create_pag:12,create_subcontext:8,create_t:3,creation:[8,12],criteria:8,current:[8,10,11,12,13,14,22,24,25,26],custom:12,custom_editor:12,custom_excepthook:[1,18],data:[3,6,8,10,12,16,26],datafram:3,debug:[13,22],decid:10,decor:[13,24],decreas:7,deep:[16,26],def:[10,22,23,24],default_prefer:11,default_valu:24,defin:[7,9,10,11,13,24,25,26],definit:[8,11,24],delet:[2,3],delete_exist:3,demonstr:26,depend:[10,11,25],descend:11,describ:[8,9,10,23],descript:[3,12],desir:[3,8,23],destin:2,destroi:[8,17,25],destroy_subcontext:8,detail:[10,24],determin:[8,10],develop:[11,25,26],dialog:[12,13,25],dict:[3,10,13],dict_nod:[1,2],dictat:23,dictionari:[3,8,10,13,20,22],did:[11,22],differ:[3,8,10],dir_context:[1,18],dircontext:8,direct:25,directi:10,directli:[11,22,25],directori:[8,21,22],disabl:[16,26],disappear:25,discard:16,disconnect:[14,25],disconnect_selection_listen:[14,25],discuss:[22,23],disk:[3,10,22,26],displai:[4,25],distinct:25,divid:7,docstr:3,document:[3,22,24,26],doe:[2,3,4,8,10,11,13,17,22,23],doesn:[12,22],don:[22,23],done:[13,24,25,26],drawback:11,dtype:3,dump:[10,11,12,22],dump_stat:10,duplic:[10,14],dure:10,dynam:[7,8],dynamic_context:[1,18],dynamiccontext:8,each:[7,8,10,11,15,22,24,26],easi:[3,10,11],easili:[10,24],edit:[9,10,26],editor:[7,9,10,12,25,26],editor_factori:12,editorfactori:12,effect:[16,26],either:[3,9,10,11,22,24],element:[4,8,14,25,26],elimin:10,els:[3,10,15],email:6,embed:10,emit:4,empti:[8,11,14,16,26],encapsul:10,encod:10,encourag:[11,20],end:[4,8,10,13,16,24,26],end_macro:[16,26],endo:26,endpoint:8,enlib:5,enough:24,enqueu:4,ensur:[10,16,26],enthought:[2,8,10,11,13,16,26],enthoughtbas:4,entir:10,entri:26,environ:[3,8],envisag:[5,6,8,19,22,25],equal:7,error:[1,9,10,18],especi:25,etc:[4,8,10,11,17,22],etsconfig:22,even:14,event:[8,14,17,25,26],ever:[16,26],everi:4,everyth:10,exactli:22,examin:8,exampl:[3,9,10,11,12,22,25,26],excel:22,except:[1,4,9,10,11,13,14,18],exclud:10,execut:[4,13,16,24,26],exist:[2,3,8,10,11,12,13,22],exists_error:3,expect:14,explan:7,explicit:22,explicitli:[3,8,11,14,23,26],expos:[3,6,25],extend:[3,8],extens:[5,23,25],extern:25,extra:23,facil:24,factori:[8,12,26],fairli:24,fals:[3,5,6,7,8,11,12,13,14,22,24],featur:[1,26],few:24,fgcolor:22,file:[1,4,5,6,8,10,11,13,18,19,22,23],file_or_filenam:11,file_path:[1,18],filenam:[3,11,22],filenod:3,filepath:10,filesystem:22,fill:14,filter:8,find:[8,11,13,21,24],find_bind:8,find_class:[9,10],fire:[8,14,25,26],first:[3,10,22,24,25],fix:[7,10],fix_import:10,fixm:[5,11,13],flexibl:10,flush:[3,11,22],folder:2,follow:[13,22,24,26],forc:[7,11],form:[10,12],format:[6,8,10,22],formatt:4,formatted_record:7,forwardproperti:7,found:[8,14],fraction:7,framework:[8,13,19,24],fred:22,free:23,friend:[10,24],from:[4,6,8,10,11,12,13,14,16,22,24],from_address:5,from_available_item:14,fromaddr:[5,6],fruit:12,full:24,fulli:15,func:[13,14],further:19,gener:[0,4,8,10,12,22,26],get:[3,4,8,10,11,15,22,23,24,25],get_atom:3,get_attribut:8,get_cod:13,get_default_prefer:11,get_editor:9,get_initial_context:8,get_latest:10,get_mro:15,get_object_inst:8,get_object_path:13,get_prefer:23,get_record:13,get_scop:[11,22],get_script_id:13,get_select:[14,25],get_stat:10,get_state_to_bind:8,get_unique_nam:8,get_vers:10,get_width:7,give:[10,26],given:[3,7,8,10,11,13,14,15,16,24,25,26],global:[11,13,24],going:[10,24],great:17,group:[3,7],group_path:3,group_subpath:3,grow:24,guarante:[13,16,26],gui:[25,26],guid:9,gunzip_str:10,gzip:10,gzip_str:10,h5_group:3,h5_group_wrapp:3,h5attr:3,h5dictnod:3,h5file:3,h5filter:3,h5group:3,h5tablenod:3,halt:4,handi:24,handl:[10,12,25,26],handler:[4,7,9,10,12,13],handlerregistri:10,happen:8,happi:22,hard:5,has:[4,7,8,10,12,13,14,15,22,25,26],has_inst:10,has_new_record:4,has_selection_provid:14,has_trait:[2,5,6,8,11,12,13,14,16],hasprivatetrait:[2,8],hasstricttrait:24,hastrait:[5,6,8,9,11,12,13,14,16,24],have:[3,7,10,11,16,22,24,25,26],hdf5:[3,19],heirarchi:[8,21],help:[3,10,12],help_id:5,helper:[22,23],henc:[22,23],here:[5,10,11,21,22,24],hid_quality_agent_dlg:5,hierarch:22,hierarchi:[10,11,13,22,24],high:24,highli:[10,11],hold:22,hopefulli:22,horizont:7,how:[8,22,23,26],howev:[10,26],html:26,human:24,i_command:[1,18],i_command_stack:[1,18],i_prefer:[1,18],i_preferences_pag:[1,11],i_select:[1,18],i_selection_provid:[1,18],i_undo_manag:[1,18],icommand:16,icommandstack:[16,26],idconflicterror:14,idea:[10,22],ideal:[10,25],identifi:[3,14,26],ignor:[10,13,14,24,26],ignore_miss:[14,25],ilistselect:14,imag:[12,22],immedi:26,implement:[3,9,10,11,14,15,16,20,21,22,23,25,26],implent:11,import_nam:10,inadequ:10,inappropri:26,includ:[9,10,11,16,21,26],include_environ:6,include_project:5,include_userdata:[5,6],increas:[7,24],increment:[10,26],indent:[11,12],independ:26,index:[12,26],indic:[8,14,25,26],info:[8,9,12,13],info_text:9,inform:[3,8,9,10,14,22,24,25],infrastructur:24,inherit:[11,22],ini:[22,23],init:12,initarg:10,initi:[3,8,10,11,12,14,25],initial_context:[1,18],initial_context_factori:[1,18],initialcontext:8,initialcontextfactori:8,inject:13,input:3,insert:12,insert_aft:12,insert_befor:12,insid:24,instal:3,instanc:[3,7,8,9,10,11,12,13,14,15,16,17,24,25,26],instanti:[13,24],instead:[9,10,25],insuffici:10,integ:[7,26],intend:26,intent:11,interact:8,intercept:3,interest:[8,22],interfac:[3,7,9,11,12,13,14,16,21,22,24,25,26],intermedi:[2,8],intern:[14,24,25],interpret:[10,13],invalid:8,invalidnameerror:8,invert:13,investig:20,invok:[13,26],iprefer:[11,22],ipreferencespag:12,irrespect:26,is_context:8,is_dict_nod:3,is_empti:14,is_ok:[12,13],is_open:3,is_regist:13,is_table_nod:3,iselect:[14,25],iselectionprovid:[14,25],isn:22,issu:10,item:[3,7,8,12,14,25,26],items:3,iter:3,iter_group:3,iterator_length:3,iteritem:3,its:[7,8,10,11,12,13,14,15,22,24,25,26],itself:[8,10,11,13,22,23,24,26],iundomanag:[16,26],java:21,jndi:21,join:3,join_path:3,json:3,just:[10,11,22,23,24],keep:[10,16,25],kei:[3,10,11,24],keyboard:26,keyerror:15,keyword:[3,13,24,25],klass:10,know:10,knowledg:10,known:[10,13],kwarg:[3,5,6,7],larg:10,last:[10,15,16,17,26],later:[4,10,16],latest:10,layer:22,lazi:26,lazili:15,lazyregistri:15,learn:24,leav:[7,16,26],let:[22,24],level:[4,8,24],lib:5,librari:[20,24],like:[3,4,10,11,22,23,24],limit:22,line:[11,13,24],link:26,list:[3,4,6,7,8,10,11,13,14,15,24,25],list_bind:8,list_nam:8,list_select:[1,18],listen:[11,13,14,17,19,22,24],listenernotconnectederror:14,listselect:[14,25],littl:7,live:8,load:[8,10,11,22,23,26],load_build:10,load_project:10,load_stat:10,loads_stat:10,local:8,locat:[8,24],log:[4,6,7],log_point:[1,18],log_queue_handl:[1,18],log_record:7,log_records_editor:7,logfilehandl:4,logger:[1,18],logger_plugin:[1,4],logger_prefer:[1,4],logger_preferences_pag:[1,4,6],logger_servic:[1,4],logger_view:[1,4,6],loggerplugin:6,loggerprefer:6,loggerpreferencespag:7,loggerservic:6,loggerview:7,logic:13,logqueuehandl:4,logrecord:7,logrecordadapt:7,longer:[10,17,25],look:[3,8,11,15,22,24],lookup:[8,12,15],lookup_al:15,lookup_all_by_typ:15,lookup_bind:8,lookup_by_typ:15,lookup_context:8,lot:10,lowest:22,macro:[16,26],made:[8,12,16,22,23,26],mai:[8,10,13,16,24,26],mail_fil:6,main:25,maintain:[3,15,24,26],major:[10,26],make:[2,9,10,11,22,25],make_context:8,make_unique_nam:8,make_writ:2,manag:[3,8,10,11,12,16,22,25,26],mani:[10,22,26],manipul:[20,26],manner:24,manual:13,map:[3,10,20,24],mark:[13,24,26],match:[8,13,15],max_pass:10,maxbyt:4,maximum:7,mayavi:24,mean:[7,10,22],mechan:[8,19,23],mention:22,menu:26,merg:[11,16,26],messag:[4,5,6],metadata:[8,10,13,24],method:[3,8,9,10,11,13,15,16,17,20,22,24,25,26],might:[10,22,23,25],minu:7,miss:[2,8,11,14,22],mode:3,model:[8,16],modifi:[9,10,16,26],modul:[18,24],more:[3,4,9,11,16,22,24,25,26],motiv:10,move:[2,26],mro:10,msg:[4,5],much:[20,23],multidimension:3,multipl:[25,26],multipli:7,must:[2,3,8,9,14,15,16,24,25,26],mutablemap:3,mutat:11,my_dict:3,my_tabl:3,myapplic:23,mypackag:23,myplugin:23,name:[1,3,6,7,10,11,12,13,15,16,18,19,22,24,26],namealreadybounderror:8,namenotfounderror:8,namespac:[8,13],naming_ev:[1,18],naming_manag:[1,18],naming_trait:[1,8],namingerror:8,namingev:8,namingmanag:8,namingtraithandl:9,natur:22,navig:10,necessari:[3,10,16,26],need:[9,10,13,16,23,25,26],nest:[11,13,16,24,26],never:[8,14],new_nam:8,newest:4,newunpickl:10,next:[16,22,26],node:[3,11,12,22,23],node_attr:3,node_exist:11,node_nam:11,node_path:3,node_subpath:3,nodepath:3,non:[3,8,9,22],none:[3,4,8,10,11,12,13,15,22,26],normal:[3,7],notcontexterror:8,note:[3,7,8,9,10,11,24],noth:[2,11,13,16,17,26],notic:22,notif:25,notifi:[13,14,22,25],notion:11,now:[10,22,24],number:[10,22,23,26],numer:10,numpi:3,obj:[8,10,11,15,22],obj_class:15,object:[3,4,5,6,7,8,9,10,11,12,13,14,15,19,20,21,22,24,26],object_factori:[1,18],object_seri:[1,18],objectfactori:8,objectseri:8,obtain:10,obvious:22,occur:[9,24],often:10,ogbuji:4,old:[10,15,22],old_nam:8,oldest:4,on_trait_chang:22,on_ui_clos:13,onc:[4,16,26],one:[8,10,11,12,13,15,16,22,24,25,26],ones:10,onli:[3,9,10,11,13,15,16,17,23,25,26],onto:[15,17,26],opac:24,open:[3,10,25],open_h5fil:3,oper:[8,11,17,20,22,26],operationnotsupportederror:8,opportun:10,option:[10,13,25,26],or_non:9,orang:12,order:[8,10,11,14,15,22],organ:[24,25],organis:26,orient:7,origin:[3,8,9,10,16,26],other:[3,8,9,10,16,23,25,26],otherwis:[3,14,16,26],our:[3,7,22],out:[10,11,13,24],output:[10,13],outsid:[8,24],outstand:[16,26],over:[3,8,12],overrid:23,overridden:[9,10],overview:[19,24],overwrit:3,packag:[0,18,20,21,22,24,25],package_any_relevant_fil:5,package_glob:[1,18],package_single_project:5,package_workspac:5,page:[7,12,19],pair:3,panda:3,panel:12,panick:22,paramet:[3,9,10,13,14,15,24],parent:[3,12,13,15,24],pars:10,part:[2,3,4,8,10,11,13,16,24,25],particular:[8,9,10,11,13,14,24,26],particularli:[8,26],pass:[3,4,8,9,10,11,13,16,24,26],passiv:14,path:[2,3,4,8,10,11,13,20,24],pathlib:20,pathnam:3,pattern:26,pear:12,peopl:11,perfectli:14,perform:[7,8,11,17,22,24],perhap:10,persist:[1,6,11,18,22],phrase:9,pickl:10,pickle_filenam:10,pickler:10,pixel:7,pkgfile:23,place:[9,10,11,16,17,24,26],placehold:10,plain:[10,22,24],pleas:10,plug:8,plugin:[1,4,5,10,23,25,26],point:[5,8,22,23,24,26],pop:[13,15],popul:14,portion:21,posit:[8,26],posix:10,possibl:[10,13,15,25,26],post_setattr:9,potenti:26,power:24,pprint:10,preced:[11,22],predefin:[9,22],prefer:[1,6,7,8,18,19],preference_bind:[1,18],preference_path:11,preferencebind:11,preferences_help:[1,6,12,18],preferences_manag:[1,11],preferences_nod:[1,11],preferences_pag:[1,6,7,11],preferences_path:22,preferencesbind:11,preferenceshelp:[6,11,12,22,23],preferenceshelpwindow:12,preferencesmanag:12,preferencesmanagerhandl:12,preferencesnod:12,preferencespag:[7,12],prefix:8,prefixmap:24,present:[8,12,13],preserv:3,presum:10,pretti:[12,22],previou:[13,16,26],previous:22,primari:11,primarili:24,primit:11,print:[4,12,22,24],printabl:13,prioriti:[5,6],problem:10,process:10,produc:[8,24],program:3,project:[2,4,5,8,10,11,13,16],project_load:[1,18],project_vers:10,properli:16,properti:[3,20,24],propos:9,protocol:10,prove:[10,22],provid:[2,3,8,10,11,13,14,19,20,22,23,24,26],provider_id:[14,25],providernotregisterederror:[14,25],proxi:10,publish:[14,25],pure:10,purpos:9,push:[15,16,17,26],push_abc:15,put:4,py_context:[1,18],py_object_factori:[1,18],pycontext:8,pyf:8,pyfac:[5,7,17,26],pyfs_context:[1,18],pyfs_context_factori:[1,18],pyfs_initial_context_factori:[1,18],pyfs_object_factori:[1,18],pyfs_state_factori:[1,18],pyfscontext:8,pyfscontextfactori:8,pyfsinitialcontextfactori:8,pyfsobjectfactori:8,pyfsstatefactori:8,pyobjectfactori:8,pytabl:[3,20],pytables_group:3,pytables_nod:3,python:[3,4,8,10,11,13,20,21,24],qualifi:15,quality_agent_mail:[1,4],quality_agent_view:[1,4],qualityagentview:5,queue:4,quick:22,quickest:24,quit:[10,25],rais:[3,8,9,11,14,15,25],rang:24,rather:[3,8,9],ratio:[22,23],raw:22,reach:8,react:25,read:[3,10,19,24],readabl:[3,13,24],readi:4,readonly_editor:12,real:10,realli:[10,22],reason:[15,24],rebind:8,rec:13,receiv:[9,25],recogn:8,recognis:[8,16],recommend:11,reconstitut:10,record:[1,3,4,7,16,18,19],record_funct:13,recorder_with_ui:[1,18],recordererror:13,recorderwithui:[13,24],recreat:[10,24],red:22,redo:[16,17,26],redo_act:[1,16],redo_nam:26,redoabl:26,redoact:17,redon:[16,26],refactor:10,refer:[1,7,9,10,11,16,18,24,26],referenc:[1,18],referenceable_state_factori:[1,18],referenceablestatefactori:8,reflect:[8,26],refresh:8,regard:[8,25],regist:[10,13,14,15,23,24,25],registi:10,registri:[10,13,14,15],reimplement:17,rel:[3,8,10],rel_pth:10,releas:10,relev:[5,10],reli:24,remain:[5,10,14],remov:[3,11,12,14,26],remove_group:3,remove_nod:3,remove_preferences_listen:11,remove_selection_provid:[14,25],renam:8,repeat:26,repeatedli:[10,14],replac:16,report:6,repres:[8,10,12,13,14,22,25],represent:[2,8,10,24],request:[8,14,25],requir:[3,7,8,9,10,14,17,26],reset:4,reset_button:7,resiz:7,resolut:[8,15],resolv:8,resolve_class:9,respect:10,respons:[16,26],rest:14,restor:26,result:[7,8,9,16,17,26],reus:5,revers:[10,24],revert:26,revis:10,rewrit:5,ring_buff:[1,18],ringbuff:4,ringbufferful:4,root:[3,11,12,22],rotatingfilehandl:4,row:3,safer:8,saferepr:10,sai:[10,22],same:[7,8,10,14,16,22,25,26],satisfi:20,save:[6,8,10,11,13,16,22,24,26],save_prefer:6,scan:10,scheme:11,scope:[8,11,19,23],scope_nam:11,scoped_prefer:[1,18],scopedprefer:[11,22,23],screen:22,script:[1,16,18,19],script_id:[13,24],search:[8,15,19,22],second:10,section:[0,22,23,26],see:[3,10,22,24],select:[1,18,19],selection_servic:[1,18],selectionservic:[14,19],self:[10,23,24],send:[6,25],send_bug_report:6,sens:9,sentenc:9,seq:10,sequenc:[3,8,14,26],sequence_nr:[16,26],serial:8,servic:[5,6,7,14,19],set:[3,7,8,10,11,13,14,22,23,24,26],set_absolut:10,set_attribut:8,set_default_prefer:11,set_record:[13,24],set_rel:10,set_select:[14,25],set_stat:10,setup:[10,24],shape:3,shiva:24,shortcut:26,should:[3,5,8,9,10,11,14,22,24,25,26],show:[7,8,12,25],show_button:7,show_label:7,shown:13,signatur:14,silent:14,similar:[9,20,26],similarli:10,simpl:[7,10,12,13,22,24],simple_editor:12,simpli:[10,22,24],simplifi:10,sinc:[3,8,10,13,24,26],singl:[16,26],size:[4,5,7],size_max:4,small:24,smtp_server:[5,6],some:[3,8,22,26],someon:[8,10],sometim:24,somewher:8,soon:25,sort:[8,26],sound:22,sourc:[2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,24],space:7,special:10,specif:[8,9,10,11,13,14,21,24,25],specifi:[3,7,8,9,10,11,12,13,15,22,24,25],splash:22,splash_screen:22,splashscreenprefer:[22,23],split:3,split_path:3,sprocket:9,sprout:12,squar:9,stack:[4,15,16,17,26],stack_trac:[5,6],stack_upd:26,stage:10,standard:[8,10,11,20,26],start:[4,6,8,13,22,23,24],start_record:13,startup:4,state:[8,10,13,16,26],state_factori:[1,18],state_pickl:[1,18],statedict:10,statefactori:8,statelist:10,statement:13,statepickl:10,statepicklererror:10,statesett:10,statesettererror:10,statetupl:10,stateunpickl:10,stateunpicklererror:10,statu:26,stdout:[11,12],stop:[6,13,24],stop_record:13,storag:[3,10],store:[3,8,10,11,13,22,24],str:[3,5,7,9,11,13,14,22,24,26],strict:10,strightfoward:10,string:[3,8,9,10,11,12,13,15,24],strip:10,structur:12,stuff:[5,10],style:[7,15,20],sub:[8,20,24,26],subclass:[9,11,13],subgroup_nam:3,subject:[5,6],submodul:[1,18],subpackag:18,subsequ:[16,24,26],suffici:26,suggest:24,suit:[2,8,10,11,13,16,24,26],suitabl:8,superclass:15,support:[8,10,13,16,19,25,26],sure:[11,22,25],surfac:24,synchron:[11,25],synchronis:26,syntax:[8,22],system:[2,4,8,22],tabl:[3,7],table_nod:[1,2],tabular_adapt:7,tabular_editor:7,tabularadapt:7,tabulareditor:7,take:[11,17,22,25,26],taken:[10,24],technic:24,tediou:24,tell:26,terribl:22,test:24,text:[6,10,13,26],text_editor:12,than:[3,4,8,9],thei:[10,11,16,20,22,24,25,26],them:[4,8,10,11,22,26],themselv:[8,13],therefor:9,thi:[0,2,3,4,5,7,8,9,10,11,12,13,14,16,17,20,22,23,24,25,26],thin:3,thing:23,think:[8,10,11],those:[8,13,16,26],though:[8,10],thought:26,through:[8,24,25],thrown:8,thu:7,time:[4,7,8,10,25,26],titl:5,to_address:5,to_datafram:3,toaddr:[5,6],togeth:7,toi:24,too:[10,22,23,24],tool:[2,8,10,11,13,16,24,26],toolbar:25,toolkiteditorfactori:7,top:22,total:[7,12],tour:22,trace:4,traceback:4,tradit:22,trail:11,trait:[2,5,6,7,8,9,11,12,13,14,16,17,20,22,23,24,26],trait_def:[1,8],trait_handl:9,trait_modifi:7,trait_nam:[11,22],trait_name_on_par:13,trait_typ:[5,6,7],trait_view:7,traiterror:9,traithandl:9,traits_ui_view:7,traits_view:12,traitsui:[7,12,13],traitsuiview:7,transform:10,treat:[7,8,26],tree:[8,12,25],tree_item:[1,11],treeitem:12,tri:[23,24],trigger:14,tupl:[3,5,10,24],turn:[10,11,20,24],tutori:21,two:[10,11,16,22,25,26],typ:15,type:[3,4,7,8,9,10,11,15,19,24,26],type_registri:[1,18],typeregistri:15,typic:[10,16,25,26],uch:4,ui_sav:13,unbind:8,unchang:7,undo:[1,18,19],undo_act:[1,16],undo_manag:[1,18,26],undo_nam:26,undoabl:26,undoact:17,undomanag:16,undon:[16,17,26],unhook:17,unimpl:5,uniqu:[8,14,24,25,26],unique_nam:[1,18],unless:25,unlik:10,unnorm:7,unpickl:10,unregist:[10,13,24],unseri:10,until:[8,10,16,26],unzip:10,updat:[1,7,18,26],update1:10,update2:10,update3:10,update_st:10,updater_path:10,upgrad:10,upgrade_project:10,usabl:8,usag:26,use:[7,8,9,10,11,13,20,22,23,25],useag:22,used:[3,8,10,11,12,13,16,22,23,24,25,26],useful:[8,10,13,17,22],user:[7,8,9,10,11,12,13,20,22,24,25,26],uses:[11,23],using:[3,8,9,10,13,22,24,25],usual:[10,11,12,22],util:[1,2,5,18],valid:[3,9,10,14],validate_fail:9,valu:[3,4,7,8,9,10,11,16,22,26],valueerror:[3,11,14],variou:[10,26],veg:12,veri:[10,24],verifi:9,version:[8,10,22],version_registri:[1,18],versioned_unpickl:[1,18],versionedunpickl:10,via:[5,10,11,13,22,23,24],view:[1,3,4,6,10,12,25],visibl:[11,22,23],visitor:8,visual:24,wai:[8,11,22,24],walk:[10,24],want:[10,11,13,22,24,25],well:[10,24],were:[16,26],what:[7,10,22,24,25],whatev:[9,11,24],when:[3,4,8,10,13,14,15,16,17,22,23,24,25,26],whenev:[3,9,14,25,26],where:[3,8,10,11,13,22,25],whether:[3,9],which:[3,7,8,10,11,12,13,16,22,23,24,25,26],white:22,whole:10,whole_log_text:6,whose:[9,10],wide:11,widget:[11,12],widget_editor:[1,11],widgeteditor:12,width:[7,11,22,23],win:22,window:25,wire:24,wirefram:24,wish:25,within:8,without:[11,16,26],word:26,work:[3,10,12,16,22,24,26],workbench:[6,7,26],worth:[10,11,22],would:[3,10,11,16,17,22,23,25],wrap:[3,13,17,26],wrapper:[3,20,26],write:[3,10,22],write_script_id_in_namespac:13,writeabl:2,writer:8,written:8,xmarshal:10,xxx:3,year:24,yellow:23,yet:5,you:[3,10,11,13,21,22,23,24],your:[22,23,24],yourself:22,zip:5},titles:["API documentation","apptools package","apptools.io package","apptools.io.h5 package","apptools.logger package","apptools.logger.agent package","apptools.logger.plugin package","apptools.logger.plugin.view package","apptools.naming package","apptools.naming.trait_defs package","apptools.persistence package","apptools.preferences package","apptools.preferences.ui package","apptools.scripting package","apptools.selection package","apptools.type_registry package","apptools.undo package","apptools.undo.action package","apptools","AppTools Documentation","File I/O","Naming","Preferences","Preferences in Envisage","Automatic script recording","The selection service","Undo Framework"],titleterms:{"case":24,The:[22,24,25],abstract_command:16,abstract_command_stack_act:17,abstractcommand:26,access:22,action:17,activ:25,address:8,advanc:24,agent:5,api:[0,2,4,8,9,11,12,13,14,15,16,17,24,26],apptool:[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19],attach:5,automat:24,basic:22,bind:8,caveat:10,command_act:17,command_stack:16,commandact:26,commandstack:26,concept:26,content:[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17],context:8,custom_excepthook:4,dict_nod:3,dir_context:8,document:[0,19],dynamic_context:8,envisag:23,error:14,exampl:24,except:8,featur:10,file:[2,3,20],file_path:10,framework:26,further:22,gloriou:22,hdf5:20,i_command:16,i_command_stack:16,i_prefer:11,i_preferences_pag:12,i_select:14,i_selection_provid:14,i_undo_manag:16,icommand:26,initial_context:8,initial_context_factori:8,list_select:14,listen:25,log_point:4,log_queue_handl:4,logger:[4,5,6,7],logger_plugin:6,logger_prefer:6,logger_preferences_pag:7,logger_servic:6,logger_view:7,mechan:22,modul:[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17],name:[8,9,21],naming_ev:8,naming_manag:8,naming_trait:9,object:25,object_factori:8,object_seri:8,overview:26,packag:[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17],package_glob:[11,13],particular:22,passiv:25,persist:10,plugin:[6,7],prefer:[11,12,22,23],preference_bind:11,preferences_help:11,preferences_manag:12,preferences_nod:12,preferences_pag:12,project_load:10,provid:25,py_context:8,py_object_factori:8,pyfs_context:8,pyfs_context_factori:8,pyfs_initial_context_factori:8,pyfs_object_factori:8,pyfs_state_factori:8,quality_agent_mail:5,quality_agent_view:5,queri:25,read:22,record:[13,24],recorder_with_ui:13,redo_act:17,redoact:26,refer:8,referenc:8,referenceable_state_factori:8,registr:25,ring_buff:4,scope:22,scoped_prefer:11,script:[13,24],select:[14,25],selection_servic:14,selectionservic:25,servic:25,set:25,state_factori:8,state_pickl:10,string:22,submodul:[2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17],subpackag:[1,2,4,6,8,11,16],support:20,table_nod:3,tour:24,trait_def:9,tree_item:12,type:22,type_registri:15,undo:[16,17,26],undo_act:17,undo_manag:16,undoact:26,undomanag:26,unique_nam:8,updat:10,use:24,util:[3,13],version_registri:10,versioned_unpickl:10,view:7,widget_editor:12}}) \ No newline at end of file diff --git a/5.0/selection/selection.html b/5.0/selection/selection.html new file mode 100644 index 000000000..9d1f7613a --- /dev/null +++ b/5.0/selection/selection.html @@ -0,0 +1,282 @@ + + + + + + + The selection service — Apptools Documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +
+

The selection service

+

It is quite common in GUI applications to have a UI element displaying a +collection of items that a user can select (“selection providers”), while +other parts of the application must react to changes in the selection +(“selection listeners”).

+

Ideally, the listeners would not have a direct dependency on the UI object. +This is especially important in extensible envisage applications, where +a plugin might need to react to a selection change, but we do not want to +expose the internal organization of the application to external developers.

+

This package defines a selection service that manages the communication +between providers and listener.

+
+

The SelectionService object

+

The SelectionService object is the central manager that handles +the communication between selection providers and listener.

+

Selection providers are components that wish to +publish information about their current selection for public consumption. +They register to a selection +service instance when they first have a selection available (e.g., when the +UI showing a list of selectable items is initialized), and un-register as soon +as the selection is not available anymore (e.g., the UI is destroyed when the +windows is closed).

+

Selection listeners can query the selection +service to get the current selection published by a provider, using the +provider unique ID.

+

The service acts as a broker between providers and listeners, making sure that +they are notified when the +selection +event is fired.

+
+
+

Selection providers

+

Any object can become a selection provider by implementing the +ISelectionProvider +interface, and registering to the selection service.

+

Selection providers must provide a unique ID +provider_id, +which is used by listeners to request its current selection.

+

Whenever its selection changes, providers fire a +selection +event. The content of the event is an instance implementing +ISelection that contains information about the selected items. +For example, a ListSelection object contains a list of selected +items, and their indices.

+

Selection providers can also be queried directly about their current selection +using the +get_selection +method, and can be requested to change their selection to a new one with the +set_selection +method.

+
+

Registration

+

Selection providers publish their selection by registering to the selection +service using the +add_selection_provider +method. When the selection is no longer available, selection providers +should un-register through +remove_selection_provider.

+

Typically, selection providers are UI objects showing a list or tree of items, +they register as soon as the UI component is initialized, and un-register +when the UI component disappears (e.g., because their window has been closed). +In more complex applications, the registration could be done by a controller +object instead.

+
+
+
+

Selection listeners

+

Selection listeners request information regarding the current selection +of a selection provider given their provider ID. The SelectionService +supports two distinct use cases:

+
+
    +
  1. Passively listening to selection changes: listener connect to a specific +provider and are notified when the provider’s selection changes.

  2. +
  3. Actively querying a provider for its current selection: the selection +service can be used to query a provider using its unique ID.

  4. +
+
+
+

Passive listening

+

Listeners connect to the selection events for a given provider using the +connect_selection_listener +method. They need to provide the unique ID of the provider, and a function +(or callable) that is called to send the event. This callback function takes +one argument, an implementation of the ISelection that represents +the selection.

+

It is possible for a listener to connect to a provider ID before it is +registered. As soon as the provider is registered, the listener will receive +a notification containing the provider’s initial selection.

+

To disconnect a listener use the methods +disconnect_selection_listener.

+
+
+

Active querying

+

In other instances, an element of the application only needs the current +selection at a specific time. For example, a toolbar button could open dialog +representing a user action based on what is currently selected in the active +editor.

+

The +get_selection +method calls the corresponding method on the provider with the given ID and +returns an ISelection instance.

+
+
+

Setting a selection

+

Finally, it is possible to request a provider to set its selection to a given +set of objects with +set_selection. +The main use case for this method is multiple views of the same list of +objects, which need to keep their selection synchronized.

+

If the items specified in the arguments are not available in the provider, +a ProviderNotRegisteredError is raised, +unless the optional keyword argument ignore_missing is set to True.

+
+
+
+ + +
+
+
+
+
+ +

Table of Contents

+ + +

Previous topic

+

Undo Framework

+

Next topic

+

Naming

+

This Page

+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/5.0/undo/Introduction.html b/5.0/undo/Introduction.html new file mode 100644 index 000000000..4351eaac1 --- /dev/null +++ b/5.0/undo/Introduction.html @@ -0,0 +1,386 @@ + + + + + + + Undo Framework — Apptools Documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +
+

Undo Framework

+

The Undo Framework is a component of the Enthought Tool Suite that provides +developers with an API that implements the standard pattern for do/undo/redo +commands.

+

The framework is completely configurable. Alternate implementations of all +major components can be provided if necessary.

+
+

Framework Concepts

+

The following are the concepts supported by the framework.

+
    +
  • Command

    +

    A command is an application defined operation that can be done (i.e. +executed), undone (i.e. reverted) and redone (i.e. repeated).

    +

    A command operates on some data and maintains sufficient state to allow it to +revert or repeat a change to the data.

    +

    Commands may be merged so that potentially long sequences of similar +commands (e.g. to add a character to some text) can be collapsed into a +single command (e.g. to add a word to some text).

    +
  • +
  • Macro

    +

    A macro is a sequence of commands that is treated as a single command when +being undone or redone.

    +
  • +
  • Command Stack

    +

    A command is done by pushing it onto a command stack. The last command can +be undone and redone by calling appropriate command stack methods. It is +also possible to move the stack’s position to any point and the command stack +will ensure that commands are undone or redone as required.

    +

    A command stack maintains a clean state which is updated as commands are +done and undone. It may be explicitly set, for example when the data being +manipulated by the commands is saved to disk.

    +

    Canned PyFace actions are provided as wrappers around command stack methods +to implement common menu items.

    +
  • +
  • Undo Manager

    +

    An undo manager is responsible for one or more command stacks and maintains +a reference to the currently active stack. It provides convenience undo and +redo methods that operate on the currently active stack.

    +

    An undo manager ensures that each command execution is allocated a unique +sequence number, irrespective of which command stack it is pushed to. Using +this it is possible to synchronise multiple command stacks and restore them +to a particular point in time.

    +

    An undo manager will generate an event whenever the clean state of the active +stack changes. This can be used to maintain some sort of GUI status +indicator to tell the user that their data has been modified since it was +last saved.

    +
  • +
+

Typically an application will have one undo manager and one undo stack for +each data type that can be edited. However this is not a requirement: how the +command stack’s in particular are organised and linked (with the user +manager’s sequence number) can need careful thought so as not to confuse the +user - particularly in a plugin based application that may have many editors.

+

To support this typical usage the PyFace Workbench class has an +undo_manager trait and the PyFace Editor class has a command_stack +trait. Both are lazy loaded so can be completely ignored if they are not used.

+
+
+

API Overview

+

This section gives a brief overview of the various classes implemented in the +framework. The complete API documentation is available as endo generated +HTML.

+

The example application demonstrates all the major features of the framework.

+
+

UndoManager

+

The UndoManager class is the default implementation of the IUndoManager +interface.

+
+
active_stack

This trait is a reference to the currently active command stack and may be +None. Typically it is set when some sort of editor becomes active.

+
+
active_stack_clean

This boolean trait reflects the clean state of the currently active +command stack. It is intended to support a “document modified” indicator +in the GUI. It is maintained by the undo manager.

+
+
stack_updated

This event is fired when the index of a command stack is changed. A +reference to the stack is passed as an argument to the event and may not +be the currently active stack.

+
+
undo_name

This Str trait is the name of the command that can be undone, and will +be empty if there is no such command. It is maintained by the undo +manager.

+
+
redo_name

This Str trait is the name of the command that can be redone, and will +be empty if there is no such command. It is maintained by the undo +manager.

+
+
sequence_nr

This integer trait is the sequence number of the next command to be +executed. It is incremented immediately before a command’s do() +method is called. A particular sequence number identifies the state of +all command stacks handled by the undo manager and allows those stacks to +be set to the point they were at at a particular point in time. In other +words, the sequence number allows otherwise independent command stacks to +be synchronised.

+
+
undo()

This method calls the undo() method of the last command on the active +command stack.

+
+
redo()

This method calls the redo() method of the last undone command on the +active command stack.

+
+
+
+
+

CommandStack

+

The CommandStack class is the default implementation of the +ICommandStack interface.

+
+
clean

This boolean traits reflects the clean state of the command stack. Its +value changes as commands are executed, undone and redone. It may also be +explicitly set to mark the current stack position as being clean (when +data is saved to disk for example).

+
+
undo_name

This Str trait is the name of the command that can be undone, and will +be empty if there is no such command. It is maintained by the command +stack.

+
+
redo_name

This Str trait is the name of the command that can be redone, and will +be empty if there is no such command. It is maintained by the command +stack.

+
+
undo_manager

This trait is a reference to the undo manager that manages the command +stack.

+
+
push(command)

This method executes the given command by calling its do() method. +Any value returned by do() is returned by push(). If the command +couldn’t be merged with the previous one then it is saved on the command +stack.

+
+
undo(sequence_nr=0)

This method undoes the last command. If a sequence number is given then +all commands are undone up to an including the sequence number.

+
+
redo(sequence_nr=0)

This method redoes the last command and returns any result. If a sequence +number is given then all commands are redone up to an including the +sequence number and any result of the last of these is returned.

+
+
clear()

This method clears the command stack, without undoing or redoing any +commands, and leaves the stack in a clean state. It is typically used +when all changes to the data have been abandoned.

+
+
begin_macro(name)

This method begins a macro by creating an empty command with the given +name. The commands passed to all subsequent calls to push() will be +contained in the macro until the next call to end_macro(). Macros may +be nested. The command stack is disabled (ie. nothing can be undone or +redone) while a macro is being created (ie. while there is an outstanding +end_macro() call).

+
+
end_macro()

This method ends the current macro.

+
+
+
+
+

ICommand

+

The ICommand interface defines the interface that must be implemented by +any undoable/redoable command.

+
+
data

This optional trait is a reference to the data object that the command +operates on. It is not used by the framework itself.

+
+
name

This Str trait is the name of the command as it will appear in any GUI +element (e.g. in the text of an undo and redo menu entry). It may include +& to indicate a keyboard shortcut which will be automatically removed +whenever it is inappropriate.

+
+
__init__(*args)

If the command takes arguments then the command must ensure that deep +copies should be made if appropriate.

+
+
do()

This method is called by a command stack to execute the command and to +return any result. The command must save any state necessary for the +undo() and redo() methods to work. It is guaranteed that this +will only ever be called once and that it will be called before any call +to undo() or redo().

+
+
undo()

This method is called by a command stack to undo the command.

+
+
redo()

This method is called by a command stack to redo the command and to return +any result.

+
+
merge(other)

This method is called by the command stack to try and merge the other +command with this one. True should be returned if the commands were +merged. If the commands are merged then other will not be placed on +the command stack. A subsequent undo or redo of this modified command +must have the same effect as the two original commands.

+
+
+
+
+

AbstractCommand

+

AbstractCommand is an abstract base class that implements the ICommand +interface. It provides a default implementation of the merge() method.

+
+
+

CommandAction

+

The CommandAction class is a sub-class of the PyFace Action class that +is used to wrap commands.

+
+
command

This callable trait must be set to a factory that will return an object +that implements ICommand. It will be called when the action is invoked +and the object created pushed onto the command stack.

+
+
command_stack

This instance trait must be set to the command stack that commands invoked +by the action are pushed to.

+
+
data

This optional trait is a reference to the data object that will be passed +to the command factory when it is called.

+
+
+
+
+

UndoAction

+

The UndoAction class is a canned PyFace action that undoes the last +command of the active command stack.

+
+
+

RedoAction

+

The RedoAction class is a canned PyFace action that redoes the last +command undone of the active command stack.

+
+
+
+ + +
+
+
+
+
+ +

Table of Contents

+ + +

Previous topic

+

Automatic script recording

+

Next topic

+

The selection service

+

This Page

+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/io/file.html b/_modules/apptools/io/file.html new file mode 100644 index 000000000..a996811e5 --- /dev/null +++ b/_modules/apptools/io/file.html @@ -0,0 +1,443 @@ + + + + + + + apptools.io.file — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.io.file

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" A representation of files and folders in a file system. """
+
+
+# Standard/built-in imports.
+import mimetypes
+import os
+import shutil
+import stat
+
+# Enthought library imports.
+from traits.api import Bool, HasPrivateTraits, Instance, List, Property
+from traits.api import Str
+
+
+
[docs]class File(HasPrivateTraits): + """ A representation of files and folders in a file system. """ + + #### 'File' interface ##################################################### + + # The absolute path name of this file/folder. + absolute_path = Property(Str) + + # The folder's children (for files this is always None). + children = Property(List("File")) + + # The file extension (for folders this is always the empty string). + # + # fixme: Currently the extension includes the '.' (ie. we have '.py' and + # not 'py'). This is because things like 'os.path.splitext' leave the '.' + # on, but I'm not sure that this is a good idea! + ext = Property(Str) + + # Does the file/folder exist? + exists = Property(Bool) + + # Is this an existing file? + is_file = Property(Bool) + + # Is this an existing folder? + is_folder = Property(Bool) + + # Is this a Python package (ie. a folder contaning an '__init__.py' file. + is_package = Property(Bool) + + # Is the file/folder readonly? + is_readonly = Property(Bool) + + # The MIME type of the file (for a folder this will always be + # 'context/unknown' (is that what it should be?)). + mime_type = Property(Str) + + # The last component of the path without the extension. + name = Property(Str) + + # The parent of this file/folder (None if it has no parent). + parent = Property(Instance("File")) + + # The path name of this file/folder. + path = Str + + # A URL reference to the file. + url = Property(Str) + + ########################################################################### + # 'object' interface. + ########################################################################### + + def __init__(self, path, **traits): + """ Creates a new representation of the specified path. """ + + super(File, self).__init__(path=path, **traits) + + def __str__(self): + """ Returns an 'informal' string representation of the object. """ + + return "File(%s)" % self.path + + ########################################################################### + # 'File' interface. + ########################################################################### + + #### Properties ########################################################### + + def _get_absolute_path(self): + """ Returns the absolute path of this file/folder. """ + + return os.path.abspath(self.path) + + def _get_children(self): + """Returns the folder's children. + + Returns None if the path does not exist or is not a folder. + + """ + + if self.is_folder: + children = [] + for name in os.listdir(self.path): + children.append(File(os.path.join(self.path, name))) + + else: + children = None + + return children + + def _get_exists(self): + """ Returns True if the file exists, otherwise False. """ + + return os.path.exists(self.path) + + def _get_ext(self): + """ Returns the file extension. """ + + name, ext = os.path.splitext(self.path) + + return ext + + def _get_is_file(self): + """ Returns True if the path exists and is a file. """ + + return self.exists and os.path.isfile(self.path) + + def _get_is_folder(self): + """ Returns True if the path exists and is a folder. """ + + return self.exists and os.path.isdir(self.path) + + def _get_is_package(self): + """ Returns True if the path exists and is a Python package. """ + + return self.is_folder and "__init__.py" in os.listdir(self.path) + + def _get_is_readonly(self): + """ Returns True if the file/folder is readonly, otherwise False. """ + + # If the File object is a folder, os.access cannot be used because it + # returns True for both read-only and writable folders on Windows + # systems. + if self.is_folder: + + # Mask for the write-permission bits on the folder. If these bits + # are set to zero, the folder is read-only. + WRITE_MASK = 0x92 + permissions = os.stat(self.path)[0] + + if permissions & WRITE_MASK == 0: + readonly = True + else: + readonly = False + + elif self.is_file: + readonly = not os.access(self.path, os.W_OK) + + else: + readonly = False + + return readonly + + def _get_mime_type(self): + """ Returns the mime-type of this file/folder. """ + + mime_type, encoding = mimetypes.guess_type(self.path) + if mime_type is None: + mime_type = "content/unknown" + + return mime_type + + def _get_name(self): + """ Returns the last component of the path without the extension. """ + + basename = os.path.basename(self.path) + + name, ext = os.path.splitext(basename) + + return name + + def _get_parent(self): + """ Returns the parent of this file/folder. """ + + return File(os.path.dirname(self.path)) + + def _get_url(self): + """ Returns the path as a URL. """ + + # Strip out the leading slash on POSIX systems. + return "file:///%s" % self.absolute_path.lstrip("/") + + #### Methods ############################################################## + +
[docs] def copy(self, destination): + """ Copies this file/folder. """ + + # Allow the destination to be a string. + if not isinstance(destination, File): + destination = File(destination) + + if self.is_folder: + shutil.copytree(self.path, destination.path) + + elif self.is_file: + shutil.copyfile(self.path, destination.path)
+ +
[docs] def create_file(self, contents=""): + """ Creates a file at this path. """ + + if self.exists: + raise ValueError("file %s already exists" % self.path) + + f = open(self.path, "w") + f.write(contents) + f.close()
+ +
[docs] def create_folder(self): + """Creates a folder at this path. + + All intermediate folders MUST already exist. + + """ + + if self.exists: + raise ValueError("folder %s already exists" % self.path) + + os.mkdir(self.path)
+ +
[docs] def create_folders(self): + """Creates a folder at this path. + + This will attempt to create any missing intermediate folders. + + """ + + if self.exists: + raise ValueError("folder %s already exists" % self.path) + + os.makedirs(self.path)
+ +
[docs] def create_package(self): + """Creates a package at this path. + + All intermediate folders/packages MUST already exist. + + """ + + if self.exists: + raise ValueError("package %s already exists" % self.path) + + os.mkdir(self.path) + + # Create the '__init__.py' file that actually turns the folder into a + # package! + init = File(os.path.join(self.path, "__init__.py")) + init.create_file()
+ +
[docs] def delete(self): + """Deletes this file/folder. + + Does nothing if the file/folder does not exist. + + """ + + if self.is_folder: + # Try to make sure that everything in the folder is writeable. + self.make_writeable() + + # Delete it! + shutil.rmtree(self.path) + + elif self.is_file: + # Try to make sure that the file is writeable. + self.make_writeable() + + # Delete it! + os.remove(self.path)
+ +
[docs] def make_writeable(self): + """ Attempt to make the file/folder writeable. """ + + if self.is_folder: + # Try to make sure that everything in the folder is writeable + # (i.e., can be deleted!). This comes in especially handy when + # deleting '.svn' directories. + for path, dirnames, filenames in os.walk(self.path): + for name in dirnames + filenames: + filename = os.path.join(path, name) + if not os.access(filename, os.W_OK): + os.chmod(filename, stat.S_IWUSR) + + elif self.is_file: + # Try to make sure that the file is writeable (i.e., can be + # deleted!). + if not os.access(self.path, os.W_OK): + os.chmod(self.path, stat.S_IWUSR)
+ +
[docs] def move(self, destination): + """ Moves this file/folder. """ + + # Allow the destination to be a string. + if not isinstance(destination, File): + destination = File(destination) + + # Try to make sure that everything in the directory is writeable. + self.make_writeable() + + # Move it! + shutil.move(self.path, destination.path)
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/io/h5/dict_node.html b/_modules/apptools/io/h5/dict_node.html new file mode 100644 index 000000000..1497f2caa --- /dev/null +++ b/_modules/apptools/io/h5/dict_node.html @@ -0,0 +1,362 @@ + + + + + + + apptools.io.h5.dict_node — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.io.h5.dict_node

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+from contextlib import closing
+import json
+
+from numpy import ndarray
+
+from tables import Group as PyTablesGroup
+from tables.nodes import filenode
+
+
+#: The key name which identifies array objects in the JSON dict.
+ARRAY_PROXY_KEY = "__array__"
+NODE_KEY = "node_name"
+
+
+
[docs]class H5DictNode(object): + """Dictionary-like node interface. + + Data for the dict is stored as a JSON file in a PyTables FileNode. This + allows easy storage of Python objects, such as dictionaries and lists of + different data types. + + Note that this is implemented using a group-node assuming that arrays are + valid inputs and will be stored as H5 array nodes. + + Parameters + ---------- + h5_group : H5Group instance + Group node which will be used as a dictionary store. + auto_flush : bool + If True, write data to disk whenever the dict data is altered. + Otherwise, call `flush()` explicitly to write data to disk. + """ + + #: Name of filenode where dict data is stored. + _pyobject_data_node = "_pyobject_data" + + def __init__(self, h5_group, auto_flush=True): + assert self.is_dict_node(h5_group) + + h5_group = self._get_pyt_group(h5_group) + self._h5_group = h5_group + self.auto_flush = auto_flush + + # Load dict data from the file node. + dict_node = getattr(h5_group, self._pyobject_data_node) + with closing(filenode.open_node(dict_node)) as f: + self._pyobject_data = json.loads( + f.read().decode("ascii"), object_hook=self._object_hook + ) + + # -------------------------------------------------------------------------- + # Dictionary interface + # -------------------------------------------------------------------------- + + def __getitem__(self, key): + return self.data[key] + + def __setitem__(self, key, value): + self.data[key] = value + if self.auto_flush: + self.flush() + + def __delitem__(self, key): + del self.data[key] + if self.auto_flush: + self.flush() + + def __contains__(self, key): + return key in self.data + +
[docs] def keys(self): + return self.data.keys()
+ + # -------------------------------------------------------------------------- + # Public interface + # -------------------------------------------------------------------------- + + @property + def data(self): + return self._pyobject_data + + @data.setter + def data(self, new_data_dict): + self._pyobject_data = new_data_dict + if self.auto_flush: + self.flush() + +
[docs] def flush(self): + """ Write buffered data to disk. """ + self._remove_pyobject_node() + self._write_pyobject_node()
+ +
[docs] @classmethod + def add_to_h5file(cls, h5, node_path, data=None, **kwargs): + """Add dict node to an H5 file at the specified path. + + Parameters + ---------- + h5 : H5File + The H5 file where the dictionary data will be stored. + node_path : str + Path to node where data is stored (e.g. '/path/to/my_dict') + data : dict + Data for initialization, if desired. + """ + h5.create_group(node_path) + group = h5[node_path] + + cls._create_pyobject_node(h5._h5, node_path, data=data) + return cls(group, **kwargs)
+ +
[docs] @classmethod + def is_dict_node(cls, pytables_node): + """Return True if PyTables node looks like an H5DictNode. + + NOTE: That this returns False if the node is an `H5DictNode` instance, + since the input node should be a normal PyTables Group node. + """ + # Import here to prevent circular imports + from .file import H5Group + + if isinstance(pytables_node, H5Group): + pytables_node = cls._get_pyt_group(pytables_node) + + if not isinstance(pytables_node, PyTablesGroup): + return False + + return cls._pyobject_data_node in pytables_node._v_children
+ + # -------------------------------------------------------------------------- + # Private interface + # -------------------------------------------------------------------------- + + def _f_remove(self): + """This is called by H5File whenever a node is removed. + + All nodes in `_h5_group` will be removed. + """ + for name in self._h5_group._v_children.keys(): + if name != self._pyobject_data_node: + self._h5_group.__getattr__(name)._f_remove() + # Remove the dict node + self._remove_pyobject_node() + # Remove the group node + self._h5_group._f_remove() + + def _object_hook(self, dct): + """This gets passed object dictionaries by `json.load(s)` and if it + finds `ARRAY_PROXY_KEY` in the object description it returns the + proxied array object. + """ + if ARRAY_PROXY_KEY in dct: + node_name = dct[NODE_KEY] + return getattr(self._h5_group, node_name)[:] + return dct + + def _remove_pyobject_node(self): + node = getattr(self._h5_group, self._pyobject_data_node) + node._f_remove() + + def _write_pyobject_node(self): + pyt_file = self._h5_group._v_file + node_path = self._h5_group._v_pathname + self._create_pyobject_node(pyt_file, node_path, self.data) + + @classmethod + def _create_pyobject_node(cls, pyt_file, node_path, data=None): + if data is None: + data = {} + + # Stash the array values in their own h5 nodes and return a dictionary + # which is appropriate for JSON serialization. + out_data = cls._handle_array_values(pyt_file, node_path, data) + + kwargs = dict(where=node_path, name=cls._pyobject_data_node) + with closing(filenode.new_node(pyt_file, **kwargs)) as f: + f.write(json.dumps(out_data).encode("ascii")) + + @classmethod + def _get_pyt_group(self, group): + if hasattr(group, "_h5_group"): + group = group._h5_group + return group + + @classmethod + def _array_proxy(cls, pyt_file, group, key, array): + """Stores an array as a normal H5 node and returns the proxy object + which will be serialized to JSON. + + `ARRAY_PROXY_KEY` marks the object dictionary as an array proxy so that + `_object_hook` can recognize it. `NODE_KEY` stores the node name of the + array so that `_object_hook` can load the array data when the dict node + is deserialized. + + """ + if key in group: + pyt_file.remove_node(group, key) + pyt_file.create_array(group, key, array) + return {ARRAY_PROXY_KEY: True, NODE_KEY: key} + + @classmethod + def _handle_array_values(cls, pyt_file, group_path, data): + group = pyt_file.get_node(group_path) + + # Convert numpy array values to H5 array nodes. + out_data = {} + for key in data.keys(): + value = data[key] + if isinstance(value, ndarray): + out_data[key] = cls._array_proxy(pyt_file, group, key, value) + else: + out_data[key] = value + + # Remove stored arrays which are no longer in the data dictionary. + pyt_children = group._v_children + nodes_to_remove = [] + for key in pyt_children.keys(): + if key not in data: + nodes_to_remove.append(key) + + for key in nodes_to_remove: + pyt_file.remove_node(group, key) + + return out_data
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/io/h5/file.html b/_modules/apptools/io/h5/file.html new file mode 100644 index 000000000..5fdccad23 --- /dev/null +++ b/_modules/apptools/io/h5/file.html @@ -0,0 +1,671 @@ + + + + + + + apptools.io.h5.file — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.io.h5.file

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+from collections import Mapping, MutableMapping
+from functools import partial
+import inspect
+
+import numpy as np
+import tables
+
+from .dict_node import H5DictNode
+from .table_node import H5TableNode
+
+
+
[docs]def get_atom(dtype): + """Return a PyTables Atom for the given dtype or dtype string.""" + return tables.Atom.from_dtype(np.dtype(dtype))
+ + +
[docs]def iterator_length(iterator): + return sum(1 for _ in iterator)
+ + +def _update_wrapped_docstring(wrapped, original=None): + PREAMBLE = """\ +** H5Group wrapper for H5File.{func_name}: ** +Note that the first argument is a nodepath relative to the group, rather than +an absolute path. Below is the original docstring: + +""".format( + func_name=wrapped.__name__ + ) + wrapped.__doc__ = PREAMBLE + inspect.cleandoc(original.__doc__) + return wrapped + + +
[docs]def h5_group_wrapper(original): + return partial(_update_wrapped_docstring, original=original)
+ + +
[docs]class H5File(Mapping): + """File object for HDF5 files. + + This class wraps PyTables to provide a cleaner, but only implements an + interface for accessing arrays. + + Parameters + ---------- + filename : str or a `tables.File` instance + Filename for an HDF5 file, or a PyTables `File` object. + mode : str + Mode to open the file: + + 'r' : Read-only + 'w' : Write; create new file (an existing file would be deleted). + 'a' : Read and write to file; create if not existing + 'r+': Read and write to file; must already exist + + delete_existing : bool + If True, an existing node will be deleted when a `create_*` method is + called. Otherwise, a ValueError will be raise. + auto_groups : bool + If True, `create_array` will automatically create parent groups. + auto_open : bool + If True, open the file automatically on initialization. Otherwise, + you can call `H5File.open()` explicitly after initialization. + chunked : bool + If True, the default behavior of `create_array` will be a chunked + array (see PyTables `create_carray`). + + """ + + exists_error = ( + "'{}' exists in '{}'; set `delete_existing` attribute " + "to True to overwrite existing calculations." + ) + + def __init__( + self, + filename, + mode="r+", + delete_existing=False, + auto_groups=True, + auto_open=True, + h5filters=None, + ): + self.mode = mode + self.delete_existing = delete_existing + self.auto_groups = auto_groups + if h5filters is None: + self.h5filters = tables.Filters( + complib="blosc", complevel=5, shuffle=True + ) + self._h5 = None + + if isinstance(filename, tables.File): + pyt_file = filename + filename = pyt_file.filename + if pyt_file.isopen: + self._h5 = pyt_file + + self.filename = filename + if auto_open: + self.open() + +
[docs] def open(self): + if not self.is_open: + self._h5 = tables.open_file(self.filename, mode=self.mode)
+ +
[docs] def close(self): + if self.is_open: + self._h5.close() + self._h5 = None
+ + @property + def root(self): + return self["/"] + + @property + def is_open(self): + return self._h5 is not None + + def __str__(self): + return str(self._h5) + + def __repr__(self): + return repr(self._h5) + + def __contains__(self, node_path): + return node_path in self._h5 + + def __getitem__(self, node_path): + try: + node = self._h5.get_node(node_path) + except tables.NoSuchNodeError: + msg = "Node {0!r} not found in {1!r}" + raise NameError(msg.format(node_path, self.filename)) + return _wrap_node(node) + + def __iter__(self): + return (_wrap_node(n) for n in self._h5.iter_nodes(where="/")) + + def __len__(self): + return iterator_length(self) + +
[docs] def iteritems(self, path="/"): + """ Iterate over node paths and nodes of the h5 file. """ + for node in self._h5.walk_nodes(where=path): + node_path = node._v_pathname + yield node_path, _wrap_node(node)
+ +
[docs] def create_array( + self, + node_path, + array_or_shape, + dtype=None, + chunked=False, + extendable=False, + **kwargs + ): + """Create node to store an array. + + Parameters + ---------- + node_path : str + PyTable node path; e.g. '/path/to/node'. + array_or_shape : array or shape tuple + Array or shape tuple for an array. If given a shape tuple, the + `dtype` parameter must also specified. + dtype : str or numpy.dtype + Data type of array. Only necessary if `array_or_shape` is a shape. + chunked : bool + Controls whether the array is chunked. + extendable : {None | bool} + Controls whether the array is extendable. + kwargs : key/value pairs + Keyword args passed to PyTables `File.create_(c|e)array`. + """ + self._check_node(node_path) + self._assert_valid_path(node_path) + + h5 = self._h5 + + if isinstance(array_or_shape, tuple): + if dtype is None: + msg = "`dtype` must be specified if only given array shape." + raise ValueError(msg) + array = None + dtype = dtype + shape = array_or_shape + else: + array = array_or_shape + dtype = array.dtype.name + shape = array.shape + + path, name = self.split_path(node_path) + if extendable: + shape = (0,) + shape[1:] + atom = get_atom(dtype) + node = h5.create_earray( + path, name, atom, shape, filters=self.h5filters, **kwargs + ) + if array is not None: + node.append(array) + elif chunked: + atom = get_atom(dtype) + node = h5.create_carray( + path, name, atom, shape, filters=self.h5filters, **kwargs + ) + if array is not None: + node[:] = array + else: + if array is None: + array = np.zeros(shape, dtype=dtype) + node = h5.create_array(path, name, array, **kwargs) + return node
+ +
[docs] def create_group(self, group_path, **kwargs): + """Create group. + + Parameters + ---------- + group_path : str + PyTable group path; e.g. '/path/to/group'. + kwargs : key/value pairs + Keyword args passed to PyTables `File.create_group`. + """ + self._check_node(group_path) + self._assert_valid_path(group_path) + path, name = self.split_path(group_path) + self._h5.create_group(path, name, **kwargs) + return self[group_path]
+ +
[docs] def create_dict(self, node_path, data=None, **kwargs): + """Create dict node at the specified path. + + Parameters + ---------- + node_path : str + Path to node where data is stored (e.g. '/path/to/my_dict') + data : dict + Data for initialization, if desired. + """ + self._check_node(node_path) + self._assert_valid_path(node_path) + H5DictNode.add_to_h5file(self, node_path, data=data, **kwargs) + return self[node_path]
+ +
[docs] def create_table(self, node_path, description, **kwargs): + """Create table node at the specified path. + + Parameters + ---------- + node_path : str + Path to node where data is stored (e.g. '/path/to/my_dict') + description : dict or numpy dtype object + The description of the columns in the table. This is either a dict + of column name -> dtype items or a numpy record array dtype. For + more information, see the documentation for Table in pytables. + """ + self._check_node(node_path) + self._assert_valid_path(node_path) + H5TableNode.add_to_h5file(self, node_path, description, **kwargs) + return self[node_path]
+ + def _check_node(self, node_path): + """Check if node exists and create parent groups if necessary. + + Either raise error or delete depending on `delete_existing` attribute. + """ + if self.auto_groups: + path, name = self.split_path(node_path) + self._create_required_groups(path) + + if node_path in self: + if self.delete_existing: + if isinstance(self[node_path], H5Group): + self.remove_group(node_path, recursive=True) + else: + self.remove_node(node_path) + else: + msg = self.exists_error.format(node_path, self.filename) + raise ValueError(msg) + + def _create_required_groups(self, path): + if path not in self: + parent, missing = self.split_path(path) + # Call recursively to ensure that all parent groups exist. + self._create_required_groups(parent) + self.create_group(path) + +
[docs] def remove_node(self, node_path): + """Remove node + + Parameters + ---------- + node_path : str + PyTable node path; e.g. '/path/to/node'. + """ + node = self[node_path] + if isinstance(node, H5Group): + msg = "{!r} is a group. Use `remove_group` to remove group nodes." + raise ValueError(msg.format(node.pathname)) + node._f_remove()
+ +
[docs] def remove_group(self, group_path, **kwargs): + """Remove group + + Parameters + ---------- + group_path : str + PyTable group path; e.g. '/path/to/group'. + """ + self[group_path]._h5_group._g_remove(**kwargs)
+ + @classmethod + def _assert_valid_path(self, node_path): + if "attrs" in node_path.split("/"): + raise ValueError("'attrs' is an invalid node name.") + +
[docs] @classmethod + def split_path(cls, node_path): + """Split node path returning the base path and node name. + + For example: '/path/to/node' will return '/path/to' and 'node' + + Parameters + ---------- + node_path : str + PyTable node path; e.g. '/path/to/node'. + """ + i = node_path.rfind("/") + if i == 0: + return "/", node_path[1:] + else: + return node_path[:i], node_path[i + 1:]
+ +
[docs] @classmethod + def join_path(cls, *args): + """Join parts of an h5 path. + + For example, the 3 argmuments 'path', 'to', 'node' will return + '/path/to/node'. + + Parameters + ---------- + args : str + Parts of path to be joined. + """ + path = "/".join(part.strip("/") for part in args) + if not path.startswith("/"): + path = "/" + path + return path
+ + +
[docs]class H5Attrs(MutableMapping): + """An attributes dictionary for an h5 node. + + This intercepts `__setitem__` so that python sequences can be converted to + numpy arrays. This helps preserve the readability of our HDF5 files by + other (non-python) programs. + """ + + def __init__(self, node_attrs): + self._node_attrs = node_attrs + + def __delitem__(self, key): + del self._node_attrs[key] + + def __getitem__(self, key): + return self._node_attrs[key] + + def __iter__(self): + return iter(self.keys()) + + def __len__(self): + return len(self._node_attrs._f_list()) + + def __setitem__(self, key, value): + if isinstance(value, tuple) or isinstance(value, list): + value = np.array(value) + self._node_attrs[key] = value + +
[docs] def get(self, key, default=None): + return default if key not in self else self[key]
+ +
[docs] def keys(self): + return self._node_attrs._f_list()
+ +
[docs] def values(self): + return [self[k] for k in self.keys()]
+ +
[docs] def items(self): + return [(k, self[k]) for k in self.keys()]
+ + +
[docs]class H5Group(Mapping): + """A group node in an H5File. + + This is a thin wrapper around PyTables' Group object to expose attributes + and maintain the dict interface of H5File. + """ + + def __init__(self, pytables_group): + self._h5_group = pytables_group + self.attrs = H5Attrs(self._h5_group._v_attrs) + + def __contains__(self, node_path): + return node_path in self._h5_group + + def __str__(self): + return str(self._h5_group) + + def __repr__(self): + return repr(self._h5_group) + + def __getitem__(self, node_path): + parts = node_path.split("/") + # PyTables stores children as attributes + node = self._h5_group.__getattr__(parts[0]) + node = _wrap_node(node) + if len(parts) == 1: + return node + else: + return node["/".join(parts[1:])] + + def __iter__(self): + return (_wrap_node(c) for c in self._h5_group) + + def __len__(self): + return iterator_length(self) + + @property + def pathname(self): + return self._h5_group._v_pathname + + @property + def name(self): + return self._h5_group._v_name + + @property + def filename(self): + return self._h5_group._v_file.filename + + @property + def root(self): + return _wrap_node(self._h5_group._v_file.root) + + @property + def children_names(self): + return list(self._h5_group._v_children.keys()) + + @property + def subgroup_names(self): + return list(self._h5_group._v_groups.keys()) + +
[docs] def iter_groups(self): + """ Iterate over `H5Group` nodes that are children of this group. """ + groups = self._h5_group._v_groups + + # not using the groups.values() method here, because groups is a + # `proxydict` object whose .values() method is non-lazy. Related: + # PyTables/PyTables#784. + return (_wrap_node(groups[group_name]) for group_name in groups)
+ +
[docs] @h5_group_wrapper(H5File.create_group) + def create_group(self, group_subpath, delete_existing=False, **kwargs): + return self._delegate_to_h5file( + "create_group", + group_subpath, + delete_existing=delete_existing, + **kwargs + )
+ +
[docs] @h5_group_wrapper(H5File.remove_group) + def remove_group(self, group_subpath, **kwargs): + return self._delegate_to_h5file( + "remove_group", group_subpath, **kwargs + )
+ +
[docs] @h5_group_wrapper(H5File.create_array) + def create_array( + self, + node_subpath, + array_or_shape, + dtype=None, + chunked=False, + extendable=False, + **kwargs + ): + return self._delegate_to_h5file( + "create_array", + node_subpath, + array_or_shape, + dtype=dtype, + chunked=chunked, + extendable=extendable, + **kwargs + )
+ +
[docs] @h5_group_wrapper(H5File.create_table) + def create_table(self, node_subpath, description, *args, **kwargs): + return self._delegate_to_h5file( + "create_table", node_subpath, description, *args, **kwargs + )
+ +
[docs] @h5_group_wrapper(H5File.create_dict) + def create_dict(self, node_subpath, data=None, **kwargs): + return self._delegate_to_h5file( + "create_dict", node_subpath, data=data, **kwargs + )
+ +
[docs] @h5_group_wrapper(H5File.remove_node) + def remove_node(self, node_subpath, **kwargs): + return self._delegate_to_h5file("remove_node", node_subpath, **kwargs)
+ + def _delegate_to_h5file( + self, function_name, node_subpath, *args, **kwargs + ): + delete_existing = kwargs.pop("delete_existing", False) + h5 = H5File(self._h5_group._v_file, delete_existing=delete_existing) + group_path = h5.join_path(self.pathname, node_subpath) + func = getattr(h5, function_name) + return func(group_path, *args, **kwargs)
+ + +def _wrap_node(node): + """ Wrap PyTables node object, if necessary. """ + if isinstance(node, tables.Group): + if H5DictNode.is_dict_node(node): + node = H5DictNode(node) + else: + node = H5Group(node) + elif H5TableNode.is_table_node(node): + node = H5TableNode(node) + return node +
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/io/h5/table_node.html b/_modules/apptools/io/h5/table_node.html new file mode 100644 index 000000000..755c69191 --- /dev/null +++ b/_modules/apptools/io/h5/table_node.html @@ -0,0 +1,287 @@ + + + + + + + apptools.io.h5.table_node — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.io.h5.table_node

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+import numpy as np
+
+from tables.table import Table as PyTablesTable
+
+
+class _TableRowAccessor(object):
+    """A simple object which provides read access to the rows in a Table."""
+
+    def __init__(self, h5_table):
+        self._h5_table = h5_table
+
+    def __getitem__(self, key):
+        return self._h5_table[key]
+
+
+
[docs]class H5TableNode(object): + """A wrapper for PyTables Table nodes. + + Parameters + ---------- + node : tables.Table instance + An H5 node which is a pytables.Table or H5TableNode instance + """ + + def __init__(self, node): + # Avoid a circular import + from .file import H5Attrs + + assert self.is_table_node(node) + self._h5_table = node._h5_table if hasattr(node, "_h5_table") else node + self.attrs = H5Attrs(self._h5_table._v_attrs) + + # -------------------------------------------------------------------------- + # Creation methods + # -------------------------------------------------------------------------- + +
[docs] @classmethod + def add_to_h5file(cls, h5, node_path, description, **kwargs): + """Add table node to an H5 file at the specified path. + + Parameters + ---------- + h5 : H5File + The H5 file where the table node will be stored. + node_path : str + Path to node where data is stored (e.g. '/path/to/my_table') + description : list of tuples or numpy dtype object + The description of the columns in the table. This is either a list + of (column name, dtype, [, shape or itemsize]) tuples or a numpy + record array dtype. For more information, see the documentation for + `Table` in PyTables. + **kwargs : dict + Additional keyword arguments to pass to pytables.File.create_table + """ + if isinstance(description, (tuple, list)): + description = np.dtype(description) + + cls._create_pytables_node(h5, node_path, description, **kwargs) + node = h5[node_path] + + return cls(node)
+ +
[docs] @classmethod + def is_table_node(cls, pytables_node): + """Return True if pytables_node is a pytables.Table or a H5TableNode. + """ + return isinstance(pytables_node, (PyTablesTable, H5TableNode))
+ + # -------------------------------------------------------------------------- + # Public interface + # -------------------------------------------------------------------------- + +
[docs] def append(self, data): + """Add some data to the table. + + Parameters + ---------- + data : dict + A dictionary of column name -> values items + """ + rows = list(zip(*[data[name] for name in self.keys()])) + self._h5_table.append(rows)
+ + def __getitem__(self, col_or_cols): + """Return one or more columns of data from the table. + + Parameters + ---------- + col_or_cols : str or list of str + A single column name or a list of column names + + Return + ------ + data : ndarray + An array of column data with the column order matching that of + `col_or_cols`. + """ + if isinstance(col_or_cols, str): + return self._h5_table.col(col_or_cols) + + column_data = [self._h5_table.col(name) for name in col_or_cols] + return np.column_stack(column_data) + + @property + def ix(self): + """Return an object which provides access to row data.""" + return _TableRowAccessor(self._h5_table) + +
[docs] def keys(self): + return self._h5_table.colnames
+ +
[docs] def to_dataframe(self): + """Return table data as a pandas `DataFrame`. + + XXX: This does not work if the table contains a multidimensional column + + This method requires pandas to have been installed in the environment. + """ + from pandas import DataFrame + + # Slicing rows gives a numpy struct array, which DataFrame understands. + return DataFrame(self.ix[:])
+ + # -------------------------------------------------------------------------- + # Object interface + # -------------------------------------------------------------------------- + + def __repr__(self): + return repr(self._h5_table) + + def __len__(self): + return self._h5_table.nrows + + # -------------------------------------------------------------------------- + # Private interface + # -------------------------------------------------------------------------- + + def _f_remove(self): + """Implement the PyTables `Node._f_remove` method so that H5File + doesn't choke when trying to remove our node. + """ + self._h5_table._f_remove() + self._h5_table = None + + @classmethod + def _create_pytables_node(cls, h5, node_path, description, **kwargs): + path, name = h5.split_path(node_path) + pyt_file = h5._h5 + pyt_file.create_table(path, name, description, **kwargs)
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/io/h5/utils.html b/_modules/apptools/io/h5/utils.html new file mode 100644 index 000000000..a35477daa --- /dev/null +++ b/_modules/apptools/io/h5/utils.html @@ -0,0 +1,166 @@ + + + + + + + apptools.io.h5.utils — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.io.h5.utils

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+from contextlib import contextmanager
+
+from .file import H5File
+
+
+
[docs]@contextmanager +def open_h5file(filename, mode="r+", **kwargs): + """Context manager for reading an HDF5 file as an H5File object. + + Parameters + ---------- + filename : str + HDF5 file name. + mode : str + Mode to open the file: + + 'r' : Read-only + 'w' : Write; create new file (an existing file would be deleted). + 'a' : Read and write to file; create if not existing + 'r+': Read and write to file; must already exist + + See `H5File` for additional keyword arguments. + """ + h5 = H5File(filename, mode=mode, **kwargs) + try: + yield h5 + finally: + h5.close()
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/logger/agent/attachments.html b/_modules/apptools/logger/agent/attachments.html new file mode 100644 index 000000000..f0d664cbb --- /dev/null +++ b/_modules/apptools/logger/agent/attachments.html @@ -0,0 +1,235 @@ + + + + + + + apptools.logger.agent.attachments — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.logger.agent.attachments

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" Attach relevant project files.
+
+FIXME: there are no public project plugins for Envisage 3, yet. In any case,
+this stuff should not be hard-coded, but extensible via extension points. The
+code remains here because we can reuse the zip utility code in that extensible
+rewrite.
+"""
+
+import logging
+import os.path
+from email import encoders
+from email.mime.base import MIMEBase
+
+from traits.api import Any, HasTraits
+
+
+logger = logging.getLogger(__name__)
+
+
+
[docs]class Attachments(HasTraits): + + application = Any() + message = Any() + + def __init__(self, message, **traits): + traits = traits.copy() + traits["message"] = message + super(Attachments, self).__init__(**traits) + + # FIXME: all of the package_*() methods refer to deprecated project plugins + +
[docs] def package_workspace(self): + if self.application is None: + pass + + workspace = self.application.get_service("envisage.project.IWorkspace") + if workspace is not None: + dir = workspace.path + self._attach_directory(dir)
+ +
[docs] def package_single_project(self): + if self.application is None: + pass + + single_project = self.application.get_service( + "envisage.single_project.ModelService" + ) + if single_project is not None: + dir = single_project.location + self._attach_directory(dir)
+ +
[docs] def package_any_relevant_files(self): + self.package_workspace() + self.package_single_project()
+ + def _attach_directory(self, dir): + relpath = os.path.basename(dir) + + import zipfile + from io import BytesIO + + ctype = "application/octet-stream" + maintype, subtype = ctype.split("/", 1) + msg = MIMEBase(maintype, subtype) + + file_object = BytesIO() + zip = zipfile.ZipFile(file_object, "w") + _append_to_zip_archive(zip, dir, relpath) + zip.close() + + msg.set_payload(file_object.getvalue()) + + encoders.encode_base64(msg) # Encode the payload using Base64 + msg.add_header( + "Content-Disposition", "attachment", filename="project.zip" + ) + + self.message.attach(msg) + + file_object.close()
+ + +def _append_to_zip_archive(zip, dir, relpath): + """ Add all files in and below directory dir into zip archive""" + for filename in os.listdir(dir): + path = os.path.join(dir, filename) + + if os.path.isfile(path): + name = os.path.join(relpath, filename) + zip.write(path, name) + logger.debug("adding %s to error report" % path) + else: + if filename != ".svn": # skip svn files if any + subdir = os.path.join(dir, filename) + _append_to_zip_archive( + zip, subdir, os.path.join(relpath, filename) + ) +
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/logger/agent/quality_agent_mailer.html b/_modules/apptools/logger/agent/quality_agent_mailer.html new file mode 100644 index 000000000..d6d7b3c5c --- /dev/null +++ b/_modules/apptools/logger/agent/quality_agent_mailer.html @@ -0,0 +1,244 @@ + + + + + + + apptools.logger.agent.quality_agent_mailer — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.logger.agent.quality_agent_mailer

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+
+# Standard library imports.
+import logging
+import os
+
+# Enthought library imports.
+from traits.util.home_directory import get_home_directory
+
+
+# Setup a logger for this module.
+logger = logging.getLogger(__name__)
+
+
+
[docs]def create_email_message( + fromaddr, + toaddrs, + ccaddrs, + subject, + priority, + include_project=False, + stack_trace="", + comments="", +): + # format a message suitable to be sent to the Roundup bug tracker + + from email.MIMEMultipart import MIMEMultipart + from email.MIMEText import MIMEText + from email.MIMEBase import MIMEBase + + message = MIMEMultipart() + message["Subject"] = "%s [priority=%s]" % (subject, priority) + message["To"] = ", ".join(toaddrs) + message["Cc"] = ", ".join(ccaddrs) + message["From"] = fromaddr + message.preamble = "You will not see this in a MIME-aware mail reader.\n" + message.epilogue = " " # To guarantee the message ends with a newline + + # First section is simple ASCII data ... + m = [] + m.append("Bug Report") + m.append("==============================") + m.append("") + + if len(comments) > 0: + m.append("Comments:") + m.append("========") + m.append(comments) + m.append("") + + if len(stack_trace) > 0: + m.append("Stack Trace:") + m.append("===========") + m.append(stack_trace) + m.append("") + + msg = MIMEText("\n".join(m)) + message.attach(msg) + + # Include the log file ... + if True: + try: + log = os.path.join(get_home_directory(), "envisage.log") + f = open(log, "r") + entries = f.readlines() + f.close() + + ctype = "application/octet-stream" + maintype, subtype = ctype.split("/", 1) + msg = MIMEBase(maintype, subtype) + + msg = MIMEText("".join(entries)) + msg.add_header( + "Content-Disposition", "attachment", filename="logfile.txt" + ) + message.attach(msg) + except Exception: + logger.exception("Failed to include log file with message") + + # Include the environment variables ... + if True: + """ + Transmit the user's environment settings as well. Main purpose is to + work out the user name to help with following up on bug reports and + in future we should probably send less data. + """ + try: + entries = [] + for key, value in os.environ.items(): + entries.append("%30s : %s\n" % (key, value)) + + ctype = "application/octet-stream" + maintype, subtype = ctype.split("/", 1) + msg = MIMEBase(maintype, subtype) + + msg = MIMEText("".join(entries)) + msg.add_header( + "Content-Disposition", "attachment", filename="environment.txt" + ) + message.attach(msg) + + except Exception: + logger.exception( + "Failed to include environment variables with message" + ) + + return message
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/logger/agent/quality_agent_view.html b/_modules/apptools/logger/agent/quality_agent_view.html new file mode 100644 index 000000000..8a09b4bfa --- /dev/null +++ b/_modules/apptools/logger/agent/quality_agent_view.html @@ -0,0 +1,510 @@ + + + + + + + apptools.logger.agent.quality_agent_view — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.logger.agent.quality_agent_view

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+
+# Standard library imports.
+import logging
+
+# Enthought library imports.
+from pyface.api import Dialog
+from traits.api import Any, Str, Tuple
+
+
+# Setup a logger for this module.
+logger = logging.getLogger(__name__)
+
+
+priority_levels = ["Low", "Medium", "High", "Critical"]
+
+
+
[docs]class QualityAgentView(Dialog): + + size = Tuple((700, 900)) + title = Str("Quality Agent") + + # The associated LoggerService. + service = Any() + + msg = Str("") + subject = Str("Untitled Error Report") + to_address = Str() + cc_address = Str("") + from_address = Str() + smtp_server = Str() + priority = Str(priority_levels[2]) + comments = Str("None") + include_userdata = Any + + ########################################################################### + # Protected 'Dialog' interface. + ########################################################################### + + # fixme: Ideally, this should be passed in; this topic ID belongs to the + # Enlib help project/plug-in. + help_id = "enlib|HID_Quality_Agent_Dlg" + + def _create_dialog_area(self, parent): + """ Creates the main content of the dialog. """ + import wx + + parent.SetSizeHints(minW=300, minH=575) + + # Add the main panel + sizer = wx.BoxSizer(wx.VERTICAL) + panel = wx.Panel(parent, -1) + panel.SetSizer(sizer) + panel.SetAutoLayout(True) + + # Add a descriptive label at the top ... + label = wx.StaticText(panel, -1, "Send a comment or bug report ...") + sizer.Add(label, 0, wx.ALL, border=5) + + # Add the stack trace view ... + error_panel = self._create_error_panel(panel) + sizer.Add( + error_panel, 1, wx.ALL | wx.EXPAND | wx.CLIP_CHILDREN, border=5 + ) + + # Update the layout: + sizer.Fit(panel) + + # Add the error report view ... + report_panel = self._create_report_panel(panel) + sizer.Add( + report_panel, 2, wx.ALL | wx.EXPAND | wx.CLIP_CHILDREN, border=5 + ) + + # Update the layout: + sizer.Fit(panel) + + return panel + + def _create_buttons(self, parent): + """ Creates the buttons. """ + import wx + + sizer = wx.BoxSizer(wx.HORIZONTAL) + + # 'Send' button. + send = wx.Button(parent, wx.ID_OK, "Send") + wx.EVT_BUTTON(parent, wx.ID_OK, self._on_send) + sizer.Add(send) + send.SetDefault() + + # 'Cancel' button. + cancel = wx.Button(parent, wx.ID_CANCEL, "Cancel") + wx.EVT_BUTTON(parent, wx.ID_CANCEL, self._wx_on_cancel) + sizer.Add(cancel, 0, wx.LEFT, 10) + + # 'Help' button. + if len(self.help_id) > 0: + help = wx.Button(parent, wx.ID_HELP, "Help") + wx.EVT_BUTTON(parent, wx.ID_HELP, self._wx_on_help) + sizer.Add(help, 0, wx.LEFT, 10) + + return sizer + + ### Utility methods ####################################################### + + def _create_error_panel(self, parent): + import wx + + box = wx.StaticBox(parent, -1, "Message:") + sizer = wx.StaticBoxSizer(box, wx.VERTICAL) + + # Print the stack trace + label2 = wx.StaticText( + parent, + -1, + "The following information will be included in the report:", + ) + sizer.Add( + label2, + 0, + wx.LEFT | wx.TOP | wx.BOTTOM | wx.CLIP_CHILDREN, + border=5, + ) + + details = wx.TextCtrl( + parent, + -1, + self.msg, + size=(-1, 75), + style=wx.TE_MULTILINE + | wx.TE_READONLY + | wx.HSCROLL + | wx.VSCROLL + | wx.TE_RICH2 + | wx.CLIP_CHILDREN, + ) + details.SetSizeHints(minW=-1, minH=75) + # Set the font to not be proportional + font = wx.Font(12, wx.MODERN, wx.NORMAL, wx.NORMAL) + details.SetStyle(0, len(self.msg), wx.TextAttr(font=font)) + sizer.Add(details, 1, wx.EXPAND | wx.ALL | wx.CLIP_CHILDREN, 5) + + return sizer + + def _create_report_panel(self, parent): + import wx + + box = wx.StaticBox(parent, -1, "Report Information:") + sizer = wx.StaticBoxSizer(box, wx.VERTICAL) + + # Add email info ... + sizer.Add(self._create_email_info(parent), 0, wx.ALL | wx.EXPAND, 5) + + # Add priority combo: + sizer.Add(self._create_priority_combo(parent), 0, wx.ALL | wx.RIGHT, 5) + + # Extra comments from the user: + label3 = wx.StaticText(parent, -1, "Additional Comments:") + sizer.Add( + label3, 0, wx.LEFT | wx.TOP | wx.BOTTOM | wx.CLIP_CHILDREN, 5 + ) + + comments_field = wx.TextCtrl( + parent, + -1, + self.comments, + size=(-1, 75), + style=wx.TE_MULTILINE | wx.TE_RICH2 | wx.CLIP_CHILDREN, + ) + comments_field.SetSizeHints(minW=-1, minH=75) + font = wx.Font(12, wx.MODERN, wx.NORMAL, wx.NORMAL) + comments_field.SetStyle(0, len(self.comments), wx.TextAttr(font=font)) + sizer.Add(comments_field, 1, wx.ALL | wx.EXPAND | wx.CLIP_CHILDREN, 5) + wx.EVT_TEXT(parent, comments_field.GetId(), self._on_comments) + + # Include the project combobox? + if len(self.service.mail_files) > 0: + sizer.Add(self._create_project_upload(parent), 0, wx.ALL, border=5) + + return sizer + + def _create_email_info(self, parent): + import wx + + # Layout setup .. + sizer = wx.FlexGridSizer(5, 2, 10, 10) + sizer.AddGrowableCol(1) + + title_label = wx.StaticText(parent, -1, "Subject:") + sizer.Add(title_label, 0, wx.ALL | wx.ALIGN_RIGHT) + title_field = wx.TextCtrl(parent, -1, self.subject, wx.Point(-1, -1)) + sizer.Add( + title_field, + 1, + wx.EXPAND | wx.ALL | wx.ALIGN_RIGHT | wx.CLIP_CHILDREN, + ) + wx.EVT_TEXT(parent, title_field.GetId(), self._on_subject) + + to_label = wx.StaticText(parent, -1, "To:") + sizer.Add(to_label, 0, wx.ALL | wx.ALIGN_RIGHT) + to_field = wx.TextCtrl(parent, -1, self.to_address) + sizer.Add( + to_field, 1, wx.EXPAND | wx.ALL | wx.ALIGN_RIGHT | wx.CLIP_CHILDREN + ) + wx.EVT_TEXT(parent, to_field.GetId(), self._on_to) + + cc_label = wx.StaticText(parent, -1, "Cc:") + sizer.Add(cc_label, 0, wx.ALL | wx.ALIGN_RIGHT) + cc_field = wx.TextCtrl(parent, -1, "") + sizer.Add( + cc_field, 1, wx.EXPAND | wx.ALL | wx.ALIGN_RIGHT | wx.CLIP_CHILDREN + ) + wx.EVT_TEXT(parent, cc_field.GetId(), self._on_cc) + + from_label = wx.StaticText(parent, -1, "From:") + sizer.Add(from_label, 0, wx.ALL | wx.ALIGN_RIGHT) + from_field = wx.TextCtrl(parent, -1, self.from_address) + sizer.Add( + from_field, + 1, + wx.EXPAND | wx.ALL | wx.ALIGN_RIGHT | wx.CLIP_CHILDREN, + ) + wx.EVT_TEXT(parent, from_field.GetId(), self._on_from) + + smtp_label = wx.StaticText(parent, -1, "SMTP Server:") + sizer.Add(smtp_label, 0, wx.ALL | wx.ALIGN_RIGHT) + smtp_server_field = wx.TextCtrl(parent, -1, self.smtp_server) + sizer.Add( + smtp_server_field, + 1, + wx.EXPAND | wx.ALL | wx.ALIGN_RIGHT | wx.CLIP_CHILDREN, + ) + wx.EVT_TEXT(parent, smtp_server_field.GetId(), self._on_smtp_server) + + return sizer + + def _create_priority_combo(self, parent): + import wx + + sizer = wx.BoxSizer(wx.HORIZONTAL) + + label = wx.StaticText(parent, -1, "How critical is this issue?") + sizer.Add(label, 0, wx.ALL, border=0) + + cb = wx.ComboBox( + parent, + -1, + self.priority, + wx.Point(90, 50), + wx.Size(95, -1), + priority_levels, + wx.CB_READONLY, + ) + sizer.Add(cb, 1, wx.EXPAND | wx.LEFT | wx.CLIP_CHILDREN, border=10) + + wx.EVT_COMBOBOX(parent, cb.GetId(), self._on_priority) + + return sizer + + def _create_project_upload(self, parent): + import wx + + id = wx.NewId() + cb = wx.CheckBox( + parent, + id, + "Include Workspace Files (will increase email size) ", + wx.Point(65, 80), + wx.Size(-1, 20), + wx.NO_BORDER, + ) + wx.EVT_CHECKBOX(parent, id, self._on_project) + + return cb + + ## UI Listeners ########################################################### + + def _on_subject(self, event): + self.subject = event.GetEventObject().GetValue() + + def _on_to(self, event): + self.to_address = event.GetEventObject().GetValue() + + def _on_cc(self, event): + self.cc_address = event.GetEventObject().GetValue() + + def _on_from(self, event): + self.from_address = event.GetEventObject().GetValue() + + def _on_smtp_server(self, event): + self.smtp_server = event.GetEventObject().GetValue() + + def _on_priority(self, event): + self.priority = event.GetEventObject().GetStringSelection() + + def _on_comments(self, event): + self.comments = event.GetEventObject().GetValue() + + def _on_project(self, event): + self.include_userdata = event.Checked() + cb = event.GetEventObject() + + if event.Checked(): + cb.SetLabel( + "Include Workspace Files (approx. %.2f MBytes)" + % self._compute_project_size() + ) + else: + cb.SetLabel("Include Workspace Files (will increase email size)") + + def _on_send(self, event): + + # Disable the Send button while we go through the possibly + # time-consuming email-sending process. + button = event.GetEventObject() + button.Enable(0) + + fromaddr, toaddrs, ccaddrs = self._create_email_addresses() + message = self._create_email(fromaddr, toaddrs, ccaddrs) + + self.service.send_bug_report( + self.smtp_server, fromaddr, toaddrs, ccaddrs, message + ) + + # save the user's preferences + self.service.preferences.smtp_server = self.smtp_server + self.service.preferences.to_address = self.to_address + self.service.preferences.from_address = self.from_address + + # finally we close the dialog + self._wx_on_ok(event) + + ## Private ################################################################ + + def _create_email_addresses(self): + # utility function map addresses from ui into the standard format + # FIXME: We should use standard To: header parsing instead of this ad + # hoc whitespace-only approach. + fromaddr = self.from_address + if "" == fromaddr.strip(): + fromaddr = "anonymous" + toaddrs = self.to_address.split() + ccaddrs = self.cc_address.split() + + return fromaddr, toaddrs, ccaddrs + + def _compute_project_size(self): + # determine size of email in MBytes + fromaddr, toaddrs, ccaddrs = self._create_email_addresses() + message = self._create_email(fromaddr, toaddrs, ccaddrs) + return len(message.as_string()) / (2.0 ** 20) + + def _create_email(self, fromaddr, toaddrs, ccaddrs): + return self.service.create_email_message( + fromaddr, + toaddrs, + ccaddrs, + self.subject, + self.priority, + self.include_userdata, + self.msg, + self.comments, + ) + + def _to_address_default(self): + return self.service.preferences.to_address + + def _from_address_default(self): + return self.service.preferences.from_address + + def _smtp_server_default(self): + return self.service.preferences.smtp_server
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/logger/custom_excepthook.html b/_modules/apptools/logger/custom_excepthook.html new file mode 100644 index 000000000..371e3aa28 --- /dev/null +++ b/_modules/apptools/logger/custom_excepthook.html @@ -0,0 +1,165 @@ + + + + + + + apptools.logger.custom_excepthook — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.logger.custom_excepthook

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+
+
+# Standard library imports.
+import logging
+from traceback import format_exception
+
+
+"""
+    To catch exceptions with our own code this code needs to be added
+    sys.excepthook = custom_excepthook
+"""
+
+
+
[docs]def custom_excepthook(type, value, traceback): + """ Pass on the exception to the logging system. """ + + msg = "Custom - Traceback (most recent call last):\n" + list = format_exception(type, value, traceback) + + msg = "".join(list) + + # Try to find the module that the exception actually came from. + name = getattr(traceback.tb_frame, "f_globals", {}).get( + "__name__", __name__ + ) + logger = logging.getLogger(name) + logger.error(msg)
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/logger/log_point.html b/_modules/apptools/logger/log_point.html new file mode 100644 index 000000000..b1b3cd71c --- /dev/null +++ b/_modules/apptools/logger/log_point.html @@ -0,0 +1,170 @@ + + + + + + + apptools.logger.log_point — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.logger.log_point

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" Prints a stack trace every time it is called but does not halt execution
+    of the application.
+
+    Copied from Uche Ogbuji's blog
+"""
+
+# Standard library imports.
+import inspect
+from io import StringIO
+
+
+
[docs]def log_point(msg="\n"): + stack = inspect.stack() + # get rid of logPoint's part of the stack: + stack = stack[1:] + stack.reverse() + output = StringIO() + if msg: + output.write(str(msg) + "\n") + for stackLine in stack: + frame, filename, line, funcname, lines, unknown = stackLine + if filename.endswith("/unittest.py"): + # unittest.py code is a boring part of the traceback + continue + if filename.startswith("./"): + filename = filename[2:] + output.write("%s:%s in %s:\n" % (filename, line, funcname)) + if lines: + output.write(" %s\n" % "".join(lines)[:-1]) + s = output.getvalue() + + return s
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/logger/log_queue_handler.html b/_modules/apptools/logger/log_queue_handler.html new file mode 100644 index 000000000..30cce403c --- /dev/null +++ b/_modules/apptools/logger/log_queue_handler.html @@ -0,0 +1,198 @@ + + + + + + + apptools.logger.log_queue_handler — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.logger.log_queue_handler

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+
+# Standard library imports.
+from logging import Handler
+
+# Local imports.
+from .ring_buffer import RingBuffer
+
+
+
[docs]class LogQueueHandler(Handler): + + """Buffers up the log messages so that we can display them later. + This is important on startup when log messages are generated before + the ui has started. By putting them in this queue we can display + them once the ui is ready. + """ + + # The view where updates will go + _view = None + + def __init__(self, size=1000): + Handler.__init__(self) + # only buffer 1000 log records + self.size = size + self.ring = RingBuffer(self.size) + self.dirty = False + +
[docs] def emit(self, record): + """ Actually this is more like an enqueue than an emit().""" + self.ring.append(record) + if self._view is not None: + try: + self._view.update() + except Exception: + pass + self.dirty = True
+ +
[docs] def get(self): + self.dirty = False + + try: + result = self.ring.get() + except Exception: + # we did our best and it won't cause too much damage + # to just return a bogus message + result = [] + + return result
+ +
[docs] def has_new_records(self): + return self.dirty
+ +
[docs] def reset(self): + # start over with a new empty buffer + self.ring = RingBuffer(self.size) + if self._view is not None: + try: + self._view.update() + except Exception: + pass + self.dirty = True
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/logger/logger.html b/_modules/apptools/logger/logger.html new file mode 100644 index 000000000..7c65ef5cc --- /dev/null +++ b/_modules/apptools/logger/logger.html @@ -0,0 +1,188 @@ + + + + + + + apptools.logger.logger — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.logger.logger

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" Convenience functions for creating logging handlers etc. """
+
+
+# Standard library imports.
+import logging
+from logging.handlers import RotatingFileHandler
+
+# Local imports.
+from .log_queue_handler import LogQueueHandler
+
+
+# The default logging level.
+LEVEL = logging.DEBUG
+
+# The default formatter.
+FORMATTER = logging.Formatter("%(levelname)s|%(asctime)s|%(message)s")
+
+
+
[docs]class LogFileHandler(RotatingFileHandler): + """The default log file handler.""" + + def __init__( + self, path, maxBytes=1000000, backupCount=3, level=None, formatter=None + ): + RotatingFileHandler.__init__( + self, path, maxBytes=maxBytes, backupCount=3 + ) + + if level is None: + level = LEVEL + if formatter is None: + formatter = FORMATTER + # Set our default formatter and log level. + self.setFormatter(formatter) + self.setLevel(level)
+ + +
[docs]def add_log_queue_handler(logger, level=None, formatter=None): + """Adds a queueing log handler to a logger.""" + if level is None: + level = LEVEL + if formatter is None: + formatter = FORMATTER + + # Add the handler to the root logger. + log_queue_handler = LogQueueHandler() + log_queue_handler.setLevel(level) + log_queue_handler.setFormatter(formatter) + logger.addHandler(log_queue_handler) + return log_queue_handler
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/logger/plugin/logger_plugin.html b/_modules/apptools/logger/plugin/logger_plugin.html new file mode 100644 index 000000000..467379f49 --- /dev/null +++ b/_modules/apptools/logger/plugin/logger_plugin.html @@ -0,0 +1,238 @@ + + + + + + + apptools.logger.plugin.logger_plugin — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.logger.plugin.logger_plugin

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" Logger plugin.
+"""
+
+# Standard library imports.
+import logging
+
+# Enthought library imports.
+from envisage.api import ExtensionPoint, Plugin
+from apptools.logger.log_queue_handler import LogQueueHandler
+from traits.api import Callable, List
+
+# Local imports.
+from .logger_preferences import LoggerPreferences
+from .logger_service import LoggerService
+
+
+ID = "apptools.logger"
+ILOGGER = ID + ".plugin.logger_service.LoggerService"
+
+
+
[docs]class LoggerPlugin(Plugin): + """Logger plugin.""" + + id = ID + name = "Logger plugin" + + #### Extension points for this plugin ##################################### + + MAIL_FILES = "apptools.logger.plugin.mail_files" + + mail_files = ExtensionPoint( + List(Callable), + id=MAIL_FILES, + desc=""" + + This extension point allows you to contribute functions which will be + called to add project files to the zip file that the user mails back + with bug reports from the Quality Agent. + + The function will be passed a zipfile.ZipFile object. + + """, + ) + + #### Contributions to extension points made by this plugin ################ + + PREFERENCES = "envisage.preferences" + PREFERENCES_PAGES = "envisage.ui.workbench.preferences_pages" + VIEWS = "envisage.ui.workbench.views" + + preferences = List(contributes_to=PREFERENCES) + preferences_pages = List(contributes_to=PREFERENCES_PAGES) + views = List(contributes_to=VIEWS) + + def _preferences_default(self): + return ["pkgfile://%s/plugin/preferences.ini" % ID] + + def _preferences_pages_default(self): + from apptools.logger.plugin.view.logger_preferences_page import ( + LoggerPreferencesPage, + ) + + return [LoggerPreferencesPage] + + def _views_default(self): + return [self._logger_view_factory] + + #### Plugin interface ##################################################### + +
[docs] def start(self): + """Starts the plugin.""" + preferences = LoggerPreferences() + service = LoggerService( + application=self.application, preferences=preferences + ) + formatter = logging.Formatter("%(levelname)s|%(asctime)s|%(message)s") + handler = LogQueueHandler() + handler.setLevel(preferences.level_) + handler.setFormatter(formatter) + root_logger = logging.getLogger() + root_logger.addHandler(handler) + root_logger.setLevel(preferences.level_) + service.handler = handler + self.application.register_service(ILOGGER, service)
+ +
[docs] def stop(self): + """Stops the plugin.""" + service = self.application.get_service(ILOGGER) + service.save_preferences()
+ + #### LoggerPlugin private interface ####################################### + + def _logger_view_factory(self, **traits): + from apptools.logger.plugin.view.logger_view import LoggerView + + service = self.application.get_service(ILOGGER) + view = LoggerView(service=service, **traits) + # Record the created view on the service. + service.plugin_view = view + return view
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/logger/plugin/logger_preferences.html b/_modules/apptools/logger/plugin/logger_preferences.html new file mode 100644 index 000000000..d3be2d62e --- /dev/null +++ b/_modules/apptools/logger/plugin/logger_preferences.html @@ -0,0 +1,169 @@ + + + + + + + apptools.logger.plugin.logger_preferences — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.logger.plugin.logger_preferences

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+import logging
+
+from apptools.preferences.api import PreferencesHelper
+from traits.api import Bool, Str, Trait
+
+
+
[docs]class LoggerPreferences(PreferencesHelper): + """The persistent service exposing the Logger plugin's API.""" + + #### Preferences ########################################################## + + # The log levels + level = Trait( + "Info", + { + "Debug": logging.DEBUG, + "Info": logging.INFO, + "Warning": logging.WARNING, + "Error": logging.ERROR, + "Critical": logging.CRITICAL, + }, + is_str=True, + ) + + enable_agent = Bool(False) + smtp_server = Str() + to_address = Str() + from_address = Str() + + # The path to the preferences node that contains the preferences. + preferences_path = Str("apptools.logger")
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/logger/plugin/logger_service.html b/_modules/apptools/logger/plugin/logger_service.html new file mode 100644 index 000000000..69028d2ce --- /dev/null +++ b/_modules/apptools/logger/plugin/logger_service.html @@ -0,0 +1,310 @@ + + + + + + + apptools.logger.plugin.logger_service — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.logger.plugin.logger_service

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+# Standard library imports
+from io import BytesIO
+import logging
+import os
+import zipfile
+
+# Enthought library imports
+from pyface.workbench.api import View as WorkbenchView
+from traits.api import (
+    Any,
+    Callable,
+    HasTraits,
+    Instance,
+    List,
+    Property,
+    Undefined,
+    on_trait_change,
+)
+
+root_logger = logging.getLogger()
+logger = logging.getLogger(__name__)
+
+
+
[docs]class LoggerService(HasTraits): + """The persistent service exposing the Logger plugin's API.""" + + # The Envisage application. + application = Any() + + # The logging Handler we use. + handler = Any() + + # Our associated LoggerPreferences. + preferences = Any() + + # The view we use. + plugin_view = Instance(WorkbenchView) + + # Contributions from other plugins. + mail_files = Property(List(Callable)) + +
[docs] def save_preferences(self): + """Save the preferences.""" + self.preferences.preferences.save()
+ +
[docs] def whole_log_text(self): + """Return all of the logged data as formatted text.""" + lines = [self.handler.format(rec) for rec in self.handler.get()] + # Ensure that we end with a newline. + lines.append("") + text = "\n".join(lines) + return text
+ +
[docs] def create_email_message( + self, + fromaddr, + toaddrs, + ccaddrs, + subject, + priority, + include_userdata=False, + stack_trace="", + comments="", + include_environment=True, + ): + """Format a bug report email from the log files.""" + from email.mime.application import MIMEApplication + from email.mime.multipart import MIMEMultipart + from email.mime.text import MIMEText + + message = MIMEMultipart() + message["Subject"] = "%s [priority=%s]" % (subject, priority) + message["To"] = ", ".join(toaddrs) + message["Cc"] = ", ".join(ccaddrs) + message["From"] = fromaddr + message.preamble = ( + "You will not see this in a MIME-aware mail " "reader.\n" + ) + message.epilogue = " " # To guarantee the message ends with a newline + + # First section is simple ASCII data ... + m = [] + m.append("Bug Report") + m.append("==============================") + m.append("") + + if len(comments) > 0: + m.append("Comments:") + m.append("========") + m.append(comments) + m.append("") + + if len(stack_trace) > 0: + m.append("Stack Trace:") + m.append("===========") + m.append(stack_trace) + m.append("") + + msg = MIMEText("\n".join(m)) + message.attach(msg) + + # Include the log file ... + logtext = self.whole_log_text() + msg = MIMEText(logtext) + msg.add_header( + "Content-Disposition", "attachment", filename="logfile.txt" + ) + message.attach(msg) + + # Include the environment variables ... + # FIXME: ask the user, maybe? + if include_environment: + # Transmit the user's environment settings as well. Main purpose + # is to work out the user name to help with following up on bug + # reports and in future we should probably send less data. + entries = [] + for key, value in sorted(os.environ.items()): + entries.append("%30s : %s\n" % (key, value)) + + msg = MIMEText("".join(entries)) + msg.add_header( + "Content-Disposition", "attachment", filename="environment.txt" + ) + message.attach(msg) + + if include_userdata and len(self.mail_files) != 0: + f = BytesIO() + zf = zipfile.ZipFile(f, "w") + for mf in self.mail_files: + mf(zf) + zf.close() + + msg = MIMEApplication(f.getvalue()) + msg.add_header( + "Content-Disposition", "attachment", filename="userdata.zip" + ) + message.attach(msg) + + return message
+ +
[docs] def send_bug_report( + self, smtp_server, fromaddr, toaddrs, ccaddrs, message + ): + """Send a bug report email.""" + try: + import smtplib + + logger.debug("Connecting to: %s" % smtp_server) + server = smtplib.SMTP(host=smtp_server) + logger.debug("Connected: %s" % server) + # server.set_debuglevel(1) + server.sendmail(fromaddr, toaddrs + ccaddrs, message.as_string()) + server.quit() + except Exception: + logger.exception("Problem sending error report")
+ + #### Traits stuff ######################################################### + + def _get_mail_files(self): + return self.application.get_extensions( + "apptools.logger.plugin.mail_files" + ) + + @on_trait_change("preferences.level_") + def _level_changed(self, new): + if ( + new is not None + and new is not Undefined + and self.handler is not None + ): + root_logger.setLevel(self.preferences.level_) + self.handler.setLevel(self.preferences.level_)
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/logger/plugin/view/logger_preferences_page.html b/_modules/apptools/logger/plugin/view/logger_preferences_page.html new file mode 100644 index 000000000..a0f07b780 --- /dev/null +++ b/_modules/apptools/logger/plugin/view/logger_preferences_page.html @@ -0,0 +1,226 @@ + + + + + + + apptools.logger.plugin.view.logger_preferences_page — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.logger.plugin.view.logger_preferences_page

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+
+import logging
+
+from apptools.preferences.ui.api import PreferencesPage
+from traits.api import Bool, Trait, Str
+from traitsui.api import EnumEditor, Group, Item, View
+
+
+
[docs]class LoggerPreferencesPage(PreferencesPage): + """A preference page for the logger plugin.""" + + #### 'PreferencesPage' interface ########################################## + + # The page's category (e.g. 'General/Appearance'). The empty string means + # that this is a top-level page. + category = "" + + # The page's help identifier (optional). If a help Id *is* provided then + # there will be a 'Help' button shown on the preference page. + help_id = "" + + # The page name (this is what is shown in the preferences dialog. + name = "Logger" + + # The path to the preferences node that contains the preferences. + preferences_path = "apptools.logger" + + #### Preferences ########################################################## + + # The log levels + level = Trait( + "Info", + { + "Debug": logging.DEBUG, + "Info": logging.INFO, + "Warning": logging.WARNING, + "Error": logging.ERROR, + "Critical": logging.CRITICAL, + }, + is_str=True, + ) + + enable_agent = Bool(False) + smtp_server = Str + to_address = Str + from_address = Str + + # The view used to change the plugin preferences + traits_view = View( + Group( + Group( + Item( + name="level", + editor=EnumEditor( + values={ + "Debug": "1:Debug", + "Info": "2:Info", + "Warning": "3:Warning", + "Error": "4:Error", + "Critical": "5:Critical", + }, + ), + style="simple", + ), + label="Logger Settings", + show_border=True, + ), + Group(Item(name="10")), + Group( + Group( + Group( + Item( + name="enable_agent", label="Enable quality agent" + ), + show_left=False, + ), + Group( + Item(name="smtp_server", label="SMTP server"), + Item(name="from_address"), + Item(name="to_address"), + enabled_when="enable_agent==True", + ), + ), + label="Quality Agent Settings", + show_border=True, + ), + ), + )
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/logger/plugin/view/logger_view.html b/_modules/apptools/logger/plugin/view/logger_view.html new file mode 100644 index 000000000..237263148 --- /dev/null +++ b/_modules/apptools/logger/plugin/view/logger_view.html @@ -0,0 +1,343 @@ + + + + + + + apptools.logger.plugin.view.logger_view — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.logger.plugin.view.logger_view

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+
+# Standard library imports
+from datetime import datetime
+import logging
+
+# Enthought library imports.
+from pyface.api import ImageResource, clipboard
+from pyface.workbench.api import TraitsUIView
+from traits.api import (
+    Button,
+    Instance,
+    List,
+    Property,
+    Str,
+    cached_property,
+    on_trait_change,
+)
+from traitsui.api import View, Group, Item, CodeEditor, TabularEditor, spring
+from traitsui.tabular_adapter import TabularAdapter
+
+# Local imports
+from apptools.logger.agent.quality_agent_view import QualityAgentView
+from apptools.logger.plugin.logger_service import LoggerService
+
+# Constants
+_IMAGE_MAP = {
+    logging.DEBUG: ImageResource("debug"),
+    logging.INFO: ImageResource("info"),
+    logging.WARNING: ImageResource("warning"),
+    logging.ERROR: ImageResource("error"),
+    logging.CRITICAL: ImageResource("crit_error"),
+}
+
+
+
[docs]class LogRecordAdapter(TabularAdapter): + """A TabularEditor adapter for logging.LogRecord objects.""" + + columns = [ + ("Level", "level"), + ("Date", "date"), + ("Time", "time"), + ("Message", "message"), + ] + column_widths = [80, 100, 120, -1] + + level_image = Property + level_text = Property(Str) + date_text = Property(Str) + time_text = Property(Str) + message_text = Property(Str) + +
[docs] def get_width(self, object, trait, column): + return self.column_widths[column]
+ + def _get_level_image(self): + return _IMAGE_MAP[self.item.levelno] + + def _get_level_text(self): + return self.item.levelname.capitalize() + + def _get_date_text(self): + dt = datetime.fromtimestamp(self.item.created) + return dt.date().isoformat() + + def _get_time_text(self): + dt = datetime.fromtimestamp(self.item.created) + return dt.time().isoformat() + + def _get_message_text(self): + # Just display the first line of multiline messages, like stacktraces. + msg = self.item.getMessage() + msgs = msg.strip().split("\n") + if len(msgs) > 1: + suffix = "... [double click for details]" + else: + suffix = "" + abbrev_msg = msgs[0] + suffix + return abbrev_msg
+ + +
[docs]class LoggerView(TraitsUIView): + """The Workbench View showing the list of log items.""" + + id = Str("apptools.logger.plugin.view.logger_view.LoggerView") + name = Str("Logger") + service = Instance(LoggerService) + + log_records = List(Instance(logging.LogRecord)) + formatted_records = Property(Str, depends_on="log_records") + + activated = Instance(logging.LogRecord) + activated_text = Property(Str, depends_on="activated") + reset_button = Button("Reset Logs") + show_button = Button("Complete Text Log") + copy_button = Button("Copy Log to Clipboard") + + code_editor = CodeEditor(lexer="null", show_line_numbers=False) + log_records_editor = TabularEditor( + adapter=LogRecordAdapter(), editable=False, activated="activated" + ) + trait_view = View( + Group( + Item("log_records", editor=log_records_editor), + Group( + Item("reset_button"), + spring, + Item("show_button"), + Item("copy_button"), + orientation="horizontal", + show_labels=False, + ), + show_labels=False, + ) + ) + + ########################################################################### + # LogQueueHandler view interface + ########################################################################### + +
[docs] def update(self, force=False): + """Update 'log_records' if our handler has new records or 'force' is + set. + """ + service = self.service + if service.handler.has_new_records() or force: + log_records = [ + rec + for rec in service.handler.get() + if rec.levelno >= service.preferences.level_ + ] + log_records.reverse() + self.log_records = log_records
+ + ########################################################################### + # Private interface + ########################################################################### + + @on_trait_change("service.preferences.level_") + def _update_log_records(self): + self.service.handler._view = self + self.update(force=True) + + def _reset_button_fired(self): + self.service.handler.reset() + self.log_records = [] + + def _show_button_fired(self): + self.edit_traits( + view=View( + Item( + "formatted_records", + editor=self.code_editor, + style="readonly", + show_label=False, + ), + width=800, + height=600, + resizable=True, + buttons=["OK"], + title="Complete Text Log", + ) + ) + + def _copy_button_fired(self): + clipboard.text_data = self.formatted_records + + @cached_property + def _get_formatted_records(self): + return "\n".join( + [ + self.service.handler.formatter.format(record) + for record in self.log_records + ] + ) + + def _activated_changed(self): + if self.activated is None: + return + msg = self.activated.getMessage() + if self.service.preferences.enable_agent: + dialog = QualityAgentView(msg=msg, service=self.service) + dialog.open() + else: + self.edit_traits( + view=View( + Item( + "activated_text", + editor=self.code_editor, + style="readonly", + show_label=False, + ), + width=800, + height=600, + resizable=True, + buttons=["OK"], + title="Log Message Detail", + ) + ) + + @cached_property + def _get_activated_text(self): + if self.activated is None: + return "" + else: + return self.activated.getMessage()
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/logger/ring_buffer.html b/_modules/apptools/logger/ring_buffer.html new file mode 100644 index 000000000..bbfb99f9f --- /dev/null +++ b/_modules/apptools/logger/ring_buffer.html @@ -0,0 +1,183 @@ + + + + + + + apptools.logger.ring_buffer — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.logger.ring_buffer

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+"""
+Copied from Python Cookbook.
+
+"""
+
+
+
[docs]class RingBuffer: + def __init__(self, size_max): + self.max = size_max + self.data = [] + +
[docs] def append(self, x): + """append an element at the end of the buffer""" + self.data.append(x) + if len(self.data) == self.max: + self.cur = 0 + self.__class__ = RingBufferFull
+ +
[docs] def get(self): + """ return a list of elements from the oldest to the newest""" + return self.data
+ + +
[docs]class RingBufferFull: + def __init__(self, n): + raise Exception("you should use RingBuffer") + +
[docs] def append(self, x): + self.data[self.cur] = x + self.cur = (self.cur + 1) % self.max
+ +
[docs] def get(self): + return self.data[self.cur:] + self.data[: self.cur]
+ + +# sample of use +"""x=RingBuffer(5) +x.append(1); x.append(2); x.append(3); x.append(4) +print(x.__class__,x.get()) +x.append(5) +print(x.__class__,x.get()) +x.append(6) +print(x.data,x.get()) +x.append(7); x.append(8); x.append(9); x.append(10) +print(x.data,x.get())""" +
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/naming/address.html b/_modules/apptools/naming/address.html new file mode 100644 index 000000000..c0bb64dc7 --- /dev/null +++ b/_modules/apptools/naming/address.html @@ -0,0 +1,158 @@ + + + + + + + apptools.naming.address — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.naming.address

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" The address of a commuications endpoint. """
+
+
+# Enthought library imports.
+from traits.api import Any, HasTraits, Str
+
+
+
[docs]class Address(HasTraits): + """The address of a communications end-point. + + It contains a type that describes the communication mechanism, and the + actual address content. + + """ + + # The type of the address. + type = Str + + # The actual content. + content = Any
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/naming/binding.html b/_modules/apptools/naming/binding.html new file mode 100644 index 000000000..396897ce3 --- /dev/null +++ b/_modules/apptools/naming/binding.html @@ -0,0 +1,233 @@ + + + + + + + apptools.naming.binding — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.naming.binding

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" The representation of a name-to-object binding in a context. """
+
+
+# Enthought libary imports.
+from traits.api import Any, HasTraits, Property, Str
+
+
+
[docs]class Binding(HasTraits): + """ The representation of a name-to-object binding in a context. """ + + #### 'Binding' interface ################################################## + + # The class name of the object bound to the name in the binding. + class_name = Property(Str) + + # The name. + name = Str + + # The object bound to the name in the binding. + obj = Any + + #### Experimental 'Binding' interface ##################################### + + # fixme: These is not part of the JNDI spec, but they do seem startlingly + # useful! + # + # fixme: And, errr, startlingly broken! If the context that the binding + # is in is required then just look up the name minus the last component! + # + # The context that the binding came from. + context = Any + + # The name of the bound object within the namespace. + namespace_name = Property(Str) + + #### 'Private' interface ################################################## + + # Shadow trait for the 'class_name' property. + _class_name = Str + + ########################################################################### + # 'object' interface. + ########################################################################### + + def __str__(self): + """ Returns an informal string representation of the object. """ + + return super(Binding, self).__str__() + "(name=%s, obj=%s)" % ( + self.name, + self.obj, + ) + + ########################################################################### + # 'Binding' interface. + ########################################################################### + + #### Properties ########################################################### + + # class_name + def _get_class_name(self): + """ Returns the class name of the object. """ + + if len(self._class_name) == 0: + if self.obj is None: + class_name = None + + else: + klass = self.obj.__class__ + + class_name = "%s.%s" % (klass.__module__, klass.__name__) + + return class_name + + def _set_class_name(self, class_name): + """ Sets the class name of the object. """ + + self._class_name = class_name + + # namespace_name + def _get_namespace_name(self): + """ Returns the name of the context within its own namespace. """ + + if self.context is not None: + base = self.context.namespace_name + + else: + base = "" + + if len(base) > 0: + namespace_name = base + "/" + self.name + + else: + namespace_name = self.name + + return namespace_name
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/naming/context.html b/_modules/apptools/naming/context.html new file mode 100644 index 000000000..d87dae45b --- /dev/null +++ b/_modules/apptools/naming/context.html @@ -0,0 +1,813 @@ + + + + + + + apptools.naming.context — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.naming.context

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" The base class for all naming contexts. """
+
+
+# Enthought library imports.
+from traits.api import Any, Dict, Event, HasTraits
+from traits.api import Property, Str
+
+# Local imports.
+from .binding import Binding
+from .exception import InvalidNameError, NameAlreadyBoundError
+from .exception import NameNotFoundError, NotContextError
+from .naming_event import NamingEvent
+from .naming_manager import naming_manager
+from .unique_name import make_unique_name
+
+
+# Constants for environment property keys.
+INITIAL_CONTEXT_FACTORY = "apptools.naming.factory.initial"
+OBJECT_FACTORIES = "apptools.naming.factory.object"
+STATE_FACTORIES = "apptools.naming.factory.state"
+
+
+# The default environment.
+ENVIRONMENT = {
+    # 'Context' properties.
+    OBJECT_FACTORIES: [],
+    STATE_FACTORIES: [],
+}
+
+
+
[docs]class Context(HasTraits): + """ The base class for all naming contexts. """ + + # Keys for environment properties. + INITIAL_CONTEXT_FACTORY = INITIAL_CONTEXT_FACTORY + OBJECT_FACTORIES = OBJECT_FACTORIES + STATE_FACTORIES = STATE_FACTORIES + + #### 'Context' interface ################################################## + + # The naming environment in effect for this context. + environment = Dict(ENVIRONMENT) + + # The name of the context within its own namespace. + namespace_name = Property(Str) + + #### Events #### + + # Fired when an object has been added to the context (either via 'bind' or + # 'create_subcontext'). + object_added = Event(NamingEvent) + + # Fired when an object has been changed (via 'rebind'). + object_changed = Event(NamingEvent) + + # Fired when an object has been removed from the context (either via + # 'unbind' or 'destroy_subcontext'). + object_removed = Event(NamingEvent) + + # Fired when an object in the context has been renamed (via 'rename'). + object_renamed = Event(NamingEvent) + + # Fired when the contents of the context have changed dramatically. + context_changed = Event(NamingEvent) + + #### Protected 'Context' interface ####################################### + + # The bindings in the context. + _bindings = Dict(Str, Any) + + ########################################################################### + # 'Context' interface. + ########################################################################### + + #### Properties ########################################################### + + def _get_namespace_name(self): + """ + Return the name of the context within its own namespace. + + That is the full-path, through the namespace this context participates + in, to get to this context. For example, if the root context of the + namespace was called 'Foo', and there was a subcontext of that called + 'Bar', and we were within that and called 'Baz', then this should + return 'Foo/Bar/Baz'. + + """ + + # FIXME: We'd like to raise an exception and force implementors to + # decide what to do. However, it appears to be pretty common that + # most Context implementations do not override this method -- possibly + # because the comments aren't clear on what this is supposed to be? + # + # Anyway, if we raise an exception then it is impossible to use any + # evaluations when building a Traits UI for a Context. That is, the + # Traits UI can't include items that have a 'visible_when' or + # 'enabled_when' evaluation. This is because the Traits evaluation + # code calls the 'get()' method on the Context which attempts to + # retrieve the current namespace_name value. + # raise OperationNotSupportedError() + return "" + + #### Methods ############################################################## + +
[docs] def bind(self, name, obj, make_contexts=False): + """Binds a name to an object. + + If 'make_contexts' is True then any missing intermediate contexts are + created automatically. + + """ + + if len(name) == 0: + raise InvalidNameError("empty name") + + # Parse the name. + components = self._parse_name(name) + + # If there is exactly one component in the name then the operation + # takes place in this context. + if len(components) == 1: + atom = components[0] + + # Is the name already bound? + if self._is_bound(atom): + raise NameAlreadyBoundError(name) + + # Do the actual bind. + self._bind(atom, obj) + + # Trait event notification. + self.object_added = NamingEvent( + new_binding=Binding(name=name, obj=obj, context=self) + ) + + # Otherwise, attempt to continue resolution into the next context. + else: + if not self._is_bound(components[0]): + if make_contexts: + self._create_subcontext(components[0]) + + else: + raise NameNotFoundError(components[0]) + + next_context = self._get_next_context(components[0]) + next_context.bind("/".join(components[1:]), obj, make_contexts)
+ +
[docs] def rebind(self, name, obj, make_contexts=False): + """Binds an object to a name that may already be bound. + + If 'make_contexts' is True then any missing intermediate contexts are + created automatically. + + The object may be a different object but may also be the same object + that is already bound to the specified name. The name may or may not be + already used. Think of this as a safer version of 'bind' since this + one will never raise an exception regarding a name being used. + + """ + + if len(name) == 0: + raise InvalidNameError("empty name") + + # Parse the name. + components = self._parse_name(name) + + # If there is exactly one component in the name then the operation + # takes place in this context. + if len(components) == 1: + # Do the actual rebind. + self._rebind(components[0], obj) + + # Trait event notification. + self.object_changed = NamingEvent( + new_binding=Binding(name=name, obj=obj, context=self) + ) + + # Otherwise, attempt to continue resolution into the next context. + else: + if not self._is_bound(components[0]): + if make_contexts: + self._create_subcontext(components[0]) + + else: + raise NameNotFoundError(components[0]) + + next_context = self._get_next_context(components[0]) + next_context.rebind("/".join(components[1:]), obj, make_contexts)
+ +
[docs] def unbind(self, name): + """ Unbinds a name. """ + + if len(name) == 0: + raise InvalidNameError("empty name") + + # Parse the name. + components = self._parse_name(name) + + # If there is exactly one component in the name then the operation + # takes place in this context. + if len(components) == 1: + atom = components[0] + + if not self._is_bound(atom): + raise NameNotFoundError(name) + + # Lookup the object that we are unbinding to use in the event + # notification. + obj = self._lookup(atom) + + # Do the actual unbind. + self._unbind(atom) + + # Trait event notification. + self.object_removed = NamingEvent( + old_binding=Binding(name=name, obj=obj, context=self) + ) + + # Otherwise, attempt to continue resolution into the next context. + else: + if not self._is_bound(components[0]): + raise NameNotFoundError(components[0]) + + next_context = self._get_next_context(components[0]) + next_context.unbind("/".join(components[1:]))
+ +
[docs] def rename(self, old_name, new_name): + """ Binds a new name to an object. """ + + if len(old_name) == 0 or len(new_name) == 0: + raise InvalidNameError("empty name") + + # Parse the names. + old_components = self._parse_name(old_name) + new_components = self._parse_name(new_name) + + # If there is axactly one component in BOTH names then the operation + # takes place ENTIRELY in this context. + if len(old_components) == 1 and len(new_components) == 1: + # Is the old name actually bound? + if not self._is_bound(old_name): + raise NameNotFoundError(old_name) + + # Is the new name already bound? + if self._is_bound(new_name): + raise NameAlreadyBoundError(new_name) + + # Do the actual rename. + self._rename(old_name, new_name) + + # Lookup the object that we are renaming to use in the event + # notification. + obj = self._lookup(new_name) + + # Trait event notification. + self.object_renamed = NamingEvent( + old_binding=Binding(name=old_name, obj=obj, context=self), + new_binding=Binding(name=new_name, obj=obj, context=self), + ) + + else: + # fixme: This really needs to be transactional in case the bind + # succeeds but the unbind fails. To be safe should we just not + # support cross-context renaming for now?!?! + # + # Lookup the object. + obj = self.lookup(old_name) + + # Bind the new name. + self.bind(new_name, obj) + + # Unbind the old one. + self.unbind(old_name)
+ +
[docs] def lookup(self, name): + """ Resolves a name relative to this context. """ + + # If the name is empty we return the context itself. + if len(name) == 0: + # fixme: The JNDI spec. says that this should return a COPY of + # the context. + return self + + # Parse the name. + components = self._parse_name(name) + + # If there is exactly one component in the name then the operation + # takes place in this context. + if len(components) == 1: + atom = components[0] + + if not self._is_bound(atom): + raise NameNotFoundError(name) + + # Do the actual lookup. + obj = self._lookup(atom) + + # Otherwise, attempt to continue resolution into the next context. + else: + if not self._is_bound(components[0]): + raise NameNotFoundError(components[0]) + + next_context = self._get_next_context(components[0]) + obj = next_context.lookup("/".join(components[1:])) + + return obj
+ + # fixme: Non-JNDI +
[docs] def lookup_binding(self, name): + """ Looks up the binding for a name relative to this context. """ + + if len(name) == 0: + raise InvalidNameError("empty name") + + # Parse the name. + components = self._parse_name(name) + + # If there is exactly one component in the name then the operation + # takes place in this context. + if len(components) == 1: + atom = components[0] + + if not self._is_bound(atom): + raise NameNotFoundError(name) + + # Do the actual lookup. + binding = self._lookup_binding(atom) + + # Otherwise, attempt to continue resolution into the next context. + else: + if not self._is_bound(components[0]): + raise NameNotFoundError(components[0]) + + next_context = self._get_next_context(components[0]) + binding = next_context.lookup_binding("/".join(components[1:])) + + return binding
+ + # fixme: Non-JNDI +
[docs] def lookup_context(self, name): + """Resolves a name relative to this context. + + The name MUST resolve to a context. + + """ + + # If the name is empty we return the context itself. + if len(name) == 0: + # fixme: The JNDI spec. says that this should return a COPY of + # the context. + return self + + # Parse the name. + components = self._parse_name(name) + + # If there is exactly one component in the name then the operation + # takes place in this context. + if len(components) == 1: + atom = components[0] + + if not self._is_bound(atom): + raise NameNotFoundError(name) + + # Do the actual lookup. + obj = self._get_next_context(atom) + + # Otherwise, attempt to continue resolution into the next context. + else: + if not self._is_bound(components[0]): + raise NameNotFoundError(components[0]) + + next_context = self._get_next_context(components[0]) + obj = next_context.lookup("/".join(components[1:])) + + return obj
+ +
[docs] def create_subcontext(self, name): + """ Creates a sub-context. """ + + if len(name) == 0: + raise InvalidNameError("empty name") + + # Parse the name. + components = self._parse_name(name) + + # If there is exactly one component in the name then the operation + # takes place in this context. + if len(components) == 1: + atom = components[0] + + # Is the name already bound? + if self._is_bound(atom): + raise NameAlreadyBoundError(name) + + # Do the actual creation of the sub-context. + sub = self._create_subcontext(atom) + + # Trait event notification. + self.object_added = NamingEvent( + new_binding=Binding(name=name, obj=sub, context=self) + ) + + # Otherwise, attempt to continue resolution into the next context. + else: + if not self._is_bound(components[0]): + raise NameNotFoundError(components[0]) + + next_context = self._get_next_context(components[0]) + sub = next_context.create_subcontext("/".join(components[1:])) + + return sub
+ +
[docs] def destroy_subcontext(self, name): + """ Destroys a sub-context. """ + + if len(name) == 0: + raise InvalidNameError("empty name") + + # Parse the name. + components = self._parse_name(name) + + # If there is exactly one component in the name then the operation + # takes place in this context. + if len(components) == 1: + atom = components[0] + + if not self._is_bound(atom): + raise NameNotFoundError(name) + + obj = self._lookup(atom) + if not self._is_context(atom): + raise NotContextError(name) + + # Do the actual destruction of the sub-context. + self._destroy_subcontext(atom) + + # Trait event notification. + self.object_removed = NamingEvent( + old_binding=Binding(name=name, obj=obj, context=self) + ) + + # Otherwise, attempt to continue resolution into the next context. + else: + if not self._is_bound(components[0]): + raise NameNotFoundError(components[0]) + + next_context = self._get_next_context(components[0]) + next_context.destroy_subcontext("/".join(components[1:]))
+ + # fixme: Non-JNDI +
[docs] def get_unique_name(self, prefix): + """Returns a name that is unique within the context. + + The name returned will start with the specified prefix. + + """ + + return make_unique_name( + prefix, existing=self.list_names(""), format="%s (%d)" + )
+ +
[docs] def list_names(self, name=""): + """ Lists the names bound in a context. """ + + # If the name is empty then the operation takes place in this context. + if len(name) == 0: + names = self._list_names() + + # Otherwise, attempt to continue resolution into the next context. + else: + # Parse the name. + components = self._parse_name(name) + + if not self._is_bound(components[0]): + raise NameNotFoundError(components[0]) + + next_context = self._get_next_context(components[0]) + names = next_context.list_names("/".join(components[1:])) + + return names
+ +
[docs] def list_bindings(self, name=""): + """ Lists the bindings in a context. """ + + # If the name is empty then the operation takes place in this context. + if len(name) == 0: + bindings = self._list_bindings() + + # Otherwise, attempt to continue resolution into the next context. + else: + # Parse the name. + components = self._parse_name(name) + + if not self._is_bound(components[0]): + raise NameNotFoundError(components[0]) + + next_context = self._get_next_context(components[0]) + bindings = next_context.list_bindings("/".join(components[1:])) + + return bindings
+ + # fixme: Non-JNDI +
[docs] def is_context(self, name): + """ Returns True if the name is bound to a context. """ + + # If the name is empty then it refers to this context. + if len(name) == 0: + is_context = True + + else: + # Parse the name. + components = self._parse_name(name) + + # If there is exactly one component in the name then the operation + # takes place in this context. + if len(components) == 1: + atom = components[0] + + if not self._is_bound(atom): + raise NameNotFoundError(name) + + # Do the actual check. + is_context = self._is_context(atom) + + # Otherwise, attempt to continue resolution into the next context. + else: + if not self._is_bound(components[0]): + raise NameNotFoundError(components[0]) + + next_context = self._get_next_context(components[0]) + is_context = next_context.is_context("/".join(components[1:])) + + return is_context
+ + # fixme: Non-JNDI +
[docs] def search(self, obj): + """ Returns a list of namespace names that are bound to obj. """ + + # don't look for None + if obj is None: + return [] + + # Obj is bound to these names relative to this context + names = [] + + # path contain the name components down to the current context + path = [] + + self._search(obj, names, path, {}) + + return names
+ + ########################################################################### + # Protected 'Context' interface. + ########################################################################### + + def _parse_name(self, name): + """Parse a name into a list of components. + + e.g. 'foo/bar/baz' -> ['foo', 'bar', 'baz'] + + """ + + return name.split("/") + + def _is_bound(self, name): + """ Is a name bound in this context? """ + + return name in self._bindings + + def _lookup(self, name): + """ Looks up a name in this context. """ + + obj = self._bindings[name] + + return naming_manager.get_object_instance(obj, name, self) + + def _lookup_binding(self, name): + """ Looks up the binding for a name in this context. """ + + return Binding(name=name, obj=self._lookup(name), context=self) + + def _bind(self, name, obj): + """ Binds a name to an object in this context. """ + + state = naming_manager.get_state_to_bind(obj, name, self) + self._bindings[name] = state + + def _rebind(self, name, obj): + """ Rebinds a name to an object in this context. """ + + self._bind(name, obj) + + def _unbind(self, name): + """ Unbinds a name from this context. """ + + del self._bindings[name] + + def _rename(self, old_name, new_name): + """ Renames an object in this context. """ + + # Bind the new name. + self._bindings[new_name] = self._bindings[old_name] + + # Unbind the old one. + del self._bindings[old_name] + + def _create_subcontext(self, name): + """ Creates a sub-context of this context. """ + + sub = self.__class__(environment=self.environment) + self._bindings[name] = sub + + return sub + + def _destroy_subcontext(self, name): + """ Destroys a sub-context of this context. """ + + del self._bindings[name] + + def _list_bindings(self): + """ Lists the bindings in this context. """ + + bindings = [] + for name in self._list_names(): + bindings.append( + Binding(name=name, obj=self._lookup(name), context=self) + ) + + return bindings + + def _list_names(self): + """ Lists the names bound in this context. """ + + return list(self._bindings.keys()) + + def _is_context(self, name): + """ Returns True if a name is bound to a context. """ + + return self._get_next_context(name) is not None + + def _get_next_context(self, name): + """ Returns the next context. """ + + obj = self._lookup(name) + + # If the object is a context then everything is just dandy. + if isinstance(obj, Context): + next_context = obj + else: + raise NotContextError(name) + + return next_context + + def _search(self, obj, names, path, searched): + """Append to names any name bound to obj. + Join path and name with '/' to for a complete name from the + top context. + """ + + # Check the bindings recursively. + for binding in self.list_bindings(): + if binding.obj is obj: + path.append(binding.name) + names.append("/".join(path)) + path.pop() + + if ( + isinstance(binding.obj, Context) + and binding.obj not in searched + ): + path.append(binding.name) + searched[binding.obj] = True + binding.obj._search(obj, names, path, searched) + path.pop()
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/naming/dir_context.html b/_modules/apptools/naming/dir_context.html new file mode 100644 index 000000000..edc6864a6 --- /dev/null +++ b/_modules/apptools/naming/dir_context.html @@ -0,0 +1,293 @@ + + + + + + + apptools.naming.dir_context — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.naming.dir_context

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" The base class for all directory contexts. """
+
+
+# Enthought library imports.
+from traits.api import Dict
+
+# Local imports.
+from .context import Context
+from .exception import NameNotFoundError
+
+
+
[docs]class DirContext(Context): + """ The base class for all directory contexts. """ + + # The attributes of every object in the context. The attributes for the + # context itself have the empty string as the key. + # + # {str name : dict attributes} + _attributes = Dict + + ########################################################################### + # 'DirContext' interface. + ########################################################################### + +
[docs] def get_attributes(self, name): + """ Returns the attributes associated with a named object. """ + + # If the name is empty then we return the attributes of this context. + if len(name) == 0: + attributes = self._get_attributes(name) + + else: + # Parse the name. + components = self._parse_name(name) + + # If there is exactly one component in the name then the operation + # takes place in this context. + if len(components) == 1: + atom = components[0] + + if not self._is_bound(atom): + raise NameNotFoundError(name) + + # Do the actual get. + attributes = self._get_attributes(atom) + + # Otherwise, attempt to continue resolution into the next context. + else: + if not self._is_bound(components[0]): + raise NameNotFoundError(components[0]) + + next_context = self._get_next_context(components[0]) + attributes = next_context.get_attributes( + "/".join(components[1:]) + ) + + return attributes
+ +
[docs] def set_attributes(self, name, attributes): + """ Sets the attributes associated with a named object. """ + + # If the name is empty then we set the attributes of this context. + if len(name) == 0: + attributes = self._set_attributes(name, attributes) + + else: + # Parse the name. + components = self._parse_name(name) + + # If there is exactly one component in the name then the operation + # takes place in this context. + if len(components) == 1: + atom = components[0] + + if not self._is_bound(atom): + raise NameNotFoundError(name) + + # Do the actual set. + self._set_attributes(atom, attributes) + + # Otherwise, attempt to continue resolution into the next context. + else: + if not self._is_bound(components[0]): + raise NameNotFoundError(components[0]) + + next_context = self._get_next_context(components[0]) + next_context.set_attributes( + "/".join(components[1:]), attributes + )
+ + # fixme: Non-JNDI +
[docs] def find_bindings(self, visitor): + """Find bindings with attributes matching criteria in visitor. + + Visitor is a function that is passed the bindings for each level of the + heirarchy and the attribute dictionary for those bindings. The visitor + examines the bindings and dictionary and returns the bindings it is + interested in. + + """ + + bindings = visitor(self.list_bindings(), self._attributes) + + # recursively check other sub contexts. + for binding in self.list_bindings(): + obj = binding.obj + if isinstance(obj, DirContext): + bindings.extend(obj.find_bindings(visitor)) + + return bindings
+ + ########################################################################### + # Protected 'DirContext' interface. + ########################################################################### + + def _get_attributes(self, name): + """ Returns the attributes of an object in this context. """ + + attributes = self._attributes.setdefault(name, {}) + + return attributes.copy() + + def _set_attributes(self, name, attributes): + """ Sets the attributes of an object in this context. """ + + self._attributes[name] = attributes + + ########################################################################### + # Protected 'Context' interface. + ########################################################################### + + def _unbind(self, name): + """ Unbinds a name from this context. """ + + super(DirContext, self)._unbind(name) + + if name in self._attributes: + del self._attributes[name] + + def _rename(self, old_name, new_name): + """ Renames an object in this context. """ + + super(DirContext, self)._rename(old_name, new_name) + + if old_name in self._attributes: + self._attributes[new_name] = self._attributes[old_name] + del self._attributes[old_name] + + def _destroy_subcontext(self, name): + """ Destroys a sub-context of this context. """ + + super(DirContext, self)._destroy_subcontext(name) + + if name in self._attributes: + del self._attributes[name]
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/naming/dynamic_context.html b/_modules/apptools/naming/dynamic_context.html new file mode 100644 index 000000000..5b69be61c --- /dev/null +++ b/_modules/apptools/naming/dynamic_context.html @@ -0,0 +1,308 @@ + + + + + + + apptools.naming.dynamic_context — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.naming.dynamic_context

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+
+""" Provider of a framework that dynamically determines the contents of a
+    context at the time of interaction with the contents rather than at the
+    time a class is written.
+
+    This capability is particularly useful when the object acting as a context
+    is part of a plug-in application -- such as Envisage.  In general, this
+    capability allows the context to be:
+
+    - Extendable by contributions from somewhere other than the original
+      code writer
+    - Dynamic in that the elements it is composed of can change each time
+      someone interacts with the contents of the context.
+
+    It should be noted that this capability is explicitly different from
+    contexts that look at another container to determine their contents, such
+    as a file system context!
+
+    Users of this framework contribute items to a dynamic context by adding
+    traits to the dynamic context instance.  (This addition can happen
+    statically through the use of a Traits Category.)  The trait value is the
+    context item's value and the trait definition's metadata determines how the
+    item is treated within the context.  The support metadata is:
+
+    context_name: A non-empty string
+        Represents the name of the item within this context.  This must be
+        present for the trait to show up as a context item though the value
+        may change over time as the item gets bound to different names.
+    context_order: A float value
+        Indicates the position for the item within this context.  All
+        dynamically contributed context items are sorted by ascending order
+        of this value using the standard list sort function.
+    is_context: A boolean value
+        True if the item is itself a context.
+"""
+
+# Standardlibrary imports
+import logging
+
+# Local imports
+from .binding import Binding
+from .context import Context
+from .exception import OperationNotSupportedError
+
+
+# Setup a logger for this module.
+logger = logging.getLogger(__name__)
+
+
+
[docs]class DynamicContext(Context): + """A framework that dynamically determines the contents of a context at + the time of interaction with the contents rather than at the time a + context class is written. + + It should be noted that this capability is explicitly different from + contexts that look at another container to determine their contents, + such as a file system context! + """ + + ########################################################################## + # 'Context' interface. + ########################################################################## + + ### protected interface ################################################## + + def _is_bound(self, name): + """Is a name bound in this context?""" + + item = self._get_contributed_context_item(name) + result = item != (None, None) + + return result + + def _is_context(self, name): + """Returns True if a name is bound to a context.""" + item = self._get_contributed_context_item(name) + if item != (None, None): + obj, trait = item + result = trait.is_context is True + else: + result = False + + return result + + def _list_bindings(self): + """Lists the bindings in this context.""" + result = [ + Binding(name=n, obj=o, context=self) + for n, o, t in self._get_contributed_context_items() + ] + + return result + + def _list_names(self): + """Lists the names bound in this context.""" + result = [n for n, o, t in self._get_contributed_context_items()] + + return result + + def _lookup(self, name): + """Looks up a name in this context.""" + item = self._get_contributed_context_item(name) + if item != (None, None): + obj, trait = item + result = obj + else: + result = None + + return result + + def _rename(self, old_name, new_name): + """Renames an object in this context.""" + + item = self._get_contributed_context_item(old_name) + if item != (None, None): + obj, trait = item + trait.context_name = new_name + else: + raise ValueError('Name "%s" not in context', old_name) + + def _unbind(self, name): + """Unbinds a name from this context.""" + # It is an error to try to unbind any contributed context items + item = self._get_contributed_context_item(name) + if item != (None, None): + raise OperationNotSupportedError( + "Unable to unbind " + "built-in with name [%s]" % name + ) + + ########################################################################## + # 'DynamicContext' interface. + ########################################################################## + + ### protected interface ################################################## + + def _get_contributed_context_item(self, name): + """If the specified name matches a contributed context item then + returns a tuple of the item's current value and trait definition + (in that order.) Otherwise, returns a tuple of (None, None). + """ + result = (None, None) + + for n, o, t in self._get_contributed_context_items(): + if n == name: + result = (o, t) + + return result + + def _get_contributed_context_items(self): + """Returns an ordered list of items to be treated as part of our + context. + + Each item in the list is a tuple of its name, object, and trait + definition (in that order.) + """ + # Our traits that get treated as context items are those that declare + # themselves via metadata on the trait definition. + filter = {"context_name": lambda v: v is not None and len(v) > 0} + traits = self.traits(**filter) + + # Sort the list of context items according to the name of the item. + traits = [(t.context_order, n, t) for n, t in traits.items()] + traits.sort() + + # Convert these trait definitions into a list of name and object tuples + result = [ + (t.context_name, getattr(self, n), t) for order, n, t in traits + ] + + return result
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/naming/exception.html b/_modules/apptools/naming/exception.html new file mode 100644 index 000000000..8c73dc462 --- /dev/null +++ b/_modules/apptools/naming/exception.html @@ -0,0 +1,184 @@ + + + + + + + apptools.naming.exception — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.naming.exception

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" Naming exceptions. """
+
+
+
[docs]class NamingError(Exception): + """Base class for all naming exceptions."""
+ + +
[docs]class InvalidNameError(NamingError): + """Invalid name. + + This exception is thrown when the name passed to a naming operation does + not conform to the syntax of the naming system (or is empty etc). + + """
+ + +
[docs]class NameAlreadyBoundError(NamingError): + """Name already bound. + + This exception is thrown when an attempt is made to bind a name that is + already bound in the current context. + + """
+ + +
[docs]class NameNotFoundError(NamingError): + """Name not found. + + This exception is thrown when a component of a name cannot be resolved + because it is not bound in the current context. + + """
+ + +
[docs]class NotContextError(NamingError): + """Not a context. + + This exception is thrown when a naming operation has reached a point where + a context is required to continue the operation, but the resolved object + is not a context. + + """
+ + +
[docs]class OperationNotSupportedError(NamingError): + """The context does support the requested operation."""
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/naming/initial_context.html b/_modules/apptools/naming/initial_context.html new file mode 100644 index 000000000..d26ad66b5 --- /dev/null +++ b/_modules/apptools/naming/initial_context.html @@ -0,0 +1,185 @@ + + + + + + + apptools.naming.initial_context — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.naming.initial_context

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" The starting point for performing naming operations. """
+
+
+# Local imports.
+from .context import Context
+
+
+
[docs]def InitialContext(environment): + """ Creates an initial context for beginning name resolution. """ + + # Get the class name of the factory that will produce the initial context. + klass_name = environment.get(Context.INITIAL_CONTEXT_FACTORY) + if klass_name is None: + raise ValueError("No initial context factory specified") + + # Import the factory class. + klass = _import_symbol(klass_name) + + # Create the factory. + factory = klass() + + # Ask the factory for a context implementation instance. + return factory.get_initial_context(environment)
+ + +# fixme: This is the same code as in the Envisage import manager but we don't +# want naming to be dependent on Envisage, so we need some other package +# for useful 'Python' tools etc. +def _import_symbol(symbol_path): + """Imports the symbol defined by 'symbol_path'. + + 'symbol_path' is a string in the form 'foo.bar.baz' which is turned + into an import statement 'from foo.bar import baz' (ie. the last + component of the name is the symbol name, the rest is the package/ + module path to load it from). + + """ + + components = symbol_path.split(".") + + module_name = ".".join(components[:-1]) + symbol_name = components[-1] + + module = __import__(module_name, globals(), locals(), [symbol_name]) + symbol = getattr(module, symbol_name) + + return symbol +
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/naming/initial_context_factory.html b/_modules/apptools/naming/initial_context_factory.html new file mode 100644 index 000000000..8c80347bc --- /dev/null +++ b/_modules/apptools/naming/initial_context_factory.html @@ -0,0 +1,159 @@ + + + + + + + apptools.naming.initial_context_factory — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.naming.initial_context_factory

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" The base class for all initial context factories. """
+
+
+# Enthought library imports.
+from traits.api import HasTraits
+
+# Local imports.
+from .context import Context
+
+
+
[docs]class InitialContextFactory(HasTraits): + """ The base class for all initial context factories. """ + + ########################################################################### + # 'InitialContextFactory' interface. + ########################################################################### + +
[docs] def get_initial_context(self, environment): + """ Creates an initial context for beginning name resolution. """ + + return Context(environment=environment)
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/naming/naming_event.html b/_modules/apptools/naming/naming_event.html new file mode 100644 index 000000000..e119c59e8 --- /dev/null +++ b/_modules/apptools/naming/naming_event.html @@ -0,0 +1,157 @@ + + + + + + + apptools.naming.naming_event — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.naming.naming_event

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" The event fired by the tree model when it changes. """
+
+
+# Enthought library imports.
+from traits.api import HasTraits, Instance
+
+# Local imports.
+from .binding import Binding
+
+
+# Classes for event traits.
+
[docs]class NamingEvent(HasTraits): + """ Information about tree model changes. """ + + # The old binding. + old_binding = Instance(Binding) + + # The new binding. + new_binding = Instance(Binding)
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/naming/naming_manager.html b/_modules/apptools/naming/naming_manager.html new file mode 100644 index 000000000..124e9b884 --- /dev/null +++ b/_modules/apptools/naming/naming_manager.html @@ -0,0 +1,213 @@ + + + + + + + apptools.naming.naming_manager — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.naming.naming_manager

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" The naming manager. """
+
+
+# Enthought library imports.
+from traits.api import HasTraits
+
+
+
[docs]class NamingManager(HasTraits): + """ The naming manager. """ + + ########################################################################### + # 'NamingManager' interface. + ########################################################################### + +
[docs] def get_state_to_bind(self, obj, name, context): + """Returns the state of an object for binding. + + The naming manager asks the context for its list of STATE factories + and then calls them one by one until it gets a non-None result + indicating that the factory recognised the object and created state + information for it. + + If none of the factories recognize the object (or if the context + has no factories) then the object itself is returned. + + """ + + # Local imports. + from .context import Context + + # We get the state factories from the context's environment. + state_factories = context.environment[Context.STATE_FACTORIES] + + for state_factory in state_factories: + state = state_factory.get_state_to_bind(obj, name, context) + if state is not None: + break + + else: + state = obj + + return state
+ +
[docs] def get_object_instance(self, info, name, context): + """Creates an object using the specified state information. + + The naming manager asks the context for its list of OBJECT factories + and calls them one by one until it gets a non-None result, indicating + that the factory recognised the information and created an object. + + If none of the factories recognize the state information (or if the + context has no factories) then the state information itself is + returned. + + """ + + # Local imports. + from .context import Context + + # We get the object factories from the context's environment. + object_factories = context.environment[Context.OBJECT_FACTORIES] + + for object_factory in object_factories: + obj = object_factory.get_object_instance(info, name, context) + if obj is not None: + break + + else: + obj = info + + return obj
+ + +# Singleton instance. +naming_manager = NamingManager() +
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/naming/object_factory.html b/_modules/apptools/naming/object_factory.html new file mode 100644 index 000000000..8277c2eec --- /dev/null +++ b/_modules/apptools/naming/object_factory.html @@ -0,0 +1,166 @@ + + + + + + + apptools.naming.object_factory — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.naming.object_factory

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" The base class for all object factories. """
+
+
+# Enthought library imports.
+from traits.api import HasTraits
+
+
+
[docs]class ObjectFactory(HasTraits): + """The base class for all object factories. + + An object factory accepts some information about how to create an object + (such as a reference) and returns an instance of that object. + + """ + + ########################################################################### + # 'ObjectFactory' interface. + ########################################################################### + +
[docs] def get_object_instance(self, state, name, context): + """Creates an object using the specified state information. + + Returns None if the factory cannot create the object (ie. it does not + recognise the state passed to it). + + """ + + raise NotImplementedError
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/naming/object_serializer.html b/_modules/apptools/naming/object_serializer.html new file mode 100644 index 000000000..8df9f1aab --- /dev/null +++ b/_modules/apptools/naming/object_serializer.html @@ -0,0 +1,222 @@ + + + + + + + apptools.naming.object_serializer — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.naming.object_serializer

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" The base class for all object serializers. """
+
+
+# Standard library imports.
+import logging
+from traceback import print_exc
+from os.path import splitext
+import pickle
+
+# Enthought library imports.
+from apptools.persistence.versioned_unpickler import VersionedUnpickler
+from traits.api import HasTraits, Str
+
+
+# Setup a logger for this module.
+logger = logging.getLogger(__name__)
+
+
+
[docs]class ObjectSerializer(HasTraits): + """ The base class for all object serializers. """ + + #### 'ObjectSerializer' interface ######################################### + + # The file extension recognized by this serializer. + ext = Str(".pickle") + + ########################################################################### + # 'ObjectSerializer' interface. + ########################################################################### + +
[docs] def can_load(self, path): + """ Returns True if the serializer can load a file. """ + + rest, ext = splitext(path) + + return ext == self.ext
+ +
[docs] def load(self, path): + """ Loads an object from a file. """ + + # Unpickle the object. + f = open(path, "rb") + try: + try: + obj = VersionedUnpickler(f).load() + except Exception as ex: + print_exc() + logger.exception( + "Failed to load pickle file: %s, %s" % (path, ex) + ) + + raise + finally: + f.close() + + return obj
+ +
[docs] def can_save(self, obj): + """ Returns True if the serializer can save an object. """ + + return True
+ +
[docs] def save(self, path, obj): + """ Saves an object to a file. """ + + if not path.endswith(self.ext): + actual_path = path + self.ext + + else: + actual_path = path + + # Pickle the object. + f = open(actual_path, "wb") + try: + pickle.dump(obj, f, 1) + except Exception as ex: + logger.exception( + "Failed to pickle into file: %s, %s, object:%s" + % (path, ex, obj) + ) + print_exc() + f.close() + + return actual_path
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/naming/py_context.html b/_modules/apptools/naming/py_context.html new file mode 100644 index 000000000..b3bb934f3 --- /dev/null +++ b/_modules/apptools/naming/py_context.html @@ -0,0 +1,311 @@ + + + + + + + apptools.naming.py_context — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.naming.py_context

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" A naming context for a Python namespace. """
+
+
+# Enthought library imports.
+from traits.api import Any, Dict, Instance, Property
+
+# Local imports.
+from .address import Address
+from .binding import Binding
+from .context import Context
+from .naming_manager import naming_manager
+from .py_object_factory import PyObjectFactory
+from .reference import Reference
+from .referenceable import Referenceable
+from .referenceable_state_factory import ReferenceableStateFactory
+
+
+# The default environment.
+ENVIRONMENT = {
+    # 'Context' properties.
+    Context.OBJECT_FACTORIES: [PyObjectFactory()],
+    Context.STATE_FACTORIES: [ReferenceableStateFactory()],
+}
+
+
+
[docs]class PyContext(Context, Referenceable): + """ A naming context for a Python namespace. """ + + #### 'Context' interface ################################################## + + # The naming environment in effect for this context. + environment = Dict(ENVIRONMENT) + + #### 'PyContext' interface ################################################ + + # The Python namespace that we represent. + namespace = Any + + # If the namespace is actual a Python object that has a '__dict__' + # attribute, then this will be that object (the namespace will be the + # object's '__dict__'. + obj = Any + + #### 'Referenceable' interface ############################################ + + # The object's reference suitable for binding in a naming context. + reference = Property(Instance(Reference)) + + ########################################################################### + # 'object' interface. + ########################################################################### + + def __init__(self, **traits): + """ Creates a new context. """ + + # Base class constructor. + super(PyContext, self).__init__(**traits) + + if type(self.namespace) is not dict: + if hasattr(self.namespace, "__dict__"): + self.obj = self.namespace + self.namespace = self.namespace.__dict__ + + else: + raise ValueError("Need a dictionary or a __dict__ attribute") + + ########################################################################### + # 'Referenceable' interface. + ########################################################################### + + #### Properties ########################################################### + + def _get_reference(self): + """ Returns a reference to this object suitable for binding. """ + + reference = Reference( + class_name=self.__class__.__name__, + addresses=[Address(type="py_context", content=self.namespace)], + ) + + return reference + + ########################################################################### + # Protected 'Context' interface. + ########################################################################### + + def _is_bound(self, name): + """ Is a name bound in this context? """ + + return name in self.namespace + + def _lookup(self, name): + """ Looks up a name in this context. """ + + obj = self.namespace[name] + + return naming_manager.get_object_instance(obj, name, self) + + def _bind(self, name, obj): + """ Binds a name to an object in this context. """ + + state = naming_manager.get_state_to_bind(obj, name, self) + self.namespace[name] = state + + def _rebind(self, name, obj): + """ Rebinds a name to a object in this context. """ + + self._bind(name, obj) + + def _unbind(self, name): + """ Unbinds a name from this context. """ + + del self.namespace[name] + + # Trait event notification. + self.trait_property_changed("context_changed", None, None) + + def _rename(self, old_name, new_name): + """ Renames an object in this context. """ + + state = self.namespace[old_name] + + # Bind the new name. + self.namespace[new_name] = state + + # Unbind the old one. + del self.namespace[old_name] + + # Trait event notification. + self.context_changed = True + + def _create_subcontext(self, name): + """ Creates a sub-context of this context. """ + + sub = self._context_factory(name, {}) + self.namespace[name] = sub + + # Trait event notification. + self.trait_property_changed("context_changed", None, None) + + return sub + + def _destroy_subcontext(self, name): + """ Destroys a sub-context of this context. """ + + del self.namespace[name] + + # Trait event notification. + self.trait_property_changed("context_changed", None, None) + + def _list_bindings(self): + """ Lists the bindings in this context. """ + + bindings = [] + for name, value in self.namespace.items(): + bindings.append( + Binding(name=name, obj=self._lookup(name), context=self) + ) + return bindings + + def _list_names(self): + """ Lists the names bound in this context. """ + + return list(self.namespace.keys()) + + ########################################################################### + # Private interface. + ########################################################################### + + def _context_factory(self, name, namespace): + """ Create a sub-context. """ + + return self.__class__(namespace=namespace)
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/naming/py_object_factory.html b/_modules/apptools/naming/py_object_factory.html new file mode 100644 index 000000000..4a049b8ad --- /dev/null +++ b/_modules/apptools/naming/py_object_factory.html @@ -0,0 +1,171 @@ + + + + + + + apptools.naming.py_object_factory — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.naming.py_object_factory

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" Object factory for Python namespace contexts. """
+
+
+# Local imports.
+from .object_factory import ObjectFactory
+from .reference import Reference
+
+
+
[docs]class PyObjectFactory(ObjectFactory): + """ Object factory for Python namespace contexts. """ + + ########################################################################### + # 'ObjectFactory' interface. + ########################################################################### + +
[docs] def get_object_instance(self, state, name, context): + """ Creates an object using the specified state information. """ + + obj = None + + if isinstance(state, Reference): + if len(state.addresses) > 0: + if state.addresses[0].type == "py_context": + namespace = state.addresses[0].content + obj = context._context_factory(name, namespace) + + elif hasattr(state, "__dict__"): + from apptools.naming.py_context import PyContext + + if not isinstance(state, PyContext): + obj = context._context_factory(name, state) + + return obj
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/naming/pyfs_context.html b/_modules/apptools/naming/pyfs_context.html new file mode 100644 index 000000000..819d1dbd6 --- /dev/null +++ b/_modules/apptools/naming/pyfs_context.html @@ -0,0 +1,674 @@ + + + + + + + apptools.naming.pyfs_context — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.naming.pyfs_context

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" A Python File System context. """
+
+
+# Standard library imports.
+import glob
+import logging
+import os
+from os.path import join, splitext
+import pickle
+
+# Enthought library imports.
+from apptools.io.api import File
+from traits.api import Any, Dict, Instance, Property, Str
+
+# Local imports.
+from .address import Address
+from .binding import Binding
+from .context import Context
+from .dir_context import DirContext
+from .naming_event import NamingEvent
+from .naming_manager import naming_manager
+from .object_serializer import ObjectSerializer
+from .pyfs_context_factory import PyFSContextFactory
+from .pyfs_object_factory import PyFSObjectFactory
+from .pyfs_state_factory import PyFSStateFactory
+from .reference import Reference
+from .referenceable import Referenceable
+
+
+# Setup a logger for this module.
+logger = logging.getLogger(__name__)
+
+
+# The name of the 'special' file in which we store object attributes.
+ATTRIBUTES_FILE = "__attributes__"
+
+# Constants for environment property keys.
+FILTERS = "apptools.naming.pyfs.filters"
+OBJECT_SERIALIZERS = "apptools.naming.pyfs.object.serializers"
+
+
+# The default environment.
+ENVIRONMENT = {
+    #### 'Context' properties #################################################
+    # Object factories.
+    Context.OBJECT_FACTORIES: [PyFSObjectFactory(), PyFSContextFactory()],
+    # State factories.
+    Context.STATE_FACTORIES: [PyFSStateFactory()],
+    #### 'PyFSContext' properties #############################################
+    # Object serializers.
+    OBJECT_SERIALIZERS: [ObjectSerializer()],
+    # List of filename patterns to ignore.  These patterns are passed to
+    # 'glob.glob', so things like '*.pyc' will do what you expect.
+    #
+    # fixme: We should have a generalized filter mechanism here, and '.svn'
+    # should be moved elsewhere!
+    FILTERS: [ATTRIBUTES_FILE, ".svn"],
+}
+
+
+
[docs]class PyFSContext(DirContext, Referenceable): + """A Python File System context. + + This context represents a directory on a local file system. + + """ + + # The name of the 'special' file in which we store object attributes. + ATTRIBUTES_FILE = ATTRIBUTES_FILE + + # Environment property keys. + FILTERS = FILTERS + OBJECT_SERIALIZERS = OBJECT_SERIALIZERS + + #### 'Context' interface ################################################## + + # The naming environment in effect for this context. + environment = Dict(ENVIRONMENT) + + # The name of the context within its own namespace. + namespace_name = Property(Str) + + #### 'PyFSContext' interface ############################################## + + # The name of the context (the last component of the path). + name = Str + + # The path name of the directory on the local file system. + path = Str + + #### 'Referenceable' interface ############################################ + + # The object's reference suitable for binding in a naming context. + reference = Property(Instance(Reference)) + + #### Private interface #################################################### + + # A mapping from bound name to the name of the corresponding file or + # directory on the file system. + _name_to_filename_map = Dict # (Str, Str) + + # The attributes of every object in the context. The attributes for the + # context itself have the empty string as the key. + # + # {str name : dict attributes} + # + # fixme: Don't use 'Dict' here as it causes problems when pickling because + # trait dicts have a reference back to the parent object (hence we end up + # pickling all kinds of things that we don't need or want to!). + _attributes = Any + + ########################################################################### + # 'object' interface. + ########################################################################### + + def __init__(self, **traits): + """ Creates a new context. """ + + # Base class constructor. + super(PyFSContext, self).__init__(**traits) + + # We cache each object as it is looked up so that all accesses to a + # serialized Python object return a reference to exactly the same one. + self._cache = {} + + ########################################################################### + # 'PyFSContext' interface. + ########################################################################### + + #### Properties ########################################################### + + def _get_namespace_name(self): + """ Returns the name of the context within its own namespace. """ + + # fixme: clean this up with an initial context API! + if "root" in self.environment: + root = self.environment["root"] + + namespace_name = self.path[len(root) + 1:] + + else: + namespace_name = self.path + + # fixme: This is a bit dodgy 'cos we actually return a name that can + # be looked up, and not the file system name... + namespace_name = "/".join(namespace_name.split(os.path.sep)) + + return namespace_name + + #### methods ############################################################## + +
[docs] def refresh(self): + """ Refresh the context to reflect changes in the file system. """ + + # fixme: This needs more work 'cos if we refresh a context then we + # will load new copies of serialized Python objects! + + # This causes the initializer to run again the next time the trait is + # accessed. + self.reset_traits(["_name_to_filename_map"]) + + # Clear out the cache. + self._cache = {} + + # fixme: This is a bit hacky since the context in the binding may + # not be None! + self.context_changed = NamingEvent( + new_binding=Binding(name=self.name, obj=self, context=None) + )
+ + ########################################################################### + # 'Referenceable' interface. + ########################################################################### + + #### Properties ########################################################### + + def _get_reference(self): + """ Returns a reference to this object suitable for binding. """ + + abspath = os.path.abspath(self.path) + + reference = Reference( + class_name=self.__class__.__name__, + addresses=[Address(type="pyfs_context", content=abspath)], + ) + + return reference + + ########################################################################### + # Protected 'Context' interface. + ########################################################################### + + def _is_bound(self, name): + """ Is a name bound in this context? """ + + return name in self._name_to_filename_map + + def _lookup(self, name): + """ Looks up a name in this context. """ + + if name in self._cache: + obj = self._cache[name] + + else: + # Get the full path to the file. + path = join(self.path, self._name_to_filename_map[name]) + + # If the file contains a serialized Python object then load it. + for serializer in self._get_object_serializers(): + if serializer.can_load(path): + try: + state = serializer.load(path) + + # If the load fails then we create a generic file resource + # (the idea being that it might be useful to have access to + # the file to see what went wrong). + except: # noqa: E722 + state = File(path) + logger.exception("Error loading resource at %s" % path) + + break + + # Otherwise, it must just be a file or folder. + else: + # Directories are contexts. + if os.path.isdir(path): + state = self._context_factory(name, path) + + # Files are just files! + elif os.path.isfile(path): + state = File(path) + + else: + raise ValueError("unrecognized file for %s" % name) + + # Get the actual object from the naming manager. + obj = naming_manager.get_object_instance(state, name, self) + + # Update the cache. + self._cache[name] = obj + + return obj + + def _bind(self, name, obj): + """ Binds a name to an object in this context. """ + + # Get the actual state to bind from the naming manager. + state = naming_manager.get_state_to_bind(obj, name, self) + + # If the object is actually an abstract file then we don't have to + # do anything. + if isinstance(state, File): + if not state.exists: + state.create_file() + + filename = name + + # Otherwise we are binding an arbitrary Python object, so find a + # serializer for it. + else: + for serializer in self._get_object_serializers(): + if serializer.can_save(obj): + path = serializer.save(join(self.path, name), obj) + filename = os.path.basename(path) + break + + else: + raise ValueError("cannot serialize object %s" % name) + + # Update the name to filename map. + self._name_to_filename_map[name] = filename + + # Update the cache. + self._cache[name] = obj + + return state + + def _rebind(self, name, obj): + """ Rebinds a name to an object in this context. """ + + self._bind(name, obj) + + def _unbind(self, name): + """ Unbinds a name from this context. """ + + # Get the full path to the file. + path = join(self.path, self._name_to_filename_map[name]) + + # Remove it! + f = File(path) + f.delete() + + # Update the name to filename map. + del self._name_to_filename_map[name] + + # Update the cache. + if name in self._cache: + del self._cache[name] + + # Remove any attributes. + if name in self._attributes: + del self._attributes[name] + self._save_attributes() + + def _rename(self, old_name, new_name): + """ Renames an object in this context. """ + + # Get the old filename. + old_filename = self._name_to_filename_map[old_name] + old_file = File(join(self.path, old_filename)) + + # Lookup the object bound to the old name. This has the side effect + # of adding the object to the cache under the name 'old_name'. + obj = self._lookup(old_name) + + # We are renaming a LOCAL context (ie. a folder)... + if old_file.is_folder: + # Create the new filename. + new_filename = new_name + new_file = File(join(self.path, new_filename)) + + # Move the folder. + old_file.move(new_file) + + # Update the 'Context' object. + obj.path = new_file.path + + # Update the cache. + self._cache[new_name] = obj + del self._cache[old_name] + + # Refreshing the context makes sure that all of its contents + # reflect the new name (i.e., sub-folders and files have the + # correct path). + # + # fixme: This currently results in new copies of serialized + # Python objects! We need to be a bit more judicious in the + # refresh. + obj.refresh() + + # We are renaming a file... + elif isinstance(obj, File): + # Create the new filename. + new_filename = new_name + new_file = File(join(self.path, new_filename)) + + # Move the file. + old_file.move(new_file) + + # Update the 'File' object. + obj.path = new_file.path + + # Update the cache. + self._cache[new_name] = obj + del self._cache[old_name] + + # We are renaming a serialized Python object... + else: + # Create the new filename. + new_filename = new_name + old_file.ext + new_file = File(join(self.path, new_filename)) + + old_file.delete() + + # Update the cache. + if old_name in self._cache: + self._cache[new_name] = self._cache[old_name] + del self._cache[old_name] + + # Force the creation of the new file. + # + # fixme: I'm not sure that this is really the place for this. We + # do it because often the 'name' of the object is actually an + # attribute of the object itself, and hence we want the serialized + # state to reflect the new name... Hmmm... + self._rebind(new_name, obj) + + # Update the name to filename map. + del self._name_to_filename_map[old_name] + self._name_to_filename_map[new_name] = new_filename + + # Move any attributes over to the new name. + if old_name in self._attributes: + self._attributes[new_name] = self._attributes[old_name] + del self._attributes[old_name] + self._save_attributes() + + def _create_subcontext(self, name): + """ Creates a sub-context of this context. """ + + path = join(self.path, name) + + # Create a directory. + os.mkdir(path) + + # Create a sub-context that represents the directory. + sub = self._context_factory(name, path) + + # Update the name to filename map. + self._name_to_filename_map[name] = name + + # Update the cache. + self._cache[name] = sub + + return sub + + def _destroy_subcontext(self, name): + """ Destroys a sub-context of this context. """ + + return self._unbind(name) + + def _list_names(self): + """ Lists the names bound in this context. """ + + return list(self._name_to_filename_map.keys()) + + # fixme: YFI this is not part of the protected 'Context' interface so + # what is it doing here? +
[docs] def get_unique_name(self, name): + + ext = splitext(name)[1] + + # specially handle '.py' files + if ext != ".py": + return super(PyFSContext, self).get_unique_name(name) + + body = splitext(name)[0] + names = self.list_names() + i = 2 + unique = name + while unique in names: + unique = body + "_" + str(i) + ".py" + i += 1 + + return unique
+ + ########################################################################### + # Protected 'DirContext' interface. + ########################################################################### + + def _get_attributes(self, name): + """ Returns the attributes of an object in this context. """ + + attributes = self._attributes.setdefault(name, {}) + + return attributes.copy() + + def _set_attributes(self, name, attributes): + """ Sets the attributes of an object in this context. """ + + self._attributes[name] = attributes + self._save_attributes() + + ########################################################################### + # Private interface. + ########################################################################### + + def _get_filters(self): + """ Returns the filters for this context. """ + + return self.environment.get(self.FILTERS, []) + + def _get_object_serializers(self): + """ Returns the object serializers for this context. """ + + return self.environment.get(self.OBJECT_SERIALIZERS, []) + + def _context_factory(self, name, path): + """ Create a sub-context. """ + + return self.__class__(path=path, environment=self.environment) + + def _save_attributes(self): + """ Saves all attributes to the attributes file. """ + + path = join(self.path, self.ATTRIBUTES_FILE) + + f = open(path, "wb") + pickle.dump(self._attributes, f, 1) + f.close() + + #### Trait initializers ################################################### + + def __name_to_filename_map_default(self): + """ Initializes the '_name_to_filename' trait. """ + + # fixme: We should have a generalized filter mechanism (instead of + # just 'glob' patterns we should have filter objects that can be a bit + # more flexible in how they do the filtering). + patterns = [join(self.path, filter) for filter in self._get_filters()] + + name_to_filename_map = {} + for filename in os.listdir(self.path): + path = join(self.path, filename) + for pattern in patterns: + if path in glob.glob(pattern): + break + + else: + for serializer in self._get_object_serializers(): + if serializer.can_load(filename): + # fixme: We should probably get the name from the + # serializer instead of assuming that we can just + # drop the file exension. + name, ext = os.path.splitext(filename) + break + + else: + name = filename + + name_to_filename_map[name] = filename + + return name_to_filename_map + + def __attributes_default(self): + """ Initializes the '_attributes' trait. """ + + attributes_file = File(join(self.path, self.ATTRIBUTES_FILE)) + if attributes_file.is_file: + f = open(attributes_file.path, "rb") + attributes = pickle.load(f) + f.close() + + else: + attributes = {} + + return attributes + + #### Trait event handlers ################################################# + + def _path_changed(self): + """ Called when the context's path has changed. """ + + basename = os.path.basename(self.path) + + self.name, ext = os.path.splitext(basename)
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/naming/pyfs_context_factory.html b/_modules/apptools/naming/pyfs_context_factory.html new file mode 100644 index 000000000..13c30f4ea --- /dev/null +++ b/_modules/apptools/naming/pyfs_context_factory.html @@ -0,0 +1,165 @@ + + + + + + + apptools.naming.pyfs_context_factory — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.naming.pyfs_context_factory

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" Object factory for Python File System contexts. """
+
+
+# Local imports.
+from .object_factory import ObjectFactory
+from .reference import Reference
+
+
+
[docs]class PyFSContextFactory(ObjectFactory): + """ Object factory for Python File System contexts. """ + + ########################################################################### + # 'ObjectFactory' interface. + ########################################################################### + +
[docs] def get_object_instance(self, state, name, context): + """ Creates an object using the specified state information. """ + + obj = None + + if isinstance(state, Reference): + if len(state.addresses) > 0: + if state.addresses[0].type == "pyfs_context": + path = state.addresses[0].content + obj = context._context_factory(name, path) + + return obj
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/naming/pyfs_initial_context_factory.html b/_modules/apptools/naming/pyfs_initial_context_factory.html new file mode 100644 index 000000000..6dc9fafdc --- /dev/null +++ b/_modules/apptools/naming/pyfs_initial_context_factory.html @@ -0,0 +1,174 @@ + + + + + + + apptools.naming.pyfs_initial_context_factory — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.naming.pyfs_initial_context_factory

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" The initial context factory for Python file system contexts. """
+
+
+# Local imports.
+from .context import Context
+from .initial_context_factory import InitialContextFactory
+from .object_serializer import ObjectSerializer
+from .pyfs_context import PyFSContext
+from .pyfs_context_factory import PyFSContextFactory
+from .pyfs_object_factory import PyFSObjectFactory
+from .pyfs_state_factory import PyFSStateFactory
+
+
+
[docs]class PyFSInitialContextFactory(InitialContextFactory): + """ The initial context factory for Python file system contexts. """ + + ########################################################################### + # 'InitialContextFactory' interface. + ########################################################################### + +
[docs] def get_initial_context(self, environment): + """ Creates an initial context for beginning name resolution. """ + + # Object factories. + object_factories = [PyFSObjectFactory(), PyFSContextFactory()] + environment[Context.OBJECT_FACTORIES] = object_factories + + # State factories. + state_factories = [PyFSStateFactory()] + environment[Context.STATE_FACTORIES] = state_factories + + # Object serializers. + object_serializers = [ObjectSerializer()] + environment[PyFSContext.OBJECT_SERIALIZERS] = object_serializers + + return PyFSContext(path=r"", environment=environment)
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/naming/pyfs_object_factory.html b/_modules/apptools/naming/pyfs_object_factory.html new file mode 100644 index 000000000..394ba4730 --- /dev/null +++ b/_modules/apptools/naming/pyfs_object_factory.html @@ -0,0 +1,166 @@ + + + + + + + apptools.naming.pyfs_object_factory — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.naming.pyfs_object_factory

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" Object factory for Python File System contexts. """
+
+
+# Enthought library imports.
+from apptools.io.api import File
+
+# Local imports.
+from .object_factory import ObjectFactory
+from .reference import Reference
+
+
+
[docs]class PyFSObjectFactory(ObjectFactory): + """ Object factory for Python File System contexts. """ + + ########################################################################### + # 'ObjectFactory' interface. + ########################################################################### + +
[docs] def get_object_instance(self, state, name, context): + """ Creates an object using the specified state information. """ + + obj = None + + if isinstance(state, Reference): + if state.class_name == "File" and len(state.addresses) > 0: + obj = File(state.addresses[0].content) + + return obj
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/naming/pyfs_state_factory.html b/_modules/apptools/naming/pyfs_state_factory.html new file mode 100644 index 000000000..b942c237e --- /dev/null +++ b/_modules/apptools/naming/pyfs_state_factory.html @@ -0,0 +1,172 @@ + + + + + + + apptools.naming.pyfs_state_factory — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.naming.pyfs_state_factory

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" State factory for Python File System contexts. """
+
+
+# Enthought library imports.
+from apptools.io.api import File
+
+# Local imports.
+from .address import Address
+from .reference import Reference
+from .state_factory import StateFactory
+
+
+
[docs]class PyFSStateFactory(StateFactory): + """ State factory for Python File System contexts. """ + + ########################################################################### + # 'StateFactory' interface. + ########################################################################### + +
[docs] def get_state_to_bind(self, obj, name, context): + """ Returns the state of an object for binding. """ + + state = None + + if isinstance(obj, File): + # If the file is not actually in the directory represented by the + # context then we create and bind a reference to it. + if obj.parent.path != context.path: + state = Reference( + class_name=obj.__class__.__name__, + addresses=[Address(type="file", content=obj.path)], + ) + + return state
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/naming/reference.html b/_modules/apptools/naming/reference.html new file mode 100644 index 000000000..4e9f94dde --- /dev/null +++ b/_modules/apptools/naming/reference.html @@ -0,0 +1,173 @@ + + + + + + + apptools.naming.reference — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.naming.reference

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" A reference to an object that lives outside of the naming system. """
+
+
+# Enthought library imports.
+from traits.api import HasPrivateTraits, List, Str
+
+# Local imports.
+from .address import Address
+
+
+
[docs]class Reference(HasPrivateTraits): + """A reference to an object that lives outside of the naming system. + + References provide a way to store the address(s) of objects that live + outside of the naming system. A reference consists of a list of + addresses that represent a communications endpoint for the object being + referenced. + + A reference also contains information to assist in the creation of an + instance of the object to which it refers. It contains the name of + the class that will be created and the class name and location of a + factory that will be used to do the actual instance creation. + + """ + + #### 'Reference' interface ################################################ + + # The list of addresses that can be used to 'contact' the object. + addresses = List(Address) + + # The class name of the object that this reference refers to. + class_name = Str + + # The class name of the object factory. + factory_class_name = Str
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/naming/referenceable.html b/_modules/apptools/naming/referenceable.html new file mode 100644 index 000000000..4989cb547 --- /dev/null +++ b/_modules/apptools/naming/referenceable.html @@ -0,0 +1,155 @@ + + + + + + + apptools.naming.referenceable — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.naming.referenceable

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" Base class for classes that can produce a reference to themselves. """
+
+
+# Enthought library imports.
+from traits.api import HasPrivateTraits, Instance
+
+# Local imports.
+from .reference import Reference
+
+
+
[docs]class Referenceable(HasPrivateTraits): + """ Base class for classes that can produce a reference to themselves. """ + + #### 'Referenceable' interface ############################################ + + # The object's reference suitable for binding in a naming context. + reference = Instance(Reference)
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/naming/referenceable_state_factory.html b/_modules/apptools/naming/referenceable_state_factory.html new file mode 100644 index 000000000..6735d1441 --- /dev/null +++ b/_modules/apptools/naming/referenceable_state_factory.html @@ -0,0 +1,164 @@ + + + + + + + apptools.naming.referenceable_state_factory — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.naming.referenceable_state_factory

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" State factory for referenceable objects. """
+
+
+# Local imports.
+from .referenceable import Referenceable
+from .state_factory import StateFactory
+
+
+
[docs]class ReferenceableStateFactory(StateFactory): + """ State factory for referenceable objects. """ + + ########################################################################### + # 'StateFactory' interface. + ########################################################################### + +
[docs] def get_state_to_bind(self, obj, name, context): + """ Returns the state of an object for binding. """ + + state = None + + # If the object knows how to create a reference to it then let it + # do so. + if isinstance(obj, Referenceable): + state = obj.reference + + return state
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/naming/state_factory.html b/_modules/apptools/naming/state_factory.html new file mode 100644 index 000000000..6e858ad08 --- /dev/null +++ b/_modules/apptools/naming/state_factory.html @@ -0,0 +1,166 @@ + + + + + + + apptools.naming.state_factory — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.naming.state_factory

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" The base class for all state factories. """
+
+
+# Enthought library imports.
+from traits.api import HasPrivateTraits
+
+
+
[docs]class StateFactory(HasPrivateTraits): + """The base class for all state factories. + + A state factory accepts an object and returns some data representing the + object that is suitable for storing in a particular context. + + """ + + ########################################################################### + # 'StateFactory' interface. + ########################################################################### + +
[docs] def get_state_to_bind(self, obj, name, context): + """Returns the state of an object for binding. + + Returns None if the factory cannot create the state (ie. it does not + recognise the object passed to it). + + """ + + raise NotImplementedError
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/naming/trait_defs/naming_traits.html b/_modules/apptools/naming/trait_defs/naming_traits.html new file mode 100644 index 000000000..ad3cc1734 --- /dev/null +++ b/_modules/apptools/naming/trait_defs/naming_traits.html @@ -0,0 +1,300 @@ + + + + + + + apptools.naming.trait_defs.naming_traits — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.naming.trait_defs.naming_traits

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+
+# -------------------------------------------------------------------------------
+#  Imports:
+# -------------------------------------------------------------------------------
+
+import sys
+
+from traits.api import Trait, TraitHandler, TraitFactory
+
+from traits.trait_base import class_of, get_module_name
+
+from apptools.naming.api import Binding
+
+
+# -------------------------------------------------------------------------------
+#  'NamingInstance' trait factory:
+# -------------------------------------------------------------------------------
+
+
+def NamingInstance(klass=None, value="", allow_none=False, **metadata):
+    metadata.setdefault("copy", "deep")
+    return Trait(
+        value,
+        NamingTraitHandler(
+            klass, or_none=allow_none, module=get_module_name()
+        ),
+        **metadata
+    )
+
+
+NamingInstance = TraitFactory(NamingInstance)
+
+# -------------------------------------------------------------------------------
+#  'NamingTraitHandler' class:
+# -------------------------------------------------------------------------------
+
+
+
[docs]class NamingTraitHandler(TraitHandler): + + # --------------------------------------------------------------------------- + # Initializes the object: + # --------------------------------------------------------------------------- + + def __init__(self, aClass, or_none, module): + """Initializes the object.""" + self.or_none = or_none is not False + self.module = module + self.aClass = aClass + if (aClass is not None) and ( + not isinstance(aClass, (str, type)) + ): + self.aClass = aClass.__class__ + +
[docs] def validate(self, object, name, value): + if isinstance(value, str): + if value == "": + if self.or_none: + return "" + else: + self.validate_failed(object, name, value) + try: + value = self._get_binding_for(value) + except: # noqa: E722 + self.validate_failed(object, name, value) + + if isinstance(self.aClass, str): + self.resolve_class(object, name, value) + + if isinstance(value, Binding) and ( + (self.aClass is None) or isinstance(value.obj, self.aClass) + ): + return value.namespace_name + self.validate_failed(object, name, value)
+ +
[docs] def info(self): + aClass = self.aClass + if aClass is None: + result = "path" + else: + if type(aClass) is not str: + aClass = aClass.__name__ + result = "path to an instance of " + class_of(aClass) + if self.or_none is None: + return result + " or an empty string" + return result
+ +
[docs] def validate_failed(self, object, name, value): + if not isinstance(value, type): + msg = "class %s" % value.__class__.__name__ + else: + msg = "%s (i.e. %s)" % (str(type(value))[1:-1], repr(value)) + self.error(object, name, msg)
+ +
[docs] def get_editor(self, trait): + if self.editor is None: + from traitsui.api import DropEditor + + self.editor = DropEditor( + klass=self.aClass, binding=True, readonly=False + ) + return self.editor
+ +
[docs] def post_setattr(self, object, name, value): + other = None + if value != "": + other = self._get_binding_for(value).obj + object.__dict__[name + "_"] = other
+ + def _get_binding_for(self, value): + + result = None + + # FIXME: The following code makes this whole component have a + # dependency on envisage, and worse, assumes the use of a particular + # project plugin! This is horrible and should be refactored out, + # possibly to a custom sub-class of whoever needs this behavior. + try: + from envisage import get_application + + workspace = get_application().service_registry.get_service( + "envisage.project.IWorkspace" + ) + result = workspace.lookup_binding(value) + except ImportError: + pass + + return result + +
[docs] def resolve_class(self, object, name, value): + aClass = self.find_class() + if aClass is None: + self.validate_failed(object, name, value) + self.aClass = aClass + + # fixme: The following is quite ugly, because it wants to try and fix + # the trait referencing this handler to use the 'fast path' now that + # the actual class has been resolved. The problem is finding the trait, + # especially in the case of List(Instance('foo')), where the + # object.base_trait(...) value is the List trait, not the Instance + # trait, so we need to check for this and pull out the List + # 'item_trait'. Obviously this does not extend well to other traits + # containing nested trait references (Dict?)... + trait = object.base_trait(name) + handler = trait.handler + if (handler is not self) and hasattr(handler, "item_trait"): + trait = handler.item_trait + trait.validate(self.fast_validate)
+ +
[docs] def find_class(self): + module = self.module + aClass = self.aClass + col = aClass.rfind(".") + if col >= 0: + module = aClass[:col] + aClass = aClass[col + 1:] + theClass = getattr(sys.modules.get(module), aClass, None) + if (theClass is None) and (col >= 0): + try: + theClass = getattr(__import__(module), aClass, None) + except Exception: + pass + return theClass
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/naming/unique_name.html b/_modules/apptools/naming/unique_name.html new file mode 100644 index 000000000..e33f1657a --- /dev/null +++ b/_modules/apptools/naming/unique_name.html @@ -0,0 +1,164 @@ + + + + + + + apptools.naming.unique_name — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.naming.unique_name

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+
+
+"""
+A re-usable method for calculating a unique name given a list of existing
+names.
+
+"""
+
+
+
[docs]def make_unique_name(base, existing=[], format="%s_%s"): + """ + Return a name, unique within a context, based on the specified name. + + base: the desired base name of the generated unique name. + existing: a sequence of the existing names to avoid returning. + format: a formatting specification for how the name is made unique. + + """ + + count = 2 + name = base + while name in existing: + name = format % (base, count) + count += 1 + + return name
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/persistence/file_path.html b/_modules/apptools/persistence/file_path.html new file mode 100644 index 000000000..2391cf819 --- /dev/null +++ b/_modules/apptools/persistence/file_path.html @@ -0,0 +1,218 @@ + + + + + + + apptools.persistence.file_path — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.persistence.file_path

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+"""Simple class to support file path objects that work well in the
+context of persistent storage with the state_pickler.
+
+"""
+
+# Standard library imports.
+import os
+from os.path import abspath, normpath, dirname, join
+
+
+
[docs]class FilePath(object): + """This class stores two paths to the file. A relative path and + an absolute one. The absolute path is used by the end user. When + this object is pickled the state_pickler sets the relative path + relative to the file that is being generated. When unpickled, the + stored relative path is used to set the absolute path correctly + based on the path of the saved file. + """ + + def __init__(self, value=""): + self.set(value) + + def __str__(self): + return self.abs_pth + + def __repr__(self): + return self.abs_pth.__repr__() + +
[docs] def get(self): + """Get the path.""" + return self.abs_pth
+ +
[docs] def set(self, value): + """Sets the value of the path.""" + self.rel_pth = value + if value: + self.abs_pth = normpath(abspath(value)) + else: + self.abs_pth = ""
+ +
[docs] def set_relative(self, base_f_name): + """Sets the path relative to `base_f_name`. Note that + `base_f_name` and self.rel_pth should be valid file names + correct on the current os. The set name is a file name that + has a POSIX path. + """ + + # Get normalized paths. + _src = abspath(base_f_name) + _dst = self.abs_pth + + # Now strip out any common prefix between the two paths. + for part in _src.split(os.sep): + if _dst.startswith(part + os.sep): + length = len(part) + 1 + _src = _src[length:] + _dst = _dst[length:] + else: + break + + # For each directory in the source, we need to add a reference to + # the parent directory to the destination. + ret = (_src.count(os.sep) * (".." + os.sep)) + _dst + + # Make it posix style. + if os.sep != "/": + ret.replace(os.sep, "/") + + # Store it. + self.rel_pth = ret
+ +
[docs] def set_absolute(self, base_f_name): + """Sets the absolute file name for the current relative file + name with respect to the given `base_f_name`. + """ + base_f_name = normpath(abspath(base_f_name)) + rel_file_name = normpath(self.rel_pth) + file_name = join(dirname(base_f_name), rel_file_name) + file_name = os.path.normpath(file_name) + self.abs_pth = file_name
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/persistence/project_loader.html b/_modules/apptools/persistence/project_loader.html new file mode 100644 index 000000000..75559d5eb --- /dev/null +++ b/_modules/apptools/persistence/project_loader.html @@ -0,0 +1,262 @@ + + + + + + + apptools.persistence.project_loader — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.persistence.project_loader

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+# Standard library imports
+import sys
+import pickle
+import logging
+
+# Enthought library imports
+from apptools.persistence.versioned_unpickler import VersionedUnpickler
+
+
+logger = logging.getLogger(__name__)
+
+
+
[docs]def load_project( + pickle_filename, updater_path, application_version, protocol, max_pass=-1 +): + """Reads a project from a pickle file and if necessary will update it to + the latest version of the application. + """ + + latest_file = pickle_filename + + # Read the pickled project's metadata. + f = open(latest_file, "rb") + metadata = VersionedUnpickler(f).load(max_pass) + f.close() + project_version = metadata.get("version", False) + + if not project_version: + raise ValueError("Could not read version number from the project file") + + logger.debug( + "Project version: %d, Application version: %d" + % (project_version, application_version) + ) + + # here you can temporarily force an upgrade each time for testing .... + # project_version = 0 + latest_file = upgrade_project( + pickle_filename, + updater_path, + project_version, + application_version, + protocol, + max_pass, + ) + + # Finally we can import the project ... + logger.info("loading %s" % latest_file) + i_f = open(latest_file, "rb") + project = VersionedUnpickler(i_f).load(max_pass) + i_f.close() + + return project
+ + +
[docs]def upgrade_project( + pickle_filename, + updater_path, + project_version, + application_version, + protocol, + max_pass=-1, +): + """Repeatedly read and write the project to disk updating it one version + at a time. + + Example the p5.project is at version 0 + The application is at version 3 + + p5.project --- Update1 ---> p5.project.v1 + p5.project.v1 --- Update2 ---> p5.project.v2 + p5.project.v2 --- Update3 ---> p5.project.v3 + p5.project.v3 ---> loaded into app + + The user then has the option to save the updated project as p5.project + """ + first_time = True + latest_file = pickle_filename + + # update the project until it's version matches the application's + while project_version < application_version: + + next_version = project_version + 1 + + if first_time: + i_f = open(pickle_filename, "rb") + data = i_f.read() + open("%s.bak" % pickle_filename, "wb").write(data) + i_f.seek(0) # rewind the file to the start + else: + name = "%s.v%d" % (pickle_filename, project_version) + i_f = open(name, "rb") + latest_file = name + + logger.info("converting %s" % latest_file) + + # find this version's updater ... + updater_name = "%s.update%d" % (updater_path, next_version) + __import__(updater_name) + mod = sys.modules[updater_name] + klass = getattr(mod, "Update%d" % next_version) + updater = klass() + + # load and update this version of the project + project = VersionedUnpickler(i_f, updater).load(max_pass) + i_f.close() + + # set the project version to be the same as the updater we just + # ran on the unpickled files ... + project.metadata["version"] = next_version + + # Persist the updated project ... + name = "%s.v%d" % (pickle_filename, next_version) + latest_file = name + o_f = open(name, "wb") + pickle.dump(project.metadata, o_f, protocol=protocol) + pickle.dump(project, o_f, protocol=protocol) + o_f.close() + + # Bump up the version number of the pickled project... + project_version += 1 + first_time = False + + return latest_file
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/persistence/state_pickler.html b/_modules/apptools/persistence/state_pickler.html new file mode 100644 index 000000000..21a0effe1 --- /dev/null +++ b/_modules/apptools/persistence/state_pickler.html @@ -0,0 +1,1168 @@ + + + + + + + apptools.persistence.state_pickler — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.persistence.state_pickler

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+"""This module provides code that allows one to pickle the state of a
+Python object to a dictionary.
+
+The motivation for this is simple.  The standard Python
+pickler/unpickler is best used to pickle simple objects and does not
+work too well for complex code.  Specifically, there are two major
+problems (1) the pickle file format is not easy to edit with a text
+editor and (2) when a pickle is unpickled, it creates all the
+necessary objects and sets the state of these objects.
+
+Issue (2) might not appear to be a problem.  However, often, the
+determination of the entire 'state' of an application requires the
+knowledge of the state of many objects that are not really in the
+users concern.  The user would ideally like to pickle just what he
+thinks is relevant.  Now, given that the user is not going to save the
+entire state of the application, the use of pickle is insufficient
+since the state is no longer completely known (or worth knowing).  The
+default `Unpickler` recreates the objects and the typical
+implementation of `__setstate__` is usually to simply update the
+object's `__dict__` attribute.  This is inadequate because the pickled
+information is taken out of the real context when it was saved.
+
+The `StatePickler` basically pickles the 'state' of an object into a
+large dictionary.  This pickled data may be easily unpickled and
+modified on the interpreter or edited with a text editor
+(`pprint.saferepr` is a friend).  The second problem is also
+eliminated.  When this state is unpickled using `StateUnpickler`, what
+you get is a special dictionary (a `State` instance).  This allows one
+to navigate the state just like the original object.  Its up to the
+user to create any new objects and set their states using this
+information.  This allows for a lot of flexibility while allowing one
+to save and set the state of (almost) any Python object.
+
+The `StateSetter` class helps set the state of a known instance.  When
+setting the state of an instance it checks to see if there is a
+`__set_pure_state__` method that in turn calls `StateSetter.set`
+appropriately.
+
+Additionally, there is support for versioning.  The class' version is
+obtain from the `__version__` class attribute.  This version along
+with the versions of the bases of a class is embedded into the
+metadata of the state and stored.  By using `version_registry.py` a
+user may register a handler for a particular class and module.  When
+the state of an object is set using `StateSetter.set_state`, then
+these handlers are called in reverse order of their MRO.  This gives
+the handler an opportunity to upgrade the state depending on its
+version.  Builtin classes are not scanned for versions.  If a class
+has no version, then by default it is assumed to be -1.
+
+
+Example::
+
+  >>> class A:
+  ...    def __init__(self):
+  ...        self.a = 'a'
+  ...
+  >>> a = A()
+  >>> a.a = 100
+  >>> import state_pickler
+  >>> s = state_pickler.dumps(a)               # Dump the state of `a`.
+  >>> state = state_pickler.loads_state(s)     # Get the state back.
+  >>> b = state_pickler.create_instance(state) # Create the object.
+  >>> state_pickler.set_state(b, state)        # Set the object's state.
+  >>> assert b.a == 100
+
+
+Features
+--------
+
+ - The output is a plain old dictionary so is easy to parse, edit etc.
+ - Handles references to avoid duplication.
+ - Gzips Numeric arrays when dumping them.
+ - Support for versioning.
+
+
+Caveats
+-------
+
+ - Does not pickle a whole bunch of stuff including code objects and
+   functions.
+ - The output is a pure dictionary and does not contain instances.  So
+   using this *as it is* in `__setstate__` will not work.  Instead
+   define a `__set_pure_state__` and use the `StateSetter` class or
+   the `set_state` function provided by this module.
+
+
+Notes
+-----
+
+  Browsing the code from XMarshaL_ and pickle.py proved useful for
+  ideas.  None of the code is taken from there though.
+
+.. _XMarshaL:  http://www.dezentral.de/soft/XMarshaL
+
+"""
+# Author: Prabhu Ramachandran <prabhu_r@users.sf.net>
+# Copyright (c) 2005-2015, Enthought, Inc.
+# License: BSD Style.
+
+# Standard library imports.
+import base64
+import sys
+import pickle
+import gzip
+from io import BytesIO, StringIO
+
+import numpy
+
+# Local imports.
+from . import version_registry
+from .file_path import FilePath
+
+NumpyArrayType = type(numpy.array([]))
+
+
+
[docs]def gzip_string(data): + """Given a string (`data`) this gzips the string and returns it.""" + s = BytesIO() + writer = gzip.GzipFile(mode="wb", fileobj=s) + writer.write(data) + writer.close() + s.seek(0) + return s.read()
+ + +
[docs]def gunzip_string(data): + """Given a gzipped string (`data`) this unzips the string and + returns it. + """ + if type(data) is bytes: + s = BytesIO(data) + else: + s = StringIO(data) + writer = gzip.GzipFile(mode="rb", fileobj=s) + data = writer.read() + writer.close() + return data
+ + +
[docs]class StatePicklerError(Exception): + pass
+ + +
[docs]class StateUnpicklerError(Exception): + pass
+ + +
[docs]class StateSetterError(Exception): + pass
+ + +###################################################################### +# `State` class +###################################################################### +
[docs]class State(dict): + """Used to encapsulate the state of an instance in a very + convenient form. The '__metadata__' attribute/key is a dictionary + that has class specific details like the class name, module name + etc. + """ + + def __init__(self, **kw): + dict.__init__(self, **kw) + self.__dict__ = self
+ + +###################################################################### +# `StateDict` class +###################################################################### +
[docs]class StateDict(dict): + """Used to encapsulate a dictionary stored in a `State` instance. + The has_instance attribute specifies if the dict has an instance + embedded in it. + """ + + def __init__(self, **kw): + dict.__init__(self, **kw) + self.has_instance = False
+ + +###################################################################### +# `StateList` class +###################################################################### +
[docs]class StateList(list): + """Used to encapsulate a list stored in a `State` instance. The + has_instance attribute specifies if the list has an instance + embedded in it. + """ + + def __init__(self, seq=None): + if seq: + list.__init__(self, seq) + else: + list.__init__(self) + self.has_instance = False
+ + +###################################################################### +# `StateTuple` class +###################################################################### +
[docs]class StateTuple(tuple): + """Used to encapsulate a tuple stored in a `State` instance. The + has_instance attribute specifies if the tuple has an instance + embedded in it. + """ + + def __new__(cls, seq=None): + if seq: + obj = super(StateTuple, cls).__new__(cls, tuple(seq)) + else: + obj = super(StateTuple, cls).__new__(cls) + obj.has_instance = False + return obj
+ + +###################################################################### +# `StatePickler` class +###################################################################### +
[docs]class StatePickler: + """Pickles the state of an object into a dictionary. The + dictionary is itself either saved as a pickled file (`dump`) or + pickled string (`dumps`). Alternatively, the `dump_state` method + will return the dictionary that is pickled. + + The format of the state dict is quite strightfoward. Basic types + (bool, int, long, float, complex, None, string) are + represented as they are. Everything else is stored as a + dictionary containing metadata information on the object's type + etc. and also the actual object in the 'data' key. For example:: + + >>> p = StatePickler() + >>> p.dump_state(1) + 1 + >>> l = [1,2.0, None, [1,2,3]] + >>> p.dump_state(l) + {'data': [1, 2.0, None, {'data': [1, 2, 3], 'type': 'list', 'id': 1}], + 'id': 0, + 'type': 'list'} + + Classes are also represented similarly. The state in this case is + obtained from the `__getstate__` method or from the `__dict__`. + Here is an example:: + + >>> class A: + ... __version__ = 1 # State version + ... def __init__(self): + ... self.attribute = 1 + ... + >>> a = A() + >>> p = StatePickler() + >>> p.dump_state(a) + {'class_name': 'A', + 'data': {'data': {'attribute': 1}, 'type': 'dict', 'id': 2}, + 'id': 0, + 'initargs': {'data': (), 'type': 'tuple', 'id': 1}, + 'module': '__main__', + 'type': 'instance', + 'version': [(('A', '__main__'), 1)]} + + When pickling data, references are taken care of. Numeric arrays + can be pickled and are stored as a gzipped base64 encoded string. + + """ + + def __init__(self): + self._clear() + type_map = { + bool: self._do_basic_type, + complex: self._do_basic_type, + float: self._do_basic_type, + int: self._do_basic_type, + type(None): self._do_basic_type, + str: self._do_basic_type, + bytes: self._do_basic_type, + tuple: self._do_tuple, + list: self._do_list, + dict: self._do_dict, + NumpyArrayType: self._do_numeric, + State: self._do_state, + } + self.type_map = type_map + +
[docs] def dump(self, value, file): + """Pickles the state of the object (`value`) into the passed + file. + """ + try: + # Store the file name we are writing to so we can munge + # file paths suitably. + self.file_name = file.name + except AttributeError: + pass + pickle.dump(self._do(value), file)
+ +
[docs] def dumps(self, value): + """Pickles the state of the object (`value`) and returns a + string. + """ + return pickle.dumps(self._do(value))
+ +
[docs] def dump_state(self, value): + """Returns a dictionary or a basic type representing the + complete state of the object (`value`). + + This value is pickled by the `dump` and `dumps` methods. + """ + return self._do(value)
+ + ###################################################################### + # Non-public methods + ###################################################################### + def _clear(self): + # Stores the file name of the file being used to dump the + # state. This is used to change any embedded paths relative + # to the saved file. + self.file_name = "" + # Caches id's to handle references. + self.obj_cache = {} + # Misc cache to cache things that are not persistent. For + # example, object.__getstate__()/__getinitargs__() usually + # returns a copy of a dict/tuple that could possibly be reused + # on another object's __getstate__. Caching these prevents + # some wierd problems with the `id` of the object. + self._misc_cache = [] + + def _flush_traits(self, obj): + """Checks if the object has traits and ensures that the traits + are set in the `__dict__` so we can pickle it. + """ + # Not needed with Traits3. + + def _do(self, obj): + obj_type = type(obj) + key = self._get_id(obj) + if key in self.obj_cache: + return self._do_reference(obj) + elif obj_type in self.type_map: + return self.type_map[obj_type](obj) + elif isinstance(obj, tuple): + # Takes care of StateTuples. + return self._do_tuple(obj) + elif isinstance(obj, list): + # Takes care of TraitListObjects. + return self._do_list(obj) + elif isinstance(obj, dict): + # Takes care of TraitDictObjects. + return self._do_dict(obj) + elif hasattr(obj, "__dict__"): + return self._do_instance(obj) + + def _get_id(self, value): + try: + key = hash(value) + except TypeError: + key = id(value) + return key + + def _register(self, value): + key = self._get_id(value) + cache = self.obj_cache + idx = len(cache) + cache[key] = idx + return idx + + def _do_basic_type(self, value): + return value + + def _do_reference(self, value): + key = self._get_id(value) + idx = self.obj_cache[key] + return dict(type="reference", id=idx, data=None) + + def _do_instance(self, value): + # Flush out the traits. + self._flush_traits(value) + + # Setup the relative paths of FilePaths before dumping. + if self.file_name and isinstance(value, FilePath): + value.set_relative(self.file_name) + + # Get the initargs. + args = () + if hasattr(value, "__getinitargs__") and value.__getinitargs__: + args = value.__getinitargs__() + + # Get the object state. + if hasattr(value, "__get_pure_state__"): + state = value.__get_pure_state__() + elif hasattr(value, "__getstate__"): + state = value.__getstate__() + else: + state = value.__dict__ + + state.pop("__traits_version__", None) + + # Cache the args and state since they are likely to be gc'd. + self._misc_cache.extend([args, state]) + # Register and process. + idx = self._register(value) + args_data = self._do(args) + data = self._do(state) + + # Get the version of the object. + version = version_registry.get_version(value) + module = value.__class__.__module__ + class_name = value.__class__.__name__ + + return dict( + type="instance", + module=module, + class_name=class_name, + version=version, + id=idx, + initargs=args_data, + data=data, + ) + + def _do_state(self, value): + metadata = value.__metadata__ + args = metadata.get("initargs") + state = dict(value) + state.pop("__metadata__") + + self._misc_cache.extend([args, state]) + + idx = self._register(value) + args_data = self._do(args) + data = self._do(state) + + return dict( + type="instance", + module=metadata["module"], + class_name=metadata["class_name"], + version=metadata["version"], + id=idx, + initargs=args_data, + data=data, + ) + + def _do_tuple(self, value): + idx = self._register(value) + data = tuple([self._do(x) for x in value]) + return dict(type="tuple", id=idx, data=data) + + def _do_list(self, value): + idx = self._register(value) + data = [self._do(x) for x in value] + return dict(type="list", id=idx, data=data) + + def _do_dict(self, value): + idx = self._register(value) + vals = [self._do(x) for x in value.values()] + data = dict(zip(value.keys(), vals)) + return dict(type="dict", id=idx, data=data) + + def _do_numeric(self, value): + idx = self._register(value) + data = base64.encodebytes(gzip_string(numpy.ndarray.dumps(value))) + return dict(type="numeric", id=idx, data=data)
+ + +###################################################################### +# `StateUnpickler` class +###################################################################### +
[docs]class StateUnpickler: + """Unpickles the state of an object saved using StatePickler. + + Please note that unlike the standard Unpickler, no instances of + any user class are created. The data for the state is obtained + from the file or string, reference objects are setup to refer to + the same state value and this state is returned in the form + usually in the form of a dictionary. For example:: + + >>> class A: + ... def __init__(self): + ... self.attribute = 1 + ... + >>> a = A() + >>> p = StatePickler() + >>> s = p.dumps(a) + >>> up = StateUnpickler() + >>> state = up.loads_state(s) + >>> state.__class__.__name__ + 'State' + >>> state.attribute + 1 + >>> state.__metadata__ + {'class_name': 'A', + 'has_instance': True, + 'id': 0, + 'initargs': (), + 'module': '__main__', + 'type': 'instance', + 'version': [(('A', '__main__'), -1)]} + + Note that the state is actually a `State` instance and is + navigable just like the original object. The details of the + instance are stored in the `__metadata__` attribute. This is + highly convenient since it is possible for someone to view and + modify the state very easily. + """ + + def __init__(self): + self._clear() + self.type_map = { + "reference": self._do_reference, + "instance": self._do_instance, + "tuple": self._do_tuple, + "list": self._do_list, + "dict": self._do_dict, + "numeric": self._do_numeric, + } + +
[docs] def load_state(self, file): + """Returns the state of an object loaded from the pickled data + in the given file. + """ + try: + self.file_name = file.name + except AttributeError: + pass + data = pickle.load(file) + result = self._process(data) + return result
+ +
[docs] def loads_state(self, string): + """Returns the state of an object loaded from the pickled data + in the given string. + """ + data = pickle.loads(string) + result = self._process(data) + return result
+ + ###################################################################### + # Non-public methods + ###################################################################### + def _clear(self): + # The file from which we are being loaded. + self.file_name = "" + # Cache of the objects. + self._obj_cache = {} + # Paths to the instances. + self._instances = [] + # Caches the references. + self._refs = {} + # Numeric arrays. + self._numeric = {} + + def _set_has_instance(self, obj, value): + if isinstance(obj, State): + obj.__metadata__["has_instance"] = value + elif isinstance(obj, (StateDict, StateList, StateTuple)): + obj.has_instance = value + + def _process(self, data): + result = self._do(data) + + # Setup all the Numeric arrays. Do this first since + # references use this. + for key, (path, val) in self._numeric.items(): + if isinstance(result, StateTuple): + result = list(result) + exec("result%s = val" % path) + result = StateTuple(result) + else: + exec("result%s = val" % path) + + # Setup the references so they really are references. + for key, paths in self._refs.items(): + for path in paths: + x = self._obj_cache[key] + if isinstance(result, StateTuple): + result = list(result) + exec("result%s = x" % path) + result = StateTuple(result) + else: + exec("result%s = x" % path) + # if the reference is to an instance append its path. + if isinstance(x, State): + self._instances.append(path) + + # Now setup the 'has_instance' attribute. If 'has_instance' + # is True then the object contains an instance somewhere + # inside it. + for path in self._instances: + pth = path + while pth: + ns = {"result": result} + exec("val = result%s" % pth, ns, ns) + self._set_has_instance(ns["val"], True) + end = pth.rfind("[") + pth = pth[:end] + # Now make sure that the first element also has_instance. + self._set_has_instance(result, True) + return result + + def _do(self, data, path=""): + if type(data) is dict: + return self.type_map[data["type"]](data, path) + else: + return data + + def _do_reference(self, value, path): + id = value["id"] + if id in self._refs: + self._refs[id].append(path) + else: + self._refs[id] = [path] + return State(__metadata__=value) + + def _handle_file_path(self, value): + if ( + (value["class_name"] == "FilePath") + and ("file_path" in value["module"]) + and self.file_name + ): + data = value["data"]["data"] + fp = FilePath(data["rel_pth"]) + fp.set_absolute(self.file_name) + data["abs_pth"] = fp.abs_pth + + def _do_instance(self, value, path): + self._instances.append(path) + initargs = self._do( + value["initargs"], path + '.__metadata__["initargs"]' + ) + # Handle FilePaths. + self._handle_file_path(value) + + d = self._do(value["data"], path) + md = dict( + type="instance", + module=value["module"], + class_name=value["class_name"], + version=value["version"], + id=value["id"], + initargs=initargs, + has_instance=True, + ) + result = State(**d) + result.__metadata__ = md + self._obj_cache[value["id"]] = result + return result + + def _do_tuple(self, value, path): + res = [] + for i, x in enumerate(value["data"]): + res.append(self._do(x, path + "[%d]" % i)) + result = StateTuple(res) + self._obj_cache[value["id"]] = result + return result + + def _do_list(self, value, path): + result = StateList() + for i, x in enumerate(value["data"]): + result.append(self._do(x, path + "[%d]" % i)) + self._obj_cache[value["id"]] = result + return result + + def _do_dict(self, value, path): + result = StateDict() + for key, val in value["data"].items(): + result[key] = self._do(val, path + '["%s"]' % key) + self._obj_cache[value["id"]] = result + return result + + def _do_numeric(self, value, path): + data = value["data"] + if isinstance(data, str): + data = value["data"].encode("utf-8") + junk = gunzip_string(base64.decodebytes(data)) + result = pickle.loads(junk, encoding="bytes") + self._numeric[value["id"]] = (path, result) + self._obj_cache[value["id"]] = result + return result
+ + +###################################################################### +# `StateSetter` class +###################################################################### +
[docs]class StateSetter: + """This is a convenience class that helps a user set the + attributes of an object given its saved state. For instances it + checks to see if a `__set_pure_state__` method exists and calls + that when it sets the state. + """ + + def __init__(self): + # Stores the ids of instances already done. + self._instance_ids = [] + self.type_map = { + State: self._do_instance, + StateTuple: self._do_tuple, + StateList: self._do_list, + StateDict: self._do_dict, + } + +
[docs] def set(self, obj, state, ignore=None, first=None, last=None): + """Sets the state of the object. + + This is to be used as a means to simplify loading the state of + an object from its `__setstate__` method using the dictionary + describing its state. Note that before the state is set, the + registered handlers for the particular class are called in + order to upgrade the version of the state to the latest + version. + + Parameters + ---------- + + - obj : `object` + + The object whose state is to be set. If this is `None` + (default) then the object is created. + + - state : `dict` + + The dictionary representing the state of the object. + + - ignore : `list(str)` + + The list of attributes specified in this list are ignored + and the state of these attributes are not set (this excludes + the ones specified in `first` and `last`). If one specifies + a '*' then all attributes are ignored except the ones + specified in `first` and `last`. + + - first : `list(str)` + + The list of attributes specified in this list are set first (in + order), before any other attributes are set. + + - last : `list(str)` + + The list of attributes specified in this list are set last (in + order), after all other attributes are set. + + """ + if (not isinstance(state, State)) and state.__metadata__[ + "type" + ] != "instance": + raise StateSetterError( + "Can only set the attributes of an instance." + ) + + # Upgrade the state to the latest using the registry. + self._update_and_check_state(obj, state) + + self._register(obj) + + # This wierdness is needed since the state's own `keys` might + # be set to something else. + state_keys = list(dict.keys(state)) + state_keys.remove("__metadata__") + + if first is None: + first = [] + if last is None: + last = [] + + # Remove all the ignored keys. + if ignore: + if "*" in ignore: + state_keys = first + last + else: + for name in ignore: + try: + state_keys.remove(name) + except KeyError: + pass + + # Do the `first` attributes. + for key in first: + state_keys.remove(key) + self._do(obj, key, state[key]) + + # Remove the `last` attributes. + for key in last: + state_keys.remove(key) + + # Set the remaining attributes. + for key in state_keys: + self._do(obj, key, state[key]) + + # Do the last ones in order. + for key in last: + self._do(obj, key, state[key])
+ + ###################################################################### + # Non-public methods. + ###################################################################### + def _register(self, obj): + idx = id(obj) + if idx not in self._instance_ids: + self._instance_ids.append(idx) + + def _is_registered(self, obj): + return id(obj) in self._instance_ids + + def _has_instance(self, value): + """Given something (`value`) that is part of the state this + returns if the value has an instance embedded in it or not. + """ + if isinstance(value, State): + return True + elif isinstance(value, (StateDict, StateList, StateTuple)): + return value.has_instance + return False + + def _get_pure(self, value): + """Returns the Python representation of the object (usually a + list, tuple or dict) that has no instances embedded within it. + """ + result = value + if self._has_instance(value): + raise StateSetterError("Value has an instance: %s" % value) + if isinstance(value, (StateList, StateTuple)): + result = [self._get_pure(x) for x in value] + if isinstance(value, StateTuple): + result = tuple(result) + elif isinstance(value, StateDict): + result = {} + for k, v in value.items(): + result[k] = self._get_pure(v) + return result + + def _update_and_check_state(self, obj, state): + """Updates the state from the registry and then checks if the + object and state have same class. + """ + # Upgrade this state object to the latest using the registry. + # This is done before testing because updating may change the + # class name/module. + version_registry.registry.update(state) + + # Make sure object and state have the same class and module names. + metadata = state.__metadata__ + cls = obj.__class__ + if metadata["class_name"] != cls.__name__: + raise StateSetterError( + "Instance (%s) and state (%s) do not have the same class" + " name!" % (cls.__name__, metadata["class_name"]) + ) + if metadata["module"] != cls.__module__: + raise StateSetterError( + "Instance (%s) and state (%s) do not have the same module" + " name!" % (cls.__module__, metadata["module"]) + ) + + def _do(self, obj, key, value): + try: + getattr(obj, key) + except AttributeError: + raise StateSetterError( + "Object %s does not have an attribute called: %s" % (obj, key) + ) + + if isinstance(value, (State, StateDict, StateList, StateTuple)): + # Special handlers are needed. + if not self._has_instance(value): + result = self._get_pure(value) + setattr(obj, key, result) + elif isinstance(value, StateTuple): + setattr(obj, key, self._do_tuple(getattr(obj, key), value)) + else: + self._do_object(getattr(obj, key), value) + else: + setattr(obj, key, value) + + def _do_object(self, obj, state): + self.type_map[state.__class__](obj, state) + + def _do_instance(self, obj, state): + if self._is_registered(obj): + return + else: + self._register(obj) + + metadata = state.__metadata__ + if hasattr(obj, "__set_pure_state__"): + self._update_and_check_state(obj, state) + obj.__set_pure_state__(state) + elif "tvtk_classes" in metadata["module"]: + self._update_and_check_state(obj, state) + tmp = self._get_pure(StateDict(**state)) + del tmp["__metadata__"] + obj.__setstate__(tmp) + else: + # No need to update or check since `set` does it for us. + self.set(obj, state) + + def _do_tuple(self, obj, state): + if not self._has_instance(state): + return self._get_pure(state) + else: + result = list(obj) + self._do_list(result, state) + return tuple(result) + + def _do_list(self, obj, state): + if len(obj) == len(state): + for i in range(len(obj)): + if not self._has_instance(state[i]): + obj[i] = self._get_pure(state[i]) + elif isinstance(state[i], tuple): + obj[i] = self._do_tuple(state[i]) + else: + self._do_object(obj[i], state[i]) + else: + raise StateSetterError( + "Cannot set state of list of incorrect size." + ) + + def _do_dict(self, obj, state): + for key, value in state.items(): + if not self._has_instance(value): + obj[key] = self._get_pure(value) + elif isinstance(value, tuple): + obj[key] = self._do_tuple(value) + else: + self._do_object(obj[key], value)
+ + +###################################################################### +# Internal Utility functions. +###################################################################### +def _get_file_read(f): + if hasattr(f, "read"): + return f + else: + return open(f, "rb") + + +def _get_file_write(f): + if hasattr(f, "write"): + return f + else: + return open(f, "wb") + + +###################################################################### +# Utility functions. +###################################################################### +
[docs]def dump(value, file): + """Pickles the state of the object (`value`) into the passed file + (or file name). + """ + f = _get_file_write(file) + try: + StatePickler().dump(value, f) + finally: + f.flush() + if f is not file: + f.close()
+ + +
[docs]def dumps(value): + """Pickles the state of the object (`value`) and returns a string.""" + return StatePickler().dumps(value)
+ + +
[docs]def load_state(file): + """Returns the state of an object loaded from the pickled data in + the given file (or file name). + """ + f = _get_file_read(file) + try: + state = StateUnpickler().load_state(f) + finally: + if f is not file: + f.close() + return state
+ + +
[docs]def loads_state(string): + """Returns the state of an object loaded from the pickled data + in the given string. + """ + return StateUnpickler().loads_state(string)
+ + +
[docs]def get_state(obj): + """Returns the state of the object (usually as a dictionary). The + returned state may be used directy to set the state of the object + via `set_state`. + """ + s = dumps(obj) + return loads_state(s)
+ + +
[docs]def set_state(obj, state, ignore=None, first=None, last=None): + StateSetter().set(obj, state, ignore, first, last)
+ + +set_state.__doc__ = StateSetter.set.__doc__ + + +
[docs]def update_state(state): + """Given the state of an object, this updates the state to the + latest version using the handlers given in the version registry. + The state is modified in-place. + """ + version_registry.registry.update(state)
+ + +
[docs]def create_instance(state): + """Create an instance from the state if possible.""" + if (not isinstance(state, State)) and ( + "class_name" not in state.__metadata__ + ): + raise StateSetterError("No class information in state") + metadata = state.__metadata__ + class_name = metadata.get("class_name") + mod_name = metadata.get("module") + if "tvtk_classes" in mod_name: + # FIXME: This sort of special-case is probably indicative of something + # that needs more thought, plus it makes it tought to decide whether + # this component depends on tvtk! + from tvtk.api import tvtk + + return getattr(tvtk, class_name)() + + initargs = metadata["initargs"] + if initargs.has_instance: + raise StateUnpicklerError("Cannot unpickle non-trivial initargs") + + __import__(mod_name, globals(), locals(), class_name) + mod = sys.modules[mod_name] + cls = getattr(mod, class_name) + return cls(*initargs)
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/persistence/updater.html b/_modules/apptools/persistence/updater.html new file mode 100644 index 000000000..8d32271d2 --- /dev/null +++ b/_modules/apptools/persistence/updater.html @@ -0,0 +1,174 @@ + + + + + + + apptools.persistence.updater — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.persistence.updater

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+
+
+def __replacement_setstate__(self, state):
+    """"""
+    state = self.__updater__(state)
+    self.__dict__.update(state)
+
+
+
[docs]class Updater: + + """An abstract class to provide functionality common to the updaters.""" + +
[docs] def get_latest(self, module, name): + """The refactorings dictionary contains mappings between old and new + module names. Since we only bump the version number one increment + there is only one possible answer. + """ + if hasattr(self, "refactorings"): + module = self.strip(module) + name = self.strip(name) + # returns the new module and name if it exists otherwise defaults + # to using the original module and name + module, name = self.refactorings.get( + (module, name), (module, name) + ) + + return module, name
+ +
[docs] def strip(self, string): + # Who would have thought that pickle would pass us + # names with \013 on the end? Is this after the files have + # manually edited? + if ord(string[-1:]) == 13: + return string[:-1] + + return string
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/persistence/version_registry.html b/_modules/apptools/persistence/version_registry.html new file mode 100644 index 000000000..040c4e8eb --- /dev/null +++ b/_modules/apptools/persistence/version_registry.html @@ -0,0 +1,239 @@ + + + + + + + apptools.persistence.version_registry — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.persistence.version_registry

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+"""A version registry that manages handlers for different state
+versions.
+"""
+
+
+# Standard library imports.
+import sys
+import inspect
+import logging
+
+
+logger = logging.getLogger(__name__)
+
+
+######################################################################
+# Utility functions.
+######################################################################
+
[docs]def get_version(obj): + """Walks the class hierarchy and obtains the versions of the + various classes and returns a list of tuples of the form + ((class_name, module), version) in reverse order of the MRO. + """ + res = [] + for cls in inspect.getmro(obj.__class__): + class_name, module = cls.__name__, cls.__module__ + if module in ["__builtin__"]: + # No point in versioning builtins. + continue + try: + version = cls.__version__ + except AttributeError: + version = -1 + res.append(((class_name, module), version)) + res.reverse() + return res
+ + +###################################################################### +# `HandlerRegistry` class. +###################################################################### +
[docs]class HandlerRegistry: + """A simple version conversion handler registry. Classes register + handlers in order to convert the state version to the latest + version. When an object's state is about to be set, the `update` + method of the registy is called. This in turn calls any handlers + registered for the class/module and this handler is then called + with the state and the version of the state. The state is + modified in-place by the handlers. + """ + + def __init__(self): + # The version conversion handlers. + # Key: (class_name, module), value: handler + self.handlers = {} + +
[docs] def register(self, class_name, module, handler): + """Register `handler` that handles versioning for class having + class name (`class_name`) and module name (`module`). The + handler function will be passed the state and its version to fix. + """ + key = (class_name, module) + if key in self.handlers: + msg = "Overwriting version handler for (%s, %s)" % (key[0], key[1]) + logger.warn(msg) + self.handlers[(class_name, module)] = handler
+ +
[docs] def unregister(self, class_name, module): + """Unregisters any handlers for a class and module.""" + self.handlers.pop((class_name, module))
+ +
[docs] def update(self, state): + """Updates the given state using the handlers. Note that the + state is modified in-place. + """ + if (not self.handlers) or (not hasattr(state, "__metadata__")): + return + versions = state.__metadata__["version"] + for ver in versions: + key = ver[0] + try: + self.handlers[key](state, ver[1]) + except KeyError: + pass
+ + +def _create_registry(): + """Creates a reload safe, singleton registry.""" + registry = None + for key in sys.modules.keys(): + if "version_registry" in key: + mod = sys.modules[key] + if hasattr(mod, "registry"): + registry = mod.registry + break + if not registry: + registry = HandlerRegistry() + return registry + + +# The singleton registry. +registry = _create_registry() +
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/persistence/versioned_unpickler.html b/_modules/apptools/persistence/versioned_unpickler.html new file mode 100644 index 000000000..667bf909c --- /dev/null +++ b/_modules/apptools/persistence/versioned_unpickler.html @@ -0,0 +1,342 @@ + + + + + + + apptools.persistence.versioned_unpickler — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.persistence.versioned_unpickler

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+# Standard library imports
+from pickle import _Unpickler as Unpickler
+from pickle import UnpicklingError, BUILD
+import logging
+from types import GeneratorType
+
+# Enthought library imports
+from apptools.persistence.updater import __replacement_setstate__
+
+
+logger = logging.getLogger(__name__)
+
+
+##############################################################################
+# class 'NewUnpickler'
+##############################################################################
+
[docs]class NewUnpickler(Unpickler): + """An unpickler that implements a two-stage pickling process to make it + possible to unpickle complicated Python object hierarchies where the + unserialized state of an object depends on the state of other objects in + the same pickle. + """ + +
[docs] def load(self, max_pass=-1): + """Read a pickled object representation from the open file. + + Return the reconstituted object hierarchy specified in the file. + """ + # List of objects to be unpickled. + self.objects = [] + + # We overload the load_build method. + dispatch = self.dispatch + dispatch[BUILD[0]] = NewUnpickler.load_build + + # call the super class' method. + ret = Unpickler.load(self) + self.initialize(max_pass) + self.objects = [] + + # Reset the Unpickler's dispatch table. + dispatch[BUILD[0]] = Unpickler.load_build + return ret
+ +
[docs] def initialize(self, max_pass): + # List of (object, generator) tuples that initialize objects. + generators = [] + + # Execute object's initialize to setup the generators. + for obj in self.objects: + if hasattr(obj, "__initialize__") and callable(obj.__initialize__): + ret = obj.__initialize__() + if isinstance(ret, GeneratorType): + generators.append((obj, ret)) + elif ret is not None: + raise UnpicklingError( + "Unexpected return value from " + "__initialize__. %s returned %s" % (obj, ret) + ) + + # Ensure a maximum number of passes + if max_pass < 0: + max_pass = len(generators) + + # Now run the generators. + count = 0 + while len(generators) > 0: + count += 1 + if count > max_pass: + not_done = [x[0] for x in generators] + msg = """Reached maximum pass count %s. You may have + a deadlock! The following objects are + uninitialized: %s""" % ( + max_pass, + not_done, + ) + raise UnpicklingError(msg) + for o, g in generators[:]: + try: + next(g) + except StopIteration: + generators.remove((o, g))
+ + # Make this a class method since dispatch is a class variable. + # Otherwise, supposing the initial VersionedUnpickler.load call (which + # would have overloaded the load_build method) makes a pickle.load call at + # some point, we would have the dispatch still pointing to + # NewPickler.load_build whereas the object being passed in will be an + # Unpickler instance, causing a TypeError. +
[docs] def load_build(cls, obj): + # Just save the instance in the list of objects. + if isinstance(obj, NewUnpickler): + obj.objects.append(obj.stack[-2]) + Unpickler.load_build(obj)
+ + load_build = classmethod(load_build)
+ + +
[docs]class VersionedUnpickler(NewUnpickler): + """This class reads in a pickled file created at revision version 'n' + and then applies the transforms specified in the updater class to + generate a new set of objects which are at revision version 'n+1'. + + I decided to keep the loading of the updater out of this generic class + because we will want updaters to be generated for each plugin's type + of project. + + This ensures that the VersionedUnpickler can remain ignorant about the + actual version numbers - all it needs to do is upgrade one release. + """ + + def __init__(self, file, updater=None): + Unpickler.__init__(self, file) + self.updater = updater + +
[docs] def find_class(self, module, name): + """Overridden method from Unpickler. + + NB __setstate__ is not called until later. + """ + + if self.updater: + # check to see if this class needs to be mapped to a new class + # or module name + original_module, original_name = module, name + module, name = self.updater.get_latest(module, name) + + # load the class... + klass = self.import_name(module, name) + + # add the updater.... TODO - why the old name? + self.add_updater(original_module, original_name, klass) + + else: + # there is no updater so we will be reading in an up to date + # version of the file... + try: + klass = Unpickler.find_class(self, module, name) + except Exception: + logger.error("Looking for [%s] [%s]" % (module, name)) + logger.exception( + "Problem using default unpickle functionality" + ) + + # restore the original __setstate__ if necessary + fn = getattr(klass, "__setstate_original__", False) + if fn: + setattr(klass, "__setstate__", fn) + + return klass
+ +
[docs] def add_updater(self, module, name, klass): + """If there is an updater defined for this class we will add it to the + class as the __setstate__ method. + """ + + fn = self.updater.setstates.get((module, name), False) + + if fn: + # move the existing __setstate__ out of the way + self.backup_setstate(module, klass) + + # add the updater into the class + setattr(klass, "__updater__", fn) + + # hook up our __setstate__ which updates self.__dict__ + setattr(klass, "__setstate__", __replacement_setstate__) + + else: + pass
+ +
[docs] def backup_setstate(self, module, klass): + """If the class has a user defined __setstate__ we back it up.""" + if getattr(klass, "__setstate__", False): + + if getattr(klass, "__setstate_original__", False): + # don't overwrite the original __setstate__ + name = "__setstate__%s" % self.updater.__class__ + else: + # backup the original __setstate__ which we will restore + # and run later when we have finished updating the class + name = "__setstate_original__" + + method = getattr(klass, "__setstate__") + setattr(klass, name, method) + + else: + # the class has no __setstate__ method so do nothing + pass
+ +
[docs] def import_name(self, module, name): + """ + If the class is needed for the latest version of the application then + it should presumably exist. + + If the class no longer exists then we should perhaps return + a proxy of the class. + + If the persisted file is at v1 say and the application is at v3 then + objects that are required for v1 and v2 do not have to exist they only + need to be placeholders for the state during an upgrade. + """ + module = __import__(module, globals(), locals(), [name]) + return vars(module)[name]
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/preferences/i_preferences.html b/_modules/apptools/preferences/i_preferences.html new file mode 100644 index 000000000..1b5559d48 --- /dev/null +++ b/_modules/apptools/preferences/i_preferences.html @@ -0,0 +1,310 @@ + + + + + + + apptools.preferences.i_preferences — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.preferences.i_preferences

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" The interface for a node in a preferences hierarchy. """
+
+
+# Enthought library imports.
+from traits.api import Instance, Interface, Str
+
+
+
[docs]class IPreferences(Interface): + """ The interface for a node in a preferences hierarchy. """ + + # The absolute path to this node from the root node (the empty string if + # this node *is* the root node). + path = Str + + # The parent node (None if this node *is* the root node). + parent = Instance("IPreferences") + + # The name of the node relative to its parent (the empty string if this + # node *is* the root node). + name = Str + + #### Methods where 'path' refers to a preference #### + +
[docs] def get(self, path, default=None, inherit=False): + """Get the value of the preference at the specified path. + + If no value exists for the path (or any part of the path does not + exist) then return the default value. + + Preference values are *always* returned as strings. + + e.g:: + + preferences.set('acme.ui.bgcolor', 'blue') + preferences.get('acme.ui.bgcolor') -> 'blue' + + preferences.set('acme.ui.width', 100) + preferences.get('acme.ui.width') -> '100' + + preferences.set('acme.ui.visible', True) + preferences.get('acme.ui.visible') -> 'True' + + If 'inherit' is True then we allow 'inherited' preference values. + + e.g. If we are looking up:: + + 'acme.ui.widget.bgcolor' + + and it does not exist then we will also try:: + + 'acme.ui.bgcolor' + 'acme.bgcolor' + 'bgcolor' + + Raise a 'ValueError' exception if the path is the empty string. + + """
+ +
[docs] def remove(self, path): + """Remove the preference at the specified path. + + Does nothing if no value exists for the path (or any part of the path + does not exist. + + Raise a 'ValueError' exception if the path is the empty string. + + e.g.:: + + preferences.remove('acme.ui.bgcolor') + + """
+ +
[docs] def set(self, path, value): + """Set the value of the preference at the specified path. + + Any missing nodes are created automatically. + + Primitive Python types can be set, but preferences are *always* + stored and returned as strings. + + e.g:: + + preferences.set('acme.ui.bgcolor', 'blue') + preferences.get('acme.ui.bgcolor') -> 'blue' + + preferences.set('acme.ui.width', 100) + preferences.get('acme.ui.width') -> '100' + + preferences.set('acme.ui.visible', True) + preferences.get('acme.ui.visible') -> 'True' + + Raise a 'ValueError' exception if the path is the empty string. + + """
+ + #### Methods where 'path' refers to a node #### + +
[docs] def clear(self, path=""): + """Remove all preference from the node at the specified path. + + If the path is the empty string (the default) then remove the + preferences in *this* node. + + This does not affect any of the node's children. + + e.g. To clear the preferences out of a node directly:: + + preferences.clear() + + Or to clear the preferences of a node at a given path:: + + preferences.clear('acme.ui') + + """
+ +
[docs] def keys(self, path=""): + """Return the preference keys of the node at the specified path. + + If the path is the empty string (the default) then return the + preference keys of *this* node. + + e.g:: + + keys = preferences.keys('acme.ui') + + """
+ +
[docs] def node(self, path=""): + """Return the node at the specified path. + + If the path is the empty string (the default) then return *this* node. + + Any missing nodes are created automatically. + + e.g:: + + node = preferences.node('acme.ui') + bgcolor = node.get('bgcolor') + + """
+ +
[docs] def node_exists(self, path=""): + """Return True if the node at the specified path exists + + If the path is the empty string (the default) then return True. + + e.g:: + + exists = preferences.exists('acme.ui') + + """
+ +
[docs] def node_names(self, path=""): + """Return the names of the children of the node at the specified path. + + If the path is the empty string (the default) then return the names of + the children of *this* node. + + e.g:: + + names = preferences.node_names('acme.ui') + + """
+ + #### Persistence methods #### + +
[docs] def flush(self): + """Force any changes in the node to the backing store. + + This includes any changes to the node's descendants. + + """
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/preferences/package_globals.html b/_modules/apptools/preferences/package_globals.html new file mode 100644 index 000000000..e477fc92e --- /dev/null +++ b/_modules/apptools/preferences/package_globals.html @@ -0,0 +1,167 @@ + + + + + + + apptools.preferences.package_globals — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.preferences.package_globals

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" Package-scope globals.
+
+The default preferences node is currently used by 'PreferencesHelper' and
+'PreferencesBinding' instances if no specific preferences node is set. This
+makes it easy for them to access the root node of an application-wide
+preferences hierarchy.
+
+"""
+
+
+# The default preferences node.
+_default_preferences = None
+
+
+
[docs]def get_default_preferences(): + """ Get the default preferences node. """ + + return _default_preferences
+ + +
[docs]def set_default_preferences(default_preferences): + """ Set the default preferences node. """ + + global _default_preferences + + _default_preferences = default_preferences + + # For convenience. + return _default_preferences
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/preferences/preference_binding.html b/_modules/apptools/preferences/preference_binding.html new file mode 100644 index 000000000..5cb1678c2 --- /dev/null +++ b/_modules/apptools/preferences/preference_binding.html @@ -0,0 +1,299 @@ + + + + + + + apptools.preferences.preference_binding — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.preferences.preference_binding

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" A binding between a trait on an object and a preference value. """
+
+
+# Enthought library imports.
+from traits.api import Any, HasTraits, Instance, Str, Undefined
+
+# Local imports.
+from .i_preferences import IPreferences
+from .package_globals import get_default_preferences
+
+
+
[docs]class PreferenceBinding(HasTraits): + """ A binding between a trait on an object and a preference value. """ + + #### 'PreferenceBinding' interface ######################################## + + # The object that we are binding the preference to. + obj = Any + + # The preferences node used by the binding. If this trait is not set then + # the package-global default preferences node is used (and if that is not + # set then the binding won't work ;^) + preferences = Instance(IPreferences) + + # The path to the preference value. + preference_path = Str + + # The name of the trait that we are binding the preference to. + trait_name = Str + + ########################################################################### + # 'object' interface. + ########################################################################### + + def __init__(self, **traits): + """ Constructor. """ + + super(PreferenceBinding, self).__init__(**traits) + + # Initialize the object's trait from the preference value. + self._set_trait(notify=False) + + # Wire-up trait change handlers etc. + self._initialize() + + ########################################################################### + # 'PreferenceBinding' interface. + ########################################################################### + + #### Trait initializers ################################################### + + def _preferences_default(self): + """ Trait initializer. """ + + return get_default_preferences() + + ########################################################################### + # Private interface. + ########################################################################### + + #### Trait change handlers ################################################ + + def _on_trait_changed(self, obj, trait_name, old, new): + """ Dynamic trait change handler. """ + + self.preferences.set(self.preference_path, new) + + #### Other observer pattern listeners ##################################### + + def _preferences_listener(self, node, key, old, new): + """ Listener called when a preference value is changed. """ + + components = self.preference_path.split(".") + if key == components[-1]: + self._set_trait() + + #### Methods ############################################################## + + # fixme: This method is mostly duplicated in 'PreferencesHelper' (the only + # difference is the line that gets the handler). + def _get_value(self, trait_name, value): + """Get the actual value to set. + + This method makes sure that any required work is done to convert the + preference value from a string. + + """ + + handler = self.obj.trait(trait_name).handler + + # If the trait type is 'Str' then we just take the raw value. + if type(handler) is Str: + pass + + # If the trait type is 'Str' then we convert the raw value. + elif type(handler) is Str: + value = str(value) + + # Otherwise, we eval it! + else: + try: + value = eval(value) + + # If the eval fails then there is probably a syntax error, but + # we will let the handler validation throw the exception. + except Exception: + pass + + return handler.validate(self, trait_name, value) + + def _initialize(self): + """ Wire-up trait change handlers etc. """ + + # Listen for the object's trait being changed. + self.obj.on_trait_change(self._on_trait_changed, self.trait_name) + + # Listen for the preference value being changed. + components = self.preference_path.split(".") + node = ".".join(components[:-1]) + + self.preferences.add_preferences_listener( + self._preferences_listener, node + ) + + def _set_trait(self, notify=True): + """ Set the object's trait to the value of the preference. """ + + value = self.preferences.get(self.preference_path, Undefined) + if value is not Undefined: + trait_value = self._get_value(self.trait_name, value) + traits = {self.trait_name: trait_value} + + self.obj.trait_set(trait_change_notify=notify, **traits)
+ + +# Factory function for creating bindings. +
[docs]def bind_preference(obj, trait_name, preference_path, preferences=None): + """ Create a new preference binding. """ + + # This may seem a bit wierd, but we manually build up a dictionary of + # the traits that need to be set at the time the 'PreferenceBinding' + # instance is created. + # + # This is because we only want to set the 'preferences' trait iff one + # is explicitly specified. If we passed it in with the default argument + # value of 'None' then it counts as 'setting' the trait which prevents + # the binding instance from defaulting to the package-global preferences. + # Also, if we try to set the 'preferences' trait *after* construction time + # then it is too late as the binding initialization is done in the + # constructor (we could of course split that out, which may be the 'right' + # way to do it ;^). + traits = { + "obj": obj, + "trait_name": trait_name, + "preference_path": preference_path, + } + + if preferences is not None: + traits["preferences"] = preferences + + return PreferenceBinding(**traits)
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/preferences/preferences.html b/_modules/apptools/preferences/preferences.html new file mode 100644 index 000000000..380b6bf59 --- /dev/null +++ b/_modules/apptools/preferences/preferences.html @@ -0,0 +1,687 @@ + + + + + + + apptools.preferences.preferences — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.preferences.preferences

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" The default implementation of a node in a preferences hierarchy. """
+
+# Standard library imports.
+import logging
+import threading
+
+# Enthought library imports.
+from traits.api import Any, Callable, Dict, HasTraits, Instance, List
+from traits.api import Property, Str, Undefined, provides
+
+# Local imports.
+from .i_preferences import IPreferences
+
+
+# Logging.
+logger = logging.getLogger(__name__)
+
+
+
[docs]@provides(IPreferences) +class Preferences(HasTraits): + """ The default implementation of a node in a preferences hierarchy. """ + + #### 'IPreferences' interface ############################################# + + # The absolute path to this node from the root node (the empty string if + # this node *is* the root node). + path = Property(Str) + + # The parent node (None if this node *is* the root node). + parent = Instance(IPreferences) + + # The name of the node relative to its parent (the empty string if this + # node *is* the root node). + name = Str + + #### 'Preferences' interface ############################################## + + # The default name of the file used to persist the preferences (if no + # filename is passed in to the 'load' and 'save' methods, then this is + # used instead). + filename = Str + + #### Protected 'Preferences' interface #################################### + + # A lock to make access to the node thread-safe. + # + # fixme: There *should* be no need to declare this as a trait, but if we + # don't then we have problems using nodes in the preferences manager UI. + # It is something to do with 'cloning' the node for use in a 'modal' traits + # UI... Hmmm... + _lk = Any + + # The node's children. + _children = Dict(Str, IPreferences) + + # The node's preferences. + _preferences = Dict(Str, Any) + + # Listeners for changes to the node's preferences. + # + # The callable must take 4 arguments, e.g:: + # + # listener(node, key, old, new) + _preferences_listeners = List(Callable) + + ########################################################################### + # 'object' interface. + ########################################################################### + + def __init__(self, **traits): + """ Constructor. """ + + # A lock to make access to the '_children', '_preferences' and + # '_preferences_listeners' traits thread-safe. + self._lk = threading.Lock() + + # Base class constructor. + super(Preferences, self).__init__(**traits) + + # If a filename has been specified then load the preferences from it. + if len(self.filename) > 0: + self.load() + + ########################################################################### + # 'IPreferences' interface. + ########################################################################### + + #### Trait properties ##################################################### + + def _get_path(self): + """ Property getter. """ + + names = [] + + node = self + while node.parent is not None: + names.append(node.name) + node = node.parent + + names.reverse() + + return ".".join(names) + + #### Methods ############################################################## + + #### Methods where 'path' refers to a preference #### + +
[docs] def get(self, path, default=None, inherit=False): + """ Get the value of the preference at the specified path. """ + + if len(path) == 0: + raise ValueError("empty path") + + components = path.split(".") + + # If there is only one component in the path then the operation takes + # place in this node. + if len(components) == 1: + value = self._get(path, Undefined) + + # Otherwise, find the next node and pass the rest of the path to that. + else: + node = self._get_child(components[0]) + if node is not None: + value = node.get(".".join(components[1:]), Undefined) + + else: + value = Undefined + + # If inherited values are allowed then try those as well. + # + # e.g. 'acme.ui.widget.bgcolor' + # 'acme.ui.bgcolor' + # 'acme.bgcolor' + # 'bgcolor' + while inherit and value is Undefined and len(components) > 1: + # Remove the penultimate component... + # + # e.g. 'acme.ui.widget.bgcolor' -> 'acme.ui.bgcolor' + del components[-2] + + # ... and try that. + value = self.get(".".join(components), default=Undefined) + + if value is Undefined: + value = default + + return value
+ +
[docs] def remove(self, path): + """ Remove the preference at the specified path. """ + + if len(path) == 0: + raise ValueError("empty path") + + components = path.split(".") + + # If there is only one component in the path then the operation takes + # place in this node. + if len(components) == 1: + self._remove(path) + + # Otherwise, find the next node and pass the rest of the path to that. + else: + node = self._get_child(components[0]) + if node is not None: + node.remove(".".join(components[1:]))
+ +
[docs] def set(self, path, value): + """ Set the value of the preference at the specified path. """ + + if len(path) == 0: + raise ValueError("empty path") + + components = path.split(".") + + # If there is only one component in the path then the operation takes + # place in this node. + if len(components) == 1: + self._set(path, value) + + # Otherwise, find the next node (creating it if it doesn't exist) + # and pass the rest of the path to that. + else: + node = self._node(components[0]) + node.set(".".join(components[1:]), value)
+ + #### Methods where 'path' refers to a node #### + +
[docs] def clear(self, path=""): + """ Remove all preferences from the node at the specified path. """ + + # If the path is empty then the operation takes place in this node. + if len(path) == 0: + self._clear() + + # Otherwise, find the next node and pass the rest of the path to that. + else: + components = path.split(".") + + node = self._get_child(components[0]) + if node is not None: + node.clear(".".join(components[1:]))
+ +
[docs] def keys(self, path=""): + """ Return the preference keys of the node at the specified path. """ + + # If the path is empty then the operation takes place in this node. + if len(path) == 0: + keys = self._keys() + + # Otherwise, find the next node and pass the rest of the path to that. + else: + components = path.split(".") + + node = self._get_child(components[0]) + if node is not None: + keys = node.keys(".".join(components[1:])) + + else: + keys = [] + + return keys
+ +
[docs] def node(self, path=""): + """ Return the node at the specified path. """ + + # If the path is empty then the operation takes place in this node. + if len(path) == 0: + node = self + + # Otherwise, find the next node and pass the rest of the path to that. + else: + components = path.split(".") + + node = self._node(components[0]) + node = node.node(".".join(components[1:])) + + return node
+ +
[docs] def node_exists(self, path=""): + """ Return True if the node at the specified path exists. """ + + # If the path is empty then the operation takes place in this node. + if len(path) == 0: + exists = True + + # Otherwise, find the next node and pass the rest of the path to that. + else: + components = path.split(".") + + node = self._get_child(components[0]) + if node is not None: + exists = node.node_exists(".".join(components[1:])) + + else: + exists = False + + return exists
+ +
[docs] def node_names(self, path=""): + """Return the names of the children of the node at the specified path. + """ + + # If the path is empty then the operation takes place in this node. + if len(path) == 0: + names = self._node_names() + + # Otherwise, find the next node and pass the rest of the path to that. + else: + components = path.split(".") + + node = self._get_child(components[0]) + if node is not None: + names = node.node_names(".".join(components[1:])) + + else: + names = [] + + return names
+ + #### Persistence methods #### + +
[docs] def flush(self): + """Force any changes in the node to the backing store. + + This includes any changes to the node's descendants. + + """ + + self.save()
+ + ########################################################################### + # 'Preferences' interface. + ########################################################################### + + #### Listener methods #### + +
[docs] def add_preferences_listener(self, listener, path=""): + """ Add a listener for changes to a node's preferences. """ + + # If the path is empty then the operation takes place in this node. + if len(path) == 0: + self._add_preferences_listener(listener) + + # Otherwise, find the next node and pass the rest of the path to that. + else: + components = path.split(".") + + node = self._node(components[0]) + node.add_preferences_listener(listener, ".".join(components[1:]))
+ +
[docs] def remove_preferences_listener(self, listener, path=""): + """ Remove a listener for changes to a node's preferences. """ + + # If the path is empty then the operation takes place in this node. + if len(path) == 0: + self._remove_preferences_listener(listener) + + # Otherwise, find the next node and pass the rest of the path to that. + else: + components = path.split(".") + + node = self._node(components[0]) + node.remove_preferences_listener( + listener, ".".join(components[1:]) + )
+ + #### Persistence methods #### + +
[docs] def load(self, file_or_filename=None): + """Load preferences from a file. + + This is a *merge* operation i.e. the contents of the file are added to + the node. + + This implementation uses 'ConfigObj' files. + + """ + + if file_or_filename is None: + file_or_filename = self.filename + + logger.debug("loading preferences from <%s>", file_or_filename) + + # Do the import here so that we don't make 'ConfigObj' a requirement + # if preferences aren't ever persisted (or a derived class chooses to + # use a different persistence mechanism). + from configobj import ConfigObj + + config_obj = ConfigObj(file_or_filename, encoding="utf-8") + + # 'name' is the section name, 'value' is a dictionary containing the + # name/value pairs in the section (the actual preferences ;^). + for name, value in config_obj.items(): + # Create/get the node from the section name. + components = name.split(".") + + node = self + for component in components: + node = node._node(component) + + # Add the contents of the section to the node. + self._add_dictionary_to_node(node, value)
+ +
[docs] def save(self, file_or_filename=None): + """Save the node's preferences to a file. + + This implementation uses 'ConfigObj' files. + + """ + + if file_or_filename is None: + file_or_filename = self.filename + + # If no file or filename is specified then don't save the preferences! + if len(file_or_filename) > 0: + # Do the import here so that we don't make 'ConfigObj' a + # requirement if preferences aren't ever persisted (or a derived + # class chooses to use a different persistence mechanism). + from configobj import ConfigObj + + logger.debug("saving preferences to <%s>", file_or_filename) + + config_obj = ConfigObj(file_or_filename, encoding="utf-8") + self._add_node_to_dictionary(self, config_obj) + config_obj.write()
+ + ########################################################################### + # Protected 'Preferences' interface. + # + # These are the only methods that should access the protected '_children' + # and '_preferences' traits. This helps make it easy to subclass this class + # to create other implementations (all the subclass has to do is to + # implement these protected methods). + # + ########################################################################### + + def _add_dictionary_to_node(self, node, dictionary): + """ Add the contents of a dictionary to a node's preferences. """ + + self._lk.acquire() + node._preferences.update(dictionary) + self._lk.release() + + def _add_node_to_dictionary(self, node, dictionary): + """ Add a node's preferences to a dictionary. """ + + # This method never manipulates the '_preferences' trait directly. + # Instead it does eveything via the other protected methods and hence + # doesn't need to grab the lock. + if len(node._keys()) > 0: + dictionary[node.path] = {} + for key in node._keys(): + dictionary[node.path][key] = node._get(key) + + for name in node._node_names(): + self._add_node_to_dictionary(node._get_child(name), dictionary) + + def _add_preferences_listener(self, listener): + """ Add a listener for changes to thisnode's preferences. """ + + self._lk.acquire() + self._preferences_listeners.append(listener) + self._lk.release() + + def _clear(self): + """ Remove all preferences from this node. """ + + self._lk.acquire() + self._preferences.clear() + self._lk.release() + + def _create_child(self, name): + """ Create a child of this node with the specified name. """ + + self._lk.acquire() + child = self._children[name] = Preferences(name=name, parent=self) + self._lk.release() + + return child + + def _get(self, key, default=None): + """ Get the value of a preference in this node. """ + + self._lk.acquire() + value = self._preferences.get(key, default) + self._lk.release() + + return value + + def _get_child(self, name): + """Return the child of this node with the specified name. + + Return None if no such child exists. + + """ + + self._lk.acquire() + child = self._children.get(name) + self._lk.release() + + return child + + def _keys(self): + """ Return the preference keys of this node. """ + + self._lk.acquire() + keys = list(self._preferences.keys()) + self._lk.release() + + return keys + + def _node(self, name): + """Return the child of this node with the specified name. + + Create the child node if it does not exist. + + """ + + node = self._get_child(name) + if node is None: + node = self._create_child(name) + + return node + + def _node_names(self): + """ Return the names of the children of this node. """ + + self._lk.acquire() + node_names = list(self._children.keys()) + self._lk.release() + + return node_names + + def _remove(self, name): + """ Remove a preference value from this node. """ + + self._lk.acquire() + if name in self._preferences: + del self._preferences[name] + self._lk.release() + + def _remove_preferences_listener(self, listener): + """ Remove a listener for changes to the node's preferences. """ + + self._lk.acquire() + if listener in self._preferences_listeners: + self._preferences_listeners.remove(listener) + self._lk.release() + + def _set(self, key, value): + """ Set the value of a preference in this node. """ + + # everything must be unicode encoded so that ConfigObj configuration + # can properly serialize the data. Python str are supposed to be ASCII + # encoded. + value = str(value) + + self._lk.acquire() + old = self._preferences.get(key) + self._preferences[key] = value + + # If the value is unchanged then don't call the listeners! + if old == value: + listeners = [] + + else: + listeners = self._preferences_listeners[:] + self._lk.release() + + for listener in listeners: + listener(self, key, old, value) + + ########################################################################### + # Debugging interface. + ########################################################################### + +
[docs] def dump(self, indent=""): + """ Dump the preferences hierarchy to stdout. """ + + if indent == "": + print() + + print(indent, "Node(%s)" % self.name, self._preferences) + indent += " " + + for child in self._children.values(): + child.dump(indent)
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/preferences/preferences_helper.html b/_modules/apptools/preferences/preferences_helper.html new file mode 100644 index 000000000..982d2dd8c --- /dev/null +++ b/_modules/apptools/preferences/preferences_helper.html @@ -0,0 +1,336 @@ + + + + + + + apptools.preferences.preferences_helper — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.preferences.preferences_helper

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" An object that can be initialized from a preferences node. """
+
+
+# Standard library imports.
+import logging
+
+# Enthought library imports.
+from traits.api import HasTraits, Instance, Str
+
+# Local imports.
+from .i_preferences import IPreferences
+from .package_globals import get_default_preferences
+
+
+# Logging.
+logger = logging.getLogger(__name__)
+
+
+
[docs]class PreferencesHelper(HasTraits): + """ A base class for objects that can be initialized from a preferences + node. + + Additional traits defined on subclasses will be listened to. Changes + are then synchronized with the preferences. Note that mutations on nested + containers e.g. List(List(Str)) cannot be synchronized and should be + avoided. + """ + + #### 'PreferencesHelper' interface ######################################## + + # The preferences node used by the helper. If this trait is not set then + # the package-global default preferences node is used. + # + # fixme: This introduces a 'sneaky' global reference to the preferences + # node! + preferences = Instance(IPreferences) + + # The path to the preference node that contains the preferences that we + # use to initialize instances of this class. + preferences_path = Str + + ########################################################################### + # 'object' interface. + ########################################################################### + + def __init__(self, **traits): + """ Constructor. """ + + super(PreferencesHelper, self).__init__(**traits) + + # Initialize the object's traits from the preferences node. + if self.preferences: + self._initialize(self.preferences) + + ########################################################################### + # Private interface. + ########################################################################### + + #### Trait initializers ################################################### + + def _preferences_default(self): + """ Trait initializer. """ + + # If no specific preferences node is set then we use the package-wide + # global node. + return get_default_preferences() + + #### Trait change handlers ################################################ + + def _anytrait_changed(self, trait_name, old, new): + """ Static trait change handler. """ + + if self.preferences is None: + return + + if self._is_preference_trait(trait_name): + self.preferences.set("%s.%s" % (self._get_path(), trait_name), new) + + # If the trait was a list or dict '_items' trait then just treat it as + # if the entire list or dict was changed. + elif trait_name.endswith('_items'): + trait_name = trait_name[:-6] + if self._is_preference_trait(trait_name): + self.preferences.set( + '%s.%s' % (self._get_path(), trait_name), + getattr(self, trait_name) + ) + + # If the change refers to a trait defined on this class, then + # the trait is not a preference trait and we do nothing. + + def _preferences_changed(self, old, new): + """ Static trait change handler. """ + + # Stop listening to the old preferences node. + if old is not None: + old.remove_preferences_listener( + self._preferences_changed_listener, self._get_path() + ) + + if new is not None: + # Initialize with the new preferences node (this also adds a + # listener for preferences being changed in the new node). + self._initialize(new, notify=True) + + #### Other observer pattern listeners ##################################### + + def _preferences_changed_listener(self, node, key, old, new): + """ Listener called when a preference value is changed. """ + + if key in self.trait_names(): + setattr(self, key, self._get_value(key, new)) + + #### Methods ############################################################## + + def _get_path(self): + """ Return the path to our preferences node. """ + + if len(self.preferences_path) > 0: + path = self.preferences_path + + else: + path = getattr(self, "PREFERENCES_PATH", None) + if path is None: + raise SystemError("no preferences path, %s" % self) + + else: + logger.warn('DEPRECATED: use "preferences_path" %s' % self) + + return path + + def _get_value(self, trait_name, value): + """Get the actual value to set. + + This method makes sure that any required work is done to convert the + preference value from a string. Str traits or those with the metadata + 'is_str=True' will just be passed the string itself. + + """ + + trait = self.trait(trait_name) + handler = trait.handler + + # If the trait type is 'Str' then we just take the raw value. + if isinstance(handler, Str) or trait.is_str: + pass + + # Otherwise, we eval it! + else: + try: + value = eval(value) + + # If the eval fails then there is probably a syntax error, but + # we will let the handler validation throw the exception. + except Exception: + pass + + if handler.validate is not None: + # Any traits have a validator of None. + validated = handler.validate(self, trait_name, value) + else: + validated = value + + return validated + + def _initialize(self, preferences, notify=False): + """ Initialize the object's traits from the preferences node. """ + + path = self._get_path() + keys = preferences.keys(path) + + traits_to_set = {} + for trait_name in self.trait_names(): + if trait_name in keys: + key = "%s.%s" % (path, trait_name) + value = self._get_value(trait_name, preferences.get(key)) + traits_to_set[trait_name] = value + + self.trait_set(trait_change_notify=notify, **traits_to_set) + + # Listen for changes to the node's preferences. + preferences.add_preferences_listener( + self._preferences_changed_listener, path + ) + + # fixme: Pretty much duplicated in 'PreferencesPage' (except for the + # class name of course!). + def _is_preference_trait(self, trait_name): + """ Return True if a trait represents a preference value. """ + + if ( + trait_name.startswith("_") + or trait_name.endswith("_") + or trait_name in PreferencesHelper.class_traits() + ): + return False + + return trait_name in self.editable_traits()
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/preferences/scoped_preferences.html b/_modules/apptools/preferences/scoped_preferences.html new file mode 100644 index 000000000..e2937244e --- /dev/null +++ b/_modules/apptools/preferences/scoped_preferences.html @@ -0,0 +1,582 @@ + + + + + + + apptools.preferences.scoped_preferences — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.preferences.scoped_preferences

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" A preferences node that adds the notion of preferences scopes. """
+
+# Standard library imports.
+from os.path import join
+
+# Enthought library imports.
+from traits.etsconfig.api import ETSConfig
+from traits.api import List, Str, Undefined
+
+# Local imports.
+from .i_preferences import IPreferences
+from .preferences import Preferences
+
+
+
[docs]class ScopedPreferences(Preferences): + """A preferences node that adds the notion of preferences scopes. + + Scopes provide a way to access preferences in a precedence order, usually + depending on where they came from, for example from the command-line, + or set by the user in a preferences file, or the defaults (set by the + developer). + + By default, this class provides two scopes - 'application' which is + persistent and 'default' which is not. + + Path names passed to 'ScopedPreferences' nodes can be either:: + + a) a preference path as used in a standard 'Preferences' node, e.g:: + + 'acme.widget.bgcolor'. + + In this case the operation either takes place in the primary scope + (for operations such as 'set' etc), or on all scopes in precedence + order (for operations such as 'get' etc). + + or + + b) a preference path that refers to a specific scope e.g:: + + 'default/acme.widget.bgcolor' + + In this case the operation takes place *only* in the specified scope. + + There is one drawback to this scheme. If you want to access a scope node + itself via the 'clear', 'keys', 'node', 'node_exists' or 'node_names' + methods then you have to append a trailing '/' to the path. Without that, + the node would try to perform the operation in the primary scope. + + e.g. To get the names of the children of the 'application' scope, use:: + + scoped.node_names('application/') + + If you did this:: + + scoped.node_names('application') + + Then the node would get the primary scope and try to find its child node + called 'application'. + + Of course you can just get the scope via:: + + application_scope = scoped.get_scope('application') + + and then call whatever methods you like on it - which is definitely more + intentional and is highly recommended:: + + application_scope.node_names() + + """ + + #### 'ScopedPreferences' interface ######################################## + + # The file that the application scope preferences are stored in. + # + # Defaults to:- + # + # os.path.join(ETSConfig.application_home, 'preferences.ini') + application_preferences_filename = Str + + # The scopes (in the order that they should be searched when looking up + # preferences). + # + # By default, this class provides two scopes - 'application' which is + # persistent and 'default' which is not. + scopes = List(IPreferences) + + # The name of the 'primary' scope. + # + # This is the scope that operations take place in if no scope is specified + # in a given path (for the 'get' operation, if no scope is specified the + # operation takes place in *all* scopes in order of precedence). If this is + # the empty string (the default) then the primary scope is the first scope + # in the 'scopes' list. + primary_scope_name = Str + + ########################################################################### + # 'IPreferences' protocol. + ########################################################################### + + #### Methods where 'path' refers to a preference #### + +
[docs] def get(self, path, default=None, inherit=False): + """ Get the value of the preference at the specified path. """ + + if len(path) == 0: + raise ValueError("empty path") + + # If the path contains a specific scope then lookup the preference in + # just that scope. + if self._path_contains_scope(path): + scope_name, path = self._parse_path(path) + nodes = [self._get_scope(scope_name)] + + # Otherwise, try each scope in turn (i.e. in order of precedence). + else: + nodes = self.scopes + + # Try all nodes first (without inheritance even if specified). + value = self._get(path, Undefined, nodes, inherit=False) + if value is Undefined: + if inherit: + value = self._get(path, default, nodes, inherit=True) + + else: + value = default + + return value
+ +
[docs] def remove(self, path): + """ Remove the preference at the specified path. """ + + if len(path) == 0: + raise ValueError("empty path") + + # If the path contains a specific scope then remove the preference from + # just that scope. + if self._path_contains_scope(path): + scope_name, path = self._parse_path(path) + node = self._get_scope(scope_name) + + # Otherwise, remove the preference from the primary scope. + else: + node = self._get_primary_scope() + + node.remove(path)
+ +
[docs] def set(self, path, value): + """ Set the value of the preference at the specified path. """ + + if len(path) == 0: + raise ValueError("empty path") + + # If the path contains a specific scope then set the value in that + # scope. + if self._path_contains_scope(path): + scope_name, path = self._parse_path(path) + node = self._get_scope(scope_name) + + # Otherwise, set the value in the primary scope. + else: + node = self._get_primary_scope() + + node.set(path, value)
+ + #### Methods where 'path' refers to a node #### + +
[docs] def clear(self, path=""): + """ Remove all preference from the node at the specified path. """ + + # If the path contains a specific scope then remove the preferences + # from a node in that scope. + if self._path_contains_scope(path): + scope_name, path = self._parse_path(path) + node = self._get_scope(scope_name) + + # Otherwise, remove the preferences from a node in the primary scope. + else: + node = self._get_primary_scope() + + return node.clear(path)
+ +
[docs] def keys(self, path=""): + """ Return the preference keys of the node at the specified path. """ + + # If the path contains a specific scope then get the keys of the node + # in that scope. + if self._path_contains_scope(path): + scope_name, path = self._parse_path(path) + nodes = [self._get_scope(scope_name)] + + # Otherwise, merge the keys of the node in all scopes. + else: + nodes = self.scopes + + keys = set() + for node in nodes: + keys.update(node.node(path).keys()) + + return list(keys)
+ +
[docs] def node(self, path=""): + """ Return the node at the specified path. """ + + if len(path) == 0: + node = self + + else: + # If the path contains a specific scope then we get the node that + # scope. + if self._path_contains_scope(path): + scope_name, path = self._parse_path(path) + node = self._get_scope(scope_name) + + # Otherwise, get the node from the primary scope. + else: + node = self._get_primary_scope() + + node = node.node(path) + + return node
+ +
[docs] def node_exists(self, path=""): + """ Return True if the node at the specified path exists. """ + + # If the path contains a specific scope then look for the node in that + # scope. + if self._path_contains_scope(path): + scope_name, path = self._parse_path(path) + node = self._get_scope(scope_name) + + # Otherwise, look for the node in the primary scope. + else: + node = self._get_primary_scope() + + return node.node_exists(path)
+ +
[docs] def node_names(self, path=""): + """Return the names of the children of the node at the specified path. + """ + + # If the path contains a specific scope then get the names of the + # children of the node in that scope. + if self._path_contains_scope(path): + scope_name, path = self._parse_path(path) + nodes = [self._get_scope(scope_name)] + + # Otherwise, merge the names of the children of the node in all scopes. + else: + nodes = self.scopes + + names = set() + for node in nodes: + names.update(node.node(path).node_names()) + + return list(names)
+ + ########################################################################### + # 'Preferences' protocol. + ########################################################################### + + #### Listener methods #### + +
[docs] def add_preferences_listener(self, listener, path=""): + """ Add a listener for changes to a node's preferences. """ + + # If the path contains a specific scope then add a preferences listener + # to the node in that scope. + if self._path_contains_scope(path): + scope_name, path = self._parse_path(path) + nodes = [self._get_scope(scope_name)] + + # Otherwise, add a preferences listener to the node in all scopes. + else: + nodes = self.scopes + + for node in nodes: + node.add_preferences_listener(listener, path)
+ +
[docs] def remove_preferences_listener(self, listener, path=""): + """ Remove a listener for changes to a node's preferences. """ + + # If the path contains a specific scope then remove a preferences + # listener from the node in that scope. + if self._path_contains_scope(path): + scope_name, path = self._parse_path(path) + nodes = [self._get_scope(scope_name)] + + # Otherwise, remove a preferences listener from the node in all scopes. + else: + nodes = self.scopes + + for node in nodes: + node.remove_preferences_listener(listener, path)
+ + #### Persistence methods #### + +
[docs] def load(self, file_or_filename=None): + """Load preferences from a file. + + This loads the preferences into the primary scope. + + fixme: I'm not sure it is worth providing an implentation here. I + think it would be better to encourage people to explicitly reference + a particular scope. + + """ + + if file_or_filename is None and len(self.filename) > 0: + file_or_filename = self.filename + + node = self._get_primary_scope() + node.load(file_or_filename)
+ +
[docs] def save(self, file_or_filename=None): + """Save the node's preferences to a file. + + This asks each scope in turn to save its preferences. + + If a file or filename is specified then it is only passed to the + primary scope. + + """ + + if file_or_filename is None and len(self.filename) > 0: + file_or_filename = self.filename + + self._get_primary_scope().save(file_or_filename) + for scope in self.scopes: + if scope is not self._get_primary_scope(): + scope.save()
+ + ########################################################################### + # 'ScopedPreferences' protocol. + ########################################################################### + + def _application_preferences_filename_default(self): + """ Trait initializer. """ + + return join(ETSConfig.application_home, "preferences.ini") + + # fixme: In hindsight, I don't think this class should have provided + # default scopes. This should have been an 'abstract' class that could + # be subclassed by classes providing specific scopes. + def _scopes_default(self): + """ Trait initializer. """ + + scopes = [ + Preferences( + name="application", + filename=self.application_preferences_filename, + ), + Preferences(name="default"), + ] + + return scopes + +
[docs] def get_scope(self, scope_name): + """Return the scope with the specified name. + + Return None if no such scope exists. + + """ + + for scope in self.scopes: + if scope_name == scope.name: + break + + else: + scope = None + + return scope
+ + ########################################################################### + # Private protocol. + ########################################################################### + + def _get(self, path, default, nodes, inherit): + """ Get a preference from a list of nodes. """ + + for node in nodes: + value = node.get(path, Undefined, inherit) + if value is not Undefined: + break + + else: + value = default + + return value + + def _get_scope(self, scope_name): + """Return the scope with the specified name. + + Raise a 'ValueError' is no such scope exists. + + """ + + scope = self.get_scope(scope_name) + if scope is None: + raise ValueError("no such scope %s" % scope_name) + + return scope + + def _get_primary_scope(self): + """Return the primary scope. + + By default, this is the first scope. + + """ + + if len(self.primary_scope_name) > 0: + scope = self._get_scope(self.primary_scope_name) + + else: + scope = self.scopes[0] + + return scope + + def _path_contains_scope(self, path): + """ Return True if the path contains a scope component. """ + + return "/" in path + + def _parse_path(self, path): + """ 'Parse' the path into two parts, the scope name and the rest! """ + + components = path.split("/") + + return components[0], "/".join(components[1:]) + + ########################################################################### + # Debugging interface. + ########################################################################### + +
[docs] def dump(self, indent=""): + """ Dump the preferences hierarchy to stdout. """ + + if indent == "": + print() + + print(indent, "Node(%s)" % self.name, self._preferences) + indent += " " + + for child in self.scopes: + child.dump(indent)
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/preferences/ui/i_preferences_page.html b/_modules/apptools/preferences/ui/i_preferences_page.html new file mode 100644 index 000000000..bada9bfa3 --- /dev/null +++ b/_modules/apptools/preferences/ui/i_preferences_page.html @@ -0,0 +1,162 @@ + + + + + + + apptools.preferences.ui.i_preferences_page — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.preferences.ui.i_preferences_page

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" The interface for pages in a preferences dialog. """
+
+
+# Enthought library imports.
+from traits.api import Interface, Str
+
+
+
[docs]class IPreferencesPage(Interface): + """ The interface for pages in a preferences dialog. """ + + # The page's category (e.g. 'General/Appearence'). The empty string means + # that this is a top-level page. + category = Str + + # The page's help identifier (optional). If a help Id *is* provided then + # there will be a 'Help' button shown on the preference page. + help_id = Str + + # The page name (this is what is shown in the preferences dialog). + name = Str + +
[docs] def apply(self): + """ Apply the page's preferences. """ + pass
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/preferences/ui/preferences_manager.html b/_modules/apptools/preferences/ui/preferences_manager.html new file mode 100644 index 000000000..c084f3ff2 --- /dev/null +++ b/_modules/apptools/preferences/ui/preferences_manager.html @@ -0,0 +1,418 @@ + + + + + + + apptools.preferences.ui.preferences_manager — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.preferences.ui.preferences_manager

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" The preferences manager. """
+
+
+# Enthought library imports.
+from traits.api import HasTraits, Instance, List, Property, Bool
+from traitsui.api import Handler, HSplit, Item, TreeEditor
+from traitsui.api import TreeNode, View, HTMLEditor
+from traitsui.menu import Action
+
+# Local imports.
+from .preferences_node import PreferencesNode
+from .preferences_page import PreferencesPage
+
+
+# A tree editor for preferences nodes.
+tree_editor = TreeEditor(
+    nodes=[
+        TreeNode(
+            node_for=[PreferencesNode],
+            auto_open=False,
+            children="children",
+            label="name",
+            rename=False,
+            copy=False,
+            delete=False,
+            insert=False,
+            menu=None,
+        ),
+    ],
+    editable=False,
+    hide_root=True,
+    selected="selected_node",
+    show_icons=False,
+)
+
+
+
[docs]class PreferencesHelpWindow(HasTraits): + """ Container class to present a view with string info. """ + +
[docs] def traits_view(self): + """ Default view to show for this class. """ + args = [] + kw_args = { + "title": "Preferences Page Help", + "buttons": ["OK"], + "width": 800, + "height": 800, + "resizable": True, + "id": "apptools.preferences.ui.preferences_manager.help", + } + to_show = {} + + for name, trait_obj in self.traits().items(): + if name != "trait_added" and name != "trait_modified": + to_show[name] = trait_obj.help + for name in to_show: + args.append(Item(name, style="readonly", editor=HTMLEditor())) + + view = View(*args, **kw_args) + return view
+ + +
[docs]class PreferencesManagerHandler(Handler): + """ The traits UI handler for the preferences manager. """ + + model = Instance(HasTraits) + + ########################################################################### + # 'Handler' interface. + ########################################################################### + +
[docs] def apply(self, info): + """ Handle the **Apply** button being clicked. """ + + info.object.apply()
+ +
[docs] def init(self, info): + """ Initialize the controls of a user interface. """ + + # Select the first node in the tree (if there is one). + self._select_first_node(info) + + return super(PreferencesManagerHandler, self).init(info)
+ +
[docs] def close(self, info, is_ok): + """ Close a dialog-based user interface. """ + + if is_ok: + info.object.apply() + + return super(PreferencesManagerHandler, self).close(info, is_ok)
+ +
[docs] def preferences_help(self, info): + """ Custom preferences help panel. The Traits help doesn't work.""" + current_page = self.model.selected_page + to_show = {} + for trait_name, trait_obj in current_page.traits().items(): + if hasattr(trait_obj, "show_help") and trait_obj.show_help: + to_show[trait_name] = trait_obj.help + + help_obj = PreferencesHelpWindow(**to_show) + help_obj.edit_traits(kind="livemodal")
+ + ########################################################################### + # Private interface. + ########################################################################### + + def _select_first_node(self, info): + """ Select the first node in the tree (if there is one). """ + + root = info.object.root + + if len(root.children) > 0: + node = root.children[0] + info.object.selected_page = node.page
+ + +
[docs]class PreferencesManager(HasTraits): + """ The preferences manager. """ + + # All of the preferences pages known to the manager. + pages = List(PreferencesPage) + + # The root of the preferences node tree. + root = Property(Instance(PreferencesNode)) + + # The preferences node currently selected in the tree. + selected_node = Instance(PreferencesNode) + + # The preferences associated with the currently selected preferences node. + selected_page = Instance(PreferencesPage) + + # Should the custom Info button be shown? If this is True, then an + # Info button is shown that pops up a trait view with an HTML entry + # for each trait of the *selected_page* with the metadata 'show_help' + # set to True. + show_help = Bool(False) + + # Should the Apply button be shown? + show_apply = Bool(False) + + #### Traits UI views ###################################################### + +
[docs] def traits_view(self): + """ Default traits view for this class. """ + + help_action = Action(name="Info", action="preferences_help") + + buttons = ["OK", "Cancel"] + + if self.show_apply: + buttons = ["Apply"] + buttons + if self.show_help: + buttons = [help_action] + buttons + + # A tree editor for preferences nodes. + tree_editor = TreeEditor( + nodes=[ + TreeNode( + node_for=[PreferencesNode], + auto_open=False, + children="children", + label="name", + rename=False, + copy=False, + delete=False, + insert=False, + menu=None, + ), + ], + on_select=self._selection_changed, + editable=False, + hide_root=True, + selected="selected_node", + show_icons=False, + ) + + view = View( + HSplit( + Item( + name="root", + editor=tree_editor, + show_label=False, + width=250, + ), + Item( + name="selected_page", + # editor = WidgetEditor(), + show_label=False, + width=450, + style="custom", + ), + ), + buttons=buttons, + handler=PreferencesManagerHandler(model=self), + resizable=True, + title="Preferences", + width=0.3, + height=0.3, + kind="modal", + ) + self.selected_page = self.pages[0] + return view
+ + ########################################################################### + # 'PreferencesManager' interface. + ########################################################################### + + #### Trait properties ##################################################### + + def _get_root(self): + """ Property getter. """ + + # Sort the pages by the length of their category path. This makes it + # easy for us to create the preference hierarchy as we know that all of + # a node's ancestors will have already been created. + def sort_key(a): + # We have the guard because if the category is the empty string + # then split will still return a list containing one item (and not + # the empty list). + if len(a.category) == 0: + len_a = 0 + + else: + len_a = len(a.category.split("/")) + + return len_a + + self.pages.sort(key=sort_key) + + # Create a corresponding preference node hierarchy (the root of the + # hierachy is NOT displayed in the preference dialog). + # + # fixme: Currently we have to create a dummy page for the root node + # event though the root does not get shown in the tree! + root_page = PreferencesPage(name="Root", preferences_path="root") + root = PreferencesNode(page=root_page) + + for page in self.pages: + # Get the page's parent node. + parent = self._get_parent(root, page) + + # Add a child node representing the page. + parent.append(PreferencesNode(page=page)) + + return root + + #### Trait change handlers ################################################ + + def _selection_changed(self, new_selection): + self.selected_node = new_selection + + def _selected_node_changed(self, new): + """ Static trait change handler. """ + if self.selected_node: + self.selected_page = self.selected_node.page + + #### Methods ############################################################## + +
[docs] def apply(self): + """ Apply all changes made in the manager. """ + + for page in self.pages: + page.apply()
+ + ########################################################################### + # Private interface. + ########################################################################### + + def _get_parent(self, root, page): + """ Return the page's parent preference node. """ + + parent = root + + if len(page.category) > 0: + components = page.category.split("/") + for component in components: + parent = parent.lookup(component) + + return parent
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/preferences/ui/preferences_node.html b/_modules/apptools/preferences/ui/preferences_node.html new file mode 100644 index 000000000..2aed6f3a0 --- /dev/null +++ b/_modules/apptools/preferences/ui/preferences_node.html @@ -0,0 +1,221 @@ + + + + + + + apptools.preferences.ui.preferences_node — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.preferences.ui.preferences_node

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" Abstract base class for a node in a preferences dialog. """
+
+# Enthought library imports.
+from traits.api import Delegate, Instance
+
+# Local imports.
+from .i_preferences_page import IPreferencesPage
+from .tree_item import TreeItem
+
+
+
[docs]class PreferencesNode(TreeItem): + """Abstract base class for a node in a preferences dialog. + + A preferences node has a name and an image which are used to represent the + node in a preferences dialog (usually in the form of a tree). + + """ + + #### 'PreferenceNode' interface ########################################### + + # The page's help identifier (optional). If a help Id *is* provided then + # there will be a 'Help' button shown on the preference page. + help_id = Delegate("page") + + # The page name (this is what is shown in the preferences dialog. + name = Delegate("page") + + # The page that we are a node for. + page = Instance(IPreferencesPage) + + ########################################################################### + # 'object' interface. + ########################################################################### + + def __str__(self): + """ Returns the string representation of the item. """ + + if self.page is None: + s = "root" + + else: + s = self.page.name + + return s + + __repr__ = __str__ + + ########################################################################### + # 'PreferencesNode' interface. + ########################################################################### + +
[docs] def create_page(self, parent): + """ Creates the preference page for this node. """ + + return self.page.create_control(parent)
+ +
[docs] def lookup(self, name): + """Returns the child of this node with the specified Id. + + Returns None if no such child exists. + + """ + + for node in self.children: + if node.name == name: + break + + else: + node = None + + return node
+ + ########################################################################### + # Debugging interface. + ########################################################################### + +
[docs] def dump(self, indent=""): + """ Pretty-print the node to stdout. """ + + print(indent, "Node", str(self)) + + for child in self.children: + child.dump(indent + " ")
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/preferences/ui/preferences_page.html b/_modules/apptools/preferences/ui/preferences_page.html new file mode 100644 index 000000000..b9aa66f87 --- /dev/null +++ b/_modules/apptools/preferences/ui/preferences_page.html @@ -0,0 +1,233 @@ + + + + + + + apptools.preferences.ui.preferences_page — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.preferences.ui.preferences_page

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" A page in a preferences dialog. """
+
+
+# Enthought library imports.
+from apptools.preferences.api import PreferencesHelper
+from traits.api import Any, Dict, Str, provides
+
+# Local imports.
+from .i_preferences_page import IPreferencesPage
+
+
+
[docs]@provides(IPreferencesPage) +class PreferencesPage(PreferencesHelper): + """ A page in a preferences dialog. """ + + #### 'IPreferencesPage' interface ######################################### + + # The page's category (e.g. 'General/Appearance'). The empty string means + # that this is a top-level page. + category = Str + + # DEPRECATED: The help_id was never fully implemented, and it's been + # over two years (now 4/2009). The original goal was for the the Help + # button to automatically appear and connect to a help page with a + # help_id. Not removing the trait right now to avoid breaking code + # that may be checking for this. + # + # Use PreferencesManager.show_help and trait show_help metadata instead. + help_id = Str + + # The page name (this is what is shown in the preferences dialog. + name = Str + + #### Private interface #################################################### + + # The traits UI that represents the page. + _ui = Any + + # A dictionary containing the traits that have been changed since the + # last call to 'apply'. + _changed = Dict + + ########################################################################### + # 'IPreferencesPage' interface. + ########################################################################### + +
[docs] def apply(self): + """ Apply the page's preferences. """ + + path = self._get_path() + + for trait_name, value in self._changed.items(): + if self._is_preference_trait(trait_name): + self.preferences.set("%s.%s" % (path, trait_name), value) + + self._changed.clear()
+ + ########################################################################### + # Private interface. + ########################################################################### + + #### Trait change handlers ################################################ + + def _anytrait_changed(self, trait_name, old, new): + """Static trait change handler. + + This is an important override! In the base-class when a trait is + changed the preferences node is updated too. Here, we stop that from + happening and just make a note of what changes have been made. The + preferences node gets updated when the 'apply' method is called. + + """ + + if self._is_preference_trait(trait_name): + self._changed[trait_name] = new + elif trait_name.endswith("_items"): + # If the trait was a list or dict '_items' trait then just treat it + # as if the entire list or dict was changed. + trait_name = trait_name[:-6] + if self._is_preference_trait(trait_name): + self._changed[trait_name] = getattr(self, trait_name) + + # fixme: Pretty much duplicated in 'PreferencesHelper' (except for the + # class name of course!). + def _is_preference_trait(self, trait_name): + """ Return True if a trait represents a preference value. """ + + if ( + trait_name.startswith("_") + or trait_name.endswith("_") + or trait_name in PreferencesPage.class_traits() + ): + return False + + return trait_name in self.editable_traits()
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/preferences/ui/tree_item.html b/_modules/apptools/preferences/ui/tree_item.html new file mode 100644 index 000000000..ffcc109ca --- /dev/null +++ b/_modules/apptools/preferences/ui/tree_item.html @@ -0,0 +1,269 @@ + + + + + + + apptools.preferences.ui.tree_item — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.preferences.ui.tree_item

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" A generic base-class for items in a tree data structure.
+
+An example:-
+
+root = TreeItem(data='Root')
+
+fruit = TreeItem(data='Fruit')
+fruit.append(TreeItem(data='Apple', allows_children=False))
+fruit.append(TreeItem(data='Orange', allows_children=False))
+fruit.append(TreeItem(data='Pear', allows_children=False))
+root.append(fruit)
+
+veg = TreeItem(data='Veg')
+veg.append(TreeItem(data='Carrot', allows_children=False))
+veg.append(TreeItem(data='Cauliflower', allows_children=False))
+veg.append(TreeItem(data='Sprout', allows_children=False))
+root.append(veg)
+
+"""
+
+
+# Enthought library imports.
+from traits.api import Any, Bool, HasTraits, Instance, List, Property
+
+
+
[docs]class TreeItem(HasTraits): + """ A generic base-class for items in a tree data structure. """ + + #### 'TreeItem' interface ################################################# + + # Does this item allow children? + allows_children = Bool(True) + + # The item's children. + children = List(Instance("TreeItem")) + + # Arbitrary data associated with the item. + data = Any + + # Does the item have any children? + has_children = Property(Bool) + + # The item's parent. + parent = Instance("TreeItem") + + ########################################################################### + # 'object' interface. + ########################################################################### + + def __str__(self): + """ Returns the informal string representation of the object. """ + + if self.data is None: + s = "" + + else: + s = str(self.data) + + return s + + ########################################################################### + # 'TreeItem' interface. + ########################################################################### + + #### Properties ########################################################### + + # has_children + def _get_has_children(self): + """ True iff the item has children. """ + + return len(self.children) != 0 + + #### Methods ############################################################## + +
[docs] def append(self, child): + """Appends a child to this item. + + This removes the child from its current parent (if it has one). + + """ + + return self.insert(len(self.children), child)
+ +
[docs] def insert(self, index, child): + """Inserts a child into this item at the specified index. + + This removes the child from its current parent (if it has one). + + """ + + if child.parent is not None: + child.parent.remove(child) + + child.parent = self + self.children.insert(index, child) + + return child
+ +
[docs] def remove(self, child): + """ Removes a child from this item. """ + + child.parent = None + self.children.remove(child) + + return child
+ +
[docs] def insert_before(self, before, child): + """Inserts a child into this item before the specified item. + + This removes the child from its current parent (if it has one). + + """ + + index = self.children.index(before) + + self.insert(index, child) + + return (index, child)
+ +
[docs] def insert_after(self, after, child): + """Inserts a child into this item after the specified item. + + This removes the child from its current parent (if it has one). + + """ + + index = self.children.index(after) + + self.insert(index + 1, child) + + return (index, child)
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/preferences/ui/widget_editor.html b/_modules/apptools/preferences/ui/widget_editor.html new file mode 100644 index 000000000..431d64f87 --- /dev/null +++ b/_modules/apptools/preferences/ui/widget_editor.html @@ -0,0 +1,232 @@ + + + + + + + apptools.preferences.ui.widget_editor — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.preferences.ui.widget_editor

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+""" An instance editor that allows total control over widget creation. """
+
+
+# Enthought library imports.
+from traits.api import Any
+from traitsui.api import EditorFactory
+from traitsui.toolkit import toolkit_object
+Editor = toolkit_object('editor:Editor')
+
+
+class _WidgetEditor(Editor):
+    """ An instance editor that allows total control over widget creation. """
+
+    #### '_WidgetEditor' interface ############################################
+
+    # The toolkit-specific parent of the editor.
+    parent = Any
+
+    ###########################################################################
+    # '_WidgetEditor' interface.
+    ###########################################################################
+
+    def init(self, parent):
+        """ Initialize the editor. """
+
+        self.parent = parent
+
+        # fixme: What if there are no pages?!?
+        page = self.object.pages[0]
+
+        # Create the editor's control.
+        self.control = page.create_control(parent)
+
+        # Listen for the page being changed.
+        self.object.on_trait_change(self._on_page_changed, "selected_page")
+
+    def dispose(self):
+        """ Dispose of the editor. """
+
+        page = self.object.selected_page
+        page.destroy_control()
+
+    def update_editor(self):
+        """ Update the editor. """
+
+        pass
+
+    ###########################################################################
+    # Private interface.
+    ###########################################################################
+
+    def _on_page_changed(self, obj, trait_name, old, new):
+        """ Dynamic trait change handler. """
+
+        if old is not None:
+            old.destroy_control()
+
+        if new is not None:
+            self.control = new.create_control(self.parent)
+
+
+
[docs]class WidgetEditor(EditorFactory): + """ A factory widget editors. """ + + ########################################################################### + # 'object' interface. + ########################################################################### + + def __call__(self, *args, **traits): + """ Call the object. """ + + return self.trait_set(**traits) + + ########################################################################### + # 'EditorFactory' interface. + ########################################################################### + +
[docs] def simple_editor(self, ui, object, name, description, parent): + """ Create a simple editor. """ + + editor = _WidgetEditor( + parent, + factory=self, + ui=ui, + object=object, + name=name, + description=description, + ) + + return editor
+ + custom_editor = simple_editor + text_editor = simple_editor + readonly_editor = simple_editor
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/scripting/package_globals.html b/_modules/apptools/scripting/package_globals.html new file mode 100644 index 000000000..34096d085 --- /dev/null +++ b/_modules/apptools/scripting/package_globals.html @@ -0,0 +1,159 @@ + + + + + + + apptools.scripting.package_globals — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.scripting.package_globals

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+"""
+Globals for the scripting package.
+"""
+
+
+# The global recorder.
+_recorder = None
+
+
+
[docs]def get_recorder(): + """Return the global recorder. Does not create a new one if none + exists. + """ + global _recorder + return _recorder
+ + +
[docs]def set_recorder(rec): + """Set the global recorder instance.""" + global _recorder + _recorder = rec
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/scripting/recordable.html b/_modules/apptools/scripting/recordable.html new file mode 100644 index 000000000..324ccca9d --- /dev/null +++ b/_modules/apptools/scripting/recordable.html @@ -0,0 +1,186 @@ + + + + + + + apptools.scripting.recordable — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.scripting.recordable

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+"""
+Decorator to mark functions and methods as recordable.
+"""
+
+from .package_globals import get_recorder
+
+
+# Guard to ensure that only the outermost recordable call is recorded
+# and nested calls ignored.
+_outermost_call = True
+
+
+
[docs]def recordable(func): + """A decorator that wraps a function into one that is recordable. + + This will record the function only if the global recorder has been + set via a `set_recorder` function call. + """ + + def _wrapper(*args, **kw): + """A wrapper returned to replace the decorated function.""" + global _outermost_call + + # Boolean to specify if the method was recorded or not. + record = False + if _outermost_call: + # Get the recorder. + rec = get_recorder() + if rec is not None: + _outermost_call = False + # Record the method if recorder is available. + record = True + try: + result = rec.record_function(func, args, kw) + finally: + _outermost_call = True + if not record: + # If the method was not recorded, just call it. + result = func(*args, **kw) + + return result + + # Mimic the actual function. + _wrapper.__name__ = func.__name__ + _wrapper.__doc__ = func.__doc__ + _wrapper.__dict__.update(func.__dict__) + + return _wrapper
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/scripting/recorder.html b/_modules/apptools/scripting/recorder.html new file mode 100644 index 000000000..2c84d2f66 --- /dev/null +++ b/_modules/apptools/scripting/recorder.html @@ -0,0 +1,874 @@ + + + + + + + apptools.scripting.recorder — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.scripting.recorder

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+"""
+Code to support recording to a readable and executable Python script.
+
+FIXME:
+    - Support for dictionaries?
+"""
+
+import builtins
+import warnings
+
+from traits.api import (
+    HasTraits,
+    List,
+    Str,
+    Dict,
+    Bool,
+    Property,
+    Int,
+    Instance,
+)
+from traits.util.camel_case import camel_case_to_python
+
+
+###############################################################################
+# `_RegistryData` class.
+###############################################################################
+class _RegistryData(HasTraits):
+    # Object's script ID
+    script_id = Property(Str)
+
+    # Path to object in object hierarchy.
+    path = Property(Str)
+
+    # Parent data for this object if any.
+    parent_data = Instance("_RegistryData", allow_none=True)
+
+    # The name of the trait on the parent which is this object.
+    trait_name_on_parent = Str("")
+
+    # List of traits we are listening for on this object.
+    names = List(Str)
+
+    # Nested recordable instances on the object.
+    sub_recordables = List(Str)
+
+    # List of traits that are lists.
+    list_names = List(Str)
+
+    _script_id = Str("")
+
+    ###########################################################################
+    # Non-public interface.
+    ###########################################################################
+    def _get_path(self):
+        pdata = self.parent_data
+        path = ""
+        if pdata is not None:
+            pid = pdata.script_id
+            ppath = pdata.path
+            tnop = self.trait_name_on_parent
+            if "[" in tnop:
+                # If the object is a nested object through an iterator,
+                # we instantiate it and don't refer to it through the
+                # path, this makes scripting convenient.
+                if len(ppath) == 0:
+                    path = pid + "." + tnop
+                else:
+                    path = ppath + "." + tnop
+            else:
+                path = ppath + "." + tnop
+
+        return path
+
+    def _get_script_id(self):
+        sid = self._script_id
+        if len(sid) == 0:
+            pdata = self.parent_data
+            sid = pdata.script_id + "." + self.trait_name_on_parent
+        return sid
+
+    def _set_script_id(self, id):
+        self._script_id = id
+
+
+###############################################################################
+# `RecorderError` class.
+###############################################################################
+
[docs]class RecorderError(Exception): + pass
+ + +############################################################################### +# `Recorder` class. +############################################################################### +
[docs]class Recorder(HasTraits): + + # The lines of code recorded. + lines = List(Str) + + # Are we recording or not? + recording = Bool(False, desc="if script recording is enabled or not") + + # The Python script we have recorded so far. This is just a + # convenience trait for the `get_code()` method. + script = Property(Str) + + ######################################## + # Private traits. + + # Dict used to store information on objects registered. It stores a + # unique name for the object and its path in the object hierarchy + # traversed. + _registry = Dict + + # Reverse registry with keys as script_id and object as value. + _reverse_registry = Dict + + # A mapping to generate unique names for objects. The key is the + # name used (which is something derived from the class name of the + # object) and the value is an integer describing the number of times + # that variable name has been used earlier. + _name_map = Dict(Str, Int) + + # A list of special reserved script IDs. This is handy when you + # want a particular object to have an easy to read script ID and not + # the default one based on its class name. This leads to slightly + # easier to read scripts. + _special_ids = List + + # What are the known names in the script? By known names we mean + # names which are actually bound to objects. + _known_ids = List(Str) + + # The known types in the namespace. + _known_types = List(Str) + + # A guard to check if we are currently in a recorded function call, + # in which case we don't want to do any recording. + _in_function = Bool(False) + + ########################################################################### + # `Recorder` interface. + ########################################################################### +
[docs] def record(self, code): + """Record a string to be stored to the output file. + + Parameters + ---------- + + code : str + A string of text. + """ + if self.recording and not self._in_function: + lines = self.lines + # Analyze the code and add extra code if needed. + self._analyze_code(code) + # Add the code. + lines.append(code)
+ +
[docs] def register( + self, + object, + parent=None, + trait_name_on_parent="", + ignore=None, + known=False, + script_id=None, + ): + """Register an object with the recorder. This sets up the + object for recording. + + By default all traits (except those starting and ending with + '_') are recorded. For attributes that are themselves + recordable, one may mark traits with a 'record' metadata as + follows: + + - If metadata `record=False` is set, the nested object will not be + recorded. + + - If `record=True`, then that object is also recorded if it is + not `None`. + + If the object is a list or dict that is marked with + `record=True`, the list is itself not listened to for changes + but all its contents are registered. + + If the `object` has a trait named `recorder` then this recorder + instance will be set to it if possible. + + Parameters + ---------- + + object : Instance(HasTraits) + The object to register in the registry. + + parent : Instance(HasTraits) + An optional parent object in which `object` is contained + + trait_name_on_parent : str + An optional trait name of the `object` in the `parent`. + + ignore : list(str) + An optional list of trait names on the `object` to be + ignored. + + known : bool + Optional specification if the `object` id is known on the + interpreter. This is needed if you are manually injecting + code to define/create an object. + + script_id : str + Optionally specify a script_id to use for this object. It + is not guaranteed that this ID will be used since it may + already be in use. + """ + registry = self._registry + + # Do nothing if the object is already registered. + if object in registry: + return + + # When parent is specified the trait_name_on_parent must also be. + if parent is not None: + assert len(trait_name_on_parent) > 0 + + if ignore is None: + ignore = [] + + if isinstance(object, HasTraits): + # Always ignore these. + ignore.extend(["trait_added", "trait_modified"]) + + sub_recordables = list(object.traits(record=True).keys()) + # Find all the trait names we must ignore. + ignore.extend(object.traits(record=False).keys()) + # The traits to listen for. + tnames = [ + t + for t in object.trait_names() + if not t.startswith("_") + and not t.endswith("_") + and t not in ignore + ] + # Find all list traits. + trts = object.traits() + list_names = [] + for t in tnames: + tt = trts[t].trait_type + if ( + hasattr(tt, "default_value_type") + and tt.default_value_type == 5 + ): + list_names.append(t) + else: + # No traits, so we can't do much. + sub_recordables = [] + tnames = [] + list_names = [] + + # Setup the registry data. + + # If a script id is supplied try and use it. + sid = "" + if script_id is not None: + r_registry = self._reverse_registry + while script_id in r_registry: + script_id = "%s1" % script_id + sid = script_id + # Add the chosen id to special_id list. + self._special_ids.append(sid) + + if parent is None: + pdata = None + if len(sid) == 0: + sid = self._get_unique_name(object) + else: + pdata = self._get_registry_data(parent) + tnop = trait_name_on_parent + if "[" in tnop: + # If the object is a nested object through an iterator, + # we instantiate it and don't refer to it through the + # path, this makes scripting convenient. + sid = self._get_unique_name(object) + + # Register the object with the data. + data = _RegistryData( + script_id=sid, + parent_data=pdata, + trait_name_on_parent=trait_name_on_parent, + names=tnames, + sub_recordables=sub_recordables, + list_names=list_names, + ) + registry[object] = data + + # Now get the script id of the object -- note that if sid is '' + # above then the script_id is computed from that of the parent. + sid = data.script_id + # Setup reverse registry so we can get the object from the + # script_id. + self._reverse_registry[sid] = object + + # Record the script_id if the known argument is explicitly set to + # True. + if known: + self._known_ids.append(sid) + + # Try and set the recorder attribute if necessary. + if hasattr(object, "recorder"): + try: + object.recorder = self + except Exception as e: + msg = "Cannot set 'recorder' trait of object %r: " "%s" % ( + object, + e, + ) + warnings.warn(msg, warnings.RuntimeWarning) + + if isinstance(object, HasTraits): + # Add handler for lists. + for name in list_names: + object.on_trait_change( + self._list_items_listner, "%s_items" % name + ) + + # Register all sub-recordables. + for name in sub_recordables: + obj = getattr(object, name) + if isinstance(obj, list): + # Don't register the object itself but register its + # children. + for i, child in enumerate(obj): + attr = "%s[%d]" % (name, i) + self.register( + child, parent=object, trait_name_on_parent=attr + ) + elif obj is not None: + self.register( + obj, parent=object, trait_name_on_parent=name + ) + # Listen for changes to the trait itself so the newly + # assigned object can also be listened to. + object.on_trait_change(self._object_changed_handler, name) + # Now add listner for the object itself. + object.on_trait_change(self._listner, tnames)
+ +
[docs] def unregister(self, object): + """Unregister the given object from the recorder. This inverts + the logic of the `register(...)` method. + """ + registry = self._registry + # Do nothing if the object isn't registered. + if object not in registry: + return + + data = registry[object] + + # Try and unset the recorder attribute if necessary. + if hasattr(object, "recorder"): + try: + object.recorder = None + except Exception as e: + msg = "Cannot unset 'recorder' trait of object %r:" "%s" % ( + object, + e, + ) + warnings.warn(msg, warnings.RuntimeWarning) + + if isinstance(object, HasTraits): + # Remove all list_items handlers. + for name in data.list_names: + object.on_trait_change( + self._list_items_listner, "%s_items" % name, remove=True + ) + + # Unregister all sub-recordables. + for name in data.sub_recordables: + obj = getattr(object, name) + if isinstance(obj, list): + # Unregister the children. + for i, child in enumerate(obj): + self.unregister(child) + elif obj is not None: + self.unregister(obj) + # Remove the trait handler for trait assignments. + object.on_trait_change( + self._object_changed_handler, name, remove=True + ) + # Now remove listner for the object itself. + object.on_trait_change(self._listner, data.names, remove=True) + + # Remove the object data from the registry etc. + if data.script_id in self._known_ids: + self._known_ids.remove(data.script_id) + del self._reverse_registry[data.script_id] + del registry[object]
+ +
[docs] def save(self, file): + """Save the recorded lines to the given file. It does not close + the file. + """ + file.write(self.get_code()) + file.flush()
+ +
[docs] def record_function(self, func, args, kw): + """Record a function call given the function and its + arguments.""" + if self.recording and not self._in_function: + # Record the function name and arguments. + call_str = self._function_as_string(func, args, kw) + # Call the function. + try: + self._in_function = True + result = func(*args, **kw) + finally: + self._in_function = False + + # Register the result if it is not None. + if func.__name__ == "__init__": + f_self = args[0] + code = self._import_class_string(f_self.__class__) + self.lines.append(code) + return_str = self._registry.get(f_self).script_id + else: + return_str = self._return_as_string(result) + if len(return_str) > 0: + self.lines.append("%s = %s" % (return_str, call_str)) + else: + self.lines.append("%s" % (call_str)) + else: + result = func(*args, **kw) + return result
+ +
[docs] def ui_save(self): + """Save recording to file, pop up a UI dialog to find out where + and close the file when done. + """ + from pyface.api import FileDialog, OK + + wildcard = "Python files (*.py)|*.py|" + FileDialog.WILDCARD_ALL + dialog = FileDialog( + title="Save Script", action="save as", wildcard=wildcard + ) + if dialog.open() == OK: + fname = dialog.path + f = open(fname, "w") + self.save(f) + f.close()
+ +
[docs] def clear(self): + """Clears all previous recorded state and unregisters all + registered objects.""" + # First unregister any registered objects. + registry = self._registry + while len(registry) > 0: + self.unregister(list(registry.keys())[0]) + # Clear the various lists. + self.lines[:] = [] + self._registry.clear() + self._known_ids[:] = [] + self._name_map.clear() + self._reverse_registry.clear() + self._known_types[:] = [] + self._special_ids[:] = []
+ +
[docs] def get_code(self): + """Returns the recorded lines as a string of printable code.""" + return "\n".join(self.lines) + "\n"
+ +
[docs] def is_registered(self, object): + """Returns True if the given object is registered with the + recorder.""" + return object in self._registry
+ +
[docs] def get_script_id(self, object): + """Returns the script_id of a registered object. Useful when + you want to manually add a record statement.""" + return self._get_registry_data(object).script_id
+ +
[docs] def get_object_path(self, object): + """Returns the path in the object hierarchy of a registered + object. Useful for debugging.""" + return self._get_registry_data(object).path
+ +
[docs] def write_script_id_in_namespace(self, script_id): + """If a script_id is not known in the current script's namespace, + this sets it using the path of the object or actually + instantiating it. If this is not possible (since the script_id + matches no existing object), nothing is recorded but the + framework is notified that the particular script_id is available + in the namespace. This is useful when you want to inject code + in the namespace to create a particular object. + """ + if not self.recording: + return + known_ids = self._known_ids + if script_id not in known_ids: + obj = self._reverse_registry.get(script_id) + # Add the ID to the known_ids. + known_ids.append(script_id) + if obj is not None: + data = self._registry.get(obj) + result = "" + if len(data.path) > 0: + # Record code for instantiation of object. + result = "%s = %s" % (script_id, data.path) + else: + # This is not the best thing to do but better than + # nothing. + result = self._import_class_string(obj.__class__) + cls = obj.__class__.__name__ + result += "\n%s = %s()" % (script_id, cls) + + if len(result) > 0: + self.lines.extend(result.split("\n"))
+ + ########################################################################### + # Non-public interface. + ########################################################################### + def _get_unique_name(self, obj): + """Return a unique object name (a string). Note that this does + not cache the object, so if called with the same object 3 times + you'll get three different names. + """ + cname = obj.__class__.__name__ + nm = self._name_map + result = "" + builtin = False + if cname in builtins.__dict__: + builtin = True + if hasattr(obj, "__name__"): + cname = obj.__name__ + else: + cname = camel_case_to_python(cname) + + special_ids = self._special_ids + while len(result) == 0 or result in special_ids: + if cname in nm: + id = nm[cname] + 1 + nm[cname] = id + result = "%s%d" % (cname, id) + else: + nm[cname] = 0 + # The first id doesn't need a number if it isn't builtin. + if builtin: + result = "%s0" % (cname) + else: + result = cname + return result + + def _get_registry_data(self, object): + """Get the data for an object from registry.""" + data = self._registry.get(object) + if data is None: + msg = ( + "Recorder: Can't get script_id since object %s not registered" + ) + raise RecorderError(msg % (object)) + return data + + def _listner(self, object, name, old, new): + """The listner for trait changes on an object. + + This is called by child listners or when any of the recordable + object's traits change when recording to a script is enabled. + + Parameters: + ----------- + + object : Object which has changed. + + name : extended name of attribute that changed. + + old : Old value. + + new : New value. + + """ + if self.recording and not self._in_function: + new_repr = repr(new) + sid = self._get_registry_data(object).script_id + if len(sid) == 0: + msg = "%s = %r" % (name, new) + else: + msg = "%s.%s = %r" % (sid, name, new) + if new_repr.startswith("<") and new_repr.endswith(">"): + self.record("# " + msg) + else: + self.record(msg) + + def _list_items_listner(self, object, name, old, event): + """The listner for *_items on list traits of the object.""" + # Set the path of registered objects in the modified list and + # all their children. This is done by unregistering the object + # and re-registering them. This is slow but. + registry = self._registry + sid = registry.get(object).script_id + trait_name = name[:-6] + items = getattr(object, trait_name) + for (i, item) in enumerate(items): + if item in registry: + data = registry.get(item) + tnop = data.trait_name_on_parent + if len(tnop) > 0: + data.trait_name_on_parent = "%s[%d]" % (trait_name, i) + + # Record the change. + if self.recording and not self._in_function: + index = event.index + removed = event.removed + added = event.added + nr = len(removed) + slice = "[%d:%d]" % (index, index + nr) + rhs = [self._object_as_string(item) for item in added] + rhs = ", ".join(rhs) + obj = "%s.%s" % (sid, name[:-6]) + msg = "%s%s = [%s]" % (obj, slice, rhs) + self.record(msg) + + def _object_changed_handler(self, object, name, old, new): + """Called when a child recordable object has been reassigned.""" + registry = self._registry + if old is not None: + if old in registry: + self.unregister(old) + if new is not None: + if new not in registry: + self.register(new, parent=object, trait_name_on_parent=name) + + def _get_script(self): + return self.get_code() + + def _analyze_code(self, code): + """Analyze the code and return extra code if needed.""" + lhs = "" + try: + lhs = code.split()[0] + except IndexError: + pass + + if "." in lhs: + ob_name = lhs.split(".")[0] + self.write_script_id_in_namespace(ob_name) + + def _function_as_string(self, func, args, kw): + """Return a string representing the function call.""" + func_name = func.__name__ + func_code = func.__code__ + # Even if func is really a decorated method it never shows up as + # a bound or unbound method here, so we have to inspect the + # argument names to figure out if this is a method or function. + if func_code.co_argcount > 0 and func_code.co_varnames[0] == "self": + # This is a method, the first argument is bound to self. + f_self = args[0] + # Convert the remaining arguments to strings. + argl = [self._object_as_string(arg) for arg in args[1:]] + + # If this is __init__ we special case it. + if func_name == "__init__": + # Register the object. + self.register(f_self, known=True) + func_name = f_self.__class__.__name__ + else: + sid = self._object_as_string(f_self) + func_name = "%s.%s" % (sid, func_name) + else: + argl = [self._object_as_string(arg) for arg in args] + + # Convert the keyword args. + kwl = [ + "%s=%s" % (key, self._object_as_string(value)) + for key, value in kw.items() + ] + argl.extend(kwl) + + # Make a string representation of the args, kw. + argstr = ", ".join(argl) + return "%s(%s)" % (func_name, argstr) + + def _is_arbitrary_object(self, object): + """Return True if the object is an arbitrary non-primitive object. + + We assume that if the hex id of the object is in its string + representation then it is an arbitrary object. + """ + ob_id = id(object) + orepr = repr(object) + hex_id = "%x" % ob_id + return hex_id.upper() in orepr.upper() + + def _object_as_string(self, object): + """Return a string representing the object.""" + registry = self._registry + if object in registry: + # Return script id if the object is known; create the script + # id on the namespace if needed before that. + sid = registry.get(object).script_id + base_id = sid.split(".")[0] + self.write_script_id_in_namespace(base_id) + return sid + else: + if not self._is_arbitrary_object(object): + return repr(object) + + # If we get here, we just register the object and call ourselves + # again to do the needful. + self.register(object) + return self._object_as_string(object) + + def _return_as_string(self, object): + """Return a string given a returned object from a function.""" + result = "" + ignore = (float, complex, bool, int, str) + if object is not None and type(object) not in ignore: + # If object is not know, register it. + registry = self._registry + if object not in registry: + self.register(object) + result = registry.get(object).script_id + # Since this is returned it is known on the namespace. + known_ids = self._known_ids + if result not in known_ids: + known_ids.append(result) + return result + + def _import_class_string(self, cls): + """Import a class if needed.""" + cname = cls.__name__ + result = "" + if cname not in builtins.__dict__: + mod = cls.__module__ + typename = "%s.%s" % (mod, cname) + if typename not in self._known_types: + result = "from %s import %s" % (mod, cname) + self._known_types.append(typename) + return result
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/scripting/recorder_with_ui.html b/_modules/apptools/scripting/recorder_with_ui.html new file mode 100644 index 000000000..8323559a9 --- /dev/null +++ b/_modules/apptools/scripting/recorder_with_ui.html @@ -0,0 +1,226 @@ + + + + + + + apptools.scripting.recorder_with_ui — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.scripting.recorder_with_ui

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+"""
+A Recorder subclass that presents a simple user interface.
+"""
+
+from traits.api import Code, Button, Int, on_trait_change, Any
+from traitsui.api import View, Item, Group, HGroup, CodeEditor, spring, Handler
+
+from .recorder import Recorder
+
+
+###############################################################################
+# `CloseHandler` class.
+###############################################################################
+
[docs]class CloseHandler(Handler): + """This class cleans up after the UI for the recorder is closed.""" + +
[docs] def close(self, info, is_ok): + """This method is invoked when the user closes the UI.""" + recorder = info.object + recorder.on_ui_close() + return True
+ + +############################################################################### +# `RecorderWithUI` class. +############################################################################### +
[docs]class RecorderWithUI(Recorder): + """ + This class represents a Recorder but with a simple user interface. + """ + + # The code to display + code = Code(editor=CodeEditor(line="current_line")) + + # Button to save script to file. + save_script = Button("Save Script") + + # The current line to show, used by the editor. + current_line = Int + + # The root object which is being recorded. + root = Any + + ######################################## + # Traits View. + view = View( + Group( + HGroup( + Item("recording", show_label=True), + spring, + Item("save_script", show_label=False), + ), + Group(Item("code", show_label=False)), + ), + width=600, + height=360, + id="apptools.scripting.recorder_with_ui", + buttons=["Cancel"], + resizable=True, + handler=CloseHandler(), + ) + + ###################################################################### + # RecorderWithUI interface. + ###################################################################### +
[docs] def on_ui_close(self): + """Called from the CloseHandler when the UI is closed. This + method basically stops the recording. + """ + from .util import stop_recording + from .package_globals import get_recorder + + if get_recorder() is self: + stop_recording(self.root, save=False) + else: + self.recording = False + self.unregister(self.root)
+ + ###################################################################### + # Non-public interface. + ###################################################################### + @on_trait_change("lines[]") + def _update_code(self): + self.code = self.get_code() + self.current_line = len(self.lines) + 1 + + def _save_script_fired(self): + self.ui_save()
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/scripting/util.html b/_modules/apptools/scripting/util.html new file mode 100644 index 000000000..39a0ea9ef --- /dev/null +++ b/_modules/apptools/scripting/util.html @@ -0,0 +1,186 @@ + + + + + + + apptools.scripting.util — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.scripting.util

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+"""Simple utility functions provided by the scripting API.
+"""
+
+from .recorder import Recorder
+from .recorder_with_ui import RecorderWithUI
+from .package_globals import get_recorder, set_recorder
+
+
+###############################################################################
+# Utility functions.
+###############################################################################
+
[docs]def start_recording(object, ui=True, **kw): + """Convenience function to start recording. Returns the recorder. + + Parameters + ---------- + + object : object to record. + + ui : bool specifying if a UI is to be shown or not + + kw : Keyword arguments to pass to the register function of the + recorder. + """ + if ui: + r = RecorderWithUI(root=object) + r.edit_traits(kind="live") + else: + r = Recorder() + # Set the global recorder. + set_recorder(r) + r.recording = True + r.register(object, **kw) + return r
+ + +
[docs]def stop_recording(object, save=True): + """Stop recording the object. If `save` is `True`, this will pop up + a UI to ask where to save the script. + """ + recorder = get_recorder() + recorder.unregister(object) + recorder.recording = False + # Set the global recorder back to None + set_recorder(None) + # Save the script. + if save: + recorder.ui_save()
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/selection/errors.html b/_modules/apptools/selection/errors.html new file mode 100644 index 000000000..441880612 --- /dev/null +++ b/_modules/apptools/selection/errors.html @@ -0,0 +1,172 @@ + + + + + + + apptools.selection.errors — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.selection.errors

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+
+
+
[docs]class ProviderNotRegisteredError(Exception): + """ Raised when a provider is requested by ID and not found. """ + + def __init__(self, provider_id): + self.provider_id = provider_id + + def __str__(self): + msg = "Selection provider with ID '{id}' not found." + return msg.format(id=self.provider_id)
+ + +
[docs]class IDConflictError(Exception): + """ Raised when a provider is added and its ID is already registered. """ + + def __init__(self, provider_id): + self.provider_id = provider_id + + def __str__(self): + msg = "A selection provider with ID '{id}' is already registered." + return msg.format(id=self.provider_id)
+ + +
[docs]class ListenerNotConnectedError(Exception): + """ Raised when a listener that was never connected is disconnected. """ + + def __init__(self, provider_id, listener): + self.provider_id = provider_id + self.listener = listener + + def __str__(self): + msg = "Selection listener {lr} is not connected to provider '{id}'." + return msg.format(lr=self.listener, id=self.provider_id)
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/selection/i_selection.html b/_modules/apptools/selection/i_selection.html new file mode 100644 index 000000000..bcb99d64a --- /dev/null +++ b/_modules/apptools/selection/i_selection.html @@ -0,0 +1,159 @@ + + + + + + + apptools.selection.i_selection — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.selection.i_selection

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+from traits.api import Interface, List, Str
+
+
+
[docs]class ISelection(Interface): + """ Collection of selected items. """ + + #: ID of the selection provider that created this selection object. + provider_id = Str + +
[docs] def is_empty(self): + """ Is the selection empty? """
+ + +
[docs]class IListSelection(ISelection): + """ Selection for ordered sequences of items. """ + + #: Selected objects. + items = List + + #: Indices of the selected objects in the selection provider. + indices = List
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/selection/i_selection_provider.html b/_modules/apptools/selection/i_selection_provider.html new file mode 100644 index 000000000..d2ffa29bc --- /dev/null +++ b/_modules/apptools/selection/i_selection_provider.html @@ -0,0 +1,176 @@ + + + + + + + apptools.selection.i_selection_provider — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.selection.i_selection_provider

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+from traits.api import Event, Interface, Str
+
+
+
[docs]class ISelectionProvider(Interface): + """ Source of selections. """ + + #: Unique ID identifying the provider. + provider_id = Str() + + #: Event triggered when the selection changes. + #: The content of the event is an :class:`~.ISelection` instance. + selection = Event + +
[docs] def get_selection(self): + """Return the current selection. + + Returns: + selection -- ISelection + Object representing the current selection. + """
+ +
[docs] def set_selection(self, items, ignore_missing=False): + """Set the current selection to the given items. + + If ``ignore_missing`` is ``True``, items that are not available in the + selection provider are silently ignored. If it is ``False`` (default), + an :class:`~.ValueError` should be raised. + + Arguments: + items -- list + List of items to be selected. + + ignore_missing -- bool + If ``False`` (default), the provider raises an exception if any + of the items in ``items`` is not available to be selected. + Otherwise, missing elements are silently ignored, and the rest + is selected. + """
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/selection/list_selection.html b/_modules/apptools/selection/list_selection.html new file mode 100644 index 000000000..cd640f662 --- /dev/null +++ b/_modules/apptools/selection/list_selection.html @@ -0,0 +1,196 @@ + + + + + + + apptools.selection.list_selection — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.selection.list_selection

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+from traits.api import HasTraits, List, provides, Str
+
+from apptools.selection.i_selection import IListSelection
+
+
+
[docs]@provides(IListSelection) +class ListSelection(HasTraits): + """Selection for ordered sequences of items. + + This is the default implementation of the :class:`~.IListSelection` + interface. + """ + + #### 'ISelection' protocol ################################################ + + #: ID of the selection provider that created this selection object. + provider_id = Str + +
[docs] def is_empty(self): + """ Is the selection empty? """ + return len(self.items) == 0
+ + #### 'IListSelection' protocol ############################################ + + #: Selected objects. + items = List + + #: Indices of the selected objects in the selection provider. + indices = List + + #### 'ListSelection' class protocol ####################################### + +
[docs] @classmethod + def from_available_items(cls, provider_id, selected, all_items): + """Create a list selection given a list of all available items. + + Fills in the required information (in particular, the indices) based + on a list of selected items and a list of all available items. + + .. note:: + - The list of available items must not contain any duplicate items. + - It is expected that ``selected`` is populated by items in + ``all_items``. + + """ + number_of_items = len(all_items) + indices = [] + + for item in selected: + for index in range(number_of_items): + if all_items[index] is item: + indices.append(index) + break + else: + msg = "Selected item: {!r}, could not be found" + raise ValueError(msg.format(item)) + + return cls(provider_id=provider_id, items=selected, indices=indices)
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/selection/selection_service.html b/_modules/apptools/selection/selection_service.html new file mode 100644 index 000000000..53ff56ffa --- /dev/null +++ b/_modules/apptools/selection/selection_service.html @@ -0,0 +1,325 @@ + + + + + + + apptools.selection.selection_service — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.selection.selection_service

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+from traits.api import Dict, HasTraits
+
+from apptools.selection.errors import (
+    ProviderNotRegisteredError,
+    IDConflictError,
+    ListenerNotConnectedError,
+)
+
+
+
[docs]class SelectionService(HasTraits): + """The selection service connects selection providers and listeners. + + The selection service is a register of selection providers, i.e., objects + that publish their current selection. + + Selections can be requested actively, by explicitly requesting the current + selection in a provider (:meth:`get_selection(id)`), or passively by + connecting selection listeners. + """ + + #### 'SelectionService' protocol ########################################## + +
[docs] def add_selection_provider(self, provider): + """Add a selection provider. + + The provider is identified by its ID. If a provider with the same + ID has been already registered, an :class:`~.IDConflictError` + is raised. + + Arguments: + provider -- ISelectionProvider + The selection provider added to the internal registry. + + """ + provider_id = provider.provider_id + if self.has_selection_provider(provider_id): + raise IDConflictError(provider_id=provider_id) + + self._providers[provider_id] = provider + + if provider_id in self._listeners: + self._connect_all_listeners(provider_id)
+ +
[docs] def has_selection_provider(self, provider_id): + """ Has a provider with the given ID been registered? """ + return provider_id in self._providers
+ +
[docs] def remove_selection_provider(self, provider): + """Remove a selection provider. + + If the provider has not been registered, a + :class:`~.ProviderNotRegisteredError` is raised. + + Arguments: + provider -- ISelectionProvider + The selection provider added to the internal registry. + """ + provider_id = provider.provider_id + self._raise_if_not_registered(provider_id) + + if provider_id in self._listeners: + self._disconnect_all_listeners(provider_id) + + del self._providers[provider_id]
+ +
[docs] def get_selection(self, provider_id): + """Return the current selection of the provider with the given ID. + + If a provider with that ID has not been registered, a + :class:`~.ProviderNotRegisteredError` is raised. + + Arguments: + provider_id -- str + The selection provider ID. + + Returns: + selection -- ISelection + The current selection of the provider. + """ + self._raise_if_not_registered(provider_id) + provider = self._providers[provider_id] + return provider.get_selection()
+ +
[docs] def set_selection(self, provider_id, items, ignore_missing=False): + """Set the current selection in a provider to the given items. + + If a provider with the given ID has not been registered, a + :class:`~.ProviderNotRegisteredError` is raised. + + If ``ignore_missing`` is ``True``, items that are not available in the + selection provider are silently ignored. If it is ``False`` (default), + a :class:`ValueError` should be raised. + + Arguments: + provider_id -- str + The selection provider ID. + + items -- list + List of items to be selected. + + ignore_missing -- bool + If ``False`` (default), the provider raises an exception if any + of the items in ``items`` is not available to be selected. + Otherwise, missing elements are silently ignored, and the rest + is selected. + """ + self._raise_if_not_registered(provider_id) + provider = self._providers[provider_id] + return provider.set_selection(items, ignore_missing=ignore_missing)
+ +
[docs] def connect_selection_listener(self, provider_id, func): + """Connect a listener to selection events from a specific provider. + + The signature if the listener callback is ``func(i_selection)``. + The listener is called: + + 1) When a provider with the given ID is registered, with its initial + selection as argument, or + + 2) whenever the provider fires a selection event. + + It is perfectly valid to connect a listener before a provider with the + given ID is registered. The listener will remain connected even if + the provider is repeatedly connected and disconnected. + + Arguments: + provider_id -- str + The selection provider ID. + func -- callable(i_selection) + A callable object that is notified when the selection changes. + """ + self._listeners.setdefault(provider_id, []) + self._listeners[provider_id].append(func) + + if self.has_selection_provider(provider_id): + self._toggle_listener(provider_id, func, remove=False)
+ +
[docs] def disconnect_selection_listener(self, provider_id, func): + """Disconnect a listener from a specific provider. + + Arguments: + provider_id -- str + The selection provider ID. + func -- callable(provider_id, i_selection) + A callable object that is notified when the selection changes. + """ + + if self.has_selection_provider(provider_id): + self._toggle_listener(provider_id, func, remove=True) + + try: + self._listeners[provider_id].remove(func) + except (ValueError, KeyError): + raise ListenerNotConnectedError( + provider_id=provider_id, listener=func + )
+ + #### Private protocol ##################################################### + + _listeners = Dict() + + _providers = Dict() + + def _toggle_listener(self, provider_id, func, remove): + provider = self._providers[provider_id] + provider.on_trait_change(func, "selection", remove=remove) + + def _connect_all_listeners(self, provider_id): + """Connect all listeners connected to a provider. + + As soon as they are connected, they receive the initial selection. + """ + provider = self._providers[provider_id] + selection = provider.get_selection() + for func in self._listeners[provider_id]: + self._toggle_listener(provider_id, func, remove=False) + # FIXME: make this robust to notifications that raise exceptions. + # Can we send the error to the traits exception hook? + func(selection) + + def _disconnect_all_listeners(self, provider_id): + for func in self._listeners[provider_id]: + self._toggle_listener(provider_id, func, remove=True) + + def _raise_if_not_registered(self, provider_id): + if not self.has_selection_provider(provider_id): + raise ProviderNotRegisteredError(provider_id=provider_id)
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/type_registry/type_registry.html b/_modules/apptools/type_registry/type_registry.html new file mode 100644 index 000000000..6a0642e6b --- /dev/null +++ b/_modules/apptools/type_registry/type_registry.html @@ -0,0 +1,403 @@ + + + + + + + apptools.type_registry.type_registry — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.type_registry.type_registry

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+
+
+
[docs]def get_mro(obj_class): + """Get a reasonable method resolution order of a class and its + superclasses for both old-style and new-style classes. + """ + if not hasattr(obj_class, "__mro__"): + # Old-style class. Mix in object to make a fake new-style class. + try: + obj_class = type(obj_class.__name__, (obj_class, object), {}) + except TypeError: + # Old-style extension type that does not descend from object. + mro = [obj_class] + else: + mro = obj_class.__mro__[1:-1] + else: + mro = obj_class.__mro__ + return mro
+ + +def _mod_name_key(typ): + """Return a '__module__:__name__' key for a type.""" + module = getattr(typ, "__module__", None) + name = getattr(typ, "__name__", None) + key = "{0}:{1}".format(module, name) + return key + + +
[docs]class TypeRegistry(object): + """Register objects for types. + + Each type maintains a stack of registered objects that can be pushed and + popped. + """ + + def __init__(self): + # Map types to lists of registered objects. The last is the most + # current and will be the one that is returned on lookup. + self.type_map = {} + + # Map '__module__:__name__' strings to lists of registered objects. + self.name_map = {} + + # Map abstract base classes to lists of registered objects. + self.abc_map = {} + + #### TypeRegistry public interface ######################################## + +
[docs] def push(self, typ, obj): + """Push an object onto the stack for the given type. + + Parameters + ---------- + typ : type or '__module__:__name__' string for a type + obj : object + The object to register. + """ + if isinstance(typ, str): + # Check the cached types. + for cls in self.type_map: + if _mod_name_key(cls) == typ: + self.type_map[cls].append(obj) + break + else: + if typ not in self.name_map: + self.name_map[typ] = [] + self.name_map[typ].append(obj) + else: + if typ not in self.type_map: + self.type_map[typ] = [] + self.type_map[typ].append(obj)
+ +
[docs] def push_abc(self, typ, obj): + """Push an object onto the stack for the given ABC. + + Parameters + ---------- + typ : abc.ABCMeta + obj : object + """ + if typ not in self.abc_map: + self.abc_map[typ] = [] + self.abc_map[typ].append(obj)
+ +
[docs] def pop(self, typ): + """Pop a registered object for the given type. + + Parameters + ---------- + typ : type or '__module__:__name__' string for a type + + Returns + ------- + obj : object + The last registered object for the type. + + Raises + ------ + KeyError if the type is not registered. + """ + if isinstance(typ, str): + if typ not in self.name_map: + # We may have it cached in the type map. We will have to + # iterate over all of the types to check. + for cls in self.type_map: + if _mod_name_key(cls) == typ: + old = self._pop_value(self.type_map, cls) + break + else: + raise KeyError("No registered value for {0!r}".format(typ)) + else: + old = self._pop_value(self.name_map, typ) + else: + if typ in self.type_map: + old = self._pop_value(self.type_map, typ) + elif typ in self.abc_map: + old = self._pop_value(self.abc_map, typ) + else: + old = self._pop_value(self.name_map, _mod_name_key(typ)) + return old
+ +
[docs] def lookup(self, instance): + """Look up the registered object for the given instance. + + Parameters + ---------- + instance : object + An instance of a possibly registered type. + + Returns + ------- + obj : object + The registered object for the type of the instance, one of the + type's superclasses, or else one of the ABCs the type implements. + + Raises + ------ + KeyError if the instance's type has not been registered. + """ + return self.lookup_by_type(type(instance))
+ +
[docs] def lookup_by_type(self, typ): + """Look up the registered object for a type. + + Parameters + ---------- + typ : type + + Returns + ------- + obj : object + The registered object for the type, one of its superclasses, or + else one of the ABCs it implements. + + Raises + ------ + KeyError if the type has not been registered. + """ + return self.lookup_all_by_type(typ)[-1]
+ +
[docs] def lookup_all(self, instance): + """Look up all the registered objects for the given instance. + + Parameters + ---------- + instance : object + An instance of a possibly registered type. + + Returns + ------- + objs : list of objects + The list of registered objects for the instance. If the given + instance is not registered, its superclasses are searched. If none + of the superclasses are registered, search the possible ABCs. + + Raises + ------ + KeyError if the instance's type has not been registered. + """ + return self.lookup_all_by_type(type(instance))
+ +
[docs] def lookup_all_by_type(self, typ): + """Look up all the registered objects for a type. + + Parameters + ---------- + typ : type + + Returns + ------- + objs : list of objects + The list of registered objects for the type. If the given type is + not registered, its superclasses are searched. If none of the + superclasses are registered, search the possible ABCs. + + Raises + ------ + KeyError if the type has not been registered. + """ + # If a concrete superclass is registered use it. + for cls in get_mro(typ): + if cls in self.type_map or self._in_name_map(cls): + objs = self.type_map[cls] + if objs: + return objs + + # None of the concrete superclasses. Check the ABCs. + for abstract, objs in self.abc_map.items(): + if issubclass(typ, abstract) and objs: + return objs + + # If we have reached here, the lookup failed. + raise KeyError("No registered value for {0!r}".format(typ))
+ + #### Private implementation ############################################### + + def _pop_value(self, mapping, key): + """Pop a value from a keyed stack in a mapping, taking care to remove + the key if the stack is depleted. + """ + objs = mapping[key] + old = objs.pop() + if not objs: + del mapping[key] + return old + + def _in_name_map(self, typ): + """Check if the given type is specified in the name map. + + Parameters + ---------- + typ : type + + Returns + ------- + is_in_name_map : bool + If True, the registered value will be moved over to the type map + for future lookups. + """ + key = _mod_name_key(typ) + if key in self.name_map: + self.type_map[typ] = self.name_map.pop(key) + return True + else: + return False
+ + +
[docs]class LazyRegistry(TypeRegistry): + """A type registry that will lazily import the registered objects. + + Register '__module__:__name__' strings for the lazily imported objects. + These will only be imported when the matching type is looked up. The module + name must be a fully-qualified absolute name with all of the parent + packages specified. + """ + +
[docs] def lookup_by_type(self, typ): + """Look up the registered object for a type.""" + mod_name = TypeRegistry.lookup_by_type(self, typ) + return self._import_object(mod_name)
+ + def _import_object(self, mod_object): + module, name = mod_object.split(":") + mod = __import__(module, {}, {}, [name], 0) + return getattr(mod, name)
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/undo/abstract_command.html b/_modules/apptools/undo/abstract_command.html new file mode 100644 index 000000000..48a26016b --- /dev/null +++ b/_modules/apptools/undo/abstract_command.html @@ -0,0 +1,205 @@ + + + + + + + apptools.undo.abstract_command — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.undo.abstract_command

+# ------------------------------------------------------------------------------
+# Copyright (c) 2007, Riverbank Computing Limited
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in enthought/LICENSE.txt and may be redistributed only
+# under the conditions described in the aforementioned license.  The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+# Thanks for using Enthought open source!
+#
+# Author: Riverbank Computing Limited
+# Description: <Enthought undo package component>
+# ------------------------------------------------------------------------------
+
+# Enthought library imports.
+from traits.api import Any, HasTraits, Str, provides
+
+# Local imports.
+from .i_command import ICommand
+
+
+
[docs]@provides(ICommand) +class AbstractCommand(HasTraits): + """The AbstractCommand class is an abstract base class that implements the + ICommand interface. + """ + + #### 'ICommand' interface ################################################# + + # This is the data on which the command operates. + data = Any + + # This is the name of the command as it will appear in any GUI element. It + # may include '&' which will be automatically removed whenever it is + # inappropriate. + name = Str + + ########################################################################### + # 'ICommand' interface. + ########################################################################### + +
[docs] def do(self): + """This is called by the command stack to do the command and to return + any value. The command must save any state necessary for the 'redo()' + and 'undo()' methods to work. The class's __init__() must also ensure + that deep copies of any arguments are made if appropriate. It is + guaranteed that this will only ever be called once and that it will be + called before any call to 'redo()' or 'undo()'. + """ + + raise NotImplementedError
+ +
[docs] def merge(self, other): + """This is called by the command stack to try and merge another + command with this one. True is returned if the commands were merged. + 'other' is the command that is about to be executed. If the commands + are merged then 'other' will discarded and not placed on the command + stack. A subsequent undo or redo of this modified command must have + the same effect as the two original commands. + """ + + # By default merges never happen. + return False
+ +
[docs] def redo(self): + """This is called by the command stack to redo the command. Any + returned value will replace the value that the command stack references + from the original call to 'do()' or previous call to 'redo()'. + """ + + raise NotImplementedError
+ +
[docs] def undo(self): + """ This is called by the command stack to undo the command. """ + + raise NotImplementedError
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/undo/action/abstract_command_stack_action.html b/_modules/apptools/undo/action/abstract_command_stack_action.html new file mode 100644 index 000000000..eef451dd3 --- /dev/null +++ b/_modules/apptools/undo/action/abstract_command_stack_action.html @@ -0,0 +1,212 @@ + + + + + + + apptools.undo.action.abstract_command_stack_action — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.undo.action.abstract_command_stack_action

+# ------------------------------------------------------------------------------
+# Copyright (c) 2008, Riverbank Computing Limited
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in enthought/LICENSE.txt and may be redistributed only
+# under the conditions described in the aforementioned license.  The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+# Thanks for using Enthought open source!
+#
+# Author: Riverbank Computing Limited
+# Description: <Enthought undo package component>
+# ------------------------------------------------------------------------------
+
+# Enthought library imports.
+from pyface.action.api import Action
+from traits.api import Instance
+
+# Local library imports
+from ..i_undo_manager import IUndoManager
+
+
+
[docs]class AbstractCommandStackAction(Action): + """The abstract base class for all actions that operate on a command + stack. + """ + + #### 'AbstractCommandStackAction' interface ############################### + + # The undo manager. + undo_manager = Instance(IUndoManager) + + ########################################################################### + # 'object' interface. + ########################################################################### + + def __init__(self, **traits): + """ Initialise the instance. """ + + super(AbstractCommandStackAction, self).__init__(**traits) + + self.undo_manager.on_trait_event( + self._on_stack_updated, "stack_updated" + ) + + # Update the action to initialise it. + self._update_action() + + ########################################################################### + # 'Action' interface. + ########################################################################### + +
[docs] def destroy(self): + """Called when the action is no longer required. + + By default this method does nothing, but this would be a great place to + unhook trait listeners etc. + + """ + + self.undo_manager.on_trait_event( + self._on_stack_updated, "stack_updated", remove=True + )
+ + ########################################################################### + # Protected interface. + ########################################################################### + + def _update_action(self): + """ Update the state of the action. """ + + raise NotImplementedError + + ########################################################################### + # Private interface. + ########################################################################### + + def _on_stack_updated(self, stack): + """ Handle changes to the state of a command stack. """ + + # Ignore unless it is the active stack. + if stack is self.undo_manager.active_stack: + self._update_action()
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/undo/action/command_action.html b/_modules/apptools/undo/action/command_action.html new file mode 100644 index 000000000..151e51aaa --- /dev/null +++ b/_modules/apptools/undo/action/command_action.html @@ -0,0 +1,187 @@ + + + + + + + apptools.undo.action.command_action — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.undo.action.command_action

+# ------------------------------------------------------------------------------
+# Copyright (c) 2007, Riverbank Computing Limited
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in enthought/LICENSE.txt and may be redistributed only
+# under the conditions described in the aforementioned license.  The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+# Thanks for using Enthought open source!
+#
+# Author: Riverbank Computing Limited
+# Description: <Enthought undo package component>
+# ------------------------------------------------------------------------------
+
+# Enthought library imports.
+from pyface.action.api import Action
+from traits.api import Any, Callable, Instance
+from ..i_command_stack import ICommandStack
+
+
+
[docs]class CommandAction(Action): + """The CommandAction class is an Action class that wraps undo/redo + commands. It is only useful for commands that do not take any arguments or + return any result. + """ + + #### 'CommandAction' interface ############################################ + + # The command to create when the action is performed. + command = Callable + + # The command stack onto which the command will be pushed when the action + # is performed. + command_stack = Instance(ICommandStack) + + # This is the data on which the command operates. + data = Any + + ########################################################################### + # 'Action' interface. + ########################################################################### + +
[docs] def perform(self, event): + """This is reimplemented to push a new command instance onto the + command stack. + """ + + self.command_stack.push(self.command(data=self.data))
+ + def _name_default(self): + """ This gets the action name from the command. """ + + if self.command: + name = self.command().name + else: + name = "" + + return name
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/undo/action/redo_action.html b/_modules/apptools/undo/action/redo_action.html new file mode 100644 index 000000000..34960ff83 --- /dev/null +++ b/_modules/apptools/undo/action/redo_action.html @@ -0,0 +1,178 @@ + + + + + + + apptools.undo.action.redo_action — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.undo.action.redo_action

+# ------------------------------------------------------------------------------
+# Copyright (c) 2007, Riverbank Computing Limited
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in enthought/LICENSE.txt and may be redistributed only
+# under the conditions described in the aforementioned license.  The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+# Thanks for using Enthought open source!
+#
+# Author: Riverbank Computing Limited
+# Description: <Enthought undo package component>
+# ------------------------------------------------------------------------------
+
+# Local imports.
+from .abstract_command_stack_action import AbstractCommandStackAction
+
+
+
[docs]class RedoAction(AbstractCommandStackAction): + """An action that redos the last command undone of the active command + stack. + """ + + ########################################################################### + # 'Action' interface. + ########################################################################### + +
[docs] def perform(self, event): + """ Perform the action. """ + + self.undo_manager.redo()
+ + ########################################################################### + # 'AbstractUndoAction' interface. + ########################################################################### + + def _update_action(self): + """ Update the state of the action. """ + + name = self.undo_manager.redo_name + + if name: + name = "&Redo " + name + self.enabled = True + else: + name = "&Redo" + self.enabled = False + + self.name = name
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/undo/action/undo_action.html b/_modules/apptools/undo/action/undo_action.html new file mode 100644 index 000000000..c5910362b --- /dev/null +++ b/_modules/apptools/undo/action/undo_action.html @@ -0,0 +1,176 @@ + + + + + + + apptools.undo.action.undo_action — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.undo.action.undo_action

+# ------------------------------------------------------------------------------
+# Copyright (c) 2007, Riverbank Computing Limited
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in enthought/LICENSE.txt and may be redistributed only
+# under the conditions described in the aforementioned license.  The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+# Thanks for using Enthought open source!
+#
+# Author: Riverbank Computing Limited
+# Description: <Enthought undo package component>
+# ------------------------------------------------------------------------------
+
+# Local imports.
+from .abstract_command_stack_action import AbstractCommandStackAction
+
+
+
[docs]class UndoAction(AbstractCommandStackAction): + """ An action that undos the last command of the active command stack. """ + + ########################################################################### + # 'Action' interface. + ########################################################################### + +
[docs] def perform(self, event): + """ Perform the action. """ + + self.undo_manager.undo()
+ + ########################################################################### + # 'AbstractUndoAction' interface. + ########################################################################### + + def _update_action(self): + """ Update the state of the action. """ + + name = self.undo_manager.undo_name + + if name: + name = "&Undo " + name + self.enabled = True + else: + name = "&Undo" + self.enabled = False + + self.name = name
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/undo/command_stack.html b/_modules/apptools/undo/command_stack.html new file mode 100644 index 000000000..bba612ffb --- /dev/null +++ b/_modules/apptools/undo/command_stack.html @@ -0,0 +1,448 @@ + + + + + + + apptools.undo.command_stack — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.undo.command_stack

+# ------------------------------------------------------------------------------
+# Copyright (c) 2008, Riverbank Computing Limited
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in enthought/LICENSE.txt and may be redistributed only
+# under the conditions described in the aforementioned license.  The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+# Thanks for using Enthought open source!
+#
+# Author: Riverbank Computing Limited
+# Description: <Enthought undo package component>
+# ------------------------------------------------------------------------------
+
+# Enthought library imports.
+from traits.api import (
+    Bool,
+    HasTraits,
+    Instance,
+    Int,
+    List,
+    Property,
+    Str,
+    provides,
+)
+
+# Local imports.
+from .abstract_command import AbstractCommand
+from .i_command import ICommand
+from .i_command_stack import ICommandStack
+from .i_undo_manager import IUndoManager
+
+
+class _StackEntry(HasTraits):
+    """ The _StackEntry class is a single entry on a command stack. """
+
+    #### '_StackEntry' interface ##############################################
+
+    # Set if the entry corresponds to a clean point on the stack.
+    clean = Bool(False)
+
+    # The command instance.
+    command = Instance(ICommand)
+
+    # The sequence number of the entry.
+    sequence_nr = Int
+
+
+class _MacroCommand(AbstractCommand):
+    """ The _MacroCommand class is an internal command that handles macros. """
+
+    #### '_MacroCommand' interface ############################################
+
+    # The commands that make up this macro.
+    macro_commands = List(Instance(ICommand))
+
+    ###########################################################################
+    # 'ICommand' interface.
+    ###########################################################################
+
+    def do(self):
+        """ Invoke the command. """
+
+        # This is a dummy.
+        return None
+
+    def merge(self, other):
+        """ Try and merge a command. """
+
+        if len(self.macro_commands) == 0:
+            merged = False
+        else:
+            merged = self.macro_commands[-1].merge(other)
+
+        return merged
+
+    def redo(self):
+        """ Redo the sub-commands. """
+
+        for cmd in self.macro_commands:
+            cmd.redo()
+
+        # Macros cannot return values.
+        return None
+
+    def undo(self):
+        """ Undo the sub-commands. """
+
+        for cmd in self.macro_commands:
+            cmd.undo()
+
+
+
[docs]@provides(ICommandStack) +class CommandStack(HasTraits): + """The CommandStack class is the default implementation of the + ICommandStack interface. + """ + + #### 'ICommandStack' interface ############################################ + + # This is the clean state of the stack. Its value changes as commands are + # undone and redone. It can also be explicity set to mark the current + # stack position as being clean (when the data is saved to disk for + # example). + clean = Property(Bool) + + # This is the name of the command that can be redone. It will be empty if + # there is no command that can be redone. It is maintained by the undo + # stack. + redo_name = Property(Str) + + # This is the undo manager that manages this stack. + undo_manager = Instance(IUndoManager) + + # This is the name of the command that can be undone. It will be empty if + # there is no command that can be undone. It is maintained by the undo + # stack. + undo_name = Property(Str) + + #### Private interface #################################################### + + # The current index into the stack (ie. the last command that was done). + _index = Int(-1) + + # The current macro stack. + _macro_stack = List(Instance(_MacroCommand)) + + # The stack itself. + _stack = List(Instance(_StackEntry)) + + ########################################################################### + # 'ICommandStack' interface. + ########################################################################### + +
[docs] def begin_macro(self, name): + """This begins a macro by creating an empty command with the given + 'name'. All subsequent calls to 'push()' create commands that will be + children of the empty command until the next call to 'end_macro()'. + Macros may be nested. The stack is disabled (ie. nothing can be undone + or redone) while a macro is being created (ie. while there is an + outstanding 'end_macro()' call). + """ + + command = _MacroCommand(name=name) + self.push(command) + self._macro_stack.append(command)
+ +
[docs] def clear(self): + """This clears the stack, without undoing or redoing any commands, and + leaves the stack in a clean state. It is typically used when all + changes to the data have been abandoned. + """ + + self._index = -1 + self._stack = [] + self._macro_stack = [] + + self.undo_manager.stack_updated = self
+ +
[docs] def end_macro(self): + """ This ends a macro. """ + + try: + self._macro_stack.pop() + except IndexError: + pass
+ +
[docs] def push(self, command): + """This executes a command and saves it on the command stack so that + it can be subsequently undone and redone. 'command' is an instance + that implements the ICommand interface. Its 'do()' method is called + to execute the command. If any value is returned by 'do()' then it is + returned by 'push()'. + """ + + # See if the command can be merged with the previous one. + if len(self._macro_stack) == 0: + if self._index >= 0: + merged = self._stack[self._index].command.merge(command) + else: + merged = False + else: + merged = self._macro_stack[-1].merge(command) + + # Increment the global sequence number. + if not merged: + self.undo_manager.sequence_nr += 1 + + # Execute the command. + result = command.do() + + # Do nothing more if the command was merged. + if merged: + return result + + # Only update the command stack if there is no current macro. + if len(self._macro_stack) == 0: + # Remove everything on the stack after the last command that was + # done. + self._index += 1 + del self._stack[self._index:] + + # Create a new stack entry and add it to the stack. + entry = _StackEntry( + command=command, sequence_nr=self.undo_manager.sequence_nr + ) + + self._stack.append(entry) + self.undo_manager.stack_updated = self + else: + # Add the command to the parent macro command. + self._macro_stack[-1].macro_commands.append(command) + + return result
+ +
[docs] def redo(self, sequence_nr=0): + """If 'sequence_nr' is 0 then the last command that was undone is + redone and any result returned. Otherwise commands are redone up to + and including the given 'sequence_nr' and any result of the last of + these is returned. + """ + + # Make sure a redo is valid in the current context. + if self.redo_name == "": + return None + + if sequence_nr == 0: + result = self._redo_one() + else: + result = None + + while self._index + 1 < len(self._stack): + if self._stack[self._index + 1].sequence_nr > sequence_nr: + break + + result = self._redo_one() + + self.undo_manager.stack_updated = self + + return result
+ +
[docs] def undo(self, sequence_nr=0): + """If 'sequence_nr' is 0 then the last command is undone. Otherwise + commands are undone up to and including the given 'sequence_nr'. + """ + + # Make sure an undo is valid in the current context. + if self.undo_name == "": + return + + if sequence_nr == 0: + self._undo_one() + else: + while self._index >= 0: + if self._stack[self._index].sequence_nr <= sequence_nr: + break + + self._undo_one() + + self.undo_manager.stack_updated = self
+ + ########################################################################### + # Private interface. + ########################################################################### + + def _redo_one(self): + """ Redo the command at the current index and return the result. """ + + self._index += 1 + entry = self._stack[self._index] + + return entry.command.redo() + + def _undo_one(self): + """ Undo the command at the current index. """ + + entry = self._stack[self._index] + self._index -= 1 + + entry.command.undo() + + def _get_clean(self): + """ Get the clean state of the stack. """ + + if self._index >= 0: + clean = self._stack[self._index].clean + else: + clean = True + + return clean + + def _set_clean(self, clean): + """ Set the clean state of the stack. """ + + if self._index >= 0: + self._stack[self._index].clean = clean + + def _get_redo_name(self): + """ Get the name of the redo command, if any. """ + + redo_name = "" + + if len(self._macro_stack) == 0 and self._index + 1 < len(self._stack): + redo_name = self._stack[self._index + 1].command.name.replace( + "&", "" + ) + + return redo_name + + def _get_undo_name(self): + """ Get the name of the undo command, if any. """ + + undo_name = "" + + if len(self._macro_stack) == 0 and self._index >= 0: + command = self._stack[self._index].command + undo_name = command.name.replace("&", "") + + return undo_name
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/undo/i_command.html b/_modules/apptools/undo/i_command.html new file mode 100644 index 000000000..2be484b01 --- /dev/null +++ b/_modules/apptools/undo/i_command.html @@ -0,0 +1,194 @@ + + + + + + + apptools.undo.i_command — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.undo.i_command

+# ------------------------------------------------------------------------------
+# Copyright (c) 2007, Riverbank Computing Limited
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in enthought/LICENSE.txt and may be redistributed only
+# under the conditions described in the aforementioned license.  The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+# Thanks for using Enthought open source!
+#
+# Author: Riverbank Computing Limited
+# Description: <Enthought undo package component>
+# ------------------------------------------------------------------------------
+
+
+# Enthought library imports.
+from traits.api import Any, Interface, Str
+
+
+
[docs]class ICommand(Interface): + """The command interface. The state of the data can be changed by passing + an instance that implements this interface to the 'push()' method of a + command stack along with any arguments. + """ + + #### 'ICommand' interface ################################################# + + # This is the data on which the command operates. + data = Any + + # This is the name of the command as it will appear in any GUI element. It + # may include '&' which will be automatically removed whenever it is + # inappropriate. + name = Str + + ########################################################################### + # 'ICommand' interface. + ########################################################################### + +
[docs] def do(self): + """This is called by the command stack to do the command and to return + any value. The command must save any state necessary for the 'redo()' + and 'undo()' methods to work. The class's __init__() must also ensure + that deep copies of any arguments are made if appropriate. It is + guaranteed that this will only ever be called once and that it will be + called before any call to 'redo()' or 'undo()'. + """
+ +
[docs] def merge(self, other): + """This is called by the command stack to try and merge another + command with this one. True is returned if the commands were merged. + 'other' is the command that is about to be executed. If the commands + are merged then 'other' will discarded and not placed on the command + stack. A subsequent undo or redo of this modified command must have + the same effect as the two original commands. + """
+ +
[docs] def redo(self): + """This is called by the command stack to redo the command. Any + returned value will replace the value that the command stack references + from the original call to 'do()' or previous call to 'redo()'. + """
+ +
[docs] def undo(self): + """ This is called by the command stack to undo the command. """
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/undo/i_command_stack.html b/_modules/apptools/undo/i_command_stack.html new file mode 100644 index 000000000..38cfbb57b --- /dev/null +++ b/_modules/apptools/undo/i_command_stack.html @@ -0,0 +1,220 @@ + + + + + + + apptools.undo.i_command_stack — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.undo.i_command_stack

+# ------------------------------------------------------------------------------
+# Copyright (c) 2007, Riverbank Computing Limited
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in enthought/LICENSE.txt and may be redistributed only
+# under the conditions described in the aforementioned license.  The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+# Thanks for using Enthought open source!
+#
+# Author: Riverbank Computing Limited
+# Description: <Enthought undo package component>
+# ------------------------------------------------------------------------------
+
+# Enthought library imports.
+from traits.api import Bool, Instance, Interface, Str
+
+# Local imports.
+from .i_undo_manager import IUndoManager
+
+
+
[docs]class ICommandStack(Interface): + """The command stack interface. A command stack is responsible for + managing the changes to a data model and recording those changes so that + they can be undone or redone. + """ + + #### 'ICommandStack' interface ############################################ + + # This is the clean state of the stack. Its value changes as commands are + # undone and redone. It can also be explicity set to mark the current + # stack position as being clean (when the data is saved to disk for + # example). + clean = Bool + + # This is the name of the command that can be redone. It will be empty if + # there is no command that can be redone. It is maintained by the undo + # stack. + redo_name = Str + + # This is the undo manager that manages this stack. + undo_manager = Instance(IUndoManager) + + # This is the name of the command that can be undone. It will be empty if + # there is no command that can be undone. It is maintained by the undo + # stack. + undo_name = Str + + ########################################################################### + # 'ICommandStack' interface. + ########################################################################### + +
[docs] def begin_macro(self, name): + """This begins a macro by creating an empty command with the given + 'name'. The commands passed to all subsequent calls to 'push()' will + be contained in the macro until the next call to 'end_macro()'. Macros + may be nested. The stack is disabled (ie. nothing can be undone or + redone) while a macro is being created (ie. while there is an + outstanding 'end_macro()' call). + """
+ +
[docs] def clear(self): + """This clears the stack, without undoing or redoing any commands, and + leaves the stack in a clean state. It is typically used when all + changes to the data have been abandoned. + """
+ +
[docs] def end_macro(self): + """ This ends a macro. """
+ +
[docs] def push(self, command): + """This executes a command and saves it on the command stack so that + it can be subsequently undone and redone. 'command' is an instance + that implements the ICommand interface. Its 'do()' method is called + to execute the command. If any value is returned by 'do()' then it is + returned by 'push()'. The command stack will keep a reference to the + result so that it can recognise it as an argument to a subsequent + command (which allows a script to properly save a result needed later). + """
+ +
[docs] def redo(self, sequence_nr=0): + """If 'sequence_nr' is 0 then the last command that was undone is + redone and any result returned. Otherwise commands are redone up to + and including the given 'sequence_nr' and any result of the last of + these is returned. + """
+ +
[docs] def undo(self, sequence_nr=0): + """If 'sequence_nr' is 0 then the last command is undone. Otherwise + commands are undone up to and including the given 'sequence_nr'. + """
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/undo/i_undo_manager.html b/_modules/apptools/undo/i_undo_manager.html new file mode 100644 index 000000000..cefd9792d --- /dev/null +++ b/_modules/apptools/undo/i_undo_manager.html @@ -0,0 +1,192 @@ + + + + + + + apptools.undo.i_undo_manager — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.undo.i_undo_manager

+# ------------------------------------------------------------------------------
+# Copyright (c) 2008, Riverbank Computing Limited
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in enthought/LICENSE.txt and may be redistributed only
+# under the conditions described in the aforementioned license.  The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+# Thanks for using Enthought open source!
+#
+# Author: Riverbank Computing Limited
+# Description: <Enthought undo package component>
+# ------------------------------------------------------------------------------
+
+# Enthought library imports.
+from traits.api import Bool, Event, Instance, Int, Interface, Str
+
+
+
[docs]class IUndoManager(Interface): + """The undo manager interface. An undo manager is responsible for one or + more command stacks. Typically an application would have a single undo + manager. + """ + + #### 'IUndoManager' interface ############################################# + + # This is the currently active command stack and may be None. Typically it + # is set when some sort of editor becomes active. + active_stack = Instance("apptools.undo.api.ICommandStack") + + # This reflects the clean state of the currently active command stack. It + # is intended to support a "document modified" indicator in the GUI. It is + # maintained by the undo manager. + active_stack_clean = Bool + + # This is the name of the command that can be redone. It will be empty if + # there is no command that can be redone. It is maintained by the undo + # manager. + redo_name = Str + + # This is the sequence number of the next command to be performed. It is + # incremented immediately before a command is invoked (by its 'do()' + # method). + sequence_nr = Int + + # This event is fired when the index of a command stack changes. Note that + # it may not be the active stack. + stack_updated = Event(Instance("apptools.undo.api.ICommandStack")) + + # This is the name of the command that can be undone. It will be empty if + # there is no command that can be undone. It is maintained by the undo + # manager. + undo_name = Str + + ########################################################################### + # 'IUndoManager' interface. + ########################################################################### + +
[docs] def redo(self): + """ Redo the last undone command of the active command stack. """
+ +
[docs] def undo(self): + """ Undo the last command of the active command stack. """
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/apptools/undo/undo_manager.html b/_modules/apptools/undo/undo_manager.html new file mode 100644 index 000000000..0707f8edf --- /dev/null +++ b/_modules/apptools/undo/undo_manager.html @@ -0,0 +1,251 @@ + + + + + + + apptools.undo.undo_manager — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for apptools.undo.undo_manager

+# ------------------------------------------------------------------------------
+# Copyright (c) 2008, Riverbank Computing Limited
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in enthought/LICENSE.txt and may be redistributed only
+# under the conditions described in the aforementioned license.  The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+# Thanks for using Enthought open source!
+#
+# Author: Riverbank Computing Limited
+# Description: <Enthought undo package component>
+# ------------------------------------------------------------------------------
+
+# Enthought library imports.
+from traits.api import (
+    Bool,
+    Event,
+    HasTraits,
+    Instance,
+    Int,
+    Property,
+    Str,
+    provides,
+)
+
+# Local imports.
+from .i_undo_manager import IUndoManager
+
+
+
[docs]@provides(IUndoManager) +class UndoManager(HasTraits): + """The UndoManager class is the default implementation of the + IUndoManager interface. + """ + + #### 'IUndoManager' interface ############################################# + + # This is the currently active command stack and may be None. Typically it + # is set when some sort of editor becomes active. + active_stack = Instance("apptools.undo.api.ICommandStack") + + # This reflects the clean state of the currently active command stack. It + # is intended to support a "document modified" indicator in the GUI. It is + # maintained by the undo manager. + active_stack_clean = Property(Bool) + + # This is the name of the command that can be redone. It will be empty if + # there is no command that can be redone. It is maintained by the undo + # manager. + redo_name = Property(Str) + + # This is the sequence number of the next command to be performed. It is + # incremented immediately before a command is invoked (by its 'do()' + # method). + sequence_nr = Int + + # This event is fired when the index of a command stack changes. The value + # of the event is the stack that has changed. Note that it may not be the + # active stack. + stack_updated = Event + + # This is the name of the command that can be undone. It will be empty if + # there is no command that can be undone. It is maintained by the undo + # manager. + undo_name = Property(Str) + + ########################################################################### + # 'IUndoManager' interface. + ########################################################################### + +
[docs] def redo(self): + """ Redo the last undone command of the active command stack. """ + + if self.active_stack is not None: + self.active_stack.redo()
+ +
[docs] def undo(self): + """ Undo the last command of the active command stack. """ + + if self.active_stack is not None: + self.active_stack.undo()
+ + ########################################################################### + # Private interface. + ########################################################################### + + def _active_stack_changed(self, new): + """ Handle a different stack becoming active. """ + + # Pretend that the stack contents have changed. + self.stack_updated = new + + def _get_active_stack_clean(self): + """ Get the current clean state. """ + + if self.active_stack is None: + active_stack_clean = True + else: + active_stack_clean = self.active_stack.clean + + return active_stack_clean + + def _get_redo_name(self): + """ Get the current redo name. """ + + if self.active_stack is None: + redo_name = "" + else: + redo_name = self.active_stack.redo_name + + return redo_name + + def _get_undo_name(self): + """ Get the current undo name. """ + + if self.active_stack is None: + undo_name = "" + else: + undo_name = self.active_stack.undo_name + + return undo_name
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/index.html b/_modules/index.html new file mode 100644 index 000000000..53b122600 --- /dev/null +++ b/_modules/index.html @@ -0,0 +1,215 @@ + + + + + + + Overview: module code — Apptools Documentation + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

All modules for which code is available

+ + +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/traits/trait_types.html b/_modules/traits/trait_types.html new file mode 100644 index 000000000..aee7cb0dd --- /dev/null +++ b/_modules/traits/trait_types.html @@ -0,0 +1,4427 @@ + + + + + + + traits.trait_types — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for traits.trait_types

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+
+""" Core Trait definitions.
+"""
+
+import collections.abc
+import datetime
+from importlib import import_module
+import operator
+import re
+import sys
+try:
+    from os import fspath
+except ImportError:
+    fspath = None
+from os.path import isfile, isdir
+from types import FunctionType, MethodType, ModuleType
+import uuid
+
+from .constants import DefaultValue, TraitKind, ValidateTrait
+from .trait_base import (
+    strx,
+    get_module_name,
+    HandleWeakRef,
+    class_of,
+    RangeTypes,
+    safe_contains,
+    SequenceTypes,
+    TypeTypes,
+    Undefined,
+    TraitsCache,
+    xgetattr,
+)
+from .trait_converters import trait_from, trait_cast
+from .trait_dict_object import TraitDictEvent, TraitDictObject
+from .trait_errors import TraitError
+from .trait_list_object import TraitListEvent, TraitListObject
+from .trait_set_object import TraitSetEvent, TraitSetObject
+from .trait_type import TraitType, _infer_default_value_type
+from .traits import (
+    Trait,
+    _TraitMaker,
+    _InstanceArgs,
+)
+from .util.import_symbol import import_symbol
+
+# TraitsUI integration imports
+from .editor_factories import (
+    code_editor,
+    html_editor,
+    password_editor,
+    shell_editor,
+    date_editor,
+    datetime_editor,
+    time_editor,
+    list_editor,
+)
+
+
+# Constants
+
+SetTypes = SequenceTypes + (set,)
+
+# Numeric type fast validator definitions
+
+# A few words about the next block of code:
+
+# The coerce validator is a generic validator for possibly coercible types
+# (see validate_trait_coerce_type in ctraits.c).
+#
+# The tuples below are of the form
+# (ValidateTrait.coerce, type1, [type2, type3, ...],
+#     [None, ctype1, [ctype2, ...]])
+#
+# 'type1' corresponds to the main type for the trait
+# 'None' acts as the separator between 'types' and 'ctypes' (coercible types)
+#
+# The validation passes if:
+# 1) The trait value type is (a subtype of) one of 'type1', 'type2',  ...
+#    in which case the value is returned as-is
+# or
+# 2) The trait value type is (a subtype of) one of 'ctype1', 'ctype2', ...
+#    in which case the value is returned coerced to trait type using
+#    'return type1(value')
+
+try:
+    # The numpy enhanced definitions:
+    from numpy import integer, floating, complexfloating, bool_
+
+    int_fast_validate = (ValidateTrait.coerce, int, integer)
+    float_fast_validate = (
+        ValidateTrait.coerce,
+        float,
+        floating,
+        None,
+        int,
+        integer,
+    )
+    complex_fast_validate = (
+        ValidateTrait.coerce,
+        complex,
+        complexfloating,
+        None,
+        float,
+        floating,
+        int,
+        integer,
+    )
+    bool_fast_validate = (ValidateTrait.coerce, bool, None, bool_)
+    # Tuple or single type suitable for an isinstance check.
+    _BOOL_TYPES = (bool, bool_)
+except ImportError:
+    # The standard python definitions (without numpy):
+    int_fast_validate = (ValidateTrait.coerce, int)
+    float_fast_validate = (ValidateTrait.coerce, float, None, int)
+    complex_fast_validate = (ValidateTrait.coerce, complex, None, float, int)
+    bool_fast_validate = (ValidateTrait.coerce, bool)
+    # Tuple or single type suitable for an isinstance check.
+    _BOOL_TYPES = bool
+
+
+def default_text_editor(trait, type=None):
+    """ Return a default text editor for a trait.
+
+    Parameters
+    ----------
+    trait : TraitType
+        The trait we are constructing the editor for.
+    type : callable, optional
+        A callable (usually a Python type) to use to evaluate the text content
+        of the editor and return the correct type of value for the trait.
+
+    Returns
+    -------
+    TextEditor
+        A TraitsUI TextEditor instance for the trait.
+    """
+    auto_set = trait.auto_set
+    if auto_set is None:
+        auto_set = True
+
+    enter_set = trait.enter_set or False
+
+    from traitsui.api import TextEditor
+
+    if type is None:
+        return TextEditor(auto_set=auto_set, enter_set=enter_set)
+
+    return TextEditor(auto_set=auto_set, enter_set=enter_set, evaluate=type)
+
+
+# Generic validators
+
+def _validate_int(value):
+    """ Convert an integer-like Python object to an int, or raise TypeError.
+    """
+    if type(value) is int:
+        return value
+    else:
+        return int(operator.index(value))
+
+
+def _validate_float(value):
+    """ Convert an arbitrary Python object to a float, or raise TypeError.
+    """
+    if type(value) is float:  # fast path for common case
+        return value
+    try:
+        nb_float = type(value).__float__
+    except AttributeError:
+        raise TypeError(
+            "Object of type {!r} not convertible to float".format(type(value))
+        )
+    return nb_float(value)
+
+
+# Trait Types
+
+class Any(TraitType):
+    """ A trait type whose value can be anything.
+    """
+
+    #: The default value for the trait:
+    default_value = None
+
+    #: A description of the type of value this trait accepts:
+    info_text = "any value"
+
+
+class BaseInt(TraitType):
+    """ A trait type whose value must be an int.
+
+    Values which support the Python index protocol will validate and will be
+    converted to the corresponding int value.
+    """
+
+    #: The function to use for evaluating strings to this type:
+    evaluate = int
+
+    #: The default value for the trait:
+    default_value = 0
+
+    #: A description of the type of value this trait accepts:
+    info_text = "an integer"
+
+    def validate(self, object, name, value):
+        """ Validates that a specified value is valid for this trait.
+        """
+        try:
+            return _validate_int(value)
+        except TypeError:
+            self.error(object, name, value)
+
+    def create_editor(self):
+        """ Returns the default traits UI editor for this type of trait.
+        """
+        return default_text_editor(self, int)
+
+
+class Int(BaseInt):
+    """ A fast-validating trait type whose value must be an integer.
+
+    Values which support the Python index protocol will validate and will be
+    converted to the corresponding int value.
+    """
+
+    #: The C-level fast validator to use:
+    fast_validate = (ValidateTrait.int,)
+
+
+class BaseFloat(TraitType):
+    """ A trait type whose value must be a float.
+
+    Values which support automatic conversion to floats via the Python
+    __float__ method will validate and will be converted to the corresponding
+    float value.
+    """
+
+    #: The function to use for evaluating strings to this type:
+    evaluate = float
+
+    #: The default value for the trait:
+    default_value = 0.0
+
+    #: A description of the type of value this trait accepts:
+    info_text = "a float"
+
+    def validate(self, object, name, value):
+        """ Validates that a specified value is valid for this trait.
+
+        Note: The 'fast validator' version performs this check in C.
+        """
+        try:
+            return _validate_float(value)
+        except TypeError:
+            self.error(object, name, value)
+
+    def create_editor(self):
+        """ Returns the default traits UI editor for this type of trait.
+        """
+        return default_text_editor(self, float)
+
+
+class Float(BaseFloat):
+    """ A fast-validating trait type whose value must be a float.
+
+    Values which support automatic conversion to floats via the Python
+    __float__ method will validate and will be converted to the corresponding
+    float value.
+    """
+
+    #: The C-level fast validator to use:
+    fast_validate = (ValidateTrait.float,)
+
+
+class BaseComplex(TraitType):
+    """ A trait type whose value must be a complex number.
+
+    Integers and floating-point numbers will be converted to the
+    corresponding complex value.
+    """
+
+    #: The function to use for evaluating strings to this type:
+    evaluate = complex
+
+    #: The default value for the trait:
+    default_value = 0.0 + 0.0j
+
+    #: A description of the type of value this trait accepts:
+    info_text = "a complex number"
+
+    def validate(self, object, name, value):
+        """ Validates that a specified value is valid for this trait.
+
+        Note: The 'fast validator' version performs this check in C.
+        """
+        if isinstance(value, complex):
+            return value
+
+        if isinstance(value, (float, int)):
+            return complex(value)
+
+        self.error(object, name, value)
+
+    def create_editor(self):
+        """ Returns the default traits UI editor for this type of trait.
+        """
+        return default_text_editor(self, complex)
+
+
+class Complex(BaseComplex):
+    """ A fast-validating trait type whose value must be a complex number.
+
+    Integers and floating-point numbers will be converted to the
+    corresponding complex value.
+    """
+
+    #: The C-level fast validator to use:
+    fast_validate = complex_fast_validate
+
+
+class BaseStr(TraitType):
+    """ A trait type whose value must be a string.
+    """
+
+    #: The default value for the trait:
+    default_value = ""
+
+    #: A description of the type of value this trait accepts:
+    info_text = "a string"
+
+    def validate(self, object, name, value):
+        """ Validates that a specified value is valid for this trait.
+
+        Note: The 'fast validator' version performs this check in C.
+        """
+        if isinstance(value, str):
+            return value
+
+        self.error(object, name, value)
+
+    def create_editor(self):
+        """ Returns the default traits UI editor for this type of trait.
+        """
+        from .editor_factories import multi_line_text_editor
+
+        auto_set = self.auto_set
+        if auto_set is None:
+            auto_set = True
+        enter_set = self.enter_set or False
+
+        return multi_line_text_editor(auto_set, enter_set)
+
+
+class Str(BaseStr):
+    """ A fast-validating trait type whose value must be a complex number.
+    """
+
+    #: The C-level fast validator to use:
+    fast_validate = (ValidateTrait.coerce, str)
+
+
+class Title(Str):
+    """ A Str trait which by default uses a TraitsUI TitleEditor.
+    """
+
+    def create_editor(self):
+        """ Returns the default traits UI editor to use for a trait.
+        """
+        from traitsui.api import TitleEditor
+
+        if hasattr(self, "allow_selection"):
+            return TitleEditor(allow_selection=self.allow_selection)
+        else:
+            return TitleEditor()
+
+
+class BaseBytes(TraitType):
+    """ A trait type whose value must be a bytestring.
+    """
+
+    #: The default value for the trait:
+    default_value = b""
+
+    #: A description of the type of value this trait accepts:
+    info_text = "a bytes string"
+
+    #: An encoding to use with TraitsUI editors
+    encoding = None
+
+    def validate(self, object, name, value):
+        """ Validates that a specified value is valid for this trait.
+
+        Note: The 'fast validator' version performs this check in C.
+        """
+        if isinstance(value, bytes):
+            return value
+
+        self.error(object, name, value)
+
+    def create_editor(self):
+        """ Returns the default traits UI editor for this type of trait.
+        """
+        from .traits import bytes_editor
+
+        auto_set = self.auto_set
+        if auto_set is None:
+            auto_set = True
+        enter_set = self.enter_set or False
+
+        return bytes_editor(auto_set, enter_set, self.encoding)
+
+
+class Bytes(BaseBytes):
+    """ A fast-validating trait type whose value must be a bytestring.
+    """
+
+    #: The C-level fast validator to use:
+    fast_validate = (ValidateTrait.coerce, bytes)
+
+
+class BaseBool(TraitType):
+    """ A trait type whose value must be a bool.
+    """
+
+    #: The function to use for evaluating strings to this type:
+    evaluate = bool
+
+    #: The default value for the trait:
+    default_value = False
+
+    #: A description of the type of value this trait accepts:
+    info_text = "a boolean"
+
+    def validate(self, object, name, value):
+        """ Validates that a specified value is valid for this trait.
+
+        Note: The 'fast validator' version performs this check in C.
+        """
+        if isinstance(value, _BOOL_TYPES):
+            return bool(value)
+
+        self.error(object, name, value)
+
+    def create_editor(self):
+        """ Returns the default traits UI editor for this type of trait.
+        """
+        from traitsui.api import BooleanEditor
+
+        return BooleanEditor()
+
+
+class Bool(BaseBool):
+    """ A fast-validating trait type whose value must be a bool.
+    """
+
+    #: The C-level fast validator to use:
+    fast_validate = bool_fast_validate
+
+
+class BaseCInt(BaseInt):
+    """ A coercing trait type whose value is an integer.
+    """
+
+    #: The function to use for evaluating strings to this type:
+    evaluate = int
+
+    def validate(self, object, name, value):
+        """ Validates that a specified value is valid for this trait.
+
+        Note: The 'fast validator' version performs this check in C.
+        """
+        try:
+            return int(value)
+        except (ValueError, TypeError):
+            self.error(object, name, value)
+
+
+class CInt(BaseCInt):
+    """ A fast-validating, coercing trait type whose value is an int.
+    """
+
+    #: The C-level fast validator to use:
+    fast_validate = (ValidateTrait.cast, int)
+
+
+class BaseCFloat(BaseFloat):
+    """ A coercing trait type whose value is a float.
+    """
+
+    #: The function to use for evaluating strings to this type:
+    evaluate = float
+
+    def validate(self, object, name, value):
+        """ Validates that a specified value is valid for this trait.
+
+        Note: The 'fast validator' version performs this check in C.
+        """
+        try:
+            return float(value)
+        except (ValueError, TypeError):
+            self.error(object, name, value)
+
+
+class CFloat(BaseCFloat):
+    """ A fast-validating, coercing trait type whose value is a float.
+    """
+
+    #: The C-level fast validator to use:
+    fast_validate = (ValidateTrait.cast, float)
+
+
+class BaseCComplex(BaseComplex):
+    """ A coercing trait type whose value is a complex number.
+    """
+
+    #: The function to use for evaluating strings to this type:
+    evaluate = complex
+
+    def validate(self, object, name, value):
+        """ Validates that a specified value is valid for this trait.
+
+        Note: The 'fast validator' version performs this check in C.
+        """
+        try:
+            return complex(value)
+        except (ValueError, TypeError):
+            self.error(object, name, value)
+
+
+class CComplex(BaseCComplex):
+    """ A fast-validating, coercing trait type whose value is a complex number.
+    """
+
+    #: The C-level fast validator to use:
+    fast_validate = (ValidateTrait.cast, complex)
+
+
+class BaseCStr(BaseStr):
+    """ A coercing trait type whose value is a string.
+    """
+
+    def validate(self, object, name, value):
+        """ Validates that a specified value is valid for this trait.
+
+        Note: The 'fast validator' version performs this check in C.
+        """
+        try:
+            return str(value)
+        except:
+            self.error(object, name, value)
+
+
+class CStr(BaseCStr):
+    """ A fast-validating, coercing trait type whose value is a string.
+    """
+
+    #: The C-level fast validator to use:
+    fast_validate = (ValidateTrait.cast, str)
+
+
+class BaseCBytes(BaseBytes):
+    """ A coercing trait type whose value is a bytestring.
+    """
+
+    def validate(self, object, name, value):
+        """ Validates that a specified value is valid for this trait.
+
+        Note: The 'fast validator' version performs this check in C.
+        """
+        try:
+            return bytes(value)
+        except:
+            self.error(object, name, value)
+
+
+class CBytes(BaseCBytes):
+    """ A fast-validating, coercing trait type whose value is a bytestring.
+    """
+
+    #: The C-level fast validator to use:
+    fast_validate = (ValidateTrait.cast, bytes)
+
+
+class BaseCBool(BaseBool):
+    """ A coercing trait type whose value is a bool.
+    """
+
+    #: The function to use for evaluating strings to this type:
+    evaluate = bool
+
+    def validate(self, object, name, value):
+        """ Validates that a specified value is valid for this trait.
+
+        Note: The 'fast validator' version performs this check in C.
+        """
+        try:
+            return bool(value)
+        except:
+            self.error(object, name, value)
+
+
+class CBool(BaseCBool):
+    """ A fast-validating, coercing trait type whose value is a bool.
+    """
+
+    #: The C-level fast validator to use:
+    fast_validate = (ValidateTrait.cast, bool)
+
+
+class String(TraitType):
+    """ A trait type whose value must be a string with optional constraints.
+
+    The value is a string whose length is in a specified range, and which
+    optionally matches a specified regular expression.
+
+    Parameters
+    ----------
+    value : str
+        The default value for the string.
+    minlen : integer
+        The minimum length allowed for the string.
+    maxlen : integer
+        The maximum length allowed for the string.
+    regex : str
+        A Python regular expression that the string must match.
+    **metadata
+        The trait metadata for the trait.
+
+    Attributes
+    ----------
+    minlen : integer
+        The minimum length allowed for the string.
+    maxlen : integer
+        The maximum length allowed for the string.
+    regex : str
+        A Python regular expression that the string must match.
+    """
+
+    def __init__(
+        self, value="", minlen=0, maxlen=sys.maxsize, regex="", **metadata
+    ):
+        super(String, self).__init__(value, **metadata)
+        self.minlen = max(0, minlen)
+        self.maxlen = max(self.minlen, maxlen)
+        self.regex = regex
+        self._init()
+
+    def _init(self):
+        """ Completes initialization of the trait at construction or unpickling
+        time.
+        """
+        self._validate = "validate_all"
+        if self.regex != "":
+            self.match = re.compile(self.regex).match
+            if (self.minlen == 0) and (self.maxlen == sys.maxsize):
+                self._validate = "validate_regex"
+        elif (self.minlen == 0) and (self.maxlen == sys.maxsize):
+            self._validate = "validate_str"
+        else:
+            self._validate = "validate_len"
+
+    def validate(self, object, name, value):
+        """ Validates that the value is a valid string.
+        """
+        return getattr(self, self._validate)(object, name, value)
+
+    def validate_all(self, object, name, value):
+        """ Validates that the value is a valid string in the specified length
+            range which matches the specified regular expression.
+        """
+        try:
+            value = strx(value)
+            if (self.minlen <= len(value) <= self.maxlen) and (
+                self.match(value) is not None
+            ):
+                return value
+        except:
+            pass
+
+        self.error(object, name, value)
+
+    def validate_str(self, object, name, value):
+        """ Validates that the value is a valid string.
+        """
+        try:
+            return strx(value)
+        except:
+            pass
+
+        self.error(object, name, value)
+
+    def validate_len(self, object, name, value):
+        """ Validates that the value is a valid string in the specified length
+        range.
+        """
+        try:
+            value = strx(value)
+            if self.minlen <= len(value) <= self.maxlen:
+                return value
+        except:
+            pass
+
+        self.error(object, name, value)
+
+    def validate_regex(self, object, name, value):
+        """ Validates that the value is a valid string which matches the
+        specified regular expression.
+        """
+        try:
+            value = strx(value)
+            if self.match(value) is not None:
+                return value
+        except:
+            pass
+
+        self.error(object, name, value)
+
+    def info(self):
+        """ Returns a description of the trait.
+        """
+        msg = ""
+        if (self.minlen != 0) and (self.maxlen != sys.maxsize):
+            msg = " between %d and %d characters long" % (
+                self.minlen,
+                self.maxlen,
+            )
+        elif self.maxlen != sys.maxsize:
+            msg = " <= %d characters long" % self.maxlen
+        elif self.minlen != 0:
+            msg = " >= %d characters long" % self.minlen
+        if self.regex != "":
+            if msg != "":
+                msg += " and"
+            msg += " matching the pattern '%s'" % self.regex
+        return "a string" + msg
+
+    def create_editor(self):
+        """ Returns the default traits UI editor for this type of trait.
+        """
+        return default_text_editor(self)
+
+    def __getstate__(self):
+        """ Returns the current state of the trait.
+        """
+        result = self.__dict__.copy()
+        for name in ["validate", "match"]:
+            if name in result:
+                del result[name]
+
+        return result
+
+    def __setstate__(self, state):
+        """ Sets the current state of the trait.
+        """
+        self.__dict__.update(state)
+        self._init()
+
+
+class Regex(String):
+    """ A trait type whose value must match a regular expression.
+
+    Parameters
+    ----------
+    value : str
+        The default value of the trait.
+    regex : str
+        The regular expression that the trait value must match.
+    **metadata
+        Trait metadata.
+    """
+
+    def __init__(self, value="", regex=".*", **metadata):
+        super(Regex, self).__init__(value=value, regex=regex, **metadata)
+
+
+class Code(String):
+    """ A trait type whose value holds a string of source code.
+
+    Validation does not perform any sort of syntax checking. The default
+    TraitsUI editor is a CodeEditor.
+    """
+
+    #: The standard metadata for the trait:
+    metadata = {"editor": code_editor}
+
+
+class HTML(String):
+    """ A trait type whose value holds an HTML string.
+
+    The validation of the value does not enforce HTML syntax.  The default
+    TraitsUI editor is an HTMLEditor.
+    """
+
+    #: The standard metadata for the trait:
+    metadata = {"editor": html_editor}
+
+
+class Password(String):
+    """ A trait type whose value holds a password string.
+
+    The default TraitsUI editor is an PasswordEditor, which obscures text
+    entered by the user.
+    """
+
+    #: The standard metadata for the trait:
+    metadata = {"editor": password_editor}
+
+
+class BaseCallable(TraitType):
+    """ A trait type whose value must be a Python callable.
+    """
+
+    #: The standard metadata for the trait:
+    metadata = {"copy": "ref"}
+
+    #: The default value for the trait:
+    default_value = None
+
+    #: A description of the type of value this trait accepts:
+    info_text = "a callable value"
+
+    def validate(self, object, name, value):
+        """ Validates that the value is a Python callable.
+        """
+        if (value is None) or callable(value):
+            return value
+
+        self.error(object, name, value)
+
+
+class Callable(BaseCallable):
+    """ A fast-validating trait type whose value must be a Python callable.
+    """
+    def __init__(self, value=None, allow_none=True, **metadata):
+
+        self.fast_validate = (ValidateTrait.callable, allow_none)
+
+        default_value = metadata.pop("default_value", value)
+
+        super().__init__(default_value, **metadata)
+
+
+class BaseType(TraitType):
+    """ A trait type whose value must be an instance of a Python type.
+
+    This is an abstract class and should not be directly instantiated.
+    """
+
+    def validate(self, object, name, value):
+        """ Validates that the value is a Python callable.
+        """
+        if isinstance(value, self.fast_validate[1:]):
+            return value
+
+        self.error(object, name, value)
+
+
+class This(BaseType):
+    """ A trait type whose value must be an instance of the defining class.
+    """
+
+    #: The C-level fast validator to use:
+    fast_validate = (ValidateTrait.self_type,)
+
+    #: A description of the type of value this trait accepts:
+    info_text = "an instance of the same type as the receiver"
+
+    def __init__(self, value=None, allow_none=True, **metadata):
+        super(This, self).__init__(value, **metadata)
+
+        if allow_none:
+            self.fast_validate = (ValidateTrait.self_type, None)
+            self.validate = self.validate_none
+            self.info = self.info_none
+
+    def validate(self, object, name, value):
+        if isinstance(value, object.__class__):
+            return value
+
+        self.error(object, name, value)
+
+    def validate_none(self, object, name, value):
+        if isinstance(value, object.__class__) or (value is None):
+            return value
+
+        self.error(object, name, value)
+
+    def info(self):
+        return "an instance of the same type as the receiver"
+
+    def info_none(self):
+        return "an instance of the same type as the receiver or None"
+
+
+class self(This):
+    """ A trait type whose default value is the object containing the trait.
+
+    The trait can be assigned to, but any new value must be an instance of
+    the defining class.
+    """
+
+    #: The default value type to use (i.e. 'self'):
+    default_value_type = DefaultValue.object
+
+
+class Function(TraitType):
+    """ A trait type whose value must be a function.
+    """
+
+    #: The C-level fast validator to use:
+    fast_validate = (ValidateTrait.coerce, FunctionType)
+
+    #: A description of the type of value this trait accepts:
+    info_text = "a function"
+
+
+class Method(TraitType):
+    """ A trait type whose value must be a method.
+    """
+
+    #: The C-level fast validator to use:
+    fast_validate = (ValidateTrait.coerce, MethodType)
+
+    #: A description of the type of value this trait accepts:
+    info_text = "a method"
+
+
+class Module(TraitType):
+    """ A trait type whose value must be a module.
+    """
+
+    #: The C-level fast validator to use:
+    fast_validate = (ValidateTrait.coerce, ModuleType)
+
+    #: A description of the type of value this trait accepts:
+    info_text = "a module"
+
+
+class Python(TraitType):
+    """ A trait type that behaves as a standard Python attribute.
+
+    This trait type allows any value to be assigned, and raises an
+    ValueError if an attempt is made to get the value before one has been
+    assigned. It has no default value. This trait is most often used in
+    conjunction with wildcard naming. See the *Traits User Manual* for
+    details on wildcards.
+    """
+
+    #: The standard metadata for the trait:
+    metadata = {"type": "python"}
+
+    #: The default value for the trait:
+    default_value = Undefined
+
+
+class ReadOnly(TraitType):
+    """ A trait type that is write-once, and then read-only.
+
+    The initial value of the attribute is the special, singleton object
+    Undefined. The trait allows any value to be assigned to the attribute
+    if the current value is the Undefined object. Once any other value is
+    assigned, no further assignment is allowed. Normally, the initial
+    assignment to the attribute is performed in the class constructor,
+    based on information passed to the constructor. If the read-only value
+    is known in advance of run time, use Constant instead of ReadOnly to
+    define the trait.
+    """
+
+    # Defines the CTrait type to use for this trait:
+    ctrait_type = TraitKind.read_only
+
+    #: The default value for the trait:
+    default_value = Undefined
+
+
+# Create a singleton instance as the trait:
+ReadOnly = ReadOnly()
+
+
+class Disallow(TraitType):
+    """ A trait that prevents any value from being assigned or read.
+
+    Any attempt to get or set the value of the trait attribute raises an
+    exception. This trait is most often used in conjunction with wildcard
+    naming, for example, to catch spelling mistakes in attribute names.
+
+    See the *Traits User Manual* for details on wildcards.
+    """
+
+    #: Defines the CTrait type to use for this trait:
+    ctrait_type = TraitKind.disallow
+
+
+# Create a singleton instance as the trait:
+Disallow = Disallow()
+
+
+class Constant(TraitType):
+    """ A trait type whose value is a constant.
+
+    Traits of this type are very space efficient (and fast) because
+    *value* is not stored in each instance using the trait, but only in
+    the trait object itself.
+
+    Parameters
+    ----------
+    value : any
+        The constant value for the trait.
+    **metadata
+        Trait metadata for the trait.
+    """
+
+    #: Defines the CTrait type to use for this trait:
+    ctrait_type = TraitKind.constant
+
+    #: The standard metadata for the trait:
+    metadata = {"type": "constant", "transient": True}
+
+
+class Delegate(TraitType):
+    """ A trait type whose value is delegated to a trait on another object.
+
+    This is a base class that shouldn't be used directly, rather use one of
+    the subclasses DelegatesTo or PrototypesFrom, depending on desired
+    behaviour.
+
+    An object containing a delegator trait attribute must contain a
+    second attribute that references the object containing the delegate
+    trait attribute. The name of this second attribute is passed as the
+    *delegate* argument.
+
+    The following rules govern the application of the prefix parameter:
+
+    * If *prefix* is empty or omitted, the delegation is to an attribute
+      of the delegate object with the same name as the delegator
+      attribute.
+    * If *prefix* is a valid Python attribute name, then the delegation
+      is to an attribute whose name is the value of *prefix*.
+    * If *prefix* ends with an asterisk ('*') and is longer than one
+      character, then the delegation is to an attribute whose name is
+      the value of *prefix*, minus the trailing asterisk, prepended to
+      the delegator attribute name.
+    * If *prefix* is equal to a single asterisk, the delegation is to an
+      attribute whose name is the value of the delegator object's
+      __prefix__ attribute prepended to delegator attribute name.
+
+    Parameters
+    ----------
+    delegate : str
+        The name of the trait that holds the HasTraits instance that the
+        value is delegated to.
+    prefix : str
+        The name of the trait on the delegate that holds the delegated
+        value.  If empty, then the name of this trait will be used.
+    modify : bool
+        Whether modifications of this trait are applied to the delegated
+        object.  This differentiates the behaviour of DelegatesTo and
+        PrototypedFrom.
+    listenable : bool
+        Whether changes to the delegated trait will fire listeners to
+        this trait.
+
+    Attributes
+    ----------
+    delegate : str
+        The name of the trait that holds the HasTraits instance that the
+        value is delegated to.
+    prefix : str
+        The name of the trait on the delegate that holds the delegated
+        value.  If empty, then the name of this trait will be used.
+    prefix_type : int
+        An integer giving the type of prefix being used.
+    modify : bool
+        Whether modifications of this trait are applied to the delegated
+        object.  This differentiates the behaviour of DelegatesTo and
+        PrototypedFrom.
+    """
+
+    #: Defines the CTrait type to use for this trait:
+    ctrait_type = TraitKind.delegate
+
+    #: The standard metadata for the trait:
+    metadata = {"type": "delegate", "transient": False}
+
+    def __init__(
+        self, delegate, prefix="", modify=False, listenable=True, **metadata
+    ):
+        """ Creates a Delegate trait.
+        """
+        if prefix == "":
+            prefix_type = 0
+        elif prefix[-1:] != "*":
+            prefix_type = 1
+        else:
+            prefix = prefix[:-1]
+            if prefix != "":
+                prefix_type = 2
+            else:
+                prefix_type = 3
+
+        metadata["_delegate"] = delegate
+        metadata["_prefix"] = prefix
+        metadata["_listenable"] = listenable
+
+        super(Delegate, self).__init__(**metadata)
+
+        self.delegate = delegate
+        self.prefix = prefix
+        self.prefix_type = prefix_type
+        self.modify = modify
+
+    def as_ctrait(self):
+        """ Returns a CTrait corresponding to the trait defined by this class.
+        """
+        trait = super(Delegate, self).as_ctrait()
+        trait.delegate(
+            self.delegate, self.prefix, self.prefix_type, self.modify
+        )
+
+        return trait
+
+
+class DelegatesTo(Delegate):
+    """ A trait type that matches the 'delegate' design pattern.
+
+    This defines a trait whose value and definition is "delegated" to
+    another trait on a different object.
+
+    An object containing a delegator trait attribute must contain a
+    second attribute that references the object containing the delegate
+    trait attribute. The name of this second attribute is passed as the
+    *delegate* argument to the DelegatesTo() function.
+
+    The following rules govern the application of the prefix parameter:
+
+    * If *prefix* is empty or omitted, the delegation is to an attribute
+      of the delegate object with the same name as the delegator
+      attribute.
+    * If *prefix* is a valid Python attribute name, then the delegation
+      is to an attribute whose name is the value of *prefix*.
+    * If *prefix* ends with an asterisk ('*') and is longer than one
+      character, then the delegation is to an attribute whose name is
+      the value of *prefix*, minus the trailing asterisk, prepended to
+      the delegator attribute name.
+    * If *prefix* is equal to a single asterisk, the delegation is to an
+      attribute whose name is the value of the delegator object's
+      __prefix__ attribute prepended to delegator attribute name.
+
+    Note that any changes to the delegator attribute are actually
+    applied to the corresponding attribute on the delegate object. The
+    original object containing the delegator trait is not modified.
+
+    Parameters
+    ----------
+    delegate : str
+        Name of the attribute on the current object which references
+        the object that is the trait's delegate.
+    prefix : str
+        A prefix or substitution applied to the original attribute when
+        looking up the delegated attribute.
+    listenable : bool
+        Indicates whether a listener can be attached to this attribute
+        such that changes to the delegated attribute will trigger it.
+    **metadata
+        Trait metadata for the trait.
+    """
+
+    def __init__(self, delegate, prefix="", listenable=True, **metadata):
+        super(DelegatesTo, self).__init__(
+            delegate,
+            prefix=prefix,
+            modify=True,
+            listenable=listenable,
+            **metadata
+        )
+
+
+class PrototypedFrom(Delegate):
+    """ A trait type that matches the 'prototype' design pattern.
+
+    This defines a trait whose default value and definition is "prototyped"
+    from another trait on a different object.
+
+    An object containing a prototyped trait attribute must contain a
+    second attribute that references the object containing the prototype
+    trait attribute. The name of this second attribute is passed as the
+    *prototype* argument to the PrototypedFrom() function.
+
+    The following rules govern the application of the prefix parameter:
+
+    * If *prefix* is empty or omitted, the prototype delegation is to an
+      attribute of the prototype object with the same name as the
+      prototyped attribute.
+    * If *prefix* is a valid Python attribute name, then the prototype
+      delegation is to an attribute whose name is the value of *prefix*.
+    * If *prefix* ends with an asterisk ('*') and is longer than one
+      character, then the prototype delegation is to an attribute whose
+      name is the value of *prefix*, minus the trailing asterisk,
+      prepended to the prototyped attribute name.
+    * If *prefix* is equal to a single asterisk, the prototype
+      delegation is to an attribute whose name is the value of the
+      prototype object's __prefix__ attribute prepended to the
+      prototyped attribute name.
+
+    Note that any changes to the prototyped attribute are made to the
+    original object, not the prototype object. The prototype object is
+    only used to define to trait type and default value.
+
+    Parameters
+    ----------
+    prototype : str
+        Name of the attribute on the current object which references the
+        object that is the trait's prototype.
+    prefix : str
+        A prefix or substitution applied to the original attribute when
+        looking up the prototyped attribute.
+    listenable : bool
+        Indicates whether a listener can be attached to this attribute
+        such that changes to the corresponding attribute on the
+        prototype object will trigger it.
+    **metadata
+        Trait metadata for the trait.
+    """
+
+    def __init__(self, prototype, prefix="", listenable=True, **metadata):
+        super(PrototypedFrom, self).__init__(
+            prototype,
+            prefix=prefix,
+            modify=False,
+            listenable=listenable,
+            **metadata
+        )
+
+
+class Expression(TraitType):
+    """ A trait type whose value must be a valid Python expression.
+
+    The compiled form of a valid expression is stored as the mapped value of
+    the trait.
+    """
+
+    #: The default value for the trait:
+    default_value = "0"
+
+    #: A description of the type of value this trait accepts:
+    info_text = "a valid Python expression"
+
+    #: Indicate that this is a mapped trait:
+    is_mapped = True
+
+    def validate(self, object, name, value):
+        """ Validates that a specified value is valid for this trait.
+        """
+        try:
+            return compile(value, "<string>", "eval")
+        except:
+            self.error(object, name, value)
+
+    def post_setattr(self, object, name, value):
+        """ Performs additional post-assignment processing.
+        """
+        object.__dict__[name + "_"] = value
+
+    def mapped_value(self, value):
+        """ Returns the 'mapped' value for the specified **value**.
+        """
+        return compile(value, "<string>", "eval")
+
+    def as_ctrait(self):
+        """ Returns a CTrait corresponding to the trait defined by this class.
+        """
+        # Tell the C code that 'setattr' should store the original, unadapted
+        # value passed to it:
+        ctrait = super(Expression, self).as_ctrait()
+        ctrait.setattr_original_value = True
+        return ctrait
+
+
+class PythonValue(Any):
+    """ A trait type whose value can be of any type.
+
+    The default editor is a ShellEditor.
+    """
+
+    #: The standard metadata for the trait:
+    metadata = {"editor": shell_editor}
+
+
+class BaseFile(BaseStr):
+    """ A trait type whose value must be a file path string.
+
+    For Python 3.6 and later this will accept os.pathlib Path objects,
+    converting them to the corresponding string value.
+
+    Parameters
+    ----------
+    value : str
+        The default value for the trait.
+    filter : str
+        A wildcard string to filter filenames in the file dialog box used by
+        the attribute trait editor.
+    auto_set : bool
+        Indicates whether the file editor updates the trait value after
+        every key stroke.
+    entries : int
+        A hint to the TraitsUI editor about how many values to display in
+        the editor.
+    exists : bool
+        Indicates whether the trait value must be an existing file or
+        not.
+
+    Attributes
+    ----------
+    filter : str
+        A wildcard string to filter filenames in the file dialog box used by
+        the attribute trait editor.
+    auto_set : bool
+        Indicates whether the file editor updates the trait value after
+        every key stroke.
+    entries : int
+        A hint to the TraitsUI editor about how many values to display in
+        the editor.
+    exists : bool
+        Indicates whether the trait value must be an existing file or
+        not.
+    """
+
+    #: A description of the type of value this trait accepts:
+    info_text = "a filename or object implementing the os.PathLike interface"
+
+    def __init__(
+        self,
+        value="",
+        filter=None,
+        auto_set=False,
+        entries=0,
+        exists=False,
+        **metadata
+    ):
+        self.filter = filter
+        self.auto_set = auto_set
+        self.entries = entries
+        self.exists = exists
+
+        super(BaseFile, self).__init__(value, **metadata)
+
+    def validate(self, object, name, value):
+        """ Validates that a specified value is valid for this trait.
+
+            Note: The 'fast validator' version performs this check in C.
+        """
+        if fspath is not None:
+            # Python 3.5 does not implement __fspath__
+            try:
+                # If value is of type os.PathLike, get the path representation
+                # The path representation could be either a str or bytes type
+                # If fspath returns bytes, further validation will fail.
+                value = fspath(value)
+            except TypeError:
+                pass
+
+        validated_value = super(BaseFile, self).validate(object, name, value)
+        if not self.exists:
+            return validated_value
+        elif isfile(value):
+            return validated_value
+
+        self.error(object, name, value)
+
+    def create_editor(self):
+        from traitsui.editors.file_editor import FileEditor
+
+        editor = FileEditor(
+            filter=self.filter or [],
+            auto_set=self.auto_set,
+            entries=self.entries,
+            dialog_style="open" if self.exists else "save",
+        )
+        return editor
+
+
+class File(BaseFile):
+    """ A fast-validating trait type whose value must be a file path string.
+
+    For Python 3.6 and later this will accept os.pathlib Path objects,
+    converting them to the corresponding string value.
+
+    Parameters
+    ----------
+    value : str
+        The default value for the trait.
+    filter : str
+        A wildcard string to filter filenames in the file dialog box used by
+        the attribute trait editor.
+    auto_set : bool
+        Indicates whether the file editor updates the trait value after
+        every key stroke.
+    entries : int
+        A hint to the TraitsUI editor about how many values to display in
+        the editor.
+    exists : bool
+        Indicates whether the trait value must be an existing file or
+        not.
+
+    Attributes
+    ----------
+    filter : str
+        A wildcard string to filter filenames in the file dialog box used by
+        the attribute trait editor.
+    auto_set : bool
+        Indicates whether the file editor updates the trait value after
+        every key stroke.
+    entries : int
+        A hint to the TraitsUI editor about how many values to display in
+        the editor.
+    exists : bool
+        Indicates whether the trait value must be an existing file or
+        not.
+    """
+
+    def __init__(
+        self,
+        value="",
+        filter=None,
+        auto_set=False,
+        entries=0,
+        exists=False,
+        **metadata
+    ):
+        super(File, self).__init__(
+            value, filter, auto_set, entries, exists, **metadata
+        )
+
+
+class BaseDirectory(BaseStr):
+    """ A trait type whose value must be a directory path string.
+
+    For Python 3.6 and greater, it also accepts objects implementing
+    the :class:`os.PathLike` interface, converting them to the corresponding
+    string.
+
+    Parameters
+    ----------
+    value : str
+        The default value for the trait.
+    auto_set : bool
+        Indicates whether the directory editor updates the trait value
+        after every key stroke.
+    entries : int
+        A hint to the TraitsUI editor about how many values to display in
+        the editor.
+    exists : bool
+        Indicates whether the trait value must be an existing directory or
+        not.
+
+    Attributes
+    ----------
+    auto_set : bool
+        Indicates whether the directory editor updates the trait value
+        after every key stroke.
+    entries : int
+        A hint to the TraitsUI editor about how many values to display in
+        the editor.
+    exists : bool
+        Indicates whether the trait value must be an existing directory or
+        not.
+    """
+
+    #: A description of the type of value this trait accepts:
+    info_text = ("a directory name or an object implementing "
+                 "the os.PathLike interface")
+
+    def __init__(
+        self, value="", auto_set=False, entries=0, exists=False, **metadata
+    ):
+        self.entries = entries
+        self.auto_set = auto_set
+        self.exists = exists
+
+        super(BaseDirectory, self).__init__(value, **metadata)
+
+    def validate(self, object, name, value):
+        """ Validates that a specified value is valid for this trait.
+
+        Note: The 'fast validator' version performs this check in C.
+        """
+        if fspath is not None:
+            # Python 3.5 does not implement __fspath__
+            try:
+                value = fspath(value)
+            except TypeError:
+                pass
+
+        validated_value = super(BaseDirectory, self).validate(
+            object, name, value
+        )
+        if not self.exists:
+            return validated_value
+        elif isdir(value):
+            return validated_value
+
+        self.error(object, name, value)
+
+    def create_editor(self):
+        from traitsui.editors.directory_editor import DirectoryEditor
+
+        editor = DirectoryEditor(auto_set=self.auto_set, entries=self.entries)
+        return editor
+
+
+class Directory(BaseDirectory):
+    """ A fast-validating trait type whose value is a directory path string.
+
+    For Python 3.6 and greater, it also accepts objects implementing
+    the :class:`os.PathLike` interface, converting them to the corresponding
+    string.
+
+    Parameters
+    ----------
+    value : str
+        The default value for the trait.
+    auto_set : bool
+        Indicates whether the directory editor updates the trait value
+        after every key stroke.
+    entries : int
+        A hint to the TraitsUI editor about how many values to display in
+        the editor.
+    exists : bool
+        Indicates whether the trait value must be an existing directory or
+        not.
+
+    Attributes
+    ----------
+    auto_set : bool
+        Indicates whether the directory editor updates the trait value
+        after every key stroke.
+    entries : int
+        A hint to the TraitsUI editor about how many values to display in
+        the editor.
+    exists : bool
+        Indicates whether the trait value must be an existing directory or
+        not.
+    """
+
+    def __init__(
+        self, value="", auto_set=False, entries=0, exists=False, **metadata
+    ):
+        # Fast validation is disabled (Github issue #877).
+        super(Directory, self).__init__(
+            value, auto_set, entries, exists, **metadata
+        )
+
+
+class BaseRange(TraitType):
+    """ A trait type whose numeric value lies inside a range.
+
+    The value held will be either an integer or a float, which type is
+    determined by whether the *low*, *high* and *value* arguments are
+    integers or floats.
+
+    The *low*, *high*, and *value* arguments must be of the same type
+    (integer or float), except in the case where either *low* or *high* is
+    a string (i.e. extended trait name).
+
+    If *value* is None or omitted, the default value is *low*, unless *low*
+    is None or omitted, in which case the default value is *high*.
+
+    Parameters
+    ----------
+    low : integer, float or string (i.e. extended trait name)
+        The low end of the range.
+    high : integer, float or string (i.e. extended trait name)
+        The high end of the range.
+    value : integer, float or string (i.e. extended trait name)
+        The default value of the trait.
+    exclude_low : bool
+        Indicates whether the low end of the range is exclusive.
+    exclude_high : bool
+        Indicates whether the high end of the range is exclusive.
+    """
+
+    def __init__(
+        self,
+        low=None,
+        high=None,
+        value=None,
+        exclude_low=False,
+        exclude_high=False,
+        **metadata
+    ):
+        if value is None:
+            if low is not None:
+                value = low
+            else:
+                value = high
+
+        super(BaseRange, self).__init__(value, **metadata)
+
+        vtype = type(high)
+        if (low is not None) and (
+            not issubclass(vtype, (float, str))
+        ):
+            vtype = type(low)
+
+        is_static = not issubclass(vtype, str)
+        if is_static and (vtype not in RangeTypes):
+            raise TraitError(
+                "Range can only be use for int or float "
+                "values, but a value of type %s was specified." % vtype
+            )
+
+        self._low_name = self._high_name = ""
+        self._vtype = Undefined
+
+        kind = None
+
+        if vtype is float:
+            self._validate = "float_validate"
+            kind = ValidateTrait.float_range
+            self._type_desc = "a floating point number"
+            if low is not None:
+                low = float(low)
+
+            if high is not None:
+                high = float(high)
+
+        elif vtype is int:
+            self._validate = "int_validate"
+            self._type_desc = "an integer"
+            if low is not None:
+                low = int(low)
+
+            if high is not None:
+                high = int(high)
+        else:
+            self.get, self.set, self.validate = self._get, self._set, None
+            self._vtype = None
+            self._type_desc = "a number"
+
+            if isinstance(high, str):
+                self._high_name = high = "object." + high
+            else:
+                self._vtype = type(high)
+            high = compile(str(high), "<string>", "eval")
+
+            if isinstance(low, str):
+                self._low_name = low = "object." + low
+            else:
+                self._vtype = type(low)
+            low = compile(str(low), "<string>", "eval")
+
+            if isinstance(value, str):
+                value = "object." + value
+            self._value = compile(str(value), "<string>", "eval")
+
+            self.default_value_type = DefaultValue.callable
+            self.default_value = self._get_default_value
+
+        exclude_mask = 0
+        if exclude_low:
+            exclude_mask |= 1
+
+        if exclude_high:
+            exclude_mask |= 2
+
+        if is_static and kind is not None:
+            self.init_fast_validate(kind, low, high, exclude_mask)
+
+        #: Assign type-corrected arguments to handler attributes:
+        self._low = low
+        self._high = high
+        self._exclude_low = exclude_low
+        self._exclude_high = exclude_high
+
+    def init_fast_validate(self, *args):
+        """ Does nothing for the BaseRange class. Used in the Range class to
+        set up the fast validator.
+        """
+        pass
+
+    def validate(self, object, name, value):
+        """ Validate that the value is in the specified range.
+        """
+        return getattr(self, self._validate)(object, name, value)
+
+    def float_validate(self, object, name, value):
+        """ Validate that the value is a float value in the specified range.
+        """
+        # Convert to exact type float, re-raising a TypeError as a TraitError
+        # and letting other errors propagate. Keep original value for
+        # error-reporting purposes.
+        original_value = value
+        try:
+            value = _validate_float(value)
+        except TypeError:
+            self.error(object, name, original_value)
+
+        if (
+            (
+                (self._low is None)
+                or (self._exclude_low and (self._low < value))
+                or ((not self._exclude_low) and (self._low <= value))
+            )
+            and (
+                (self._high is None)
+                or (self._exclude_high and (self._high > value))
+                or ((not self._exclude_high) and (self._high >= value))
+            )
+        ):
+            return value
+
+        self.error(object, name, original_value)
+
+    def int_validate(self, object, name, value):
+        """ Validate that the value is an int value in the specified range.
+        """
+        # Convert to exact type float, re-raising a TypeError as a TraitError
+        # and letting other errors propagate. Keep original value for
+        # error-reporting purposes.
+        original_value = value
+        try:
+            value = _validate_int(value)
+        except TypeError:
+            self.error(object, name, original_value)
+
+        if (
+            (
+                (self._low is None)
+                or (self._exclude_low and (self._low < value))
+                or ((not self._exclude_low) and (self._low <= value))
+            )
+            and (
+                (self._high is None)
+                or (self._exclude_high and (self._high > value))
+                or ((not self._exclude_high) and (self._high >= value))
+            )
+        ):
+            return value
+
+        self.error(object, name, original_value)
+
+    def _get_default_value(self, object):
+        """ Returns the default value of the range.
+        """
+        return eval(self._value)
+
+    def _get(self, object, name, trait):
+        """ Returns the current value of a dynamic range trait.
+        """
+        cname = "_traits_cache_" + name
+        value = object.__dict__.get(cname, Undefined)
+        if value is Undefined:
+            object.__dict__[cname] = value = eval(self._value)
+
+        low = eval(self._low)
+        high = eval(self._high)
+        if (low is not None) and (value < low):
+            value = low
+        elif (high is not None) and (value > high):
+            value = high
+
+        return self._typed_value(value, low, high)
+
+    def _set(self, object, name, value):
+        """ Sets the current value of a dynamic range trait.
+        """
+        if not isinstance(value, str):
+            try:
+                low = eval(self._low)
+                high = eval(self._high)
+                if (low is None) and (high is None):
+                    if isinstance(value, RangeTypes):
+                        self._set_value(object, name, value)
+                        return
+                else:
+                    new_value = self._typed_value(value, low, high)
+                    if (
+                        (low is None)
+                        or (self._exclude_low and (low < new_value))
+                        or ((not self._exclude_low) and (low <= new_value))
+                    ) and (
+                        (high is None)
+                        or (self._exclude_high and (high > new_value))
+                        or ((not self._exclude_high) and (high >= new_value))
+                    ):
+                        self._set_value(object, name, new_value)
+                        return
+            except:
+                pass
+
+        self.error(object, name, value)
+
+    def _typed_value(self, value, low, high):
+        """ Returns the specified value with the correct type for the current
+            dynamic range.
+        """
+        vtype = self._vtype
+        if vtype is None:
+            if low is not None:
+                vtype = type(low)
+            elif high is not None:
+                vtype = type(high)
+            else:
+                vtype = lambda x: x
+
+        return vtype(value)
+
+    def _set_value(self, object, name, value):
+        """ Sets the specified value as the value of the dynamic range.
+        """
+        cname = "_traits_cache_" + name
+        old = object.__dict__.get(cname, Undefined)
+        if old is Undefined:
+            old = eval(self._value)
+        object.__dict__[cname] = value
+        if value != old:
+            object.trait_property_changed(name, old, value)
+
+    def full_info(self, object, name, value):
+        """ Returns a description of the trait.
+        """
+        if self._vtype is not Undefined:
+            low = eval(self._low)
+            high = eval(self._high)
+            low, high = (
+                self._typed_value(low, low, high),
+                self._typed_value(high, low, high),
+            )
+        else:
+            low = self._low
+            high = self._high
+
+        if low is None:
+            if high is None:
+                return self._type_desc
+
+            return "%s <%s %s" % (
+                self._type_desc,
+                "="[self._exclude_high:],
+                high,
+            )
+
+        elif high is None:
+            return "%s >%s %s" % (
+                self._type_desc,
+                "="[self._exclude_low:],
+                low,
+            )
+
+        return "%s <%s %s <%s %s" % (
+            low,
+            "="[self._exclude_low:],
+            self._type_desc,
+            "="[self._exclude_high:],
+            high,
+        )
+
+    def create_editor(self):
+        """ Returns the default UI editor for the trait.
+        """
+        # fixme: Needs to support a dynamic range editor.
+
+        auto_set = self.auto_set
+        if auto_set is None:
+            auto_set = True
+
+        from traitsui.api import RangeEditor
+
+        return RangeEditor(
+            self,
+            mode=self.mode or "auto",
+            cols=self.cols or 3,
+            auto_set=auto_set,
+            enter_set=self.enter_set or False,
+            low_label=self.low or "",
+            high_label=self.high or "",
+            low_name=self._low_name,
+            high_name=self._high_name,
+        )
+
+
+class Range(BaseRange):
+    """ A fast-validating trait type whose numeric value lies inside a range.
+    """
+
+    def init_fast_validate(self, *args):
+        """ Set up the C-level fast validator.
+        """
+        self.fast_validate = args
+
+
+class BaseEnum(TraitType):
+    """ A trait type whose value is an element of a finite collection.
+
+    This trait type can be either *static*, with the collection of valid values
+    specified directly in the constructor, or *dynamic*, with the collection
+    provided by the value of another trait attribute.
+
+    For both static and dynamic enumerations, a default value can be provided
+    as a positional argument. If no default is provided, the default is the
+    first item (in iteration order) of the underlying collection.
+
+    Notes
+    -----
+
+    1. If the enumeration is based on an unordered collection like a
+       ``set``, and no explicit default is given, the default used will
+       effectively be arbitrary (the first element of the set in iteration
+       order). It's recommended that a default be given explicitly in this
+       case.
+
+    2. Instances of ``str``, ``bytes`` and ``bytearray`` are not treated
+       as collections for the purposes of this trait type, both for pragmatic
+       reasons (it's more likely that a user wants to use a string as an
+       element in a collection than as a collection in its own right), and
+       because the behavior of the ``in`` operator for those types does not
+       express the usual membership semantics (for example, ``"bc" in "abc"``
+       is ``True``).
+
+    Parameters
+    ----------
+    *args
+        The enumeration of all valid values for the trait. For a static
+        enumeration trait (where the *values* keyword argument is not given)
+        the supported signatures for ``args`` are as follows:
+
+        (collection,)
+            A nonempty collection of valid values. The default is the first
+            element of the collection, in iteration order.
+        (default, collection)
+            The default value, followed by a nonempty collection of valid
+            values. The default should be an element of the collection, but
+            this is not checked.
+        (item1, item2, ..., itemn)
+            One or more items giving the valid values for the collection.
+            The default is *item1*.
+
+        For a dynamic enumeration trait, where the *values* keyword argument
+        is given, the supported signatures for ``args`` are:
+
+        ()
+            No arguments given. In this case the default is the first item
+            of the collection, in iteration order.
+        (default,)
+            The default value for the collection.
+
+        For the static case, the ambiguity in the signatures is resolved
+        as follows: if ``args`` has length ``1`` or ``2``, ``args[-1]`` can be
+        iterated over, and ``args[-1]`` is not an instance of ``str``,
+        ``bytes`` or ``bytearray``, then ``args[-1]`` is assumed to give the
+        collection of values. Otherwise, all elements of ``args`` are assumed
+        to be items in the collection. Thus the first two signatures are safe
+        from ambiguity, and it's recommended to use one of these two signatures
+        in preference to the third form.
+    values : str, optional
+        The name of a trait holding the valid values. If given, this is
+        a dynamic enumeration, otherwise it's a static numeration.
+    **metadata
+        Metadata for the trait.
+
+    Attributes
+    ----------
+    values : tuple or None
+        For a static enumeration, this is a tuple holding the valid values.
+        For a dynamic enumeration, this is None.
+    name : str or None
+        For a dynamic enumeration, this is the name of a trait holding
+        the collection of valid values. For a static enumeration, this is
+        None.
+    """
+
+    def __init__(self, *args, values=None, **metadata):
+        self.name = values
+
+        nargs = len(args)
+        if self.name is not None:
+            # Dynamic enumeration
+            self.values = None
+            self.get, self.set, self.validate = self._get, self._set, None
+            if nargs == 0:
+                super(BaseEnum, self).__init__(**metadata)
+            elif nargs == 1:
+                default_value = args[0]
+                super(BaseEnum, self).__init__(default_value, **metadata)
+            else:
+                raise TraitError(
+                    "Incorrect number of arguments specified "
+                    "when using the 'values' keyword"
+                )
+        else:
+            # Static enumeration
+            if nargs == 0:
+                raise TraitError("Enum trait requires at least 1 argument")
+
+            # If we have either 1 or 2 arguments and the last argument is a
+            # collection, then that collection provides the values of the
+            # enumeration. Otherwise, args itself is the collection.
+            have_collection_arg = (
+                nargs <= 2
+                and not isinstance(args[-1], (str, bytes, bytearray))
+                and isinstance(args[-1], collections.abc.Iterable)
+            )
+            self.values = tuple(args[-1]) if have_collection_arg else args
+            if not self.values:
+                raise TraitError("Enum collection should be nonempty")
+
+            # In the two-argument collection case, the first argument is
+            # the default. Otherwise, we take the first element of self.values.
+            if have_collection_arg and nargs == 2:
+                default_value = args[0]
+            else:
+                default_value = self.values[0]
+
+            self.init_fast_validate(ValidateTrait.enum, self.values)
+
+            super(BaseEnum, self).__init__(default_value, **metadata)
+
+    def init_fast_validate(self, *args):
+        """ Does nothing for the BaseEnum class. Used in the Enum class to set
+            up the fast validator.
+        """
+        pass
+
+    def validate(self, object, name, value):
+        """ Validates that the value is one of the enumerated set of valid
+        values.
+        """
+        if value in self.values:
+            return value
+
+        self.error(object, name, value)
+
+    def full_info(self, object, name, value):
+        """ Returns a description of the trait.
+        """
+        if self.name is None:
+            values = self.values
+        else:
+            values = xgetattr(object, self.name)
+
+        return " or ".join([repr(x) for x in values])
+
+    def create_editor(self):
+        """ Returns the default UI editor for the trait.
+        """
+        from traitsui.api import EnumEditor
+
+        if self.name is None:
+            values = self.values
+            name = ""
+        else:
+            values = None
+            name = self.name
+
+        editor = EnumEditor(
+            name=name,
+            cols=self.cols or 3,
+            evaluate=self.evaluate,
+            format_func=self.format_func,
+            mode=self.mode if self.mode else "radio",
+        )
+        # Workaround enthought/traitsui#782
+        if values is not None:
+            editor.values = values
+        return editor
+
+    def _get(self, object, name, trait):
+        """ Returns the current value of a dynamic enum trait.
+        """
+        value = self.get_value(object, name, trait)
+        values = xgetattr(object, self.name)
+        if not safe_contains(value, values):
+            value = next(iter(values), None)
+        return value
+
+    def _set(self, object, name, value):
+        """ Sets the current value of a dynamic range trait.
+        """
+        if safe_contains(value, xgetattr(object, self.name)):
+            self.set_value(object, name, value)
+        else:
+            self.error(object, name, value)
+
+
+class Enum(BaseEnum):
+    """ A fast-validating trait type whose value is an element of a finite
+    collection.
+
+    This trait type can be either *static*, with the collection of valid values
+    specified directly in the constructor, or *dynamic*, with the collection
+    provided by the value of another trait attribute.
+
+    For both static and dynamic enumerations, a default value can be provided
+    as a positional argument. If no default is provided, the default is the
+    first item (in iteration order) of the underlying collection.
+
+    Notes
+    -----
+
+    1. If the enumeration is based on an unordered collection like a
+       ``set``, and no explicit default is given, the default used will
+       effectively be arbitrary (the first element of the set in iteration
+       order). It's recommended that a default be given explicitly in this
+       case.
+
+    2. Instances of ``str``, ``bytes`` and ``bytearray`` are not treated
+       as collections for the purposes of this trait type, both for pragmatic
+       reasons (it's more likely that a user wants to use a string as an
+       element in a collection than as a collection in its own right), and
+       because the behavior of the ``in`` operator for those types does not
+       express the usual membership semantics (for example, ``"bc" in "abc"``
+       is ``True``).
+
+    Parameters
+    ----------
+    *args
+        The enumeration of all valid values for the trait. For a static
+        enumeration trait (where the *values* keyword argument is not given)
+        the supported signatures for ``args`` are as follows:
+
+        (collection,)
+            A nonempty collection of valid values. The default is the first
+            element of the collection, in iteration order.
+        (default, collection)
+            The default value, followed by a nonempty collection of valid
+            values. The default should be an element of the collection, but
+            this is not checked.
+        (item1, item2, ..., itemn)
+            One or more items giving the valid values for the collection.
+            The default is *item1*.
+
+        For a dynamic enumeration trait, where the *values* keyword argument
+        is given, the supported signatures for ``args`` are:
+
+        ()
+            No arguments given. In this case the default is the first item
+            of the collection, in iteration order.
+        (default,)
+            The default value for the collection.
+
+        For the static case, the ambiguity in the signatures is resolved
+        as follows: if ``args`` has length ``1`` or ``2``, ``args[-1]`` can be
+        iterated over, and ``args[-1]`` is not an instance of ``str``,
+        ``bytes`` or ``bytearray``, then ``args[-1]`` is assumed to give the
+        collection of values. Otherwise, all elements of ``args`` are assumed
+        to be items in the collection. Thus the first two signatures are safe
+        from ambiguity, and it's recommended to use one of these two signatures
+        in preference to the third form.
+    values : str, optional
+        The name of a trait holding the valid values. If given, this is
+        a dynamic enumeration, otherwise it's a static numeration.
+    **metadata
+        Metadata for the trait.
+
+    Attributes
+    ----------
+    values : tuple or None
+        For a static enumeration, this is a tuple holding the valid values.
+        For a dynamic enumeration, this is None.
+    name : str or None
+        For a dynamic enumeration, this is the name of a trait holding
+        the collection of valid values. For a static enumeration, this is
+        None.
+    """
+
+    def init_fast_validate(self, *args):
+        """ Set up C-level fast validation. """
+        self.fast_validate = args
+
+
+class BaseTuple(TraitType):
+    """ A trait type holding a tuple with typed elements.
+
+    The default value is determined as follows:
+
+    1.  If no arguments are specified, the default value is ().
+    2.  If a tuple is specified as the first argument, it is the default
+        value.
+    3.  If a tuple is not specified as the first argument, the default
+        value is a tuple whose length is the length of the argument list,
+        and whose values are the default values for the corresponding trait
+        types.
+
+    Example for case #2::
+
+        mytuple = Tuple(('Fred', 'Betty', 5))
+
+    The trait's value must be a 3-element tuple whose first and second
+    elements are strings, and whose third element is an integer. The
+    default value is ``('Fred', 'Betty', 5)``.
+
+    Example for case #3::
+
+        mytuple = Tuple('Fred', 'Betty', 5)
+
+    The trait's value must be a 3-element tuple whose first and second
+    elements are strings, and whose third element is an integer. The
+    default value is ``('','',0)``.
+
+    Parameters
+    ----------
+    *types
+        Definition of the default and allowed tuples. If the first item of
+        *types* is a tuple, it is used as the default value.
+        The remaining argument list is used to form a tuple that constrains
+        the  values assigned to the returned trait. The trait's value must
+        be a tuple of the same length as the remaining argument list, whose
+        elements must match the types specified by the corresponding items
+        of the remaining argument list.
+    **metadata
+        Trait metadata for the trait.
+
+    Attributes
+    ----------
+    types : tuple
+        The tuple of traits specifying the type of each element in order.
+    no_type_check : bool
+        Flag to indicate whether validation should check the type of each
+        element.
+    """
+
+    def __init__(self, *types, **metadata):
+        if len(types) == 0:
+            self.init_fast_validate(ValidateTrait.coerce, tuple, None, list)
+
+            super(BaseTuple, self).__init__((), **metadata)
+
+            return
+
+        default_value = None
+
+        if isinstance(types[0], tuple):
+            default_value, types = types[0], types[1:]
+            if len(types) == 0:
+                types = [Trait(element) for element in default_value]
+
+        self.types = tuple([trait_from(type) for type in types])
+        self.init_fast_validate(ValidateTrait.tuple, self.types)
+
+        if default_value is None:
+            default_value = tuple(
+                [type.default_value()[1] for type in self.types]
+            )
+
+        super(BaseTuple, self).__init__(default_value, **metadata)
+
+    def init_fast_validate(self, *args):
+        """ Saves the validation parameters.
+        """
+        self.no_type_check = args[0] == ValidateTrait.coerce
+
+    def validate(self, object, name, value):
+        """ Validates that the value is a valid tuple.
+        """
+        if self.no_type_check:
+            if isinstance(value, tuple):
+                return value
+
+            if isinstance(value, list):
+                return tuple(value)
+
+            self.error(object, name, value)
+
+        try:
+            if isinstance(value, list):
+                value = tuple(value)
+
+            if isinstance(value, tuple):
+                types = self.types
+                if len(value) == len(types):
+                    values = []
+                    for i, type in enumerate(types):
+                        values.append(type.validate(object, name, value[i]))
+
+                    return tuple(values)
+        except:
+            pass
+
+        self.error(object, name, value)
+
+    def full_info(self, object, name, value):
+        """ Returns a description of the trait.
+        """
+        if self.no_type_check:
+            return "a tuple"
+
+        return "a tuple of the form: (%s)" % (
+            ", ".join(
+                [type.full_info(object, name, value) for type in self.types]
+            )
+        )
+
+    def create_editor(self):
+        """ Returns the default UI editor for the trait.
+        """
+        from traitsui.api import TupleEditor
+
+        auto_set = self.auto_set
+        if auto_set is None:
+            auto_set = True
+        enter_set = self.enter_set or False
+
+        return TupleEditor(
+            types=self.types,
+            labels=self.labels or [],
+            cols=self.cols or 1,
+            auto_set=auto_set,
+            enter_set=enter_set,
+        )
+
+
+class Tuple(BaseTuple):
+    """ A fast-validating trait type holding a tuple with typed elements.
+    """
+
+    def init_fast_validate(self, *args):
+        """ Set up the C-level fast validator.
+        """
+        super(Tuple, self).init_fast_validate(*args)
+
+        self.fast_validate = args
+
+
+class ValidatedTuple(BaseTuple):
+    """ A trait type holding a tuple with customized validation.
+
+    Parameters
+    ----------
+    *types
+        Definition of the default and allowed tuples. (see
+        :class:`~.BaseTuple` for more details)
+    fvalidate : callable, optional
+        A callable to provide the additional custom validation for the
+        tuple. The callable will be passed the tuple value and should
+        return True or False.
+    fvalidate_info : string, optional
+        A string describing the custom validation to use for the error
+        messages.
+    **metadata
+        Trait metadata for the trait.
+
+    Example
+    -------
+    The definition::
+
+        value_range = ValidatedTuple(
+            Int(0), Int(1), fvalidate=lambda x: x[0] < x[1])
+
+    will accept only tuples ``(a, b)`` containing two integers that
+    satisfy ``a < b``.
+    """
+
+    def __init__(self, *types, **metadata):
+        metadata.setdefault("fvalidate", None)
+        metadata.setdefault("fvalidate_info", "")
+        super(ValidatedTuple, self).__init__(*types, **metadata)
+
+    def validate(self, object, name, value):
+        """ Validates that the value is a valid tuple.
+        """
+        values = super(ValidatedTuple, self).validate(object, name, value)
+        # Exceptions in the fvalidate function will not result in a TraitError
+        # but will be allowed to propagate up the frame stacks.
+        if self.fvalidate is None or self.fvalidate(values):
+            return values
+        else:
+            self.error(object, name, value)
+
+    def full_info(self, object, name, value):
+        """ Returns a description of the trait.
+        """
+        message = "a tuple of the form: ({0}) that passes custom validation{1}"
+        types_info = ", ".join(
+            [type_.full_info(object, name, value) for type_ in self.types]
+        )
+        if self.fvalidate_info is not None:
+            fvalidate_info = ": {0}".format(self.fvalidate_info)
+        else:
+            fvalidate_info = ""
+        return message.format(types_info, fvalidate_info)
+
+
+class List(TraitType):
+    """ A trait type for a list of values of the specified type.
+
+    The length of the list assigned to the trait must be such that::
+
+        minlen <= len(list) <= maxlen
+
+    Parameters
+    ----------
+    trait : a trait or value that can be converted using trait_from()
+        The type of item that the list contains. If not specified, the list
+        can contain items of any type.
+    value : list
+        Default value for the list.
+    minlen : integer
+        The minimum length of a list that can be assigned to the trait.
+    maxlen : integer
+        The maximum length of a list that can be assigned to the trait.
+    items : bool
+        Whether there is a corresponding `<name>_items` trait.
+    **metadata
+        Trait metadata for the trait.
+
+    Attributes
+    ----------
+    item_trait : trait
+        The type of item that the list contains.
+    minlen : integer
+        The minimum length of a list that can be assigned to the trait.
+    maxlen : integer
+        The maximum length of a list that can be assigned to the trait.
+    has_items : bool
+        Whether there is a corresponding `<name>_items` trait.
+    """
+
+    info_trait = None
+    default_value_type = DefaultValue.trait_list_object
+    _items_event = None
+
+    def __init__(
+        self,
+        trait=None,
+        value=None,
+        minlen=0,
+        maxlen=sys.maxsize,
+        items=True,
+        **metadata
+    ):
+        metadata.setdefault("copy", "deep")
+
+        if isinstance(trait, SequenceTypes):
+            trait, value = value, list(trait)
+
+        if value is None:
+            value = []
+
+        self.item_trait = trait_from(trait)
+        self.minlen = max(0, minlen)
+        self.maxlen = max(minlen, maxlen)
+        self.has_items = items
+
+        if self.item_trait.instance_handler == "_instance_changed_handler":
+            metadata.setdefault("instance_handler", "_list_changed_handler")
+
+        super(List, self).__init__(value, **metadata)
+
+    def validate(self, object, name, value):
+        """ Validates that the values is a valid list.
+
+        .. note::
+
+            `object` can be None when validating a default value (see e.g.
+            :meth:`~traits.trait_handlers.TraitType.clone`)
+
+        """
+        if isinstance(value, list) and (
+            self.minlen <= len(value) <= self.maxlen
+        ):
+            if object is None:
+                return value
+
+            return TraitListObject(self, object, name, value)
+
+        self.error(object, name, value)
+
+    def full_info(self, object, name, value):
+        """ Returns a description of the trait.
+        """
+        if self.minlen == 0:
+            if self.maxlen == sys.maxsize:
+                size = "items"
+            else:
+                size = "at most %d items" % self.maxlen
+        else:
+            if self.maxlen == sys.maxsize:
+                size = "at least %d items" % self.minlen
+            else:
+                size = "from %s to %s items" % (self.minlen, self.maxlen)
+
+        return "a list of %s which are %s" % (
+            size,
+            self.item_trait.full_info(object, name, value),
+        )
+
+    def create_editor(self):
+        """ Returns the default UI editor for the trait.
+        """
+        return list_editor(self, self)
+
+    def inner_traits(self):
+        """ Returns the *inner trait* (or traits) for this trait.
+        """
+        return (self.item_trait,)
+
+    # -- Private Methods ------------------------------------------------------
+
+    def items_event(self):
+        cls = self.__class__
+        if cls._items_event is None:
+            cls._items_event = Event(
+                TraitListEvent, is_base=False
+            ).as_ctrait()
+
+        return cls._items_event
+
+
+class CList(List):
+    """ A coercing trait type for a list of values of the specified type.
+    """
+
+    def validate(self, object, name, value):
+        """ Validates that the values is a valid list.
+        """
+        if not isinstance(value, list):
+            try:
+                # Should work for all iterables as well as strings (which do
+                # not define an __iter__ method)
+                value = list(value)
+            except (ValueError, TypeError):
+                value = [value]
+
+        return super(CList, self).validate(object, name, value)
+
+    def full_info(self, object, name, value):
+        """ Returns a description of the trait.
+        """
+        return "%s or %s" % (
+            self.item_trait.full_info(object, name, value),
+            super(CList, self).full_info(object, name, value),
+        )
+
+
+class PrefixList(TraitType):
+    r"""Ensures that a value assigned to the attribute is a member of a list of
+     specified string values, or is a unique prefix of one of those values.
+
+    The values that can be assigned to a trait attribute of type PrefixList
+    type is the set of all strings supplied to the PrefixList constructor,
+    as well as any unique prefix of those strings. That is, if the set of
+    strings supplied to the constructor is described by
+    [*s*\ :sub:`1`\ , *s*\ :sub:`2`\ , ..., *s*\ :sub:`n`\ ], then the
+    string *v* is a valid value for the trait if *v* == *s*\ :sub:`i[:j]`
+    for one and only one pair of values (i, j). If *v* is a valid value,
+    then the actual value assigned to the trait attribute is the
+    corresponding *s*\ :sub:`i` value that *v* matched.
+
+    The legal values can be provided as an iterable of values.
+
+    Example
+    -------
+    ::
+        class Person(HasTraits):
+            married = PrefixList(['yes', 'no'])
+
+    The Person class has a **married** trait that accepts any of the
+    strings 'y', 'ye', 'yes', 'n', or 'no' as valid values. However, the
+    actual values assigned as the value of the trait attribute are limited
+    to either 'yes' or 'no'. That is, if the value 'y' is assigned to the
+    **married** attribute, the actual value assigned will be 'yes'.
+
+    Note that the algorithm used by PrefixList in determining whether
+    a string is a valid value is fairly efficient in terms of both time and
+    space, and is not based on a brute force set of comparisons.
+
+    Parameters
+    ----------
+    values
+        A single iterable of legal string values.
+
+    Attributes
+    ----------
+    values : tuple of strings
+        Enumeration of all legal values for a trait.
+    """
+
+    #: The default value for the trait:
+    default_value = None
+
+    #: The default value type to use (i.e. 'constant'):
+    default_value_type = DefaultValue.constant
+
+    def __init__(self, values, **metadata):
+        if isinstance(values, (str, bytes, bytearray)):
+            raise TypeError(
+                "Legal values should be provided via an iterable of strings, "
+                "got {!r}.".format(values)
+            )
+        self.values = list(values)
+        self.values_ = values_ = {}
+        for key in values:
+            values_[key] = key
+
+        default = self.default_value
+        if 'default_value' in metadata:
+            default = metadata.pop('default_value')
+            default = self.value_for(default)
+        elif self.values:
+            default = self.values[0]
+
+        super().__init__(default, **metadata)
+
+    def value_for(self, value):
+        if not isinstance(value, str):
+            raise TraitError(
+                "The value of a {} trait must be {}, but a value of {!r} {!r} "
+                "was specified.".format(
+                    self.__class__.__name__, self.info(), value, type(value))
+            )
+
+        if value in self.values_:
+            return self.values_[value]
+
+        matches = [key for key in self.values if key.startswith(value)]
+        if len(matches) == 1:
+            self.values_[value] = match = matches[0]
+            return match
+
+        raise TraitError(
+            "The value of a {} trait must be {}, but a value of {!r} {!r} was "
+            "specified.".format(
+                self.__class__.__name__, self.info(), value, type(value))
+        )
+
+    def info(self):
+        return (
+            " or ".join([repr(x) for x in self.values])
+            + " (or any unique prefix)"
+        )
+
+
+class Set(TraitType):
+    """ A trait type for a set of values of the specified type.
+
+    Parameters
+    ----------
+    trait : a trait or value that can be converted to a trait using Trait()
+        The type of item that the list contains. If not specified, the list
+        can contain items of any type.
+    value : set
+        Default value for the set.
+    items : bool
+        Whether there is a corresponding `<name>_items` trait.
+    **metadata
+        Trait metadata for the trait.
+
+    Attributes
+    ----------
+    item_trait : a trait or value that can be converted to a trait
+        The type of item that the list contains. If not specified, the list
+        can contain items of any type.
+    has_items : bool
+        Whether there is a corresponding `<name>_items` trait.
+    """
+
+    info_trait = None
+    default_value_type = DefaultValue.trait_set_object
+    _items_event = None
+
+    def __init__(self, trait=None, value=None, items=True, **metadata):
+        metadata.setdefault("copy", "deep")
+
+        if isinstance(trait, SetTypes):
+            trait, value = value, set(trait)
+
+        if value is None:
+            value = set()
+
+        self.item_trait = trait_from(trait)
+        self.has_items = items
+
+        super(Set, self).__init__(value, **metadata)
+
+    def validate(self, object, name, value):
+        """ Validates that the values is a valid set.
+
+        .. note::
+
+            `object` can be None when validating a default value (see e.g.
+            :meth:`~traits.trait_handlers.TraitType.clone`)
+
+        """
+        if isinstance(value, set):
+            if object is None:
+                return value
+
+            return TraitSetObject(self, object, name, value)
+
+        self.error(object, name, value)
+
+    def full_info(self, object, name, value):
+        """ Returns a description of the trait.
+        """
+        return "a set of %s" % self.item_trait.full_info(object, name, value)
+
+    def create_editor(self):
+        """ Returns the default UI editor for the trait.
+        """
+        from traitsui.api import TextEditor
+
+        return TextEditor(evaluate=eval)
+
+    def inner_traits(self):
+        """ Returns the *inner trait* (or traits) for this trait.
+        """
+        return (self.item_trait,)
+
+    # -- Private Methods ------------------------------------------------------
+
+    def items_event(self):
+        if self.__class__._items_event is None:
+            self.__class__._items_event = Event(
+                TraitSetEvent, is_base=False
+            ).as_ctrait()
+
+        return self.__class__._items_event
+
+
+class CSet(Set):
+    """ A coercing trait type for a set of values of the specified type.
+    """
+
+    def validate(self, object, name, value):
+        """ Validates that the values is a valid list.
+        """
+        if not isinstance(value, set):
+            try:
+                # Should work for all iterables as well as strings (which do
+                # not define an __iter__ method)
+                value = set(value)
+            except (ValueError, TypeError):
+                value = set([value])
+
+        return super(CSet, self).validate(object, name, value)
+
+    def full_info(self, object, name, value):
+        """ Returns a description of the trait.
+        """
+        return "%s or %s" % (
+            self.item_trait.full_info(object, name, value),
+            super(CSet, self).full_info(object, name, value),
+        )
+
+
+class Dict(TraitType):
+    """ A trait type for a dictionary with specified key and value types.
+
+
+    Parameters
+    ----------
+    key_trait : a trait or value that can be converted using trait_from()
+        The trait type for keys in the dictionary; if not specified, any
+        values can be used as keys.
+    value_trait : a trait or value that can be converted using trait_from()
+        The trait type for values in the dictionary; if not specified, any
+        values can be used as dictionary values.
+    value : dict
+        The default value for the returned trait.
+    items : bool
+        Indicates whether the value contains items.
+
+    Attributes
+    ----------
+    key_trait : a trait
+        The trait type for keys in the dictionary; if not specified, any
+        values can be used as keys.
+    value_trait : a trait
+        The trait type for values in the dictionary; if not specified, any
+        values can be used as dictionary values.
+    value_trait_handler : TraitHandler
+        The TraitHandler for the value_trait.
+    has_items : bool
+        Indicates whether the value contains items.
+    """
+
+    info_trait = None
+    default_value_type = DefaultValue.trait_dict_object
+    _items_event = None
+
+    def __init__(
+        self,
+        key_trait=None,
+        value_trait=None,
+        value=None,
+        items=True,
+        **metadata
+    ):
+        if isinstance(key_trait, dict):
+            key_trait, value_trait, value = value_trait, value, key_trait
+
+        if value is None:
+            value = {}
+
+        self.key_trait = trait_from(key_trait)
+        self.value_trait = trait_from(value_trait)
+        self.has_items = items
+
+        handler = self.value_trait.handler
+        if (handler is not None) and handler.has_items:
+            handler = handler.clone()
+            handler.has_items = False
+        self.value_handler = handler
+
+        super(Dict, self).__init__(value, **metadata)
+
+    def validate(self, object, name, value):
+        """ Validates that the value is a valid dictionary.
+
+        Note
+        ----
+        `object` can be None when validating a default value (see e.g.
+        :meth:`~traits.trait_handlers.TraitType.clone`)
+        """
+        if isinstance(value, dict):
+            if object is None:
+                return value
+            return TraitDictObject(self, object, name, value)
+
+        self.error(object, name, value)
+
+    def full_info(self, object, name, value):
+        """ Returns a description of the trait.
+        """
+        return (
+            "a dictionary with keys which are %s and with values which "
+            "are %s"
+        ) % (
+            self.key_trait.full_info(object, name, value),
+            self.value_trait.full_info(object, name, value),
+        )
+
+    def create_editor(self):
+        """ Returns the default UI editor for the trait.
+        """
+        from traitsui.api import TextEditor
+
+        return TextEditor(evaluate=eval)
+
+    def inner_traits(self):
+        """ Returns the *inner trait* (or traits) for this trait.
+        """
+        return (self.key_trait, self.value_trait)
+
+    # -- Private Methods ------------------------------------------------------
+
+    def items_event(self):
+        cls = self.__class__
+        if cls._items_event is None:
+            cls._items_event = Event(TraitDictEvent, is_base=False).as_ctrait()
+
+        return cls._items_event
+
+
+#: Allowed values and mappings for the 'adapt' keyword.
+#:
+#: - 'no': Adaptation is not allowed.
+#: - 'yes': Adaptation is allowed. If adaptation fails, an
+#:   exception should be raised.
+#: - 'default': Adaptation is allowed. If adaptation fails, the
+#:   default value for the trait should be used.
+AdaptMap = {"no": 0, "yes": 1, "default": 2}
+
+
+class Map(TraitType):
+    """ Checks that the value assigned to a trait attribute is a key of a
+        specified dictionary, and also assigns the dictionary value
+        corresponding to that key to a *shadow* attribute.
+
+        A trait attribute of type Map is called a *mapped* trait
+        attribute. In practice, this means that the resulting object actually
+        contains two attributes: one whose value is a key of the Map
+        dictionary, and the other whose value is the corresponding value of the
+        Map dictionary. The name of the shadow attribute is simply the base
+        attribute name with an underscore ('_') appended. Mapped trait
+        attributes can be used to allow a variety of user-friendly input values
+        to be mapped to a set of internal, program-friendly values.
+
+        Example
+        -------
+
+        The following example defines a ``Person`` class::
+
+            >>> class Person(HasTraits):
+            ...     married = Map({'yes': 1, 'no': 0 }, default_value="yes")
+            ...
+            >>> bob = Person()
+            >>> print(bob.married)
+            yes
+            >>> print(bob.married_)
+            1
+
+        In this example, the default value of the ``married`` attribute of the
+        Person class is 'yes'. Because this attribute is defined using
+        Map, instances of Person have another attribute,
+        ``married_``, whose default value is 1, the dictionary value
+        corresponding to the key 'yes'.
+
+        Parameters
+        ----------
+        map : dict
+            A dictionary whose keys are valid values for the trait attribute,
+            and whose corresponding values are the values for the shadow
+            trait attribute.
+        default_value : object, optional
+            The default value for the trait. If given, this should be a key
+            from the mapping. If not given, the first key from the mapping (in
+            normal dictionary iteration order) will be used as the default.
+
+        Attributes
+        ----------
+        map : dict
+            A dictionary whose keys are valid values for the trait attribute,
+            and whose corresponding values are the values for the shadow
+            trait attribute.
+    """
+
+    is_mapped = True
+
+    def __init__(self, map, **metadata):
+
+        self.map = map
+        self.fast_validate = (ValidateTrait.map, map)
+
+        try:
+            default_value = metadata.pop("default_value")
+        except KeyError:
+            default_value = next(iter(self.map))
+
+        super().__init__(default_value, **metadata)
+
+    def validate(self, object, name, value):
+        try:
+            if value in self.map:
+                return value
+        except TypeError:
+            pass
+
+        self.error(object, name, value)
+
+    def mapped_value(self, value):
+        """ Get the mapped value for a value. """
+        return self.map[value]
+
+    def post_setattr(self, object, name, value):
+        setattr(object, name + "_", self.mapped_value(value))
+
+    def info(self):
+        keys = sorted(repr(x) for x in self.map.keys())
+        return " or ".join(keys)
+
+    def get_editor(self, trait):
+        from traitsui.api import EnumEditor
+
+        return EnumEditor(values=self, cols=trait.cols or 3)
+
+
+class PrefixMap(TraitType):
+    """ A cross between the PrefixList and Map classes.
+
+    Like Map, PrefixMap is created using a dictionary, but in this
+    case, the keys of the dictionary must be strings. Like PrefixList,
+    a string *v* is a valid value for the trait attribute if it is a prefix of
+    one and only one key *k* in the dictionary. The actual values assigned to
+    the trait attribute is *k*, and its corresponding mapped attribute is
+    *map*[*k*].
+
+    Example
+    -------
+    ::
+
+        mapping = {'true': 1, 'yes': 1, 'false': 0, 'no': 0 }
+        boolean_map = PrefixMap(mapping)
+
+    This example defines a Boolean trait that accepts any prefix of 'true',
+    'yes', 'false', or 'no', and maps them to 1 or 0.
+
+    Parameters
+    ----------
+    map : dict
+        A dictionary whose keys are strings that are valid values for the
+        trait attribute, and whose corresponding values are the values for
+        the shadow trait attribute.
+    default_value : object, optional
+        The default value for the trait. If given, this should be either a key
+        from the mapping or a unique prefix of a key from the mapping. If not
+        given, the first key from the mapping (in normal dictionary iteration
+        order) will be used as the default.
+
+    Attributes
+    ----------
+    map : dict
+        A dictionary whose keys are strings that are valid values for the
+        trait attribute, and whose corresponding values are the values for
+        the shadow trait attribute.
+    """
+    is_mapped = True
+
+    def __init__(self, map, **metadata):
+        self.map = map
+        self._map = {}
+        for key in map.keys():
+            self._map[key] = key
+
+        try:
+            default_value = metadata.pop("default_value")
+        except KeyError:
+            default_value = next(iter(self.map))
+        else:
+            default_value = self.value_for(default_value)
+
+        super().__init__(default_value, **metadata)
+
+    def value_for(self, value):
+        if not isinstance(value, str):
+            raise TraitError(
+                "Value must be {}, but a value {!r} was specified.".format(
+                    self.info(), value)
+            )
+
+        if value in self._map:
+            return self._map[value]
+
+        matches = [key for key in self.map if key.startswith(value)]
+        if len(matches) == 1:
+            self._map[value] = match = matches[0]
+            return match
+
+        raise TraitError(
+            "Value must be {}, but a value {!r} was specified.".format(
+                self.info(), value)
+        )
+
+    def mapped_value(self, value):
+        """ Get the mapped value for a value. """
+        return self.map[value]
+
+    def post_setattr(self, object, name, value):
+        setattr(object, name + "_", self.mapped_value(value))
+
+    def info(self):
+        keys = sorted(repr(x) for x in self.map.keys())
+        return " or ".join(keys) + " (or any unique prefix)"
+
+    def get_editor(self, trait):
+        from traitsui.api import EnumEditor
+
+        return EnumEditor(values=self, cols=trait.cols or 3)
+
+
+class BaseClass(TraitType):
+    """ A base trait type for trait types which have an associated class.
+
+    Traits sometimes need to be able to access classes which have not
+    yet been defined, or which are from a module that we want to defer
+    importing from.  To support this, classes can be determined
+    dynamically by specifying a string name for the class (e.g.
+    ``'package1.package2.module.class'``).  This base class provides the
+    machinery for this sort of deferred access to classes.
+
+    Any subclass must define instances with 'klass' and 'module' attributes
+    that contain the string name of the class (or actual class object) and
+    the module name that contained the original trait definition (used for
+    resolving local class names (e.g. 'LocalClass')).
+
+    This is an abstract class that only provides helper methods used to
+    resolve the class name into an actual class object.
+
+    Attributes
+    ----------
+    klass : type or str
+        The class object or a string that refers to it.
+    module : str
+        The name of the module that contains the class.
+    """
+
+    def resolve_class(self, object, name, value):
+        """ Resolve the class object as part of validation.
+
+        This is called when the ``klass`` attribute is a string and sets the
+        ``klass`` attribute to the actual klass object as a side-effect.  If
+        the class cannot be resolved, it will call validate_failed().
+        """
+        klass = self.validate_class(self.find_class(self.klass))
+        if klass is None:
+            self.validate_failed(object, name, value)
+
+        self.klass = klass
+
+    def validate_class(self, klass):
+        """ Validate a class object. """
+        return klass
+
+    def find_class(self, klass):
+        """ Given a string describing a class, get the class object.
+        """
+        module = self.module
+        col = klass.rfind(".")
+        if col >= 0:
+            module = klass[:col]
+            klass = klass[col + 1:]
+
+        theClass = getattr(sys.modules.get(module), klass, None)
+        if (theClass is None) and (col >= 0):
+            try:
+                mod = import_module(module)
+                theClass = getattr(mod, klass, None)
+            except Exception:
+                pass
+
+        return theClass
+
+    def validate_failed(self, object, name, value):
+        """ Raise a TraitError if the class could not be resolved. """
+        self.error(object, name, value)
+
+
+class BaseInstance(BaseClass):
+    """ A trait type whose value is an instance of a class or its subclasses.
+
+    The default value is **None** if *klass* is an instance or if it is a
+    class and *args* and *kw* are not specified. Otherwise, the default value
+    is the instance obtained by calling ``klass(*args, **kw)``. Note that the
+    constructor call is performed each time a default value is assigned, so
+    each default value assigned is a unique instance.
+
+    Parameters
+    ----------
+    klass : class, str or instance
+        The object that forms the basis for the trait; if it is an
+        instance, then trait values must be instances of the same class or
+        a subclass. This object is not the default value, even if it is an
+        instance.  If the provided value is a string, it is expected to be
+        a reference to a class that will be resolved at run-time.
+    factory : callable
+        A callable, typically a class, that when called with *args* and
+        *kw*, returns the default value for the trait. If not specified,
+        or *None*, *klass* is used as the factory.
+    args : tuple
+        Positional arguments for generating the default value.
+    kw : dictionary
+        Keyword arguments for generating the default value.
+    allow_none : bool
+        Indicates whether None is allowed as a value.
+    adapt : str
+        A string specifying how adaptation should be applied. The possible
+        values are:
+
+        - 'no': Adaptation is not allowed.
+        - 'yes': Adaptation is allowed. If adaptation fails, an
+          exception should be raised.
+        - 'default': Adaptation is allowed. If adaptation fails, the
+          default value for the trait should be used.
+
+    Attributes
+    ----------
+    factory : callable
+        A callable, typically a class, that when called with *args* and
+        *kw*, returns the default value for the trait. If not specified,
+        or *None*, *klass* is used as the factory.
+    args : tuple
+        Positional arguments for generating the default value.
+    kw : dictionary
+        Keyword arguments for generating the default value.
+    allow_none : bool
+        Indicates whether None is allowed as a value.
+    adapt : str
+        A string specifying how adaptation should be applied. The possible
+        values are:
+
+        - 'no': Adaptation is not allowed.
+        - 'yes': Adaptation is allowed. If adaptation fails, an
+          exception should be raised.
+        - 'default': Adaptation is allowed. If adaptation fails, the
+          default value for the trait should be used.
+    """
+
+    #: Default adaptation behavior.
+    adapt_default = "no"
+
+    def __init__(
+        self,
+        klass=None,
+        factory=None,
+        args=None,
+        kw=None,
+        allow_none=True,
+        adapt=None,
+        module=None,
+        **metadata
+    ):
+        if klass is None:
+            raise TraitError(
+                "A %s trait must have a class specified."
+                % self.__class__.__name__
+            )
+
+        metadata.setdefault("copy", "deep")
+        metadata.setdefault("instance_handler", "_instance_changed_handler")
+
+        adapt = adapt or self.adapt_default
+        if adapt not in AdaptMap:
+            raise TraitError("'adapt' must be 'yes', 'no' or 'default'.")
+
+        if isinstance(factory, tuple):
+            if args is None:
+                args, factory = factory, klass
+            elif isinstance(args, dict):
+                factory, args, kw = klass, factory, args
+
+        elif (kw is None) and isinstance(factory, dict):
+            kw, factory = factory, klass
+
+        elif ((args is not None) or (kw is not None)) and (factory is None):
+            factory = klass
+
+        self._allow_none = allow_none
+        self.adapt = AdaptMap[adapt]
+        self.module = module or get_module_name()
+
+        if isinstance(klass, str):
+            self.klass = klass
+        else:
+            if not isinstance(klass, type):
+                klass = klass.__class__
+
+            self.klass = klass
+            self.init_fast_validate()
+
+        value = factory
+        if factory is not None:
+            if args is None:
+                args = ()
+
+            if kw is None:
+                if isinstance(args, dict):
+                    kw = args
+                    args = ()
+                else:
+                    kw = {}
+            elif not isinstance(kw, dict):
+                raise TraitError("The 'kw' argument must be a dictionary.")
+
+            if (not callable(factory)) and (
+                not isinstance(factory, str)
+            ):
+                if (len(args) > 0) or (len(kw) > 0):
+                    raise TraitError("'factory' must be callable")
+            else:
+                value = _InstanceArgs(factory, args, kw)
+
+        self.default_value = value
+
+        super(BaseInstance, self).__init__(value, **metadata)
+
+    def validate(self, object, name, value):
+        """ Validates that the value is a valid object instance.
+        """
+        from traits.adaptation.api import adapt
+
+        if value is None:
+            if self._allow_none:
+                return value
+
+            self.validate_failed(object, name, value)
+
+        if isinstance(self.klass, str):
+            self.resolve_class(object, name, value)
+
+        # Adaptation mode 0: do a simple isinstance check.
+        if self.adapt == 0:
+            if isinstance(value, self.klass):
+                return value
+            else:
+                self.validate_failed(object, name, value)
+
+        # Try adaptation; return adapted value on success.
+        result = adapt(value, self.klass, None)
+        if result is not None:
+            return result
+
+        # Adaptation failed. Move on to an isinstance check.
+        if isinstance(value, self.klass):
+            return value
+
+        # Adaptation and isinstance both failed. In mode 1, fail.
+        # Otherwise, return the default.
+        if self.adapt == 1:
+            self.validate_failed(object, name, value)
+        else:
+            result = self.default_value
+            if isinstance(result, _InstanceArgs):
+                return result[0](*result[1], **result[2])
+            else:
+                return result
+
+    def info(self):
+        """ Returns a description of the trait.
+        """
+        klass = self.klass
+        if not isinstance(klass, str):
+            klass = klass.__name__
+
+        if self.adapt == 0:
+            result = class_of(klass)
+        else:
+            result = (
+                "an implementor of, or can be adapted to implement, %s" % klass
+            )
+
+        if self._allow_none:
+            return result + " or None"
+
+        return result
+
+    def get_default_value(self):
+        """ Returns a tuple of the form: ( default_value_type, default_value )
+            which describes the default value for this trait.
+        """
+        dv = self.default_value
+        dvt = self.default_value_type
+        if dvt < 0:
+            if not isinstance(dv, _InstanceArgs):
+                return super(BaseInstance, self).get_default_value()
+
+            self.default_value_type = dvt = DefaultValue.callable_and_args
+            self.default_value = dv = (
+                self.create_default_value,
+                dv.args,
+                dv.kw,
+            )
+
+        return (dvt, dv)
+
+    def create_editor(self):
+        """ Returns the default traits UI editor for this type of trait.
+        """
+        from traitsui.api import InstanceEditor
+
+        return InstanceEditor(
+            label=self.label or "",
+            view=self.view or "",
+            kind=self.kind or "live",
+        )
+
+    # -- Private Methods ------------------------------------------------------
+
+    def create_default_value(self, *args, **kw):
+        klass = args[0]
+        if isinstance(klass, str):
+            klass = self.validate_class(self.find_class(klass))
+            if klass is None:
+                raise TraitError("Unable to locate class: " + args[0])
+
+        return klass(*args[1:], **kw)
+
+    #: fixme: Do we still need this method using the new style?...
+    def allow_none(self):
+        self._allow_none = True
+        self.init_fast_validate()
+
+    def init_fast_validate(self):
+        """ Does nothing for the BaseInstance' class. Used by the 'Instance',
+            'Supports' and 'AdaptsTo' classes to set up the C-level fast
+            validator.
+        """
+        pass
+
+    def resolve_class(self, object, name, value):
+        super(BaseInstance, self).resolve_class(object, name, value)
+
+        # fixme: The following is quite ugly, because it wants to try and fix
+        # the trait referencing this handler to use the 'fast path' now that
+        # the actual class has been resolved. The problem is finding the trait,
+        # especially in the case of List(Instance('foo')), where the
+        # object.base_trait(...) value is the List trait, not the Instance
+        # trait, so we need to check for this and pull out the List
+        # 'item_trait'. Obviously this does not extend well to other traits
+        # containing nested trait references (Dict?)...
+        self.init_fast_validate()
+        trait = object.base_trait(name)
+        handler = trait.handler
+        if handler is not self:
+            set_validate = getattr(handler, "set_validate", None)
+            if set_validate is not None:
+                # The outer trait is a TraitCompound. Recompute its
+                # fast_validate table now that we have updated ours.
+                # FIXME: there are probably still issues if the TraitCompound
+                # is further nested.
+                set_validate()
+            else:
+                item_trait = getattr(handler, "item_trait", None)
+                if item_trait is not None and item_trait.handler is self:
+                    # The outer trait is a List trait.
+                    trait = item_trait
+                    handler = self
+                else:
+                    return
+        if handler.fast_validate is not None:
+            trait.set_validate(handler.fast_validate)
+
+
+class Instance(BaseInstance):
+    """ A fast-validated trait type whose value is an instance of a class.
+    """
+
+    def init_fast_validate(self):
+        """ Sets up the C-level fast validator. """
+
+        if self.adapt == 0:
+            fast_validate = [ValidateTrait.instance, self.klass]
+            if self._allow_none:
+                fast_validate = [ValidateTrait.instance, None, self.klass]
+            else:
+                fast_validate = [ValidateTrait.instance, self.klass]
+
+            if self.klass in TypeTypes:
+                fast_validate[0] = ValidateTrait.type
+
+            self.fast_validate = tuple(fast_validate)
+        else:
+            self.fast_validate = (
+                ValidateTrait.adapt, self.klass, self.adapt, self._allow_none)
+
+
+class Supports(Instance):
+    """ A trait type whose value is adapted to a specified protocol.
+
+    In other words, the value of the trait directly provide, or can be adapted
+    to, the given protocol (Interface or type).
+
+    The value of the trait after assignment is the possibly adapted value
+    (i.e., it is the original assigned value if that provides the protocol,
+    or is an adapter otherwise).
+
+    The original, unadapted value is stored in a "shadow" attribute with
+    the same name followed by an underscore (e.g., ``foo`` and ``foo_``).
+    """
+
+    adapt_default = "yes"
+
+    def post_setattr(self, object, name, value):
+        """ Performs additional post-assignment processing.
+        """
+        # Save the original, unadapted value in the mapped trait:
+        object.__dict__[name + "_"] = value
+
+    def as_ctrait(self):
+        """ Returns a CTrait corresponding to the trait defined by this class.
+        """
+        return self.modify_ctrait(super(Supports, self).as_ctrait())
+
+    def modify_ctrait(self, ctrait):
+
+        # Tell the C code that the 'post_setattr' method wants the original,
+        # unadapted value passed to 'setattr':
+        ctrait.post_setattr_original_value = True
+        return ctrait
+
+
+class AdaptsTo(Supports):
+    """ A trait type whose value must support a specified protocol.
+
+    In other words, the value of the trait directly provide, or can be adapted
+    to, the given protocol (Interface or type).
+
+    The value of the trait after assignment is the original, unadapted value.
+
+    A possibly adapted value is stored in a "shadow" attribute with
+    the same name followed by an underscore (e.g., ``foo`` and ``foo_``).
+    """
+
+    def modify_ctrait(self, ctrait):
+        # Tell the C code that 'setattr' should store the original, unadapted
+        # value passed to it:
+        ctrait.setattr_original_value = True
+        return ctrait
+
+
+class Type(BaseClass):
+    """ A trait type whose value must be a subclass of a specified class.
+
+    Parameters
+    ----------
+    value : class or None
+        The default value of the trait.
+    klass : class, str or None
+        The class that trait values must be subclasses of.  If None, then
+        the default value is used instead.  If both are None, then the
+        ``object`` type is used.  If it is a string, the first time that
+        the validate method is called, the class will be imported and
+        the value replaced with the class object.
+    allow_none : bool
+        Indicates whether None is allowed as an assignable value. Even if
+        **False**, the default *value* may be **None**.
+    **metadata
+        Trait metadata for the trait.
+
+    Attributes
+    ----------
+    klass : class or str
+        The class that trait values must be subclasses of.  If this is a
+        string, the first time that the validate method is called, the
+        class will be imported and the value replaced with the class object.
+    module : str
+        The name of the module where local class names (ie. class names
+        with no module components) are presumed to be importable from.
+        This is the caller's caller's module, as determined by the
+        ``get_module_method``.
+    """
+
+    def __init__(self, value=None, klass=None, allow_none=True, **metadata):
+        if value is None:
+            if klass is None:
+                klass = object
+
+        elif klass is None:
+            klass = value
+
+        if isinstance(klass, str):
+            self.validate = self.resolve
+
+        elif not isinstance(klass, type):
+            raise TraitError("A Type trait must specify a class.")
+
+        self.klass = klass
+        self._allow_none = allow_none
+        self.module = get_module_name()
+
+        super(Type, self).__init__(value, **metadata)
+
+    def validate(self, object, name, value):
+        """ Validates that the value is a valid object instance.
+        """
+        try:
+            if issubclass(value, self.klass):
+                return value
+        except:
+            if (value is None) and (self._allow_none):
+                return value
+
+        self.error(object, name, value)
+
+    def resolve(self, object, name, value):
+        """ Resolves a class originally specified as a string into an actual
+            class, then resets the trait so that future calls will be handled
+            by the normal validate method.
+        """
+        if isinstance(self.klass, str):
+            self.resolve_class(object, name, value)
+            del self.validate
+
+        return self.validate(object, name, value)
+
+    def info(self):
+        """ Returns a description of the trait.
+        """
+        klass = self.klass
+        if not isinstance(klass, str):
+            klass = klass.__name__
+
+        result = "a subclass of " + klass
+
+        if self._allow_none:
+            return result + " or None"
+
+        return result
+
+    def get_default_value(self):
+        """ Returns a tuple of the form: ( default_value_type, default_value )
+        which describes the default value for this trait.
+        """
+        if not isinstance(self.default_value, str):
+            return super(Type, self).get_default_value()
+
+        return (
+            DefaultValue.callable_and_args,
+            (self.resolve_default_value, (), None),
+        )
+
+    def resolve_default_value(self):
+        """ Resolves a class name into a class so that it can be used to
+            return the class as the default value of the trait.
+        """
+        if isinstance(self.klass, str):
+            try:
+                self.resolve_class(None, None, None)
+                del self.validate
+            except:
+                raise TraitError(
+                    "Could not resolve %s into a valid class" % self.klass
+                )
+
+        return self.klass
+
+
+#: An alias for the Type trait
+Subclass = Type
+
+
+class Event(TraitType):
+    """ A trait type that holds no value but can be set and listened to.
+
+    Event traits are write-only traits.  They do not hold any value, but
+    they can be assigned to, and listeners to the trait will be notified
+    of the assignment.  Since no value is held, trait change functions that
+    ask for the ``old`` value of the trait will be given the Undefined
+    special value.
+
+    Event traits can be given an optional trait type that is used to validate
+    values assigned to the trait.  If the assigned value does not validate,
+    then a TraitError will occur.
+
+    Parameters
+    ----------
+    trait : a trait
+        The type of value that can be assigned to the event.
+    """
+
+    def __init__(self, trait=None, **metadata):
+        metadata["type"] = "event"
+        metadata["transient"] = True
+
+        super(Event, self).__init__(**metadata)
+
+        self.trait = None
+        if trait is not None:
+            self.trait = trait_from(trait)
+            validate = self.trait.get_validate()
+            if validate is not None:
+                self.fast_validate = validate
+
+    def full_info(self, object, name, value):
+        """ Returns a description of the trait.
+        """
+        trait = self.trait
+        if trait is None:
+            return "any value"
+
+        return trait.full_info(object, name, value)
+
+
+class Button(Event):
+    """ An Event trait type whose UI editor is a button.
+
+    Parameters
+    ----------
+    label : str
+        The label for the button.
+    image : pyface.ImageResource
+        An image to display on the button.
+    style : 'button', 'radio', 'toolbar' or 'checkbox'
+        The style of button to display.
+    values_trait : str
+        For a "button" or "toolbar" style, the name of an enum
+        trait whose values will populate a drop-down menu on the button.
+        The selected value will replace the label on the button.
+    orientation : 'horizontal' or 'vertical'
+        The orientation of the label relative to the image.
+    width_padding : integer between 0 and 31
+        Extra padding (in pixels) added to the left and right sides of
+        the button.
+    height_padding : integer between 0 and 31
+        Extra padding (in pixels) added to the top and bottom of the
+        button.
+    view : traitsui View, optional
+        An optional View to display when the button is clicked.
+    **metadata
+        Trait metadata for the trait.
+
+    Attributes
+    ----------
+    label : str
+        The label for the button.
+    image : pyface.ImageResource
+        An image to display on the button.
+    style : 'button', 'radio', 'toolbar' or 'checkbox'
+        The style of button to display.
+    values_trait : str
+        For a "button" or "toolbar" style, the name of an enum
+        trait whose values will populate a drop-down menu on the button.
+        The selected value will replace the label on the button.
+    orientation : 'horizontal' or 'vertical'
+        The orientation of the label relative to the image.
+    width_padding : integer between 0 and 31
+        Extra padding (in pixels) added to the left and right sides of
+        the button.
+    height_padding : integer between 0 and 31
+        Extra padding (in pixels) added to the top and bottom of the
+        button.
+    view : traitsui View, optional
+        An optional View to display when the button is clicked.
+    """
+
+    def __init__(
+        self,
+        label="",
+        image=None,
+        values_trait=None,
+        style="button",
+        orientation="vertical",
+        width_padding=7,
+        height_padding=5,
+        view=None,
+        **metadata
+    ):
+        self.label = label
+        self.values_trait = values_trait
+        self.image = image
+        self.style = style
+        self.orientation = orientation
+        self.width_padding = width_padding
+        self.height_padding = height_padding
+        self.view = view
+        super(Button, self).__init__(**metadata)
+
+    def create_editor(self):
+        from traitsui.api import ButtonEditor
+
+        editor = ButtonEditor(
+            label=self.label,
+            values_trait=self.values_trait,
+            image=self.image,
+            style=self.style,
+            orientation=self.orientation,
+            width_padding=self.width_padding,
+            height_padding=self.height_padding,
+            view=self.view,
+        )
+        return editor
+
+
+class ToolbarButton(Button):
+    """ A Button trait type whose UI editor is a toolbar button.
+
+    This is just a Button trait with different defaults to style it like
+    a toolbar button.
+
+    Parameters
+    ----------
+    label : str
+        The label for the button.
+    image : pyface.ImageResource
+        An image to display on the button.
+    style : 'button', 'radio', 'toolbar' or 'checkbox'
+        The style of button to display.
+    orientation : 'horizontal' or 'vertical'
+        The orientation of the label relative to the image.
+    width_padding : integer between 0 and 31
+        Extra padding (in pixels) added to the left and right sides of
+        the button.
+    height_padding : integer between 0 and 31
+        Extra padding (in pixels) added to the top and bottom of the
+        button.
+    **metadata
+        Trait metadata for the trait.
+
+    Attributes
+    ----------
+    label : str
+        The label for the button.
+    image : pyface.ImageResource
+        An image to display on the button.
+    style : 'button', 'radio', 'toolbar' or 'checkbox'
+        The style of button to display.
+    values_trait : str
+        For a "button" or "toolbar" style, the name of an enum
+        trait whose values will populate a drop-down menu on the button.
+        The selected value will replace the label on the button.
+    orientation : 'horizontal' or 'vertical'
+        The orientation of the label relative to the image.
+    width_padding : integer between 0 and 31
+        Extra padding (in pixels) added to the left and right sides of
+        the button.
+    height_padding : integer between 0 and 31
+        Extra padding (in pixels) added to the top and bottom of the
+        button.
+    view : traitsui View, optional
+        An optional View to display when the button is clicked.
+    """
+
+    def __init__(
+        self,
+        label="",
+        image=None,
+        style="toolbar",
+        orientation="vertical",
+        width_padding=2,
+        height_padding=2,
+        **metadata
+    ):
+        super(ToolbarButton, self).__init__(
+            label,
+            image=image,
+            style=style,
+            orientation=orientation,
+            width_padding=width_padding,
+            height_padding=height_padding,
+            **metadata
+        )
+
+
+class Either(TraitType):
+    """ A trait type whose value can be any of of a specified list of traits.
+
+    Parameters
+    ----------
+    *traits
+        Arguments that define allowable trait values.
+    **metadata
+        Trait metadata for the trait.
+
+    Attributes
+    ----------
+    trait_maker : TraitHandler
+        A TraitHandler generated by _TraitMaker from the arguments.
+    """
+
+    def __init__(self, *traits, **metadata):
+        self.trait_maker = _TraitMaker(
+            metadata.pop("default", None), *traits, **metadata
+        )
+
+    def as_ctrait(self):
+        """ Returns a CTrait corresponding to the trait defined by this class.
+        """
+        return self.trait_maker.as_ctrait()
+
+
+class _NoneTrait(TraitType):
+    """ Defines a trait that only accepts the None value
+
+    This is primarily used for supporting ``Union``.
+    """
+
+    info_text = "None"
+
+    default_value = None
+
+    default_value_type = DefaultValue.constant
+
+    def __init__(self, **metadata):
+        default_value = metadata.pop("default_value", None)
+        if default_value is not None:
+            raise ValueError("Cannot set default value {} "
+                             "for _NoneTrait".format(default_value))
+        super(_NoneTrait, self).__init__(**metadata)
+
+    def validate(self, obj, name, value):
+        if value is None:
+            return value
+
+        self.error(obj, name, value)
+
+
+class Union(TraitType):
+    """ Defines a trait whose value can be any of of a specified list of
+    trait types or list of trait type instances or None
+
+    If the default value is not defined on Union, the default value from the
+    first trait will be used.
+    """
+
+    def __init__(self, *traits, **metadata):
+        self.list_ctrait_instances = []
+
+        if not traits:
+            traits = (_NoneTrait,)
+
+        for trait in traits:
+            if trait is None:
+                trait = _NoneTrait
+            ctrait_instance = trait_cast(trait)
+            if ctrait_instance is None:
+                raise ValueError("Union trait declaration expects a trait "
+                                 "type or an instance of trait type or None,"
+                                 " but got {!r} instead".format(trait))
+
+            self.list_ctrait_instances.append(ctrait_instance)
+
+        # ``Either`` uses 'default' for defining static default values.
+        # Raise if 'default' is found in order to help code migrate to Union
+        if "default" in metadata:
+            raise ValueError(
+                "Union default value should be set via 'default_value', not "
+                "'default'."
+            )
+
+        default_value = None
+        if 'default_value' in metadata:
+            default_value = metadata.pop("default_value")
+        elif self.list_ctrait_instances:
+            default_value = self.list_ctrait_instances[0].default
+
+        self.default_value_type = _infer_default_value_type(default_value)
+        super().__init__(default_value, **metadata)
+
+    def validate(self, obj, name, value):
+        """ Return the value by the first trait in the list that can
+        validate the assigned value, raise an error if none of them can.
+        """
+        for trait_type_instance in self.list_ctrait_instances:
+            try:
+                return trait_type_instance.validate(obj, name, value)
+            except TraitError:
+                pass
+
+        self.error(obj, name, value)
+
+    def info(self):
+        return " or ".join([ctrait.info() for ctrait in
+                            self.list_ctrait_instances])
+
+    def inner_traits(self):
+        return tuple(self.list_ctrait_instances)
+
+    def get_editor(self, trait):
+        from traitsui.api import TextEditor, CompoundEditor
+
+        the_editors = [x.get_editor() for x in self.list_ctrait_instances]
+        text_editor = TextEditor()
+        count = 0
+        editors = []
+        for editor in the_editors:
+            if isinstance(text_editor, editor.__class__):
+                count += 1
+                if count > 1:
+                    continue
+            editors.append(editor)
+
+        return CompoundEditor(editors=editors)
+
+
+# -------------------------------------------------------------------------------
+#  'Symbol' trait:
+# -------------------------------------------------------------------------------
+class Symbol(TraitType):
+    """ A property trait type that refers to a Python object by name.
+
+    The value set to the trait must be a value of the form
+    ``'[package.package...package.]module[:symbol[([arg1,...,argn])]]'``
+    which is imported and evaluated to get underlying value.
+
+    The value returned by the trait is the actual object that this string
+    refers to.  The value is cached, so any calls are only evaluated once.
+    """
+
+    #: A description of the type of value this trait accepts:
+    info_text = (
+        "an object or a string of the form "
+        "'[package.package...package.]module[:symbol[([arg1,...,argn])]]' "
+        "specifying where to locate the object"
+    )
+
+    def get(self, object, name):
+        value = object.__dict__.get(name, Undefined)
+        if value is Undefined:
+            cache = TraitsCache + name
+            ref = object.__dict__.get(cache)
+            if ref is None:
+                object.__dict__[cache] = ref = object.trait(
+                    name
+                ).default_value_for(object, name)
+
+            if isinstance(ref, str):
+                object.__dict__[name] = value = self._resolve(ref)
+
+        return value
+
+    def set(self, object, name, value):
+        dict = object.__dict__
+        old = dict.get(name, Undefined)
+        if isinstance(value, str):
+            dict.pop(name, None)
+            dict[TraitsCache + name] = value
+            object.trait_property_changed(name, old)
+        else:
+            dict[name] = value
+            object.trait_property_changed(name, old, value)
+
+    def _resolve(self, ref):
+        try:
+            elements = ref.split("(", 1)
+            symbol = import_symbol(elements[0])
+            if len(elements) == 1:
+                return symbol
+
+            args = eval("(" + elements[1])
+            if not isinstance(args, tuple):
+                args = (args,)
+
+            return symbol(*args)
+        except Exception:
+            raise TraitError(
+                "Could not resolve '%s' into a valid symbol." % ref
+            )
+
+
+class UUID(TraitType):
+    """ A read-only trait type whose value is a globally unique UUID (type 4).
+
+    Parameters
+    ----------
+    can_init : bool
+        Whether the value can be set during object instantiation.  Otherwise
+        the UUID is generated automatically.
+
+    Example
+    -------
+
+    Passing `can_init=True` allows the UUID value to be set during
+    object instantiation, e.g.::
+
+        class A(HasTraits):
+            id = UUID
+
+        class B(HasTraits):
+            id = UUID(can_init=True)
+
+        # TraitError!
+        A(id=uuid.uuid4())
+
+        # Okay!
+        B(id=uuid.uuid4())
+
+    Note however that in both cases, the UUID trait is set automatically
+    to a `uuid.UUID` instance (assuming none is provided during initialization
+    in the latter case).
+    """
+
+    #: A description of the type of value this trait accepts:
+    info_text = "a read-only UUID"
+
+    def __init__(self, can_init=False, **metadata):
+        super(UUID, self).__init__(None, **metadata)
+        self.can_init = can_init
+
+    def validate(self, object, name, value):
+        """ Raises an error, since no values can be assigned to the trait.
+        """
+        if not self.can_init:
+            raise TraitError(
+                "The '%s' trait of %s instance is a read-only "
+                "UUID." % (name, class_of(object))
+            )
+
+        if object.traits_inited():
+            msg = ("Initializable UUID trait is read-only "
+                   "after initialization")
+            raise TraitError(msg)
+
+        if isinstance(value, uuid.UUID):
+            return value
+
+        try:
+            # Construct the UUID from a string
+            return uuid.UUID(value)
+        except ValueError:
+            msg = ("The '{}' trait of '{}' expects an RFC 4122-compatible "
+                   "UUID value, but '{}' was given")
+            raise TraitError(msg.format(name, type(object).__name__, value))
+
+    def get_default_value(self):
+        """ Return a Traits default value tuple for the trait.
+
+        This uses the _create_uuid method to generate the defualt value.
+        """
+        return (
+            DefaultValue.callable_and_args,
+            (self._create_uuid, (), None),
+        )
+
+    # -- Private Methods ---------------------------------------------------
+
+    def _create_uuid(self):
+        return uuid.uuid4()
+
+
+class WeakRef(Instance):
+    """ A trait type holding a weak reference to an instance of a class.
+
+    Only a weak reference is maintained to any object assigned to a WeakRef
+    trait. If no other references exist to the assigned value, the value
+    may be garbage collected, in which case the value of the trait becomes
+    None. In all other cases, the value returned by the trait is the
+    original object.
+
+    Parameters
+    ----------
+    klass : class, str or instance
+        The object that forms the basis for the trait. If *klass* is
+        omitted, then values must be an instance of HasTraits.  If a string,
+        the value will be resolved to a class object at runtime.
+    allow_none : boolean
+        Indicates whether None can be _assigned_.  The trait attribute may
+        give a None value if the object referred to has been garbage collected
+        even if allow_none is False.
+    adapt : str
+        How to use the adaptation infrastructure when setting the value.
+    """
+
+    def __init__(
+        self,
+        klass="traits.has_traits.HasTraits",
+        allow_none=False,
+        adapt="yes",
+        **metadata
+    ):
+        metadata.setdefault("copy", "ref")
+
+        super(WeakRef, self).__init__(
+            klass,
+            allow_none=allow_none,
+            adapt=adapt,
+            module=get_module_name(),
+            **metadata
+        )
+
+    def get(self, object, name):
+        value = getattr(object, name + "_", None)
+        if value is not None:
+            return value.value()
+
+        return None
+
+    def set(self, object, name, value):
+        old = self.get(object, name)
+
+        if value is None:
+            object.__dict__[name + "_"] = None
+        else:
+            object.__dict__[name + "_"] = HandleWeakRef(object, name, value)
+
+        if value is not old:
+            object.trait_property_changed(name, old, value)
+
+    def resolve_class(self, object, name, value):
+        # fixme: We have to override this method to prevent the 'fast validate'
+        # from being set up, since the trait using this is a 'property' style
+        # trait which is not currently compatible with the 'fast_validate'
+        # style (causes internal Python SystemError messages).
+        klass = self.find_class(self.klass)
+        if klass is None:
+            self.validate_failed(object, name, value)
+
+        self.klass = klass
+
+
+#: A trait type for datetime.date instances.
+Date = BaseInstance(datetime.date, editor=date_editor)
+
+
+#: A trait type for datetime.datetime instances.
+Datetime = BaseInstance(datetime.datetime, editor=datetime_editor)
+
+
+#: A trait type for datetime.time instances.
+Time = BaseInstance(datetime.time, editor=time_editor)
+
+
+# Predefined, reusable trait instances
+
+# Everything from this point onwards is deprecated, and has a simple
+# drop-in replacement.
+
+#: A trait whose value must support a specified protocol. This is
+#: an alias for :class:`Supports`. Use ``Supports`` instead.
+AdaptedTo = Supports
+
+#: A trait whose value must be a (Unicode) string. This is an alias for
+#: :class:`BaseStr`. Use ``BaseStr`` instead.
+BaseUnicode = BaseStr
+
+#: A trait whose value must be a (Unicode) string, using a C-level
+#: fast validator. This is an alias for :class:`Str`. Use ``Str`` instead.
+Unicode = Str
+
+#: A trait whose value must be a (Unicode) string and which supports
+#: coercions of non-string values to string. This is
+#: an alias for :class:`BaseCStr`. Use ``BaseCStr`` instead.
+BaseCUnicode = BaseCStr
+
+#: A trait whose value must be a (Unicode) string and which supports
+#: coercions of non-string values to string, using a C-level fast validator.
+#: This is an alias for :class:`CStr`. Use ``CStr`` instead.
+CUnicode = CStr
+
+#: A trait whose value must be an integer. This is an alias for
+#: :class:`BaseInt`. Use ``BaseInt`` instead.
+BaseLong = BaseInt
+
+#: A trait whose value must be an integer, using a C-level fast validator.
+#: This is an alias for :class:`Int`. Use ``Int`` instead.
+Long = Int
+
+#: A trait whose value must be an integer and which supports coercions
+#: of non-integer values to integer. This is an alias for
+#: :class:`BaseCInt`. Use ``BaseCInt`` instead.
+BaseCLong = BaseCInt
+
+#: A trait whose value must be an integer and which supports coercions
+#: of non-integer values to integer, using a C-level fast validator.
+#: This is an alias for :class:`CInt`. Use ``CInt`` instead.
+CLong = CInt
+
+#: Synonym for Bool; default value is ``False``. This trait type is
+#: deprecated. Use ``Bool(False)`` or ``Bool()`` instead.
+false = Bool
+
+#: Boolean values only; default value is ``True``. This trait type is
+#: deprecated. Use ``Bool(True)`` instead.
+true = Bool(True)
+
+#: Allows any value to be assigned; no type-checking is performed.
+#: Default value is ``Undefined``. This trait type is deprecated. Use
+#: ``Any(Undefined)`` instead.
+undefined = Any(Undefined)
+
+# -- List Traits --------------------------------------------------------------
+
+#: List of integer values; default value is ``[]``. This trait type is
+#: deprecated. Use ``List(Int)`` instead.
+ListInt = List(int)
+
+#: List of float values; default value is ``[]``. This trait type is
+#: deprecated. Use ``List(Float)`` instead.
+ListFloat = List(float)
+
+#: List of string values; default value is ``[]``. This trait type is
+#: deprecated. Use ``List(Str)`` instead.
+ListStr = List(str)
+
+#: List of string values; default value is ``[]``. This trait type is
+#: deprecated. Use ``List(Str)`` instead.
+ListUnicode = List(str)
+
+#: List of complex values; default value is ``[]``. This trait type is
+#: deprecated. Use ``List(Complex)`` instead.
+ListComplex = List(complex)
+
+#: List of Boolean values; default value is ``[]``. This trait type is
+#: deprecated. Use ``List(Bool)`` instead.
+ListBool = List(bool)
+
+#: List of function values; default value is ``[]``. This trait type is
+#: deprecated. Use ``List(Instance(types.FunctionType, allow_none=False))``
+#: instead.
+ListFunction = List(FunctionType)
+
+#: List of method values; default value is ``[]``. This trait type is
+#: deprecated. Use ``List(Instance(types.MethodType, allow_none=False))``
+#: instead.
+ListMethod = List(MethodType)
+
+#: List of container type values; default value is ``[]``. This trait type is
+#: deprecated. Use ``List(This(allow_none=False))`` instead.
+ListThis = List(This(allow_none=False))
+
+# -- Dictionary Traits --------------------------------------------------------
+
+#: Only a dictionary with strings as keys can be assigned; only string keys
+#: can be inserted. The default value is {}. This trait type is deprecated. Use
+#: ``Dict(Str, Any)`` instead.
+DictStrAny = Dict(str, Any)
+
+#: Only a dictionary mapping strings to strings can be assigned; only string
+#: keys with string values can be inserted. The default value is {}. This trait
+#: type is deprecated. Use ``Dict(Str, Str)`` instead.
+DictStrStr = Dict(str, str)
+
+#: Only a dictionary mapping strings to integers can be assigned; only string
+#: keys with integer values can be inserted. The default value is {}. This
+#: trait type is deprecated. Use ``Dict(Str, Int)`` instead.
+DictStrInt = Dict(str, int)
+
+#: Only a dictionary mapping strings to floats can be assigned; only string
+#: keys with float values can be inserted. The default value is {}. This trait
+#: type is deprecated. Use ``Dict(Str, Float)`` instead.
+DictStrFloat = Dict(str, float)
+
+#: Only a dictionary mapping strings to booleans can be assigned; only string
+#: keys with boolean values can be inserted. The default value is {}. This
+#: trait type is deprecated. Use ``Dict(Str, Bool)`` instead.
+DictStrBool = Dict(str, bool)
+
+#: Only a dictionary mapping strings to lists can be assigned; only string keys
+#: with list values can be inserted. The default value is {}. This trait type
+#: is deprecated. Use ``Dict(Str, List)`` instead.
+DictStrList = Dict(str, list)
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/traits/traits.html b/_modules/traits/traits.html new file mode 100644 index 000000000..4b1a7ea95 --- /dev/null +++ b/_modules/traits/traits.html @@ -0,0 +1,833 @@ + + + + + + + traits.traits — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for traits.traits

+# (C) Copyright 2005-2020 Enthought, Inc., Austin, TX
+# All rights reserved.
+#
+# This software is provided without warranty under the terms of the BSD
+# license included in LICENSE.txt and may be redistributed only under
+# the conditions described in the aforementioned license. The license
+# is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+# Thanks for using Enthought open source!
+
+"""
+Defines the 'core' traits for the Traits package. A trait is a type definition
+that can be used for normal Python object attributes, giving the attributes
+some additional characteristics:
+
+Initialization:
+    Traits have predefined values that do not need to be explicitly
+    initialized in the class constructor or elsewhere.
+Validation:
+    Trait attributes have flexible, type-checked values.
+Delegation:
+    Trait attributes' values can be delegated to other objects.
+Notification:
+    Trait attributes can automatically notify interested parties when
+    their values change.
+Visualization:
+    Trait attributes can automatically construct (automatic or
+    programmer-defined) user interfaces that allow their values to be
+    edited or displayed)
+
+.. note:: 'trait' is a synonym for 'property', but is used instead of the
+    word 'property' to differentiate it from the Python language 'property'
+    feature.
+"""
+
+from types import FunctionType, MethodType
+import warnings
+
+from .constants import (
+    ComparisonMode,
+    DefaultValue,
+    TraitKind,
+)
+from .ctrait import CTrait
+from .trait_errors import TraitError
+from .trait_base import (
+    SequenceTypes,
+    TypeTypes,
+    add_article,
+)
+from .trait_converters import (
+    trait_cast,
+    check_trait as try_trait_cast,
+)
+
+from .trait_handler import TraitHandler
+from .trait_type import (
+    _infer_default_value_type,
+    _read_only,
+    _write_only,
+)
+from .trait_handlers import (
+    TraitInstance,
+    TraitFunction,
+    TraitCoerceType,
+    TraitCastType,
+    TraitEnum,
+    TraitCompound,
+    TraitMap,
+    _undefined_get,
+    _undefined_set,
+)
+from .trait_factory import (
+    TraitFactory,
+)
+from .util.deprecated import deprecated
+
+# Constants
+
+NoneType = type(None)  # Python 3's types does not include NoneType
+
+ConstantTypes = (NoneType, int, float, complex, str)
+
+PythonTypes = (
+    str,
+    int,
+    float,
+    complex,
+    list,
+    tuple,
+    dict,
+    FunctionType,
+    MethodType,
+    type,
+    NoneType,
+)
+
+CallableTypes = (FunctionType, MethodType)
+
+TraitTypes = (TraitHandler, CTrait)
+
+DefaultValues = {
+    str: "",
+    int: 0,
+    float: 0.0,
+    complex: 0j,
+    list: [],
+    tuple: (),
+    dict: {},
+    bool: False,
+}
+
+
+# This function is needed when unpickling historical pickles (pickles
+# created on versions of Traits prior to 6.0). It can be removed when
+# there's no longer any need to support pickles generated on older
+# versions of Traits.
+
+def __newobj__(cls, *args):
+    """ Unpickles new-style objects.
+    """
+    return cls.__new__(cls, *args)
+
+
+# --- 'instance' traits -------------------------------------------------------
+
+
+class _InstanceArgs(object):
+    def __init__(self, factory, args, kw):
+        self.args = (factory,) + args
+        self.kw = kw
+
+
+# --- 'creates a run-time default value' --------------------------------------
+
+
+class Default(object):
+    """ Generates a value the first time it is accessed.
+
+    A Default object can be used anywhere a default trait value would normally
+    be specified, to generate a default value dynamically.
+    """
+
+    def __init__(self, func=None, args=(), kw=None):
+        self.default_value = (func, args, kw)
+
+
+def Trait(*value_type, **metadata):
+    """ Creates a trait definition.
+
+    This function accepts a variety of forms of parameter lists:
+
+    +-------------------+---------------+-------------------------------------+
+    | Format            | Example       | Description                         |
+    +===================+===============+=====================================+
+    | Trait(*default*)  | Trait(150.0)  | The type of the trait is inferred   |
+    |                   |               | from the type of the default value, |
+    |                   |               | which must be in *ConstantTypes*.   |
+    +-------------------+---------------+-------------------------------------+
+    | Trait(*default*,  | Trait(None,   | The trait accepts any of the        |
+    | *other1*,         | 0, 1, 2,      | enumerated values, with the first   |
+    | *other2*, ...)    | 'many')       | value being the default value. The  |
+    |                   |               | values must be of types in          |
+    |                   |               | *ConstantTypes*, but they need not  |
+    |                   |               | be of the same type. The *default*  |
+    |                   |               | value is not valid for assignment   |
+    |                   |               | unless it is repeated later in the  |
+    |                   |               | list.                               |
+    +-------------------+---------------+-------------------------------------+
+    | Trait([*default*, | Trait([None,  | Similar to the previous format, but |
+    | *other1*,         | 0, 1, 2,      | takes an explicit list or a list    |
+    | *other2*, ...])   | 'many'])      | variable.                           |
+    +-------------------+---------------+-------------------------------------+
+    | Trait(*type*)     | Trait(Int)    | The *type* parameter must be a name |
+    |                   |               | of a Python type (see               |
+    |                   |               | *PythonTypes*). Assigned values     |
+    |                   |               | must be of exactly the specified    |
+    |                   |               | type; no casting or coercion is     |
+    |                   |               | performed. The default value is the |
+    |                   |               | appropriate form of zero, False,    |
+    |                   |               | or emtpy string, set or sequence.   |
+    +-------------------+---------------+-------------------------------------+
+    | Trait(*class*)    |::             | Values must be instances of *class* |
+    |                   |               | or of a subclass of *class*. The    |
+    |                   | class MyClass:| default value is None, but None     |
+    |                   |    pass       | cannot be assigned as a value.      |
+    |                   | foo = Trait(  |                                     |
+    |                   | MyClass)      |                                     |
+    +-------------------+---------------+-------------------------------------+
+    | Trait(None,       |::             | Similar to the previous format, but |
+    | *class*)          |               | None *can* be assigned as a value.  |
+    |                   | class MyClass:|                                     |
+    |                   |   pass        |                                     |
+    |                   | foo = Trait(  |                                     |
+    |                   | None, MyClass)|                                     |
+    +-------------------+---------------+-------------------------------------+
+    | Trait(*instance*) |::             | Values must be instances of the     |
+    |                   |               | same class as *instance*, or of a   |
+    |                   | class MyClass:| subclass of that class. The         |
+    |                   |    pass       | specified instance is the default   |
+    |                   | i = MyClass() | value.                              |
+    |                   | foo =         |                                     |
+    |                   |   Trait(i)    |                                     |
+    +-------------------+---------------+-------------------------------------+
+    | Trait(*handler*)  | Trait(        | Assignment to this trait is         |
+    |                   | TraitEnum )   | validated by an object derived from |
+    |                   |               | **traits.TraitHandler**.            |
+    +-------------------+---------------+-------------------------------------+
+    | Trait(*default*,  | Trait(0.0, 0.0| This is the most general form of    |
+    | { *type* |        | 'stuff',      | the function. The notation:         |
+    | *constant* |      | TupleType)    | ``{...|...|...}+`` means a list of  |
+    | *dict* | *class* ||               | one or more of any of the items     |
+    | *function* |      |               | listed between the braces. Thus, the|
+    | *handler* |       |               | most general form of the function   |
+    | *trait* }+ )      |               | consists of a default value,        |
+    |                   |               | followed by one or more of several  |
+    |                   |               | possible items. A trait defined by  |
+    |                   |               | multiple items is called a          |
+    |                   |               | "compound" trait.                   |
+    +-------------------+---------------+-------------------------------------+
+
+    All forms of the Trait function accept both predefined and arbitrary
+    keyword arguments. The value of each keyword argument becomes bound to the
+    resulting trait object as the value of an attribute having the same name
+    as the keyword. This feature lets you associate metadata with a trait.
+
+    The following predefined keywords are accepted:
+
+    desc : str
+        Describes the intended meaning of the trait. It is used in
+        exception messages and fly-over help in user interfaces.
+    label : str
+        Provides a human-readable name for the trait. It is used to label user
+        interface editors for traits.
+    editor : traits.api.Editor
+        Instance of a subclass Editor object to use when creating a user
+        interface editor for the trait. See the "Traits UI User Guide" for
+        more information on trait editors.
+    comparison_mode : int
+        Indicates when trait change notifications should be generated based
+        upon the result of comparing the old and new values of a trait
+        assignment. Possible values come from the ``ComparisonMode`` enum:
+
+        * 0 (none): The values are not compared and a trait change
+          notification is generated on each assignment.
+        * 1 (identity): A trait change notification is
+          generated if the old and new values are not the same object.
+        * 2 (equality): A trait change notification is generated if the
+          old and new values are not equal using Python's standard equality
+          testing. This is the default.
+
+    """
+    return _TraitMaker(*value_type, **metadata).as_ctrait()
+
+
+class _TraitMaker(object):
+
+    # Ctrait type map for special trait types:
+    type_map = {"event": TraitKind.event, "constant": TraitKind.constant}
+
+    def __init__(self, *value_type, **metadata):
+        metadata.setdefault("type", "trait")
+        self.define(*value_type, **metadata)
+
+    def define(self, *value_type, **metadata):
+        """ Define the trait. """
+        default_value_type = DefaultValue.unspecified
+        default_value = handler = clone = None
+
+        if len(value_type) > 0:
+            default_value = value_type[0]
+            value_type = value_type[1:]
+
+            if (len(value_type) == 0) and (
+                type(default_value) in SequenceTypes
+            ):
+                default_value, value_type = default_value[0], default_value
+
+            if len(value_type) == 0:
+                default_value = try_trait_cast(default_value)
+
+                if default_value in PythonTypes:
+                    handler = TraitCoerceType(default_value)
+                    default_value = DefaultValues.get(default_value)
+
+                elif isinstance(default_value, CTrait):
+                    clone = default_value
+                    default_value_type, default_value = clone.default_value()
+                    metadata["type"] = clone.type
+
+                elif isinstance(default_value, TraitHandler):
+                    handler = default_value
+                    default_value = None
+
+                else:
+                    typeValue = type(default_value)
+                    if typeValue in TypeTypes:
+                        handler = TraitCastType(typeValue)
+
+                    else:
+                        metadata.setdefault(
+                            "instance_handler", "_instance_changed_handler"
+                        )
+                        handler = TraitInstance(default_value)
+                        if default_value is handler.aClass:
+                            default_value = DefaultValues.get(default_value)
+            else:
+                enum = []
+                other = []
+                map = {}
+                self.do_list(value_type, enum, map, other)
+
+                if ((len(enum) == 1) and (enum[0] is None)) and (
+                    (len(other) == 1) and isinstance(other[0], TraitInstance)
+                ):
+                    enum = []
+                    other[0].allow_none()
+                    metadata.setdefault(
+                        "instance_handler", "_instance_changed_handler"
+                    )
+                if len(enum) > 0:
+                    if ((len(map) + len(other)) == 0) and (
+                        default_value not in enum
+                    ):
+                        enum.insert(0, default_value)
+
+                    other.append(TraitEnum(enum))
+
+                if len(map) > 0:
+                    other.append(TraitMap(map))
+
+                if len(other) == 0:
+                    handler = TraitHandler()
+
+                elif len(other) == 1:
+                    handler = other[0]
+                    if isinstance(handler, CTrait):
+                        clone, handler = handler, None
+                        metadata["type"] = clone.type
+
+                    elif isinstance(handler, TraitInstance):
+                        metadata.setdefault(
+                            "instance_handler", "_instance_changed_handler"
+                        )
+
+                        if default_value is None:
+                            handler.allow_none()
+
+                        elif isinstance(default_value, _InstanceArgs):
+                            default_value_type = (
+                                DefaultValue.callable_and_args
+                            )
+                            default_value = (
+                                handler.create_default_value,
+                                default_value.args,
+                                default_value.kw,
+                            )
+
+                        elif (len(enum) == 0) and (len(map) == 0):
+                            aClass = handler.aClass
+                            typeValue = type(default_value)
+
+                            if typeValue is dict:
+                                default_value_type = (
+                                    DefaultValue.callable_and_args
+                                )
+                                default_value = (aClass, (), default_value)
+                            elif not isinstance(default_value, aClass):
+                                if typeValue is not tuple:
+                                    default_value = (default_value,)
+                                default_value_type = (
+                                    DefaultValue.callable_and_args
+                                )
+                                default_value = (aClass, default_value, None)
+                else:
+                    for i, item in enumerate(other):
+                        if isinstance(item, CTrait):
+                            if item.type != "trait":
+                                raise TraitError(
+                                    "Cannot create a complex "
+                                    "trait containing %s trait."
+                                    % add_article(item.type)
+                                )
+                            handler = item.handler
+                            if handler is None:
+                                break
+                            other[i] = handler
+                    else:
+                        handler = TraitCompound(other)
+
+        # Save the results:
+        self.handler = handler
+        self.clone = clone
+
+        if default_value_type < 0:
+            if isinstance(default_value, Default):
+                default_value_type = DefaultValue.callable_and_args
+                default_value = default_value.default_value
+            else:
+                if (handler is None) and (clone is not None):
+                    handler = clone.handler
+
+                if handler is not None:
+                    default_value_type = handler.default_value_type
+                    if default_value_type < 0:
+                        try:
+                            default_value = handler.validate(
+                                None, "", default_value
+                            )
+                        except:
+                            pass
+
+                if default_value_type < 0:
+                    default_value_type = _infer_default_value_type(
+                        default_value
+                    )
+
+        self.default_value_type = default_value_type
+        self.default_value = default_value
+        self.metadata = metadata.copy()
+
+    def do_list(self, list, enum, map, other):
+        """ Determine the correct TraitHandler for each item in a list. """
+        for item in list:
+            if item in PythonTypes:
+                other.append(TraitCoerceType(item))
+            else:
+                item = try_trait_cast(item)
+                typeItem = type(item)
+
+                if typeItem in ConstantTypes:
+                    enum.append(item)
+
+                elif typeItem in SequenceTypes:
+                    self.do_list(item, enum, map, other)
+
+                elif typeItem is dict:
+                    map.update(item)
+
+                elif typeItem in CallableTypes:
+                    other.append(TraitFunction(item))
+
+                elif isinstance(item, TraitTypes):
+                    other.append(item)
+
+                else:
+                    other.append(TraitInstance(item))
+
+    def as_ctrait(self):
+        """ Return a properly initialized 'CTrait' instance. """
+        metadata = self.metadata
+        trait = CTrait(
+            self.type_map.get(metadata.get("type"), TraitKind.trait))
+        clone = self.clone
+        if clone is not None:
+            trait.clone(clone)
+            if clone.__dict__ is not None:
+                trait.__dict__ = clone.__dict__.copy()
+
+        trait.set_default_value(self.default_value_type, self.default_value)
+
+        handler = self.handler
+        if handler is not None:
+            trait.handler = handler
+            validate = getattr(handler, "fast_validate", None)
+            if validate is None:
+                validate = handler.validate
+            trait.set_validate(validate)
+
+            post_setattr = getattr(handler, "post_setattr", None)
+            if post_setattr is not None:
+                trait.post_setattr = post_setattr
+                trait.is_mapped = handler.is_mapped
+
+        rich_compare = metadata.get("rich_compare")
+        if rich_compare is not None:
+            # Ref: enthought/traits#602
+            warnings.warn(
+                "The 'rich_compare' metadata has been deprecated. Please "
+                "use the 'comparison_mode' metadata instead. In a future "
+                "release, rich_compare will have no effect.",
+                DeprecationWarning,
+                stacklevel=4,
+            )
+            if rich_compare:
+                trait.comparison_mode = ComparisonMode.equality
+            else:
+                trait.comparison_mode = ComparisonMode.identity
+
+        comparison_mode = metadata.pop("comparison_mode", None)
+        if comparison_mode is not None:
+            trait.comparison_mode = comparison_mode
+
+        if len(metadata) > 0:
+            if trait.__dict__ is None:
+                trait.__dict__ = metadata
+            else:
+                trait.__dict__.update(metadata)
+
+        return trait
+
+
+def Property(
+    fget=None,
+    fset=None,
+    fvalidate=None,
+    force=False,
+    handler=None,
+    trait=None,
+    **metadata
+):
+    """ Returns a trait whose value is a Python property.
+
+    If no getter, setter or validate functions are specified (and **force** is
+    not True), it is assumed that they are defined elsewhere on the class whose
+    attribute this trait is assigned to. For example::
+
+        class Bar(HasTraits):
+
+            # A float traits Property that should be always positive.
+            foo = Property(Float)
+
+            # Shadow trait attribute
+            _foo = Float
+
+            def _set_foo(self,x):
+                self._foo = x
+
+            def _validate_foo(self, x):
+                if x <= 0:
+                    raise TraitError(
+                        'foo property should be a positive number')
+                return x
+
+            def _get_foo(self):
+                return self._foo
+
+    You can use the **depends_on** metadata attribute to indicate that the
+    property depends on the value of another trait. The value of **depends_on**
+    is an extended name specifier for traits that the property depends on. The
+    property will a trait change notification if any of the traits specified
+    by **depends_on** change. For example::
+
+        class Wheel ( Part ):
+            axle     = Instanced( Axle )
+            position = Property( depends_on = 'axle.chassis.position' )
+
+    For details of the extended trait name syntax, refer to the
+    on_trait_change() method of the HasTraits class.
+
+    Parameters
+    ----------
+    fget : function
+        The "getter" function for the property.
+    fset : function
+        The "setter" function for the property.
+    fvalidate : function
+        The validation function for the property. The method should return the
+        value to set or raise TraitError if the new value is not valid.
+    force : bool
+        Indicates whether to use only the function definitions specified by
+        **fget** and **fset**, and not look elsewhere on the class.
+    handler : function
+        A trait handler function for the trait.
+    trait : Trait or value
+        A trait definition or a value that can be converted to a trait that
+        constrains the values of the property trait.
+    """
+    metadata["type"] = "property"
+
+    # If no parameters specified, must be a forward reference (if not forced):
+    if (not force) and (fset is None):
+        sum = (
+            (fget is not None) + (fvalidate is not None) + (trait is not None)
+        )
+        if sum <= 1:
+            if sum == 0:
+                return ForwardProperty(metadata)
+
+            handler = None
+            if fget is not None:
+                trait = fget
+
+            if trait is not None:
+                trait = trait_cast(trait)
+                if trait is not None:
+                    fvalidate = handler = trait.handler
+                    if fvalidate is not None:
+                        fvalidate = handler.validate
+
+            if (fvalidate is not None) or (trait is not None):
+                if "editor" not in metadata:
+                    if (trait is not None) and (trait.editor is not None):
+                        metadata["editor"] = trait.editor
+
+                return ForwardProperty(metadata, fvalidate, handler)
+
+    if fget is None:
+        metadata["transient"] = True
+        if fset is None:
+            fget = _undefined_get
+            fset = _undefined_set
+        else:
+            fget = _write_only
+
+    elif fset is None:
+        fset = _read_only
+        metadata["transient"] = True
+
+    if trait is not None:
+        trait = trait_cast(trait)
+        handler = trait.handler
+        if (fvalidate is None) and (handler is not None):
+            fvalidate = handler.validate
+
+        if ("editor" not in metadata) and (trait.editor is not None):
+            metadata["editor"] = trait.editor
+
+    metadata.setdefault("depends_on", getattr(fget, "depends_on", None))
+    if (metadata.get("depends_on") is not None) and getattr(
+        fget, "cached_property", False
+    ):
+        metadata.setdefault("cached", True)
+
+    trait = CTrait(TraitKind.property)
+    trait.__dict__ = metadata.copy()
+    trait.property_fields = (fget, fset, fvalidate)
+    trait.handler = handler
+
+    return trait
+
+
+Property = TraitFactory(Property)
+
+
+class ForwardProperty(object):
+    """ Used to implement Property traits where accessor functions are defined
+    implicitly on the class.
+    """
+
+    def __init__(self, metadata, validate=None, handler=None):
+        self.metadata = metadata.copy()
+        self.validate = validate
+        self.handler = handler
+
+
+# Predefined, reusable trait instances
+
+# Generic trait with 'object' behavior:
+generic_trait = CTrait(TraitKind.generic)
+
+
+# User interface related color and font traits
+
+@deprecated("'Color' in 'traits' package has been deprecated. "
+            "Use 'Color' from 'traitsui' package instead.")
+def Color(*args, **metadata):
+    """ Returns a trait whose value must be a GUI toolkit-specific color.
+
+    .. deprecated:: 6.1.0
+        ``Color`` trait in this package will be removed in the future. It is
+        replaced by ``Color`` trait in TraitsUI package.
+    """
+    from traitsui.toolkit_traits import ColorTrait
+
+    return ColorTrait(*args, **metadata)
+
+
+Color = TraitFactory(Color)
+
+
+@deprecated("'RGBColor' in 'traits' package has been deprecated. "
+            "Use 'RGBColor' from 'traitsui' package instead.")
+def RGBColor(*args, **metadata):
+    """ Returns a trait whose value must be a GUI toolkit-specific RGB-based
+    color.
+
+    .. deprecated:: 6.1.0
+        ``RGBColor`` trait in this package will be removed in the future. It is
+        replaced by ``RGBColor`` trait in TraitsUI package.
+    """
+    from traitsui.toolkit_traits import RGBColorTrait
+
+    return RGBColorTrait(*args, **metadata)
+
+
+RGBColor = TraitFactory(RGBColor)
+
+
+@deprecated("'Font' in 'traits' package has been deprecated. "
+            "Use 'Font' from 'traitsui' package instead.")
+def Font(*args, **metadata):
+    """ Returns a trait whose value must be a GUI toolkit-specific font.
+
+    .. deprecated:: 6.1.0
+        ``Font`` trait in this package will be removed in the future. It is
+        replaced by ``Font`` trait in TraitsUI package.
+    """
+    from traitsui.toolkit_traits import FontTrait
+
+    return FontTrait(*args, **metadata)
+
+
+Font = TraitFactory(Font)
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/traitsui/editors/code_editor.html b/_modules/traitsui/editors/code_editor.html new file mode 100644 index 000000000..ae28fc000 --- /dev/null +++ b/_modules/traitsui/editors/code_editor.html @@ -0,0 +1,243 @@ + + + + + + + traitsui.editors.code_editor — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for traitsui.editors.code_editor

+# ------------------------------------------------------------------------------
+#
+#  Copyright (c) 2008, Enthought, Inc.
+#  All rights reserved.
+#
+#  This software is provided without warranty under the terms of the BSD
+#  license included in LICENSE.txt and may be redistributed only
+#  under the conditions described in the aforementioned license.  The license
+#  is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+#  Thanks for using Enthought open source!
+#
+#  Author: David C. Morrill
+#  Date:   01/27/2006
+#
+# ------------------------------------------------------------------------------
+
+""" Defines the code editor factory for all traits toolkit backends,
+useful for tools such as debuggers.
+"""
+
+
+
+from traits.api import Instance, Str, Enum, Bool
+
+from ..editor_factory import EditorFactory
+from ..toolkit_traits import Color
+
+# -------------------------------------------------------------------------
+#  'ToolkitEditorFactory' class:
+# -------------------------------------------------------------------------
+
+
+class ToolkitEditorFactory(EditorFactory):
+    """ Editor factory for code editors.
+    """
+
+    # -------------------------------------------------------------------------
+    #  Trait definitions:
+    # -------------------------------------------------------------------------
+
+    #: Object trait containing list of line numbers to mark (optional)
+    mark_lines = Str()
+
+    #: Background color for marking lines
+    mark_color = Color(0xECE9D8)
+
+    #: Object trait containing the currently selected line (optional)
+    selected_line = Str()
+
+    #: Object trait containing the currently selected text (optional)
+    selected_text = Str()
+
+    #: Object trait containing the currently selected text start position
+    #: (optional)
+    selected_start_pos = Str()
+
+    #: Object trait containing the currently selected text end position
+    #: (optional)
+    selected_end_pos = Str()
+
+    #: Background color for selected lines
+    selected_color = Color(0xA4FFFF)
+
+    #: Where should the search toolbar be placed?
+    search = Enum("top", "bottom", "none")
+
+    #: Background color for lines that match the current search
+    search_color = Color(0xFFFF94)
+
+    #: Current line
+    line = Str()
+
+    #: Current column
+    column = Str()
+
+    #: Should code folding be enabled?
+    foldable = Bool(True)
+
+    #: Should line numbers be displayed in the margin?
+    show_line_numbers = Bool(True)
+
+    #: Is user input set on every change?
+    auto_set = Bool(True)
+
+    #: Should the editor auto-scroll when a new **selected_line** value is set?
+    auto_scroll = Bool(True)
+
+    #: Optional key bindings associated with the editor
+    key_bindings = Instance("traitsui.key_bindings.KeyBindings")
+
+    #: Calltip clicked event
+    calltip_clicked = Str()
+
+    #: The lexer to use. Default is 'python'; 'null' indicates no lexing.
+    lexer = Str("python")
+
+    #: Object trait containing the list of line numbers to dim (optional)
+    dim_lines = Str()
+
+    #: Object trait to dim lines to. Can be of form #rrggbb or a color spec. If
+    #: not specified, dark grey is used.
+    dim_color = Str()
+
+    #: Object trait containing the list of line numbers to put squiggles under
+    #: (optional)
+    squiggle_lines = Str()
+
+    #: Object trait for the color of squiggles. If not specified, red is used.
+    squiggle_color = Str()
+
+
+# Define the Code Editor class.
+CodeEditor = ToolkitEditorFactory
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/traitsui/editors/tabular_editor.html b/_modules/traitsui/editors/tabular_editor.html new file mode 100644 index 000000000..a6db5e448 --- /dev/null +++ b/_modules/traitsui/editors/tabular_editor.html @@ -0,0 +1,290 @@ + + + + + + + traitsui.editors.tabular_editor — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for traitsui.editors.tabular_editor

+# -------------------------------------------------------------------------
+#
+#  Copyright (c) 2007, Enthought, Inc.
+#  All rights reserved.
+#
+#  This software is provided without warranty under the terms of the BSD
+#  license included in LICENSE.txt and may be redistributed only
+#  under the conditions described in the aforementioned license.  The license
+#  is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+#  Thanks for using Enthought open source!
+#
+#  Author: David C. Morrill
+#  Date:   05/20/2007
+#
+# -------------------------------------------------------------------------
+
+""" A traits UI editor for editing tabular data (arrays, list of tuples, lists
+    of objects, etc).
+"""
+
+
+
+from pyface.ui_traits import Image
+from traits.api import Str, Bool, Property, List, Enum, Instance
+
+from ..basic_editor_factory import BasicEditorFactory
+
+from ..toolkit import toolkit_object
+
+
+class TabularEditor(BasicEditorFactory):
+    """ Editor factory for tabular editors.
+    """
+
+    # -- Trait Definitions ----------------------------------------------------
+
+    #: The editor class to be created:
+    klass = Property()
+
+    #: Should column headers (i.e. titles) be displayed?
+    show_titles = Bool(True)
+
+    #: Should row headers be displayed (Qt4 only)?
+    show_row_titles = Bool(False)
+
+    #: The optional extended name of the trait used to indicate that a complete
+    #: table update is needed:
+    update = Str()
+
+    #: The optional extended name of the trait used to indicate that the table
+    #: just needs to be repainted.
+    refresh = Str()
+
+    #: Should the table update automatically when the table item's contents
+    #: change? Note that in order for this feature to work correctly, the editor
+    #: trait should be a list of objects derived from HasTraits. Also,
+    #: performance can be affected when very long lists are used, since enabling
+    #: this feature adds and removed Traits listeners to each item in the list.
+    auto_update = Bool(False)
+
+    #: The optional extended name of the trait to synchronize the selection
+    #: values with:
+    selected = Str()
+
+    #: The optional extended name of the trait to synchronize the selection rows
+    #: with:
+    selected_row = Str()
+
+    #: Whether or not to allow selection.
+    selectable = Bool(True)
+
+    #: The optional extended name of the trait to synchronize the activated value
+    #: with:
+    activated = Str()
+
+    #: The optional extended name of the trait to synchronize the activated
+    #: value's row with:
+    activated_row = Str()
+
+    #: The optional extended name of the trait to synchronize left click data
+    #: with. The data is a TabularEditorEvent:
+    clicked = Str()
+
+    #: The optional extended name of the trait to synchronize left double click
+    #: data with. The data is a TabularEditorEvent:
+    dclicked = Str()
+
+    #: The optional extended name of the trait to synchronize right click data
+    #: with. The data is a TabularEditorEvent:
+    right_clicked = Str()
+
+    #: The optional extended name of the trait to synchronize right double
+    #: clicked data with. The data is a TabularEditorEvent:
+    right_dclicked = Str()
+
+    #: The optional extended name of the trait to synchronize column
+    #: clicked data with. The data is a TabularEditorEvent:
+    column_clicked = Str()
+
+    #: The optional extended name of the trait to synchronize column
+    #: right clicked data with. The data is a TabularEditorEvent:
+    column_right_clicked = Str()
+
+    #: The optional extended name of the Event trait that should be used to
+    #: trigger a scroll-to command. The data is an integer giving the row.
+    scroll_to_row = Str()
+
+    #: The optional extended name of the Event trait that should be used to
+    #: trigger a scroll-to command. The data is an integer giving the column.
+    scroll_to_column = Str()
+
+    #: Controls behavior of scroll to row
+    scroll_to_row_hint = Enum("center", "top", "bottom", "visible")
+
+    #: Can the user edit the values?
+    editable = Bool(True)
+
+    #: Can the user edit the labels (i.e. the first column)
+    editable_labels = Bool(False)
+
+    #: Are multiple selected items allowed?
+    multi_select = Bool(False)
+
+    #: Should horizontal lines be drawn between items?
+    horizontal_lines = Bool(True)
+
+    #: Should vertical lines be drawn between items?
+    vertical_lines = Bool(True)
+
+    #: Should the columns automatically resize? Don't allow this when the amount
+    #: of data is large.
+    auto_resize = Bool(False)
+
+    #: Should the rows automatically resize (Qt4 only)? Don't allow
+    #: this when the amount of data is large.
+    auto_resize_rows = Bool(False)
+
+    #: Whether to stretch the last column to fit the available space.
+    stretch_last_section = Bool(True)
+
+    #: The adapter from trait values to editor values:
+    adapter = Instance("traitsui.tabular_adapter.TabularAdapter", ())
+
+    #: What type of operations are allowed on the list:
+    operations = List(
+        Enum("delete", "insert", "append", "edit", "move"),
+        ["delete", "insert", "append", "edit", "move"],
+    )
+
+    #: Are 'drag_move' operations allowed (i.e. True), or should they always be
+    #: treated as 'drag_copy' operations (i.e. False):
+    drag_move = Bool(True)
+
+    #: The set of images that can be used:
+    images = List(Image)
+
+    def _get_klass(self):
+        """ Returns the toolkit-specific editor class to be instantiated.
+        """
+        return toolkit_object("tabular_editor:TabularEditor")
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_modules/traitsui/view.html b/_modules/traitsui/view.html new file mode 100644 index 000000000..9ef548fe1 --- /dev/null +++ b/_modules/traitsui/view.html @@ -0,0 +1,610 @@ + + + + + + + traitsui.view — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Source code for traitsui.view

+# ------------------------------------------------------------------------------
+#
+#  Copyright (c) 2005, Enthought, Inc.
+#  All rights reserved.
+#
+#  This software is provided without warranty under the terms of the BSD
+#  license included in LICENSE.txt and may be redistributed only
+#  under the conditions described in the aforementioned license.  The license
+#  is also available online at http://www.enthought.com/licenses/BSD.txt
+#
+#  Thanks for using Enthought open source!
+#
+#  Author: David C. Morrill
+#  Date:   10/07/2004
+#
+# ------------------------------------------------------------------------------
+
+""" Defines the View class used to represent the structural content of a
+    Traits-based user interface.
+"""
+
+
+
+from pyface.ui_traits import Image
+from traits.api import (
+    Any,
+    Bool,
+    Callable,
+    Enum,
+    Event,
+    Float,
+    Instance,
+    List,
+    Str,
+    Trait,
+)
+
+from .view_element import ViewElement, ViewSubElement
+
+from .ui import UI
+
+from .ui_traits import (
+    AButton,
+    AnObject,
+    Buttons,
+    DockStyle,
+    EditorStyle,
+    ExportType,
+    HelpId,
+    Image,
+    SequenceTypes,
+    ViewStatus,
+)
+
+from .handler import Handler, default_handler
+
+from .group import Group
+
+from .item import Item
+
+from .include import Include
+
+from .helper import PrefixList
+
+# -------------------------------------------------------------------------
+#  Trait definitions:
+# -------------------------------------------------------------------------
+
+# Name of the view trait:
+AnId = Str(desc="the name of the view")
+
+# Contents of the view trait (i.e., a single Group object):
+Content = Instance(Group, desc="the content of the view")
+
+# An optional model/view factory for converting the model into a viewable
+# 'model_view' object
+AModelView = Callable(
+    desc="the factory function for converting a model "
+    "into a model/view object"
+)
+
+# Reference to a Handler object trait:
+AHandler = Any(desc="the handler for the view")
+
+# Dialog window title trait:
+ATitle = Str(desc="the window title for the view")
+
+# User interface 'kind' trait. The values have the following meanings:
+#
+# * 'panel': An embeddable panel. This type of window is intended to be used as
+#   part of a larger interface.
+# * 'subpanel': An embeddable panel that does not display command buttons,
+#   even if the View specifies them.
+# * 'modal': A modal dialog box that operates on a clone of the object until
+#   the user commits the change.
+# * 'nonmodal':  A nonmodal dialog box that operates on a clone of the object
+#   until the user commits the change
+# * 'live': A nonmodal dialog box that immediately updates the object.
+# * 'livemodal': A modal dialog box that immediately updates the object.
+# * 'popup': A temporary, frameless popup dialog that immediately updates the
+#   object and is active only while the mouse pointer is in the dialog.
+# * 'info': A temporary, frameless popup dialog that immediately updates the
+#   object and is active only while the dialog is still over the invoking
+#   control.
+# * 'wizard': A wizard modal dialog box. A wizard contains a sequence of
+#   pages, which can be accessed by clicking **Next** and **Back** buttons.
+#   Changes to attribute values are applied only when the user clicks the
+#   **Finish** button on the last page.
+AKind = PrefixList(
+    (
+        "panel",
+        "subpanel",
+        "modal",
+        "nonmodal",
+        "livemodal",
+        "live",
+        "popup",
+        "popover",
+        "info",
+        "wizard",
+    ),
+    default_value='live',
+    desc="the kind of view window to create",
+    cols=4,
+)
+
+# Apply changes handler:
+OnApply = Callable(
+    desc="the routine to call when modal changes are applied " "or reverted"
+)
+
+# Is the dialog window resizable?
+IsResizable = Bool(False, desc="whether dialog can be resized or not")
+
+# Is the view scrollable?
+IsScrollable = Bool(False, desc="whether view should be scrollable or not")
+
+# The valid categories of imported elements that can be dragged into the view:
+ImportTypes = List(
+    Str, desc="the categories of elements that can be " "dragged into the view"
+)
+
+# The view position and size traits:
+Width = Float(-1e6, desc="the width of the view window")
+Height = Float(-1e6, desc="the height of the view window")
+XCoordinate = Float(-1e6, desc="the x coordinate of the view window")
+YCoordinate = Float(-1e6, desc="the y coordinate of the view window")
+
+# The result that should be returned if the user clicks the window or dialog
+# close button or icon
+CloseResult = Enum(
+    None,
+    True,
+    False,
+    desc="the result to return when the user clicks the "
+    "window or dialog close button or icon",
+)
+
+# The KeyBindings trait:
+AKeyBindings = Instance(
+    "traitsui.key_bindings.KeyBindings",
+    desc="the global key bindings for the view",
+)
+
+
+class View(ViewElement):
+    """ A Traits-based user interface for one or more objects.
+
+        The attributes of the View object determine the contents and layout of
+        an attribute-editing window. A View object contains a set of Group,
+        Item, and Include objects. A View object can be an attribute of an
+        object derived from HasTraits, or it can be a standalone object.
+    """
+
+    # -------------------------------------------------------------------------
+    #  Trait definitions:
+    # -------------------------------------------------------------------------
+
+    #: A unique identifier for the view:
+    id = AnId
+
+    #: The top-level Group object for the view:
+    content = Content
+
+    #: The menu bar for the view. Usually requires a custom **handler**:
+    menubar = Any  # Instance( pyface.action.MenuBarManager )
+
+    #: The toolbar for the view. Usually requires a custom **handler**:
+    toolbar = Any  # Instance( pyface.action.ToolBarManager )
+
+    #: Status bar items to add to the view's status bar. The value can be:
+    #:
+    #:   - **None**: No status bar for the view (the default).
+    #:   - string: Same as [ StatusItem( name = string ) ].
+    #:   - StatusItem: Same as [ StatusItem ].
+    #:   - [ [StatusItem|string], ... ]: Create a status bar with one field for
+    #:     each StatusItem in the list (or tuple). The status bar fields are
+    #:     defined from left to right in the order specified. A string value is
+    #:     converted to: StatusItem( name = string ):
+    statusbar = ViewStatus
+
+    #: List of button actions to add to the view. The **traitsui.menu**
+    #: module defines standard buttons, such as **OKButton**, and standard sets
+    #: of buttons, such as **ModalButtons**, which can be used to define a value
+    #: for this attribute. This value can also be a list of button name strings,
+    #: such as ``['OK', 'Cancel', 'Help']``. If set to the empty list, the
+    #: view contains a default set of buttons (equivalent to **LiveButtons**:
+    #: Undo/Redo, Revert, OK, Cancel, Help). To suppress buttons in the view,
+    #: use the **NoButtons** variable, defined in **traitsui.menu**.
+    buttons = Buttons
+
+    #: The default button to activate when Enter is pressed. If not specified,
+    #: pressing Enter will not activate any button.
+    default_button = AButton
+
+    #: The set of global key bindings for the view. Each time a key is pressed
+    #: while the view has keyboard focus, the key is checked to see if it is one
+    #: of the keys recognized by the KeyBindings object. If it is, the matching
+    #: KeyBinding's method name is checked to see if it is defined on any of the
+    #: object's in the view's context. If it is, the method is invoked. If the
+    #: result of the method is **False**, then the search continues with the
+    #: next object in the context. If any invoked method returns a non-False
+    #: value, processing stops and the key is marked as having been handled. If
+    #: all invoked methods return **False**, or no matching KeyBinding object is
+    #: found, the key is processed normally. If the view has a non-empty *id*
+    #: trait, the contents of the **KeyBindings** object will be saved as part
+    #: of the view's persistent data:
+    key_bindings = AKeyBindings
+
+    #: The Handler object that provides GUI logic for handling events in the
+    #: window. Set this attribute only if you are using a custom Handler. If
+    #: not set, the default Traits UI Handler is used.
+    handler = AHandler
+
+    #: The factory function for converting a model into a model/view object:
+    model_view = AModelView
+
+    #: Title for the view, displayed in the title bar when the view appears as a
+    #: secondary window (i.e., dialog or wizard). If not specified, "Edit
+    #: properties" is used as the title.
+    title = ATitle
+
+    #: The name of the icon to display in the dialog window title bar:
+    icon = Image
+
+    #: The kind of user interface to create:
+    kind = AKind
+
+    #: The default object being edited:
+    object = AnObject
+
+    #: The default editor style of elements in the view:
+    style = EditorStyle
+
+    #: The default docking style to use for sub-groups of the view. The following
+    #: values are possible:
+    #:
+    #: * 'fixed': No rearrangement of sub-groups is allowed.
+    #: * 'horizontal': Moveable elements have a visual "handle" to the left by
+    #:   which the element can be dragged.
+    #: * 'vertical': Moveable elements have a visual "handle" above them by
+    #:   which the element can be dragged.
+    #: * 'tabbed': Moveable elements appear as tabbed pages, which can be
+    #:   arranged within the window or "stacked" so that only one appears at
+    #:   at a time.
+    dock = DockStyle
+
+    #: The image to display on notebook tabs:
+    image = Image
+
+    #: Called when modal changes are applied or reverted:
+    on_apply = OnApply
+
+    #: Can the user resize the window?
+    resizable = IsResizable
+
+    #: Can the user scroll the view? If set to True, window-level scroll bars
+    #: appear whenever the window is too small to show all of its contents at
+    #: one time. If set to False, the window does not scroll, but individual
+    #: widgets might still contain scroll bars.
+    scrollable = IsScrollable
+
+    #: The category of exported elements:
+    export = ExportType
+
+    #: The valid categories of imported elements:
+    imports = ImportTypes
+
+    #: External help context identifier, which can be used by a custom help
+    #: handler. This attribute is ignored by the default help handler.
+    help_id = HelpId
+
+    #: Requested x-coordinate (horizontal position) for the view window. This
+    #: attribute can be specified in the following ways:
+    #:
+    #: * A positive integer: indicates the number of pixels from the left edge
+    #:   of the screen to the left edge of the window.
+    #: * A negative integer: indicates the number of pixels from the right edge
+    #:   of the screen to the right edge of the window.
+    #: * A floating point value between 0 and 1: indicates the fraction of the
+    #:   total screen width between the left edge of the screen and the left edge
+    #:   of the window.
+    #: * A floating point value between -1 and 0: indicates the fraction of the
+    #:   total screen width between the right edge of the screen and the right
+    #:   edge of the window.
+    x = XCoordinate
+
+    #: Requested y-coordinate (vertical position) for the view window. This
+    #: attribute behaves exactly like the **x** attribute, except that its value
+    #: indicates the position of the top or bottom of the view window relative
+    #: to the top or bottom of the screen.
+    y = YCoordinate
+
+    #: Requested width for the view window, as an (integer) number of pixels, or
+    #: as a (floating point) fraction of the screen width.
+    width = Width
+
+    #: Requested height for the view window, as an (integer) number of pixels, or
+    #: as a (floating point) fraction of the screen height.
+    height = Height
+
+    #: Class of dropped objects that can be added:
+    drop_class = Any()
+
+    #: Event when the view has been updated:
+    updated = Event()
+
+    #: What result should be returned if the user clicks the window or dialog
+    #: close button or icon?
+    close_result = CloseResult
+
+    #: Note: Group objects delegate their 'object' and 'style' traits to the
+    #: View
+
+    # -- Deprecated Traits (DO NOT USE) ---------------------------------------
+
+    ok = Bool(False)
+    cancel = Bool(False)
+    undo = Bool(False)
+    redo = Bool(False)
+    apply = Bool(False)
+    revert = Bool(False)
+    help = Bool(False)
+
+    def __init__(self, *values, **traits):
+        """ Initializes the object.
+        """
+        ViewElement.__init__(self, **traits)
+        self.set_content(*values)
+
+    def set_content(self, *values):
+        """ Sets the content of a view.
+        """
+        content = []
+        accum = []
+        for value in values:
+            if isinstance(value, ViewSubElement):
+                content.append(value)
+            elif type(value) in SequenceTypes:
+                content.append(Group(*value))
+            elif (
+                isinstance(value, str)
+                and (value[:1] == "<")
+                and (value[-1:] == ">")
+            ):
+                # Convert string to an Include value:
+                content.append(Include(value[1:-1].strip()))
+            else:
+                content.append(Item(value))
+
+        # If there are any 'Item' objects in the content, wrap the content in a
+        # Group:
+        for item in content:
+            if isinstance(item, Item):
+                content = [Group(*content)]
+                break
+
+        # Wrap all of the content up into a Group and save it as our content:
+        self.content = Group(container=self, *content)
+
+    def ui(
+        self,
+        context,
+        parent=None,
+        kind=None,
+        view_elements=None,
+        handler=None,
+        id="",
+        scrollable=None,
+        args=None,
+    ):
+        """ Creates a **UI** object, which generates the actual GUI window or
+        panel from a set of view elements.
+
+        Parameters
+        ----------
+        context : object or dictionary
+            A single object or a dictionary of string/object pairs, whose trait
+            attributes are to be edited. If not specified, the current object is
+            used.
+        parent : window component
+            The window parent of the View object's window
+        kind : string
+            The kind of window to create. See the **AKind** trait for details.
+            If *kind* is unspecified or None, the **kind** attribute of the
+            View object is used.
+        view_elements : ViewElements object
+            The set of Group, Item, and Include objects contained in the view.
+            Do not use this parameter when calling this method directly.
+        handler : Handler object
+            A handler object used for event handling in the dialog box. If
+            None, the default handler for Traits UI is used.
+        id : string
+            A unique ID for persisting preferences about this user interface,
+            such as size and position. If not specified, no user preferences
+            are saved.
+        scrollable : Boolean
+            Indicates whether the dialog box should be scrollable. When set to
+            True, scroll bars appear on the dialog box if it is not large enough
+            to display all of the items in the view at one time.
+
+        """
+        handler = handler or self.handler or default_handler()
+        if not isinstance(handler, Handler):
+            handler = handler()
+
+        if args is not None:
+            handler.trait_set(**args)
+
+        if not isinstance(context, dict):
+            context = context.trait_context()
+
+        context.setdefault("handler", handler)
+        handler = context["handler"]
+
+        if self.model_view is not None:
+            context["object"] = self.model_view(context["object"])
+
+        self_id = self.id
+        if self_id != "":
+            if id != "":
+                id = "%s:%s" % (self_id, id)
+            else:
+                id = self_id
+
+        if scrollable is None:
+            scrollable = self.scrollable
+
+        ui = UI(
+            view=self,
+            context=context,
+            handler=handler,
+            view_elements=view_elements,
+            title=self.title,
+            id=id,
+            scrollable=scrollable,
+        )
+
+        if kind is None:
+            kind = self.kind
+
+        ui.ui(parent, kind)
+
+        return ui
+
+    def replace_include(self, view_elements):
+        """ Replaces any items that have an ID with an Include object with
+            the same ID, and puts the object with the ID into the specified
+            ViewElements object.
+        """
+        if self.content is not None:
+            self.content.replace_include(view_elements)
+
+    def __repr__(self):
+        """ Returns a "pretty print" version of the View.
+        """
+        if self.content is None:
+            return "()"
+        return "( %s )" % ", ".join(
+            [item.__repr__() for item in self.content.content]
+        )
+
+ +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/_sources/api.rst.txt b/_sources/api.rst.txt new file mode 100644 index 000000000..224ac6510 --- /dev/null +++ b/_sources/api.rst.txt @@ -0,0 +1,11 @@ +.. _api-documentation: + +API documentation +================= + +This section contains auto-generated API documentation for AppTools. + +.. toctree:: + :maxdepth: 2 + + api/modules diff --git a/_sources/api/apptools.io.h5.rst.txt b/_sources/api/apptools.io.h5.rst.txt new file mode 100644 index 000000000..dea85f58a --- /dev/null +++ b/_sources/api/apptools.io.h5.rst.txt @@ -0,0 +1,46 @@ +apptools.io.h5 package +====================== + +Submodules +---------- + +apptools.io.h5.dict\_node module +-------------------------------- + +.. automodule:: apptools.io.h5.dict_node + :members: + :undoc-members: + :show-inheritance: + +apptools.io.h5.file module +-------------------------- + +.. automodule:: apptools.io.h5.file + :members: + :undoc-members: + :show-inheritance: + +apptools.io.h5.table\_node module +--------------------------------- + +.. automodule:: apptools.io.h5.table_node + :members: + :undoc-members: + :show-inheritance: + +apptools.io.h5.utils module +--------------------------- + +.. automodule:: apptools.io.h5.utils + :members: + :undoc-members: + :show-inheritance: + + +Module contents +--------------- + +.. automodule:: apptools.io.h5 + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/api/apptools.io.rst.txt b/_sources/api/apptools.io.rst.txt new file mode 100644 index 000000000..0907ac23c --- /dev/null +++ b/_sources/api/apptools.io.rst.txt @@ -0,0 +1,37 @@ +apptools.io package +=================== + +Subpackages +----------- + +.. toctree:: + + apptools.io.h5 + +Submodules +---------- + +apptools.io.api module +---------------------- + +.. automodule:: apptools.io.api + :members: + :undoc-members: + :show-inheritance: + +apptools.io.file module +----------------------- + +.. automodule:: apptools.io.file + :members: + :undoc-members: + :show-inheritance: + + +Module contents +--------------- + +.. automodule:: apptools.io + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/api/apptools.logger.agent.rst.txt b/_sources/api/apptools.logger.agent.rst.txt new file mode 100644 index 000000000..c25c0dcfb --- /dev/null +++ b/_sources/api/apptools.logger.agent.rst.txt @@ -0,0 +1,38 @@ +apptools.logger.agent package +============================= + +Submodules +---------- + +apptools.logger.agent.attachments module +---------------------------------------- + +.. automodule:: apptools.logger.agent.attachments + :members: + :undoc-members: + :show-inheritance: + +apptools.logger.agent.quality\_agent\_mailer module +--------------------------------------------------- + +.. automodule:: apptools.logger.agent.quality_agent_mailer + :members: + :undoc-members: + :show-inheritance: + +apptools.logger.agent.quality\_agent\_view module +------------------------------------------------- + +.. automodule:: apptools.logger.agent.quality_agent_view + :members: + :undoc-members: + :show-inheritance: + + +Module contents +--------------- + +.. automodule:: apptools.logger.agent + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/api/apptools.logger.plugin.rst.txt b/_sources/api/apptools.logger.plugin.rst.txt new file mode 100644 index 000000000..b607fdd4e --- /dev/null +++ b/_sources/api/apptools.logger.plugin.rst.txt @@ -0,0 +1,45 @@ +apptools.logger.plugin package +============================== + +Subpackages +----------- + +.. toctree:: + + apptools.logger.plugin.view + +Submodules +---------- + +apptools.logger.plugin.logger\_plugin module +-------------------------------------------- + +.. automodule:: apptools.logger.plugin.logger_plugin + :members: + :undoc-members: + :show-inheritance: + +apptools.logger.plugin.logger\_preferences module +------------------------------------------------- + +.. automodule:: apptools.logger.plugin.logger_preferences + :members: + :undoc-members: + :show-inheritance: + +apptools.logger.plugin.logger\_service module +--------------------------------------------- + +.. automodule:: apptools.logger.plugin.logger_service + :members: + :undoc-members: + :show-inheritance: + + +Module contents +--------------- + +.. automodule:: apptools.logger.plugin + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/api/apptools.logger.plugin.view.rst.txt b/_sources/api/apptools.logger.plugin.view.rst.txt new file mode 100644 index 000000000..1d8dc0b3d --- /dev/null +++ b/_sources/api/apptools.logger.plugin.view.rst.txt @@ -0,0 +1,30 @@ +apptools.logger.plugin.view package +=================================== + +Submodules +---------- + +apptools.logger.plugin.view.logger\_preferences\_page module +------------------------------------------------------------ + +.. automodule:: apptools.logger.plugin.view.logger_preferences_page + :members: + :undoc-members: + :show-inheritance: + +apptools.logger.plugin.view.logger\_view module +----------------------------------------------- + +.. automodule:: apptools.logger.plugin.view.logger_view + :members: + :undoc-members: + :show-inheritance: + + +Module contents +--------------- + +.. automodule:: apptools.logger.plugin.view + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/api/apptools.logger.rst.txt b/_sources/api/apptools.logger.rst.txt new file mode 100644 index 000000000..c49196f26 --- /dev/null +++ b/_sources/api/apptools.logger.rst.txt @@ -0,0 +1,70 @@ +apptools.logger package +======================= + +Subpackages +----------- + +.. toctree:: + + apptools.logger.agent + apptools.logger.plugin + +Submodules +---------- + +apptools.logger.api module +-------------------------- + +.. automodule:: apptools.logger.api + :members: + :undoc-members: + :show-inheritance: + +apptools.logger.custom\_excepthook module +----------------------------------------- + +.. automodule:: apptools.logger.custom_excepthook + :members: + :undoc-members: + :show-inheritance: + +apptools.logger.log\_point module +--------------------------------- + +.. automodule:: apptools.logger.log_point + :members: + :undoc-members: + :show-inheritance: + +apptools.logger.log\_queue\_handler module +------------------------------------------ + +.. automodule:: apptools.logger.log_queue_handler + :members: + :undoc-members: + :show-inheritance: + +apptools.logger.logger module +----------------------------- + +.. automodule:: apptools.logger.logger + :members: + :undoc-members: + :show-inheritance: + +apptools.logger.ring\_buffer module +----------------------------------- + +.. automodule:: apptools.logger.ring_buffer + :members: + :undoc-members: + :show-inheritance: + + +Module contents +--------------- + +.. automodule:: apptools.logger + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/api/apptools.naming.rst.txt b/_sources/api/apptools.naming.rst.txt new file mode 100644 index 000000000..b4645f693 --- /dev/null +++ b/_sources/api/apptools.naming.rst.txt @@ -0,0 +1,221 @@ +apptools.naming package +======================= + +Subpackages +----------- + +.. toctree:: + + apptools.naming.trait_defs + +Submodules +---------- + +apptools.naming.address module +------------------------------ + +.. automodule:: apptools.naming.address + :members: + :undoc-members: + :show-inheritance: + +apptools.naming.api module +-------------------------- + +.. automodule:: apptools.naming.api + :members: + :undoc-members: + :show-inheritance: + +apptools.naming.binding module +------------------------------ + +.. automodule:: apptools.naming.binding + :members: + :undoc-members: + :show-inheritance: + +apptools.naming.context module +------------------------------ + +.. automodule:: apptools.naming.context + :members: + :undoc-members: + :show-inheritance: + +apptools.naming.dir\_context module +----------------------------------- + +.. automodule:: apptools.naming.dir_context + :members: + :undoc-members: + :show-inheritance: + +apptools.naming.dynamic\_context module +--------------------------------------- + +.. automodule:: apptools.naming.dynamic_context + :members: + :undoc-members: + :show-inheritance: + +apptools.naming.exception module +-------------------------------- + +.. automodule:: apptools.naming.exception + :members: + :undoc-members: + :show-inheritance: + +apptools.naming.initial\_context module +--------------------------------------- + +.. automodule:: apptools.naming.initial_context + :members: + :undoc-members: + :show-inheritance: + +apptools.naming.initial\_context\_factory module +------------------------------------------------ + +.. automodule:: apptools.naming.initial_context_factory + :members: + :undoc-members: + :show-inheritance: + +apptools.naming.naming\_event module +------------------------------------ + +.. automodule:: apptools.naming.naming_event + :members: + :undoc-members: + :show-inheritance: + +apptools.naming.naming\_manager module +-------------------------------------- + +.. automodule:: apptools.naming.naming_manager + :members: + :undoc-members: + :show-inheritance: + +apptools.naming.object\_factory module +-------------------------------------- + +.. automodule:: apptools.naming.object_factory + :members: + :undoc-members: + :show-inheritance: + +apptools.naming.object\_serializer module +----------------------------------------- + +.. automodule:: apptools.naming.object_serializer + :members: + :undoc-members: + :show-inheritance: + +apptools.naming.py\_context module +---------------------------------- + +.. automodule:: apptools.naming.py_context + :members: + :undoc-members: + :show-inheritance: + +apptools.naming.py\_object\_factory module +------------------------------------------ + +.. automodule:: apptools.naming.py_object_factory + :members: + :undoc-members: + :show-inheritance: + +apptools.naming.pyfs\_context module +------------------------------------ + +.. automodule:: apptools.naming.pyfs_context + :members: + :undoc-members: + :show-inheritance: + +apptools.naming.pyfs\_context\_factory module +--------------------------------------------- + +.. automodule:: apptools.naming.pyfs_context_factory + :members: + :undoc-members: + :show-inheritance: + +apptools.naming.pyfs\_initial\_context\_factory module +------------------------------------------------------ + +.. automodule:: apptools.naming.pyfs_initial_context_factory + :members: + :undoc-members: + :show-inheritance: + +apptools.naming.pyfs\_object\_factory module +-------------------------------------------- + +.. automodule:: apptools.naming.pyfs_object_factory + :members: + :undoc-members: + :show-inheritance: + +apptools.naming.pyfs\_state\_factory module +------------------------------------------- + +.. automodule:: apptools.naming.pyfs_state_factory + :members: + :undoc-members: + :show-inheritance: + +apptools.naming.reference module +-------------------------------- + +.. automodule:: apptools.naming.reference + :members: + :undoc-members: + :show-inheritance: + +apptools.naming.referenceable module +------------------------------------ + +.. automodule:: apptools.naming.referenceable + :members: + :undoc-members: + :show-inheritance: + +apptools.naming.referenceable\_state\_factory module +---------------------------------------------------- + +.. automodule:: apptools.naming.referenceable_state_factory + :members: + :undoc-members: + :show-inheritance: + +apptools.naming.state\_factory module +------------------------------------- + +.. automodule:: apptools.naming.state_factory + :members: + :undoc-members: + :show-inheritance: + +apptools.naming.unique\_name module +----------------------------------- + +.. automodule:: apptools.naming.unique_name + :members: + :undoc-members: + :show-inheritance: + + +Module contents +--------------- + +.. automodule:: apptools.naming + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/api/apptools.naming.trait_defs.rst.txt b/_sources/api/apptools.naming.trait_defs.rst.txt new file mode 100644 index 000000000..e2281ea3d --- /dev/null +++ b/_sources/api/apptools.naming.trait_defs.rst.txt @@ -0,0 +1,30 @@ +apptools.naming.trait\_defs package +=================================== + +Submodules +---------- + +apptools.naming.trait\_defs.api module +-------------------------------------- + +.. automodule:: apptools.naming.trait_defs.api + :members: + :undoc-members: + :show-inheritance: + +apptools.naming.trait\_defs.naming\_traits module +------------------------------------------------- + +.. automodule:: apptools.naming.trait_defs.naming_traits + :members: + :undoc-members: + :show-inheritance: + + +Module contents +--------------- + +.. automodule:: apptools.naming.trait_defs + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/api/apptools.persistence.rst.txt b/_sources/api/apptools.persistence.rst.txt new file mode 100644 index 000000000..4cda6c227 --- /dev/null +++ b/_sources/api/apptools.persistence.rst.txt @@ -0,0 +1,62 @@ +apptools.persistence package +============================ + +Submodules +---------- + +apptools.persistence.file\_path module +-------------------------------------- + +.. automodule:: apptools.persistence.file_path + :members: + :undoc-members: + :show-inheritance: + +apptools.persistence.project\_loader module +------------------------------------------- + +.. automodule:: apptools.persistence.project_loader + :members: + :undoc-members: + :show-inheritance: + +apptools.persistence.state\_pickler module +------------------------------------------ + +.. automodule:: apptools.persistence.state_pickler + :members: + :undoc-members: + :show-inheritance: + +apptools.persistence.updater module +----------------------------------- + +.. automodule:: apptools.persistence.updater + :members: + :undoc-members: + :show-inheritance: + +apptools.persistence.version\_registry module +--------------------------------------------- + +.. automodule:: apptools.persistence.version_registry + :members: + :undoc-members: + :show-inheritance: + +apptools.persistence.versioned\_unpickler module +------------------------------------------------ + +.. automodule:: apptools.persistence.versioned_unpickler + :members: + :undoc-members: + :show-inheritance: + + +Module contents +--------------- + +.. automodule:: apptools.persistence + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/api/apptools.preferences.rst.txt b/_sources/api/apptools.preferences.rst.txt new file mode 100644 index 000000000..cba31951c --- /dev/null +++ b/_sources/api/apptools.preferences.rst.txt @@ -0,0 +1,77 @@ +apptools.preferences package +============================ + +Subpackages +----------- + +.. toctree:: + + apptools.preferences.ui + +Submodules +---------- + +apptools.preferences.api module +------------------------------- + +.. automodule:: apptools.preferences.api + :members: + :undoc-members: + :show-inheritance: + +apptools.preferences.i\_preferences module +------------------------------------------ + +.. automodule:: apptools.preferences.i_preferences + :members: + :undoc-members: + :show-inheritance: + +apptools.preferences.package\_globals module +-------------------------------------------- + +.. automodule:: apptools.preferences.package_globals + :members: + :undoc-members: + :show-inheritance: + +apptools.preferences.preference\_binding module +----------------------------------------------- + +.. automodule:: apptools.preferences.preference_binding + :members: + :undoc-members: + :show-inheritance: + +apptools.preferences.preferences module +--------------------------------------- + +.. automodule:: apptools.preferences.preferences + :members: + :undoc-members: + :show-inheritance: + +apptools.preferences.preferences\_helper module +----------------------------------------------- + +.. automodule:: apptools.preferences.preferences_helper + :members: + :undoc-members: + :show-inheritance: + +apptools.preferences.scoped\_preferences module +----------------------------------------------- + +.. automodule:: apptools.preferences.scoped_preferences + :members: + :undoc-members: + :show-inheritance: + + +Module contents +--------------- + +.. automodule:: apptools.preferences + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/api/apptools.preferences.ui.rst.txt b/_sources/api/apptools.preferences.ui.rst.txt new file mode 100644 index 000000000..59a451f0a --- /dev/null +++ b/_sources/api/apptools.preferences.ui.rst.txt @@ -0,0 +1,70 @@ +apptools.preferences.ui package +=============================== + +Submodules +---------- + +apptools.preferences.ui.api module +---------------------------------- + +.. automodule:: apptools.preferences.ui.api + :members: + :undoc-members: + :show-inheritance: + +apptools.preferences.ui.i\_preferences\_page module +--------------------------------------------------- + +.. automodule:: apptools.preferences.ui.i_preferences_page + :members: + :undoc-members: + :show-inheritance: + +apptools.preferences.ui.preferences\_manager module +--------------------------------------------------- + +.. automodule:: apptools.preferences.ui.preferences_manager + :members: + :undoc-members: + :show-inheritance: + +apptools.preferences.ui.preferences\_node module +------------------------------------------------ + +.. automodule:: apptools.preferences.ui.preferences_node + :members: + :undoc-members: + :show-inheritance: + +apptools.preferences.ui.preferences\_page module +------------------------------------------------ + +.. automodule:: apptools.preferences.ui.preferences_page + :members: + :undoc-members: + :show-inheritance: + +apptools.preferences.ui.tree\_item module +----------------------------------------- + +.. automodule:: apptools.preferences.ui.tree_item + :members: + :undoc-members: + :show-inheritance: + +apptools.preferences.ui.widget\_editor module +--------------------------------------------- + +.. automodule:: apptools.preferences.ui.widget_editor + :members: + :undoc-members: + :show-inheritance: + + +Module contents +--------------- + +.. automodule:: apptools.preferences.ui + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/api/apptools.rst.txt b/_sources/api/apptools.rst.txt new file mode 100644 index 000000000..d1e57ded9 --- /dev/null +++ b/_sources/api/apptools.rst.txt @@ -0,0 +1,25 @@ +apptools package +================ + +Subpackages +----------- + +.. toctree:: + + apptools.io + apptools.logger + apptools.naming + apptools.persistence + apptools.preferences + apptools.scripting + apptools.selection + apptools.type_registry + apptools.undo + +Module contents +--------------- + +.. automodule:: apptools + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/api/apptools.scripting.rst.txt b/_sources/api/apptools.scripting.rst.txt new file mode 100644 index 000000000..4a47c7054 --- /dev/null +++ b/_sources/api/apptools.scripting.rst.txt @@ -0,0 +1,62 @@ +apptools.scripting package +========================== + +Submodules +---------- + +apptools.scripting.api module +----------------------------- + +.. automodule:: apptools.scripting.api + :members: + :undoc-members: + :show-inheritance: + +apptools.scripting.package\_globals module +------------------------------------------ + +.. automodule:: apptools.scripting.package_globals + :members: + :undoc-members: + :show-inheritance: + +apptools.scripting.recordable module +------------------------------------ + +.. automodule:: apptools.scripting.recordable + :members: + :undoc-members: + :show-inheritance: + +apptools.scripting.recorder module +---------------------------------- + +.. automodule:: apptools.scripting.recorder + :members: + :undoc-members: + :show-inheritance: + +apptools.scripting.recorder\_with\_ui module +-------------------------------------------- + +.. automodule:: apptools.scripting.recorder_with_ui + :members: + :undoc-members: + :show-inheritance: + +apptools.scripting.util module +------------------------------ + +.. automodule:: apptools.scripting.util + :members: + :undoc-members: + :show-inheritance: + + +Module contents +--------------- + +.. automodule:: apptools.scripting + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/api/apptools.selection.rst.txt b/_sources/api/apptools.selection.rst.txt new file mode 100644 index 000000000..d64022443 --- /dev/null +++ b/_sources/api/apptools.selection.rst.txt @@ -0,0 +1,62 @@ +apptools.selection package +========================== + +Submodules +---------- + +apptools.selection.api module +----------------------------- + +.. automodule:: apptools.selection.api + :members: + :undoc-members: + :show-inheritance: + +apptools.selection.errors module +-------------------------------- + +.. automodule:: apptools.selection.errors + :members: + :undoc-members: + :show-inheritance: + +apptools.selection.i\_selection module +-------------------------------------- + +.. automodule:: apptools.selection.i_selection + :members: + :undoc-members: + :show-inheritance: + +apptools.selection.i\_selection\_provider module +------------------------------------------------ + +.. automodule:: apptools.selection.i_selection_provider + :members: + :undoc-members: + :show-inheritance: + +apptools.selection.list\_selection module +----------------------------------------- + +.. automodule:: apptools.selection.list_selection + :members: + :undoc-members: + :show-inheritance: + +apptools.selection.selection\_service module +-------------------------------------------- + +.. automodule:: apptools.selection.selection_service + :members: + :undoc-members: + :show-inheritance: + + +Module contents +--------------- + +.. automodule:: apptools.selection + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/api/apptools.type_registry.rst.txt b/_sources/api/apptools.type_registry.rst.txt new file mode 100644 index 000000000..161af8365 --- /dev/null +++ b/_sources/api/apptools.type_registry.rst.txt @@ -0,0 +1,30 @@ +apptools.type\_registry package +=============================== + +Submodules +---------- + +apptools.type\_registry.api module +---------------------------------- + +.. automodule:: apptools.type_registry.api + :members: + :undoc-members: + :show-inheritance: + +apptools.type\_registry.type\_registry module +--------------------------------------------- + +.. automodule:: apptools.type_registry.type_registry + :members: + :undoc-members: + :show-inheritance: + + +Module contents +--------------- + +.. automodule:: apptools.type_registry + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/api/apptools.undo.action.rst.txt b/_sources/api/apptools.undo.action.rst.txt new file mode 100644 index 000000000..13985a67f --- /dev/null +++ b/_sources/api/apptools.undo.action.rst.txt @@ -0,0 +1,54 @@ +apptools.undo.action package +============================ + +Submodules +---------- + +apptools.undo.action.abstract\_command\_stack\_action module +------------------------------------------------------------ + +.. automodule:: apptools.undo.action.abstract_command_stack_action + :members: + :undoc-members: + :show-inheritance: + +apptools.undo.action.api module +------------------------------- + +.. automodule:: apptools.undo.action.api + :members: + :undoc-members: + :show-inheritance: + +apptools.undo.action.command\_action module +------------------------------------------- + +.. automodule:: apptools.undo.action.command_action + :members: + :undoc-members: + :show-inheritance: + +apptools.undo.action.redo\_action module +---------------------------------------- + +.. automodule:: apptools.undo.action.redo_action + :members: + :undoc-members: + :show-inheritance: + +apptools.undo.action.undo\_action module +---------------------------------------- + +.. automodule:: apptools.undo.action.undo_action + :members: + :undoc-members: + :show-inheritance: + + +Module contents +--------------- + +.. automodule:: apptools.undo.action + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/api/apptools.undo.rst.txt b/_sources/api/apptools.undo.rst.txt new file mode 100644 index 000000000..477e1bf52 --- /dev/null +++ b/_sources/api/apptools.undo.rst.txt @@ -0,0 +1,77 @@ +apptools.undo package +===================== + +Subpackages +----------- + +.. toctree:: + + apptools.undo.action + +Submodules +---------- + +apptools.undo.abstract\_command module +-------------------------------------- + +.. automodule:: apptools.undo.abstract_command + :members: + :undoc-members: + :show-inheritance: + +apptools.undo.api module +------------------------ + +.. automodule:: apptools.undo.api + :members: + :undoc-members: + :show-inheritance: + +apptools.undo.command\_stack module +----------------------------------- + +.. automodule:: apptools.undo.command_stack + :members: + :undoc-members: + :show-inheritance: + +apptools.undo.i\_command module +------------------------------- + +.. automodule:: apptools.undo.i_command + :members: + :undoc-members: + :show-inheritance: + +apptools.undo.i\_command\_stack module +-------------------------------------- + +.. automodule:: apptools.undo.i_command_stack + :members: + :undoc-members: + :show-inheritance: + +apptools.undo.i\_undo\_manager module +------------------------------------- + +.. automodule:: apptools.undo.i_undo_manager + :members: + :undoc-members: + :show-inheritance: + +apptools.undo.undo\_manager module +---------------------------------- + +.. automodule:: apptools.undo.undo_manager + :members: + :undoc-members: + :show-inheritance: + + +Module contents +--------------- + +.. automodule:: apptools.undo + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/api/modules.rst.txt b/_sources/api/modules.rst.txt new file mode 100644 index 000000000..f51f30d15 --- /dev/null +++ b/_sources/api/modules.rst.txt @@ -0,0 +1,7 @@ +apptools +======== + +.. toctree:: + :maxdepth: 4 + + apptools diff --git a/_sources/index.rst.txt b/_sources/index.rst.txt new file mode 100644 index 000000000..1891a5802 --- /dev/null +++ b/_sources/index.rst.txt @@ -0,0 +1,16 @@ +AppTools Documentation +============================================== + +.. toctree:: + :maxdepth: 2 + :glob: + + preferences/* + scripting/* + undo/* + selection/* + naming/* + io/* + api + +* :ref:`search` diff --git a/_sources/io/introduction.rst.txt b/_sources/io/introduction.rst.txt new file mode 100644 index 000000000..6bdd04ef5 --- /dev/null +++ b/_sources/io/introduction.rst.txt @@ -0,0 +1,28 @@ +File I/O +======== + +The :mod:`apptools.io` package provides a traited |File| object provides +properties and methods for common file path manipulation operations. Much of +this functionality was implemented before Python 3 `pathlib`_ standard library +became available to provide similar support. For new code we encourage users +to investigate if `pathlib`_ can satisfy their use cases before they turn to +the `apptools.io` |File| object + +HDF5 File Support +----------------- + +The :mod:`apptools.io.h5` sub-package provides a wrapper around `PyTables`_ +with a dictionary-style mapping. + + +.. + external links + +.. _pathlib: https://docs.python.org/3/library/pathlib.html +.. _PyTables: https://www.pytables.org/ + + +.. + # substitutions + +.. |File| replace:: :class:`~apptools.io.file.File` diff --git a/_sources/naming/Introduction.rst.txt b/_sources/naming/Introduction.rst.txt new file mode 100644 index 000000000..736f4b92a --- /dev/null +++ b/_sources/naming/Introduction.rst.txt @@ -0,0 +1,7 @@ +Naming +====== + +:mod:`apptools.naming` package is a Python implementation of the Naming portion of the `Java +Naming and Directory Interface `_, +including specific implementations for a heirarchy of Python objects. You can +also find the Java JNDI tutorial `here `_. diff --git a/_sources/preferences/Preferences.rst.txt b/_sources/preferences/Preferences.rst.txt new file mode 100644 index 000000000..51ee93fd8 --- /dev/null +++ b/_sources/preferences/Preferences.rst.txt @@ -0,0 +1,329 @@ +Preferences +=========== + +The preferences package provides a simple API for managing application +preferences. The classes in the package are implemented using a layered +approach where the lowest layer provides access to the raw preferences +mechanism and each layer on top providing more convenient ways to get and set +preference values. + +The Basic Preferences Mechanism +------------------------------- + +Lets start by taking a look at the lowest layer which consists of the +|IPreferences| interface and its default implementation in the |Preferences| +class. This layer implements the basic preferences system which is a +hierarchical arrangement of preferences 'nodes' (where each node is simply an +object that implements the |IPreferences| interface). Nodes in the hierarchy can +contain preference settings and/or child nodes. This layer also provides a +default way to read and write preferences from the filesystem using the +excellent `ConfigObj`_ package. + +This all sounds a bit complicated but, believe me, it isn't! To prove it +(hopefully) lets look at an example. Say I have the following preferences in +a file 'example.ini':: + + [acme.ui] + bgcolor = blue + width = 50 + ratio = 1.0 + visible = True + + [acme.ui.splash_screen] + image = splash + fgcolor = red + +I can create a preferences hierarchy from this file by:: + + >>> from apptools.preferences.api import Preferences + >>> preferences = Preferences(filename='example.ini') + >>> preferences.dump() + + Node() {} + Node(acme) {} + Node(ui) {'bgcolor': 'blue', 'width': '50', 'ratio': '1.0', 'visible': 'True'} + Node(splash_screen) {'image': 'splash', 'fgcolor': 'red'} + +The 'dump' method (useful for debugging etc) simply 'pretty prints' a +preferences hierarchy. The dictionary next to each node contains the node's +actual preferences. In this case, the root node (the node with no name) is the +preferences object that we created. This node now has one child node 'acme', +which contains no preferences. The 'acme' node has one child, 'ui', which +contains some preferences (e.g. 'bgcolor') and also a child node +'splash_screen' which also contains preferences (e.g. 'image'). + +To look up a preference we use:: + + >>> preferences.get('acme.ui.bgcolor') + 'blue' + +If no such preferences exists then, by default, None is returned:: + + >>> preferences.get('acme.ui.bogus') is None + True + +You can also specify an explicit default value:: + + >>> preferences.get('acme.ui.bogus', 'fred') + 'fred' + +To set a preference we use:: + + >>> preferences.set('acme.ui.bgcolor', 'red') + >>> preferences.get('acme.ui.bgcolor') + 'red' + +And to make sure the preferences are saved back to disk:: + + >>> preferences.flush() + +To add a new preference value we simply set it:: + + >>> preferences.set('acme.ui.fgcolor', 'black') + >>> preferences.get('acme.ui.fgcolor') + 'black' + +Any missing nodes in a call to 'set' are created automatically, hence:: + + >>> preferences.set('acme.ui.button.fgcolor', 'white') + >>> preferences.get('acme.ui.button.fgcolor') + 'white' + +Preferences can also be 'inherited'. e.g. Notice that the 'splash_screen' +node does not contain a 'bgcolor' preference, and hence:: + + >>> preferences.get('acme.ui.splash_screen.bgcolor') is None + True + +But if we allow the 'inheritance' of preference values then:: + + >>> preferences.get('acme.ui.splash_screen.bgcolor', inherit=True) + 'red' + +By using 'inheritance' here the preferences system will try the following +preferences:: + + 'acme.ui.splash_screen.bgcolor' + 'acme.ui.bgcolor' + 'acme.bgcolor' + 'bgcolor' + +Strings, Glorious Strings +~~~~~~~~~~~~~~~~~~~~~~~~~ + +At this point it is worth mentioning that preferences are *always* stored and +returned as strings. This is because of the limitations of the traditional +'.ini' file format i.e. they don't contain any type information! Now before you +start panicking, this doesn't mean that all of your preferences have to be +strings! Currently the preferences system allows, strings(!), booleans, ints, +longs, floats and complex numbers. When you store a non-string value it gets +converted to a string for you, but you *always* get a string back:: + + >>> preferences.get('acme.ui.width') + '50' + >>> preferences.set('acme.ui.width', 100) + >>> preferences.get('acme.ui.width') + '100' + + >>> preferences.get('acme.ui.visible') + 'True' + >>> preferences.set('acme.ui.visible', False) + >>> preferences.get('acme.ui.visible') + 'False' + +This is obviously not terribly convenient, and so the following section +discusses how we associate type information with our preferences to make +getting and setting them more natural. + +Preferences and Types +--------------------- + +As mentioned previously, we would like to be able to get and set non-string +preferences in a more convenient way. This is where the |PreferencesHelper| +class comes in. + +Let's take another look at 'example.ini':: + + [acme.ui] + bgcolor = blue + width = 50 + ratio = 1.0 + visible = True + + [acme.ui.splash_screen] + image = splash + fgcolor = red + +Say, I am interested in the preferences in the 'acme.ui' section. I can use a +preferences helper as follows:: + + from apptools.preferences.api import PreferencesHelper + + class SplashScreenPreferences(PreferencesHelper): + """ A preferences helper for the splash screen. """ + + preferences_path = 'acme.ui' + + bgcolor = Str + width = Int + ratio = Float + visible = Bool + + >>> preferences = Preferences(filename='example.ini') + >>> helper = SplashScreenPreferences(preferences=preferences) + >>> helper.bgcolor + 'blue' + >>> helper.width + 50 + >>> helper.ratio + 1.0 + >>> helper.visible + True + +And, obviously, I can set the value of the preferences via the helper too:: + + >>> helper.ratio = 0.5 + +And if you want to prove to yourself it really did set the preference:: + + >>> preferences.get('acme.ui.ratio') + '0.5' + +Using a preferences helper you also get notified via the usual trait +mechanism when the preferences are changed (either via the helper or via the +preferences node directly:: + + def listener(obj, trait_name, old, new): + print(trait_name, old, new) + + >>> helper.on_trait_change(listener) + >>> helper.ratio = 0.75 + ratio 0.5 0.75 + >>> preferences.set('acme.ui.ratio', 0.33) + ratio 0.75 0.33 + +Scoped Preferences +------------------ + +In many applications the idea of preferences scopes is useful. In a scoped +system, an actual preference value can be stored in any scope and when a call +is made to the 'get' method the scopes are searched in order of precedence. + +The default implementation (in the |ScopedPreferences| class) provides two +scopes by default: + +1) The application scope + +This scope stores itself in the 'ETSConfig.application_home' directory. This +scope is generally used when *setting* any user preferences. + +2) The default scope + +This scope is transient (i.e. it does not store itself anywhere). This scope +is generally used to load any predefined default values into the preferences +system. + +If you are happy with the default arrangement, then using the scoped +preferences is just like using the plain old non-scoped version:: + + >>> from apptools.preferences.api import ScopedPreferences + >>> preferences = ScopedPreferences(filename='example.ini') + >>> preferences.load('example.ini') + >>> preferences.dump() + + Node() {} + Node(application) {} + Node(acme) {} + Node(ui) {'bgcolor': 'blue', 'width': '50', 'ratio': '1.0', 'visible': 'True'} + Node(splash_screen) {'image': 'splash', 'fgcolor': 'red'} + Node(default) {} + +Here you can see that the root node now has a child node representing each +scope. + +When we are getting and setting preferences using scopes we generally want the +following behaviour: + +a) When we get a preference we want to look it up in each scope in order. The +first scope that contains a value 'wins'. + +b) When we set a preference, we want to set it in the first scope. By default +this means that when we set a preference it will be set in the application +scope. This is exactly what we want as the application scope is the scope that +is persistent. + +So usually, we just use the scoped preferences as before:: + + >>> preferences.get('acme.ui.bgcolor') + 'blue' + >>> preferences.set('acme.ui.bgcolor', 'red') + >>> preferences.dump() + + Node() {} + Node(application) {} + Node(acme) {} + Node(ui) {'bgcolor': 'red', 'width': '50', 'ratio': '1.0', 'visible': 'True'} + Node(splash_screen) {'image': 'splash', 'fgcolor': 'red'} + Node(default) {} + +And, conveniently, preference helpers work just the same with scoped +preferences too:: + + >>> helper = SplashScreenPreferences(preferences=preferences) + >>> helper.bgcolor + 'red' + >>> helper.width + 50 + >>> helper.ratio + 1.0 + >>> helper.visible + True + +Accessing a particular scope +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Should you care about getting or setting a preference in a particular scope +then you use the following syntax:: + + >>> preferences.set('default/acme.ui.bgcolor', 'red') + >>> preferences.get('default/acme.ui.bgcolor') + 'red' + >>> preferences.dump() + + Node() {} + Node(application) {} + Node(acme) {} + Node(ui) {'bgcolor': 'red', 'width': '50', 'ratio': '1.0', 'visible': 'True'} + Node(splash_screen) {'image': 'splash', 'fgcolor': 'red'} + Node(default) {} + Node(acme) {} + Node(ui) {'bgcolor': 'red'} + +You can also get hold of a scope via:: + + >>> default = preferences.get_scope('default') + +And then perform any of the usual operations on it. + +Further Reading +--------------- + +So that's a quick tour around the basic useage of the preferences API. For more +information about what is provided take a look at the :ref:`api-documentation`. + +If you are using Envisage to build your applications then you might also be +interested in the |Preferences in Envisage| section. + +.. + external links + +.. _ConfigObj: https://configobj.readthedocs.io/en/latest + +.. + # substitutions + +.. |ScopedPreferences| replace:: :class:`~apptools.preferences.scoped_preferences.ScopedPreferences` +.. |IPreferences| replace:: :class:`~apptools.preferences.i_preferences.IPreferences` +.. |Preferences| replace:: :class:`~apptools.preferences.preferences.Preferences` +.. |PreferencesHelper| replace:: :class:`~apptools.preferences.preferences_helper.PreferencesHelper` +.. |Preferences in Envisage| replace:: :ref:`preferences-in-envisage` diff --git a/_sources/preferences/PreferencesInEnvisage.rst.txt b/_sources/preferences/PreferencesInEnvisage.rst.txt new file mode 100644 index 000000000..931897eb6 --- /dev/null +++ b/_sources/preferences/PreferencesInEnvisage.rst.txt @@ -0,0 +1,52 @@ +.. _preferences-in-envisage: + +Preferences in Envisage +======================= + +This section discusses how an Envisage application uses the preferences +mechanism. Envisage tries not to dictate too much, and so this describes the +default behaviour, but you are free to override it as desired. + +Envisage uses the default implementation of the |ScopedPreferences| class which +is made available via the application's 'preferences' trait:: + + >>> application = Application(id='myapplication') + >>> application.preferences.set('acme.ui.bgcolor', 'yellow') + >>> application.preferences.get('acme.ui.bgcolor') + 'yellow' + +Hence, you use the Envisage preferences just like you would any other scoped +preferences. + +It also registers itself as the default preferences node used by the +|PreferencesHelper| class. Hence you don't need to provide a preferences node +explicitly to your helper:: + + >>> helper = SplashScreenPreferences() + >>> helper.bgcolor + 'blue' + >>> helper.width + 100 + >>> helper.ratio + 1.0 + >>> helper.visible + True + +The only extra thing that Envisage does for you is to provide an extension +point that allows you to contribute any number of '.ini' files that are +loaded into the default scope when the application is started. + +e.g. To contribute a preference file for my plugin I might use:: + + class MyPlugin(Plugin): + ... + + @contributes_to('envisage.preferences') + def get_preferences(self, application): + return ['pkgfile://mypackage:preferences.ini'] + +.. + # substitutions + +.. |PreferencesHelper| replace:: :class:`~apptools.preferences.preferences_helper.PreferencesHelper` +.. |ScopedPreferences| replace:: :class:`~apptools.preferences.scoped_preferences.ScopedPreferences` diff --git a/_sources/scripting/introduction.rst.txt b/_sources/scripting/introduction.rst.txt new file mode 100644 index 000000000..724a2767d --- /dev/null +++ b/_sources/scripting/introduction.rst.txt @@ -0,0 +1,198 @@ +.. _automatic-script-recording: + +Automatic script recording +=========================== + +This package provides a very handy and powerful Python script recording +facility. This can be used to: + + - record all actions performed on a traits based UI into a *human + readable*, Python script that should be able to recreate your UI + actions. + + - easily learn the scripting API of an application. + +This package is not just a toy framework and is powerful enough to +provide full script recording to the Mayavi_ application. Mayavi is a +powerful 3D visualization tool that is part of ETS_. + +.. _Mayavi: https://docs.enthought.com/mayavi/mayavi/ +.. _ETS: https://docs.enthought.com/ets/ + +.. _scripting-api: + + +The scripting API +------------------ + +The scripting API primarily allows you to record UI actions for objects +that have Traits. Technically the framework listens to all trait +changes so will work outside a UI. We do not document the full API +here, the best place to look for that is the +``apptools.scripting.recorder`` module which is reasonably well +documented. We provide a high level overview of the library. + +The quickest way to get started is to look at a small example. + + +.. _scripting-api-example: + +A tour by example +~~~~~~~~~~~~~~~~~~~ + +The following example is taken from the test suite. Consider a set of +simple objects organized in a hierarchy:: + + from traits.api import (HasTraits, Float, Instance, + Str, List, Bool, HasStrictTraits, Tuple, PrefixMap, Range, + Trait) + from apptools.scripting.api import (Recorder, recordable, + set_recorder) + + class Property(HasStrictTraits): + color = Tuple(Range(0.0, 1.0), Range(0.0, 1.0), Range(0.0, 1.0)) + opacity = Range(0.0, 1.0, 1.0) + representation = PrefixMap( + {"surface": 2, "wireframe": 1, "points": 0}, + default_value="surface" + ) + + class Toy(HasTraits): + color = Str + type = Str + # Note the use of the trait metadata to ignore this trait. + ignore = Bool(False, record=False) + + class Child(HasTraits): + name = Str('child') + age = Float(10.0) + # The recorder walks through sub-instances if they are marked + # with record=True + property = Instance(Property, (), record=True) + toy = Instance(Toy, record=True) + friends = List(Str) + + # The decorator records the method. + @recordable + def grow(self, x): + """Increase age by x years.""" + self.age += x + + class Parent(HasTraits): + children = List(Child, record=True) + recorder = Instance(Recorder, record=False) + +Using these simple classes we first create a simple object hierarchy as +follows:: + + p = Parent() + c = Child() + t = Toy() + c.toy = t + p.children.append(c) + +Given this hierarchy, we'd like to be able to record a script. To do +this we setup the recording infrastructure:: + + from mayavi.core.recorder import Recorder, set_recorder + # Create a recorder. + r = Recorder() + # Set the global recorder so the decorator works. + set_recorder(r) + r.register(p) + r.recording = True + +The key method here is the ``r.register(p)`` call above. It looks at +the traits of ``p`` and finds all traits and nested objects that specify +a ``record=True`` in their trait metadata (all methods starting and +ending with ``_`` are ignored). All sub-objects are in turn registered +with the recorder and so on. Callbacks are attached to traits changes +and these are wired up to produce readable and executable code. The +``set_recorder(r)`` call is also very important and sets the global +recorder so the framework listens to any functions that are decorated +with the ``recordable`` decorator. + +Now lets test this out like so:: + + # The following will be recorded. + c.name = 'Shiva' + c.property.representation = 'w' + c.property.opacity = 0.4 + c.grow(1) + +To see what's been recorded do this:: + + print(r.script) + +This prints:: + + child = parent.children[0] + child.name = 'Shiva' + child.property.representation = 'wireframe' + child.property.opacity = 0.40000000000000002 + child.grow(1) + +The recorder internally maintains a mapping between objects and unique +names for each object. It also stores the information about the +location of a particular object in the object hierarchy. For example, +the path to the ``Toy`` instance in the hierarchy above is +``parent.children[0].toy``. Since scripting with lists this way can be +tedious, the recorder first instantiates the ``child``:: + + child = parent.children[0] + +Subsequent lines use the ``child`` attribute. The recorder always tries +to instantiate the object referred to using its path information in this +manner. + +To record a function or method call one must simply decorate the +function/method with the ``recordable`` decorator. Nested recordable +functions are not recorded and trait changes are also not recorded if +done inside a recordable function. + +.. note:: + + 1. It is very important to note that the global recorder must be set + via the ``set_recorder`` method. The ``recordable`` decorator + relies on this being set to work. + + 2. The ``recordable`` decorator will work with plain Python classes + and with functions too. + +To stop recording do this:: + + r.unregister(p) + r.recording = False + +The ``r.unregister(p)`` reverses the ``r.register(p)`` call and +unregisters all nested objects as well. + + +.. _recorder-advanced-uses: + +Advanced use cases +~~~~~~~~~~~~~~~~~~~~ + +Here are a few advanced use cases. + + - The API also provides a ``RecorderWithUI`` class that provides a + simple user interface that prints the recorded script and allows the + user to save the script. + + - Sometimes it is not enough to just record trait changes, one may want + to pass an arbitrary string or command when recording is occurring. + To allow for this, if one defines a ``recorder`` trait on the object, + it is set to the current recorder. One can then use this recorder to + do whatever one wants. This is very convenient. + + - To ignore specific traits one must specify either a ``record=False`` + metadata to the trait definition or specify a list of strings to the + ``register`` method in the ``ignore`` keyword argument. + + - If you want to use a specific name for an object on the script you + can pass the ``script_id`` parameter to the register function. + + +For more details on the recorder itself we suggest reading the module +source code. It is fairly well documented and with the above background +should be enough to get you going. diff --git a/_sources/selection/selection.rst.txt b/_sources/selection/selection.rst.txt new file mode 100644 index 000000000..a57d9179e --- /dev/null +++ b/_sources/selection/selection.rst.txt @@ -0,0 +1,144 @@ +.. _selection_service: + +The selection service +===================== + +It is quite common in GUI applications to have a UI element displaying a +collection of items that a user can select ("selection providers"), while +other parts of the application must react to changes in the selection +("selection listeners"). + +Ideally, the listeners would not have a direct dependency on the UI object. +This is especially important in extensible `envisage`_ applications, where +a plugin might need to react to a selection change, but we do not want to +expose the internal organization of the application to external developers. + +This package defines a selection service that manages the communication +between providers and listener. + + +The :class:`~.SelectionService` object +-------------------------------------- + +The :class:`~.SelectionService` object is the central manager that handles +the communication between selection providers and listener. + +:ref:`Selection providers ` are components that wish to +publish information about their current selection for public consumption. +They register to a selection +service instance when they first have a selection available (e.g., when the +UI showing a list of selectable items is initialized), and un-register as soon +as the selection is not available anymore (e.g., the UI is destroyed when the +windows is closed). + +:ref:`Selection listeners ` can query the selection +service to get the current selection published by a provider, using the +provider unique ID. + +The service acts as a broker between providers and listeners, making sure that +they are notified when the +:attr:`~apptools.selection.i_selection_provider.ISelectionProvider.selection` +event is fired. + +.. _selection_providers: + +Selection providers +------------------- + +Any object can become a selection provider by implementing the +:class:`~apptools.selection.i_selection_provider.ISelectionProvider` +interface, and registering to the selection service. + +Selection providers must provide a unique ID +:attr:`~apptools.selection.i_selection_provider.ISelectionProvider.provider_id`, +which is used by listeners to request its current selection. + +Whenever its selection changes, providers fire a +:attr:`~apptools.selection.i_selection_provider.ISelectionProvider.selection` +event. The content of the event is an instance implementing +:class:`~.ISelection` that contains information about the selected items. +For example, a :class:`~.ListSelection` object contains a list of selected +items, and their indices. + +Selection providers can also be queried directly about their current selection +using the +:attr:`~apptools.selection.i_selection_provider.ISelectionProvider.get_selection` +method, and can be requested to change their selection to a new one with the +:attr:`~apptools.selection.i_selection_provider.ISelectionProvider.set_selection` +method. + +Registration +~~~~~~~~~~~~ + +Selection providers publish their selection by registering to the selection +service using the +:attr:`~apptools.selection.selection_service.SelectionService.add_selection_provider` +method. When the selection is no longer available, selection providers +should un-register through +:attr:`~apptools.selection.selection_service.SelectionService.remove_selection_provider`. + +Typically, selection providers are UI objects showing a list or tree of items, +they register as soon as the UI component is initialized, and un-register +when the UI component disappears (e.g., because their window has been closed). +In more complex applications, the registration could be done by a controller +object instead. + +.. _selection_listeners: + +Selection listeners +------------------- + +Selection listeners request information regarding the current selection +of a selection provider given their provider ID. The :class:`~.SelectionService` +supports two distinct use cases: + + 1) Passively listening to selection changes: listener connect to a specific + provider and are notified when the provider's selection changes. + + 2) Actively querying a provider for its current selection: the selection + service can be used to query a provider using its unique ID. + +Passive listening +~~~~~~~~~~~~~~~~~ + +Listeners connect to the selection events for a given provider using the +:attr:`~apptools.selection.selection_service.SelectionService.connect_selection_listener` +method. They need to provide the unique ID of the provider, and a function +(or callable) that is called to send the event. This callback function takes +one argument, an implementation of the :class:`~.ISelection` that represents +the selection. + +It is possible for a listener to connect to a provider ID before it is +registered. As soon as the provider is registered, the listener will receive +a notification containing the provider's initial selection. + +To disconnect a listener use the methods +:attr:`~apptools.selection.selection_service.SelectionService.disconnect_selection_listener`. + +Active querying +~~~~~~~~~~~~~~~ + +In other instances, an element of the application only needs the current +selection at a specific time. For example, a toolbar button could open dialog +representing a user action based on what is currently selected in the active +editor. + +The +:attr:`~apptools.selection.selection_service.SelectionService.get_selection` +method calls the corresponding method on the provider with the given ID and +returns an :class:`~.ISelection` instance. + +Setting a selection +~~~~~~~~~~~~~~~~~~~ + +Finally, it is possible to request a provider to set its selection to a given +set of objects with +:attr:`~apptools.selection.selection_service.SelectionService.set_selection`. +The main use case for this method is multiple views of the same list of +objects, which need to keep their selection synchronized. + +If the items specified in the arguments are not available in the provider, +a :class:`~apptools.selection.errors.ProviderNotRegisteredError` is raised, +unless the optional keyword argument :attr:`ignore_missing` is set to ``True``. + +.. _envisage: http://docs.enthought.com/envisage/ diff --git a/_sources/undo/Introduction.rst.txt b/_sources/undo/Introduction.rst.txt new file mode 100644 index 000000000..89dee28d6 --- /dev/null +++ b/_sources/undo/Introduction.rst.txt @@ -0,0 +1,275 @@ +Undo Framework +============== + +The Undo Framework is a component of the Enthought Tool Suite that provides +developers with an API that implements the standard pattern for do/undo/redo +commands. + +The framework is completely configurable. Alternate implementations of all +major components can be provided if necessary. + + +Framework Concepts +------------------ + +The following are the concepts supported by the framework. + +- Command + + A command is an application defined operation that can be done (i.e. + executed), undone (i.e. reverted) and redone (i.e. repeated). + + A command operates on some data and maintains sufficient state to allow it to + revert or repeat a change to the data. + + Commands may be merged so that potentially long sequences of similar + commands (e.g. to add a character to some text) can be collapsed into a + single command (e.g. to add a word to some text). + +- Macro + + A macro is a sequence of commands that is treated as a single command when + being undone or redone. + +- Command Stack + + A command is done by pushing it onto a command stack. The last command can + be undone and redone by calling appropriate command stack methods. It is + also possible to move the stack's position to any point and the command stack + will ensure that commands are undone or redone as required. + + A command stack maintains a *clean* state which is updated as commands are + done and undone. It may be explicitly set, for example when the data being + manipulated by the commands is saved to disk. + + Canned PyFace actions are provided as wrappers around command stack methods + to implement common menu items. + +- Undo Manager + + An undo manager is responsible for one or more command stacks and maintains + a reference to the currently active stack. It provides convenience undo and + redo methods that operate on the currently active stack. + + An undo manager ensures that each command execution is allocated a unique + sequence number, irrespective of which command stack it is pushed to. Using + this it is possible to synchronise multiple command stacks and restore them + to a particular point in time. + + An undo manager will generate an event whenever the clean state of the active + stack changes. This can be used to maintain some sort of GUI status + indicator to tell the user that their data has been modified since it was + last saved. + +Typically an application will have one undo manager and one undo stack for +each data type that can be edited. However this is not a requirement: how the +command stack's in particular are organised and linked (with the user +manager's sequence number) can need careful thought so as not to confuse the +user - particularly in a plugin based application that may have many editors. + +To support this typical usage the PyFace ``Workbench`` class has an +``undo_manager`` trait and the PyFace ``Editor`` class has a ``command_stack`` +trait. Both are lazy loaded so can be completely ignored if they are not used. + + +API Overview +------------ + +This section gives a brief overview of the various classes implemented in the +framework. The complete API_ documentation is available as endo generated +HTML. + +The example_ application demonstrates all the major features of the framework. + + +UndoManager +........... + +The ``UndoManager`` class is the default implementation of the ``IUndoManager`` +interface. + +``active_stack`` + This trait is a reference to the currently active command stack and may be + None. Typically it is set when some sort of editor becomes active. + +``active_stack_clean`` + This boolean trait reflects the clean state of the currently active + command stack. It is intended to support a "document modified" indicator + in the GUI. It is maintained by the undo manager. + +``stack_updated`` + This event is fired when the index of a command stack is changed. A + reference to the stack is passed as an argument to the event and may not + be the currently active stack. + +``undo_name`` + This Str trait is the name of the command that can be undone, and will + be empty if there is no such command. It is maintained by the undo + manager. + +``redo_name`` + This Str trait is the name of the command that can be redone, and will + be empty if there is no such command. It is maintained by the undo + manager. + +``sequence_nr`` + This integer trait is the sequence number of the next command to be + executed. It is incremented immediately before a command's ``do()`` + method is called. A particular sequence number identifies the state of + all command stacks handled by the undo manager and allows those stacks to + be set to the point they were at at a particular point in time. In other + words, the sequence number allows otherwise independent command stacks to + be synchronised. + +``undo()`` + This method calls the ``undo()`` method of the last command on the active + command stack. + +``redo()`` + This method calls the ``redo()`` method of the last undone command on the + active command stack. + + +CommandStack +............ + +The ``CommandStack`` class is the default implementation of the +``ICommandStack`` interface. + +``clean`` + This boolean traits reflects the clean state of the command stack. Its + value changes as commands are executed, undone and redone. It may also be + explicitly set to mark the current stack position as being clean (when + data is saved to disk for example). + +``undo_name`` + This Str trait is the name of the command that can be undone, and will + be empty if there is no such command. It is maintained by the command + stack. + +``redo_name`` + This Str trait is the name of the command that can be redone, and will + be empty if there is no such command. It is maintained by the command + stack. + +``undo_manager`` + This trait is a reference to the undo manager that manages the command + stack. + +``push(command)`` + This method executes the given command by calling its ``do()`` method. + Any value returned by ``do()`` is returned by ``push()``. If the command + couldn't be merged with the previous one then it is saved on the command + stack. + +``undo(sequence_nr=0)`` + This method undoes the last command. If a sequence number is given then + all commands are undone up to an including the sequence number. + +``redo(sequence_nr=0)`` + This method redoes the last command and returns any result. If a sequence + number is given then all commands are redone up to an including the + sequence number and any result of the last of these is returned. + +``clear()`` + This method clears the command stack, without undoing or redoing any + commands, and leaves the stack in a clean state. It is typically used + when all changes to the data have been abandoned. + +``begin_macro(name)`` + This method begins a macro by creating an empty command with the given + name. The commands passed to all subsequent calls to ``push()`` will be + contained in the macro until the next call to ``end_macro()``. Macros may + be nested. The command stack is disabled (ie. nothing can be undone or + redone) while a macro is being created (ie. while there is an outstanding + ``end_macro()`` call). + +``end_macro()`` + This method ends the current macro. + + +ICommand +........ + +The ``ICommand`` interface defines the interface that must be implemented by +any undoable/redoable command. + +``data`` + This optional trait is a reference to the data object that the command + operates on. It is not used by the framework itself. + +``name`` + This Str trait is the name of the command as it will appear in any GUI + element (e.g. in the text of an undo and redo menu entry). It may include + ``&`` to indicate a keyboard shortcut which will be automatically removed + whenever it is inappropriate. + +``__init__(*args)`` + If the command takes arguments then the command must ensure that deep + copies should be made if appropriate. + +``do()`` + This method is called by a command stack to execute the command and to + return any result. The command must save any state necessary for the + ``undo()`` and ``redo()`` methods to work. It is guaranteed that this + will only ever be called once and that it will be called before any call + to ``undo()`` or ``redo()``. + +``undo()`` + This method is called by a command stack to undo the command. + +``redo()`` + This method is called by a command stack to redo the command and to return + any result. + +``merge(other)`` + This method is called by the command stack to try and merge the ``other`` + command with this one. True should be returned if the commands were + merged. If the commands are merged then ``other`` will not be placed on + the command stack. A subsequent undo or redo of this modified command + must have the same effect as the two original commands. + + +AbstractCommand +............... + +``AbstractCommand`` is an abstract base class that implements the ``ICommand`` +interface. It provides a default implementation of the ``merge()`` method. + + +CommandAction +............. + +The ``CommandAction`` class is a sub-class of the PyFace ``Action`` class that +is used to wrap commands. + +``command`` + This callable trait must be set to a factory that will return an object + that implements ``ICommand``. It will be called when the action is invoked + and the object created pushed onto the command stack. + +``command_stack`` + This instance trait must be set to the command stack that commands invoked + by the action are pushed to. + +``data`` + This optional trait is a reference to the data object that will be passed + to the ``command`` factory when it is called. + + +UndoAction +.......... + +The ``UndoAction`` class is a canned PyFace action that undoes the last +command of the active command stack. + + +RedoAction +.......... + +The ``RedoAction`` class is a canned PyFace action that redoes the last +command undone of the active command stack. + + +.. _API: api/index.html +.. _example: https://svn.enthought.com/enthought/browser/AppTools/trunk/examples/undo/ diff --git a/_static/basic.css b/_static/basic.css new file mode 100644 index 000000000..b04360d69 --- /dev/null +++ b/_static/basic.css @@ -0,0 +1,768 @@ +/* + * basic.css + * ~~~~~~~~~ + * + * Sphinx stylesheet -- basic theme. + * + * :copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +/* -- main layout ----------------------------------------------------------- */ + +div.clearer { + clear: both; +} + +/* -- relbar ---------------------------------------------------------------- */ + +div.related { + width: 100%; + font-size: 90%; +} + +div.related h3 { + display: none; +} + +div.related ul { + margin: 0; + padding: 0 0 0 10px; + list-style: none; +} + +div.related li { + display: inline; +} + +div.related li.right { + float: right; + margin-right: 5px; +} + +/* -- sidebar --------------------------------------------------------------- */ + +div.sphinxsidebarwrapper { + padding: 10px 5px 0 10px; +} + +div.sphinxsidebar { + float: left; + width: 230px; + margin-left: -100%; + font-size: 90%; + word-wrap: break-word; + overflow-wrap : break-word; +} + +div.sphinxsidebar ul { + list-style: none; +} + +div.sphinxsidebar ul ul, +div.sphinxsidebar ul.want-points { + margin-left: 20px; + list-style: square; +} + +div.sphinxsidebar ul ul { + margin-top: 0; + margin-bottom: 0; +} + +div.sphinxsidebar form { + margin-top: 10px; +} + +div.sphinxsidebar input { + border: 1px solid #98dbcc; + font-family: sans-serif; + font-size: 1em; +} + +div.sphinxsidebar #searchbox form.search { + overflow: hidden; +} + +div.sphinxsidebar #searchbox input[type="text"] { + float: left; + width: 80%; + padding: 0.25em; + box-sizing: border-box; +} + +div.sphinxsidebar #searchbox input[type="submit"] { + float: left; + width: 20%; + border-left: none; + padding: 0.25em; + box-sizing: border-box; +} + + +img { + border: 0; + max-width: 100%; +} + +/* -- search page ----------------------------------------------------------- */ + +ul.search { + margin: 10px 0 0 20px; + padding: 0; +} + +ul.search li { + padding: 5px 0 5px 20px; + background-image: url(file.png); + background-repeat: no-repeat; + background-position: 0 7px; +} + +ul.search li a { + font-weight: bold; +} + +ul.search li div.context { + color: #888; + margin: 2px 0 0 30px; + text-align: left; +} + +ul.keywordmatches li.goodmatch a { + font-weight: bold; +} + +/* -- index page ------------------------------------------------------------ */ + +table.contentstable { + width: 90%; + margin-left: auto; + margin-right: auto; +} + +table.contentstable p.biglink { + line-height: 150%; +} + +a.biglink { + font-size: 1.3em; +} + +span.linkdescr { + font-style: italic; + padding-top: 5px; + font-size: 90%; +} + +/* -- general index --------------------------------------------------------- */ + +table.indextable { + width: 100%; +} + +table.indextable td { + text-align: left; + vertical-align: top; +} + +table.indextable ul { + margin-top: 0; + margin-bottom: 0; + list-style-type: none; +} + +table.indextable > tbody > tr > td > ul { + padding-left: 0em; +} + +table.indextable tr.pcap { + height: 10px; +} + +table.indextable tr.cap { + margin-top: 10px; + background-color: #f2f2f2; +} + +img.toggler { + margin-right: 3px; + margin-top: 3px; + cursor: pointer; +} + +div.modindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +div.genindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +/* -- domain module index --------------------------------------------------- */ + +table.modindextable td { + padding: 2px; + border-collapse: collapse; +} + +/* -- general body styles --------------------------------------------------- */ + +div.body { + min-width: 450px; + max-width: 800px; +} + +div.body p, div.body dd, div.body li, div.body blockquote { + -moz-hyphens: auto; + -ms-hyphens: auto; + -webkit-hyphens: auto; + hyphens: auto; +} + +a.headerlink { + visibility: hidden; +} + +a.brackets:before, +span.brackets > a:before{ + content: "["; +} + +a.brackets:after, +span.brackets > a:after { + content: "]"; +} + +h1:hover > a.headerlink, +h2:hover > a.headerlink, +h3:hover > a.headerlink, +h4:hover > a.headerlink, +h5:hover > a.headerlink, +h6:hover > a.headerlink, +dt:hover > a.headerlink, +caption:hover > a.headerlink, +p.caption:hover > a.headerlink, +div.code-block-caption:hover > a.headerlink { + visibility: visible; +} + +div.body p.caption { + text-align: inherit; +} + +div.body td { + text-align: left; +} + +.first { + margin-top: 0 !important; +} + +p.rubric { + margin-top: 30px; + font-weight: bold; +} + +img.align-left, .figure.align-left, object.align-left { + clear: left; + float: left; + margin-right: 1em; +} + +img.align-right, .figure.align-right, object.align-right { + clear: right; + float: right; + margin-left: 1em; +} + +img.align-center, .figure.align-center, object.align-center { + display: block; + margin-left: auto; + margin-right: auto; +} + +img.align-default, .figure.align-default { + display: block; + margin-left: auto; + margin-right: auto; +} + +.align-left { + text-align: left; +} + +.align-center { + text-align: center; +} + +.align-default { + text-align: center; +} + +.align-right { + text-align: right; +} + +/* -- sidebars -------------------------------------------------------------- */ + +div.sidebar { + margin: 0 0 0.5em 1em; + border: 1px solid #ddb; + padding: 7px 7px 0 7px; + background-color: #ffe; + width: 40%; + float: right; +} + +p.sidebar-title { + font-weight: bold; +} + +/* -- topics ---------------------------------------------------------------- */ + +div.topic { + border: 1px solid #ccc; + padding: 7px 7px 0 7px; + margin: 10px 0 10px 0; +} + +p.topic-title { + font-size: 1.1em; + font-weight: bold; + margin-top: 10px; +} + +/* -- admonitions ----------------------------------------------------------- */ + +div.admonition { + margin-top: 10px; + margin-bottom: 10px; + padding: 7px; +} + +div.admonition dt { + font-weight: bold; +} + +div.admonition dl { + margin-bottom: 0; +} + +p.admonition-title { + margin: 0px 10px 5px 0px; + font-weight: bold; +} + +div.body p.centered { + text-align: center; + margin-top: 25px; +} + +/* -- tables ---------------------------------------------------------------- */ + +table.docutils { + border: 0; + border-collapse: collapse; +} + +table.align-center { + margin-left: auto; + margin-right: auto; +} + +table.align-default { + margin-left: auto; + margin-right: auto; +} + +table caption span.caption-number { + font-style: italic; +} + +table caption span.caption-text { +} + +table.docutils td, table.docutils th { + padding: 1px 8px 1px 5px; + border-top: 0; + border-left: 0; + border-right: 0; + border-bottom: 1px solid #aaa; +} + +table.footnote td, table.footnote th { + border: 0 !important; +} + +th { + text-align: left; + padding-right: 5px; +} + +table.citation { + border-left: solid 1px gray; + margin-left: 1px; +} + +table.citation td { + border-bottom: none; +} + +th > p:first-child, +td > p:first-child { + margin-top: 0px; +} + +th > p:last-child, +td > p:last-child { + margin-bottom: 0px; +} + +/* -- figures --------------------------------------------------------------- */ + +div.figure { + margin: 0.5em; + padding: 0.5em; +} + +div.figure p.caption { + padding: 0.3em; +} + +div.figure p.caption span.caption-number { + font-style: italic; +} + +div.figure p.caption span.caption-text { +} + +/* -- field list styles ----------------------------------------------------- */ + +table.field-list td, table.field-list th { + border: 0 !important; +} + +.field-list ul { + margin: 0; + padding-left: 1em; +} + +.field-list p { + margin: 0; +} + +.field-name { + -moz-hyphens: manual; + -ms-hyphens: manual; + -webkit-hyphens: manual; + hyphens: manual; +} + +/* -- hlist styles ---------------------------------------------------------- */ + +table.hlist td { + vertical-align: top; +} + + +/* -- other body styles ----------------------------------------------------- */ + +ol.arabic { + list-style: decimal; +} + +ol.loweralpha { + list-style: lower-alpha; +} + +ol.upperalpha { + list-style: upper-alpha; +} + +ol.lowerroman { + list-style: lower-roman; +} + +ol.upperroman { + list-style: upper-roman; +} + +li > p:first-child { + margin-top: 0px; +} + +li > p:last-child { + margin-bottom: 0px; +} + +dl.footnote > dt, +dl.citation > dt { + float: left; +} + +dl.footnote > dd, +dl.citation > dd { + margin-bottom: 0em; +} + +dl.footnote > dd:after, +dl.citation > dd:after { + content: ""; + clear: both; +} + +dl.field-list { + display: grid; + grid-template-columns: fit-content(30%) auto; +} + +dl.field-list > dt { + font-weight: bold; + word-break: break-word; + padding-left: 0.5em; + padding-right: 5px; +} + +dl.field-list > dt:after { + content: ":"; +} + +dl.field-list > dd { + padding-left: 0.5em; + margin-top: 0em; + margin-left: 0em; + margin-bottom: 0em; +} + +dl { + margin-bottom: 15px; +} + +dd > p:first-child { + margin-top: 0px; +} + +dd ul, dd table { + margin-bottom: 10px; +} + +dd { + margin-top: 3px; + margin-bottom: 10px; + margin-left: 30px; +} + +dt:target, span.highlighted { + background-color: #fbe54e; +} + +rect.highlighted { + fill: #fbe54e; +} + +dl.glossary dt { + font-weight: bold; + font-size: 1.1em; +} + +.optional { + font-size: 1.3em; +} + +.sig-paren { + font-size: larger; +} + +.versionmodified { + font-style: italic; +} + +.system-message { + background-color: #fda; + padding: 5px; + border: 3px solid red; +} + +.footnote:target { + background-color: #ffa; +} + +.line-block { + display: block; + margin-top: 1em; + margin-bottom: 1em; +} + +.line-block .line-block { + margin-top: 0; + margin-bottom: 0; + margin-left: 1.5em; +} + +.guilabel, .menuselection { + font-family: sans-serif; +} + +.accelerator { + text-decoration: underline; +} + +.classifier { + font-style: oblique; +} + +.classifier:before { + font-style: normal; + margin: 0.5em; + content: ":"; +} + +abbr, acronym { + border-bottom: dotted 1px; + cursor: help; +} + +/* -- code displays --------------------------------------------------------- */ + +pre { + overflow: auto; + overflow-y: hidden; /* fixes display issues on Chrome browsers */ +} + +span.pre { + -moz-hyphens: none; + -ms-hyphens: none; + -webkit-hyphens: none; + hyphens: none; +} + +td.linenos pre { + padding: 5px 0px; + border: 0; + background-color: transparent; + color: #aaa; +} + +table.highlighttable { + margin-left: 0.5em; +} + +table.highlighttable td { + padding: 0 0.5em 0 0.5em; +} + +div.code-block-caption { + padding: 2px 5px; + font-size: small; +} + +div.code-block-caption code { + background-color: transparent; +} + +div.code-block-caption + div > div.highlight > pre { + margin-top: 0; +} + +div.doctest > div.highlight span.gp { /* gp: Generic.Prompt */ + user-select: none; +} + +div.code-block-caption span.caption-number { + padding: 0.1em 0.3em; + font-style: italic; +} + +div.code-block-caption span.caption-text { +} + +div.literal-block-wrapper { + padding: 1em 1em 0; +} + +div.literal-block-wrapper div.highlight { + margin: 0; +} + +code.descname { + background-color: transparent; + font-weight: bold; + font-size: 1.2em; +} + +code.descclassname { + background-color: transparent; +} + +code.xref, a code { + background-color: transparent; + font-weight: bold; +} + +h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { + background-color: transparent; +} + +.viewcode-link { + float: right; +} + +.viewcode-back { + float: right; + font-family: sans-serif; +} + +div.viewcode-block:target { + margin: -1px -10px; + padding: 0 10px; +} + +/* -- math display ---------------------------------------------------------- */ + +img.math { + vertical-align: middle; +} + +div.body div.math p { + text-align: center; +} + +span.eqno { + float: right; +} + +span.eqno a.headerlink { + position: relative; + left: 0px; + z-index: 1; +} + +div.math:hover a.headerlink { + visibility: visible; +} + +/* -- printout stylesheet --------------------------------------------------- */ + +@media print { + div.document, + div.documentwrapper, + div.bodywrapper { + margin: 0 !important; + width: 100%; + } + + div.sphinxsidebar, + div.related, + div.footer, + #top-link { + display: none; + } +} \ No newline at end of file diff --git a/_static/css/pygments.css b/_static/css/pygments.css new file mode 100644 index 000000000..1c9c56a51 --- /dev/null +++ b/_static/css/pygments.css @@ -0,0 +1,87 @@ +/* Styling for the source code listings: (mostly from pygments)*/ + +.highlight pre{ + overflow: auto; + padding: 5px; + background-color: #ffffff; + color: #333333; + border: 1px solid #ac9; + border-left: none; + border-right: none; +} + +/* Styling for pre elements: from http://perishablepress.com/press/2009/11/09/perfect-pre-tags/ */ +/* no vertical scrollbars for IE 6 */ +* html pre { + padding-bottom:25px; + overflow-y:hidden; + overflow:visible; + overflow-x:auto +} +/* no vertical scrollbars for IE 7 */ +*:first-child+html pre { + padding-bottom:25px; + overflow-y:hidden; + overflow:visible; + overflow-x:auto +} + +div#spc-section-body td.linenos pre { + padding: 5px 0px; + border: 0; + background-color: transparent; + color: #aaa; +} +.highlight .hll { background-color: #ffffcc } +.highlight { background: #ffffff; } +.highlight .c { color: #008000 } /* Comment */ +.highlight .k { color: #000080; font-weight: bold } /* Keyword */ +.highlight .n { color: #000000 } /* Name */ +.highlight .o { color: #000000 } /* Operator */ +.highlight .cm { color: #008000 } /* Comment.Multiline */ +.highlight .cp { color: #008000 } /* Comment.Preproc */ +.highlight .c1 { color: #008000 } /* Comment.Single */ +.highlight .cs { color: #008000 } /* Comment.Special */ +.highlight .kc { color: #000080; font-weight: bold } /* Keyword.Constant */ +.highlight .kd { color: #000080; font-weight: bold } /* Keyword.Declaration */ +.highlight .kn { color: #000080; font-weight: bold } /* Keyword.Namespace */ +.highlight .kp { color: #000080; font-weight: bold } /* Keyword.Pseudo */ +.highlight .kr { color: #000080; font-weight: bold } /* Keyword.Reserved */ +.highlight .kt { color: #000080; font-weight: bold } /* Keyword.Type */ +.highlight .m { color: #008080 } /* Literal.Number */ +.highlight .s { color: #800080 } /* Literal.String */ +.highlight .na { color: #000000 } /* Name.Attribute */ +.highlight .nb { color: #407090 } /* Name.Builtin */ +.highlight .nc { color: #0000F0; font-weight: bold } /* Name.Class */ +.highlight .no { color: #000000 } /* Name.Constant */ +.highlight .nd { color: #000000 } /* Name.Decorator */ +.highlight .ni { color: #000000 } /* Name.Entity */ +.highlight .ne { color: #000000 } /* Name.Exception */ +.highlight .nf { color: #008080; font-weight: bold } /* Name.Function */ +.highlight .nl { color: #000000 } /* Name.Label */ +.highlight .nn { color: #000000 } /* Name.Namespace */ +.highlight .nx { color: #000000 } /* Name.Other */ +.highlight .py { color: #000000 } /* Name.Property */ +.highlight .nt { color: #000000 } /* Name.Tag */ +.highlight .nv { color: #000000 } /* Name.Variable */ +.highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ +.highlight .mf { color: #008080 } /* Literal.Number.Float */ +.highlight .mh { color: #008080 } /* Literal.Number.Hex */ +.highlight .mi { color: #008080 } /* Literal.Number.Integer */ +.highlight .mo { color: #008080 } /* Literal.Number.Oct */ +.highlight .sb { color: #800080 } /* Literal.String.Backtick */ +.highlight .sc { color: #800080 } /* Literal.String.Char */ +.highlight .sd { color: #800000 } /* Literal.String.Doc */ +.highlight .s2 { color: #800080 } /* Literal.String.Double */ +.highlight .se { color: #800080 } /* Literal.String.Escape */ +.highlight .sh { color: #800080 } /* Literal.String.Heredoc */ +.highlight .si { color: #800080 } /* Literal.String.Interpol */ +.highlight .sx { color: #800080 } /* Literal.String.Other */ +.highlight .sr { color: #800080 } /* Literal.String.Regex */ +.highlight .s1 { color: #800080 } /* Literal.String.Single */ +.highlight .ss { color: #800080 } /* Literal.String.Symbol */ +.highlight .bp { color: #407090 } /* Name.Builtin.Pseudo */ +.highlight .vc { color: #000000 } /* Name.Variable.Class */ +.highlight .vg { color: #000000 } /* Name.Variable.Global */ +.highlight .vi { color: #000000 } /* Name.Variable.Instance */ +.highlight .il { color: #008080 } /* Literal.Number.Integer.Long */ diff --git a/_static/css/spc-bootstrap.css b/_static/css/spc-bootstrap.css new file mode 100644 index 000000000..9895e61fe --- /dev/null +++ b/_static/css/spc-bootstrap.css @@ -0,0 +1,6288 @@ +/*! + * Bootstrap v2.3.1 + * + * Copyright 2012 Twitter, Inc + * Licensed under the Apache License v2.0 + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Designed and built with all the love in the world @twitter by @mdo and @fat. + */ +.clearfix { + *zoom: 1; +} +.clearfix:before, +.clearfix:after { + display: table; + content: ""; + line-height: 0; +} +.clearfix:after { + clear: both; +} +.hide-text { + font: 0/0 a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0; +} +.input-block-level { + display: block; + width: 100%; + min-height: 31px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +nav, +section { + display: block; +} +audio, +canvas, +video { + display: inline-block; + *display: inline; + *zoom: 1; +} +audio:not([controls]) { + display: none; +} +html { + font-size: 100%; + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; +} +a:focus { + outline: thin dotted #333; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +a:hover, +a:active { + outline: 0; +} +sub, +sup { + position: relative; + font-size: 75%; + line-height: 0; + vertical-align: baseline; +} +sup { + top: -0.5em; +} +sub { + bottom: -0.25em; +} +img { + /* Responsive images (ensure images don't scale beyond their parents) */ + max-width: 100%; + /* Part 1: Set a maxium relative to the parent */ + width: auto\9; + /* IE7-8 need help adjusting responsive images */ + height: auto; + /* Part 2: Scale the height according to the width, otherwise you get stretching */ + vertical-align: middle; + border: 0; + -ms-interpolation-mode: bicubic; +} +#map_canvas img, +.google-maps img { + max-width: none; +} +button, +input, +select, +textarea { + margin: 0; + font-size: 100%; + vertical-align: middle; +} +button, +input { + *overflow: visible; + line-height: normal; +} +button::-moz-focus-inner, +input::-moz-focus-inner { + padding: 0; + border: 0; +} +button, +html input[type="button"], +input[type="reset"], +input[type="submit"] { + -webkit-appearance: button; + cursor: pointer; +} +label, +select, +button, +input[type="button"], +input[type="reset"], +input[type="submit"], +input[type="radio"], +input[type="checkbox"] { + cursor: pointer; +} +input[type="search"] { + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; + -webkit-appearance: textfield; +} +input[type="search"]::-webkit-search-decoration, +input[type="search"]::-webkit-search-cancel-button { + -webkit-appearance: none; +} +textarea { + overflow: auto; + vertical-align: top; +} +@media print { + * { + text-shadow: none !important; + color: #000 !important; + background: transparent !important; + box-shadow: none !important; + } + a, + a:visited { + text-decoration: underline; + } + a[href]:after { + content: " (" attr(href) ")"; + } + abbr[title]:after { + content: " (" attr(title) ")"; + } + .ir a:after, + a[href^="javascript:"]:after, + a[href^="#"]:after { + content: ""; + } + pre, + blockquote { + border: 1px solid #999; + page-break-inside: avoid; + } + thead { + display: table-header-group; + } + tr, + img { + page-break-inside: avoid; + } + img { + max-width: 100% !important; + } + @page { + margin: 0.5cm; + } + p, + h2, + h3 { + orphans: 3; + widows: 3; + } + h2, + h3 { + page-break-after: avoid; + } +} +body { + margin: 0; + font-family: 'Source Sans Pro', sans-serif; + font-size: 16px; + line-height: 21px; + color: #333333; + background-color: #ffffff; +} +a { + color: #0088cc; + text-decoration: none; +} +a:hover, +a:focus { + color: #005580; + text-decoration: underline; +} +.img-rounded { + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; +} +.img-polaroid { + padding: 4px; + background-color: #fff; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, 0.2); + -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); + -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); +} +.img-circle { + -webkit-border-radius: 500px; + -moz-border-radius: 500px; + border-radius: 500px; +} +.row { + margin-left: -20px; + *zoom: 1; +} +.row:before, +.row:after { + display: table; + content: ""; + line-height: 0; +} +.row:after { + clear: both; +} +[class*="span"] { + float: left; + min-height: 1px; + margin-left: 20px; +} +.container, +.navbar-static-top .container, +.navbar-fixed-top .container, +.navbar-fixed-bottom .container { + width: 940px; +} +.span12 { + width: 940px; +} +.span11 { + width: 860px; +} +.span10 { + width: 780px; +} +.span9 { + width: 700px; +} +.span8 { + width: 620px; +} +.span7 { + width: 540px; +} +.span6 { + width: 460px; +} +.span5 { + width: 380px; +} +.span4 { + width: 300px; +} +.span3 { + width: 220px; +} +.span2 { + width: 140px; +} +.span1 { + width: 60px; +} +.offset12 { + margin-left: 980px; +} +.offset11 { + margin-left: 900px; +} +.offset10 { + margin-left: 820px; +} +.offset9 { + margin-left: 740px; +} +.offset8 { + margin-left: 660px; +} +.offset7 { + margin-left: 580px; +} +.offset6 { + margin-left: 500px; +} +.offset5 { + margin-left: 420px; +} +.offset4 { + margin-left: 340px; +} +.offset3 { + margin-left: 260px; +} +.offset2 { + margin-left: 180px; +} +.offset1 { + margin-left: 100px; +} +.row-fluid { + width: 100%; + *zoom: 1; +} +.row-fluid:before, +.row-fluid:after { + display: table; + content: ""; + line-height: 0; +} +.row-fluid:after { + clear: both; +} +.row-fluid [class*="span"] { + display: block; + width: 100%; + min-height: 31px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + float: left; + margin-left: 2.12765957%; + *margin-left: 2.07446809%; +} +.row-fluid [class*="span"]:first-child { + margin-left: 0; +} +.row-fluid .controls-row [class*="span"] + [class*="span"] { + margin-left: 2.12765957%; +} +.row-fluid .span12 { + width: 100%; + *width: 99.94680851%; +} +.row-fluid .span11 { + width: 91.4893617%; + *width: 91.43617021%; +} +.row-fluid .span10 { + width: 82.9787234%; + *width: 82.92553191%; +} +.row-fluid .span9 { + width: 74.46808511%; + *width: 74.41489362%; +} +.row-fluid .span8 { + width: 65.95744681%; + *width: 65.90425532%; +} +.row-fluid .span7 { + width: 57.44680851%; + *width: 57.39361702%; +} +.row-fluid .span6 { + width: 48.93617021%; + *width: 48.88297872%; +} +.row-fluid .span5 { + width: 40.42553191%; + *width: 40.37234043%; +} +.row-fluid .span4 { + width: 31.91489362%; + *width: 31.86170213%; +} +.row-fluid .span3 { + width: 23.40425532%; + *width: 23.35106383%; +} +.row-fluid .span2 { + width: 14.89361702%; + *width: 14.84042553%; +} +.row-fluid .span1 { + width: 6.38297872%; + *width: 6.32978723%; +} +.row-fluid .offset12 { + margin-left: 104.25531915%; + *margin-left: 104.14893617%; +} +.row-fluid .offset12:first-child { + margin-left: 102.12765957%; + *margin-left: 102.0212766%; +} +.row-fluid .offset11 { + margin-left: 95.74468085%; + *margin-left: 95.63829787%; +} +.row-fluid .offset11:first-child { + margin-left: 93.61702128%; + *margin-left: 93.5106383%; +} +.row-fluid .offset10 { + margin-left: 87.23404255%; + *margin-left: 87.12765957%; +} +.row-fluid .offset10:first-child { + margin-left: 85.10638298%; + *margin-left: 85%; +} +.row-fluid .offset9 { + margin-left: 78.72340426%; + *margin-left: 78.61702128%; +} +.row-fluid .offset9:first-child { + margin-left: 76.59574468%; + *margin-left: 76.4893617%; +} +.row-fluid .offset8 { + margin-left: 70.21276596%; + *margin-left: 70.10638298%; +} +.row-fluid .offset8:first-child { + margin-left: 68.08510638%; + *margin-left: 67.9787234%; +} +.row-fluid .offset7 { + margin-left: 61.70212766%; + *margin-left: 61.59574468%; +} +.row-fluid .offset7:first-child { + margin-left: 59.57446809%; + *margin-left: 59.46808511%; +} +.row-fluid .offset6 { + margin-left: 53.19148936%; + *margin-left: 53.08510638%; +} +.row-fluid .offset6:first-child { + margin-left: 51.06382979%; + *margin-left: 50.95744681%; +} +.row-fluid .offset5 { + margin-left: 44.68085106%; + *margin-left: 44.57446809%; +} +.row-fluid .offset5:first-child { + margin-left: 42.55319149%; + *margin-left: 42.44680851%; +} +.row-fluid .offset4 { + margin-left: 36.17021277%; + *margin-left: 36.06382979%; +} +.row-fluid .offset4:first-child { + margin-left: 34.04255319%; + *margin-left: 33.93617021%; +} +.row-fluid .offset3 { + margin-left: 27.65957447%; + *margin-left: 27.55319149%; +} +.row-fluid .offset3:first-child { + margin-left: 25.53191489%; + *margin-left: 25.42553191%; +} +.row-fluid .offset2 { + margin-left: 19.14893617%; + *margin-left: 19.04255319%; +} +.row-fluid .offset2:first-child { + margin-left: 17.0212766%; + *margin-left: 16.91489362%; +} +.row-fluid .offset1 { + margin-left: 10.63829787%; + *margin-left: 10.53191489%; +} +.row-fluid .offset1:first-child { + margin-left: 8.5106383%; + *margin-left: 8.40425532%; +} +[class*="span"].hide, +.row-fluid [class*="span"].hide { + display: none; +} +[class*="span"].pull-right, +.row-fluid [class*="span"].pull-right { + float: right; +} +.container { + margin-right: auto; + margin-left: auto; + *zoom: 1; +} +.container:before, +.container:after { + display: table; + content: ""; + line-height: 0; +} +.container:after { + clear: both; +} +.container-fluid { + padding-right: 20px; + padding-left: 20px; + *zoom: 1; +} +.container-fluid:before, +.container-fluid:after { + display: table; + content: ""; + line-height: 0; +} +.container-fluid:after { + clear: both; +} +p { + margin: 0 0 10.5px; +} +.lead { + margin-bottom: 21px; + font-size: 24px; + font-weight: 200; + line-height: 31.5px; +} +small { + font-size: 85%; +} +strong { + font-weight: bold; +} +em { + font-style: italic; +} +cite { + font-style: normal; +} +.muted { + color: #999999; +} +a.muted:hover, +a.muted:focus { + color: #808080; +} +.text-warning { + color: #c09853; +} +a.text-warning:hover, +a.text-warning:focus { + color: #a47e3c; +} +.text-error { + color: #b94a48; +} +a.text-error:hover, +a.text-error:focus { + color: #953b39; +} +.text-info { + color: #3a87ad; +} +a.text-info:hover, +a.text-info:focus { + color: #2d6987; +} +.text-success { + color: #468847; +} +a.text-success:hover, +a.text-success:focus { + color: #356635; +} +.text-left { + text-align: left; +} +.text-right { + text-align: right; +} +.text-center { + text-align: center; +} +h1, +h2, +h3, +h4, +h5, +h6 { + margin: 10.5px 0; + font-family: inherit; + font-weight: bold; + line-height: 21px; + color: inherit; + text-rendering: optimizelegibility; +} +h1 small, +h2 small, +h3 small, +h4 small, +h5 small, +h6 small { + font-weight: normal; + line-height: 1; + color: #999999; +} +h1, +h2, +h3 { + line-height: 42px; +} +h1 { + font-size: 44px; +} +h2 { + font-size: 36px; +} +h3 { + font-size: 28px; +} +h4 { + font-size: 20px; +} +h5 { + font-size: 16px; +} +h6 { + font-size: 13.6px; +} +h1 small { + font-size: 28px; +} +h2 small { + font-size: 20px; +} +h3 small { + font-size: 16px; +} +h4 small { + font-size: 16px; +} +.page-header { + padding-bottom: 9.5px; + margin: 21px 0 31.5px; + border-bottom: 1px solid #eeeeee; +} +ul, +ol { + padding: 0; + margin: 0 0 10.5px 25px; +} +ul ul, +ul ol, +ol ol, +ol ul { + margin-bottom: 0; +} +li { + line-height: 21px; +} +ul.unstyled, +ol.unstyled { + margin-left: 0; + list-style: none; +} +ul.inline, +ol.inline { + margin-left: 0; + list-style: none; +} +ul.inline > li, +ol.inline > li { + display: inline-block; + *display: inline; + /* IE7 inline-block hack */ + *zoom: 1; + padding-left: 5px; + padding-right: 5px; +} +dl { + margin-bottom: 21px; +} +dt, +dd { + line-height: 21px; +} +dt { + font-weight: bold; +} +dd { + margin-left: 10.5px; +} +.dl-horizontal { + *zoom: 1; +} +.dl-horizontal:before, +.dl-horizontal:after { + display: table; + content: ""; + line-height: 0; +} +.dl-horizontal:after { + clear: both; +} +.dl-horizontal dt { + float: left; + width: 160px; + clear: left; + text-align: right; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.dl-horizontal dd { + margin-left: 180px; +} +hr { + margin: 21px 0; + border: 0; + border-top: 1px solid #eeeeee; + border-bottom: 1px solid #ffffff; +} +abbr[title], +abbr[data-original-title] { + cursor: help; + border-bottom: 1px dotted #999999; +} +abbr.initialism { + font-size: 90%; + text-transform: uppercase; +} +blockquote { + padding: 0 0 0 15px; + margin: 0 0 21px; + border-left: 5px solid #eeeeee; +} +blockquote p { + margin-bottom: 0; + font-size: 20px; + font-weight: 300; + line-height: 1.25; +} +blockquote small { + display: block; + line-height: 21px; + color: #999999; +} +blockquote small:before { + content: '\2014 \00A0'; +} +blockquote.pull-right { + float: right; + padding-right: 15px; + padding-left: 0; + border-right: 5px solid #eeeeee; + border-left: 0; +} +blockquote.pull-right p, +blockquote.pull-right small { + text-align: right; +} +blockquote.pull-right small:before { + content: ''; +} +blockquote.pull-right small:after { + content: '\00A0 \2014'; +} +q:before, +q:after, +blockquote:before, +blockquote:after { + content: ""; +} +address { + display: block; + margin-bottom: 21px; + font-style: normal; + line-height: 21px; +} +code, +pre { + padding: 0 3px 2px; + font-family: 'Source Code Pro', monospace; + font-size: 14px; + color: #333333; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} +code { + padding: 2px 4px; + color: #d14; + background-color: #f7f7f9; + border: 1px solid #e1e1e8; + white-space: nowrap; +} +pre { + display: block; + padding: 10px; + margin: 0 0 10.5px; + font-size: 15px; + line-height: 21px; + word-break: break-all; + word-wrap: break-word; + white-space: pre; + white-space: pre-wrap; + background-color: #f5f5f5; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, 0.15); + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} +pre.prettyprint { + margin-bottom: 21px; +} +pre code { + padding: 0; + color: inherit; + white-space: pre; + white-space: pre-wrap; + background-color: transparent; + border: 0; +} +.pre-scrollable { + max-height: 340px; + overflow-y: scroll; +} +form { + margin: 0 0 21px; +} +fieldset { + padding: 0; + margin: 0; + border: 0; +} +legend { + display: block; + width: 100%; + padding: 0; + margin-bottom: 21px; + font-size: 24px; + line-height: 42px; + color: #333333; + border: 0; + border-bottom: 1px solid #e5e5e5; +} +legend small { + font-size: 15.75px; + color: #999999; +} +label, +input, +button, +select, +textarea { + font-size: 16px; + font-weight: normal; + line-height: 21px; +} +input, +button, +select, +textarea { + font-family: 'Source Sans Pro', sans-serif; +} +label { + display: block; + margin-bottom: 5px; +} +select, +textarea, +input[type="text"], +input[type="password"], +input[type="datetime"], +input[type="datetime-local"], +input[type="date"], +input[type="month"], +input[type="time"], +input[type="week"], +input[type="number"], +input[type="email"], +input[type="url"], +input[type="search"], +input[type="tel"], +input[type="color"], +.uneditable-input { + display: inline-block; + height: 21px; + padding: 4px 6px; + margin-bottom: 10.5px; + font-size: 16px; + line-height: 21px; + color: #555555; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + vertical-align: middle; +} +input, +textarea, +.uneditable-input { + width: 206px; +} +textarea { + height: auto; +} +textarea, +input[type="text"], +input[type="password"], +input[type="datetime"], +input[type="datetime-local"], +input[type="date"], +input[type="month"], +input[type="time"], +input[type="week"], +input[type="number"], +input[type="email"], +input[type="url"], +input[type="search"], +input[type="tel"], +input[type="color"], +.uneditable-input { + background-color: #ffffff; + border: 1px solid #cccccc; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -webkit-transition: border linear .2s, box-shadow linear .2s; + -moz-transition: border linear .2s, box-shadow linear .2s; + -o-transition: border linear .2s, box-shadow linear .2s; + transition: border linear .2s, box-shadow linear .2s; +} +textarea:focus, +input[type="text"]:focus, +input[type="password"]:focus, +input[type="datetime"]:focus, +input[type="datetime-local"]:focus, +input[type="date"]:focus, +input[type="month"]:focus, +input[type="time"]:focus, +input[type="week"]:focus, +input[type="number"]:focus, +input[type="email"]:focus, +input[type="url"]:focus, +input[type="search"]:focus, +input[type="tel"]:focus, +input[type="color"]:focus, +.uneditable-input:focus { + border-color: rgba(82, 168, 236, 0.8); + outline: 0; + outline: thin dotted \9; + /* IE6-9 */ + -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(82,168,236,.6); + -moz-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(82,168,236,.6); + box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(82,168,236,.6); +} +input[type="radio"], +input[type="checkbox"] { + margin: 4px 0 0; + *margin-top: 0; + /* IE7 */ + margin-top: 1px \9; + /* IE8-9 */ + line-height: normal; +} +input[type="file"], +input[type="image"], +input[type="submit"], +input[type="reset"], +input[type="button"], +input[type="radio"], +input[type="checkbox"] { + width: auto; +} +select, +input[type="file"] { + height: 31px; + /* In IE7, the height of the select element cannot be changed by height, only font-size */ + *margin-top: 4px; + /* For IE7, add top margin to align select with labels */ + line-height: 31px; +} +select { + width: 220px; + border: 1px solid #cccccc; + background-color: #ffffff; +} +select[multiple], +select[size] { + height: auto; +} +select:focus, +input[type="file"]:focus, +input[type="radio"]:focus, +input[type="checkbox"]:focus { + outline: thin dotted #333; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +.uneditable-input, +.uneditable-textarea { + color: #999999; + background-color: #fcfcfc; + border-color: #cccccc; + -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025); + -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025); + cursor: not-allowed; +} +.uneditable-input { + overflow: hidden; + white-space: nowrap; +} +.uneditable-textarea { + width: auto; + height: auto; +} +input:-moz-placeholder, +textarea:-moz-placeholder { + color: #999999; +} +input:-ms-input-placeholder, +textarea:-ms-input-placeholder { + color: #999999; +} +input::-webkit-input-placeholder, +textarea::-webkit-input-placeholder { + color: #999999; +} +.radio, +.checkbox { + min-height: 21px; + padding-left: 20px; +} +.radio input[type="radio"], +.checkbox input[type="checkbox"] { + float: left; + margin-left: -20px; +} +.controls > .radio:first-child, +.controls > .checkbox:first-child { + padding-top: 5px; +} +.radio.inline, +.checkbox.inline { + display: inline-block; + padding-top: 5px; + margin-bottom: 0; + vertical-align: middle; +} +.radio.inline + .radio.inline, +.checkbox.inline + .checkbox.inline { + margin-left: 10px; +} +.input-mini { + width: 60px; +} +.input-small { + width: 90px; +} +.input-medium { + width: 150px; +} +.input-large { + width: 210px; +} +.input-xlarge { + width: 270px; +} +.input-xxlarge { + width: 530px; +} +input[class*="span"], +select[class*="span"], +textarea[class*="span"], +.uneditable-input[class*="span"], +.row-fluid input[class*="span"], +.row-fluid select[class*="span"], +.row-fluid textarea[class*="span"], +.row-fluid .uneditable-input[class*="span"] { + float: none; + margin-left: 0; +} +.input-append input[class*="span"], +.input-append .uneditable-input[class*="span"], +.input-prepend input[class*="span"], +.input-prepend .uneditable-input[class*="span"], +.row-fluid input[class*="span"], +.row-fluid select[class*="span"], +.row-fluid textarea[class*="span"], +.row-fluid .uneditable-input[class*="span"], +.row-fluid .input-prepend [class*="span"], +.row-fluid .input-append [class*="span"] { + display: inline-block; +} +input, +textarea, +.uneditable-input { + margin-left: 0; +} +.controls-row [class*="span"] + [class*="span"] { + margin-left: 20px; +} +input.span12, +textarea.span12, +.uneditable-input.span12 { + width: 926px; +} +input.span11, +textarea.span11, +.uneditable-input.span11 { + width: 846px; +} +input.span10, +textarea.span10, +.uneditable-input.span10 { + width: 766px; +} +input.span9, +textarea.span9, +.uneditable-input.span9 { + width: 686px; +} +input.span8, +textarea.span8, +.uneditable-input.span8 { + width: 606px; +} +input.span7, +textarea.span7, +.uneditable-input.span7 { + width: 526px; +} +input.span6, +textarea.span6, +.uneditable-input.span6 { + width: 446px; +} +input.span5, +textarea.span5, +.uneditable-input.span5 { + width: 366px; +} +input.span4, +textarea.span4, +.uneditable-input.span4 { + width: 286px; +} +input.span3, +textarea.span3, +.uneditable-input.span3 { + width: 206px; +} +input.span2, +textarea.span2, +.uneditable-input.span2 { + width: 126px; +} +input.span1, +textarea.span1, +.uneditable-input.span1 { + width: 46px; +} +.controls-row { + *zoom: 1; +} +.controls-row:before, +.controls-row:after { + display: table; + content: ""; + line-height: 0; +} +.controls-row:after { + clear: both; +} +.controls-row [class*="span"], +.row-fluid .controls-row [class*="span"] { + float: left; +} +.controls-row .checkbox[class*="span"], +.controls-row .radio[class*="span"] { + padding-top: 5px; +} +input[disabled], +select[disabled], +textarea[disabled], +input[readonly], +select[readonly], +textarea[readonly] { + cursor: not-allowed; + background-color: #eeeeee; +} +input[type="radio"][disabled], +input[type="checkbox"][disabled], +input[type="radio"][readonly], +input[type="checkbox"][readonly] { + background-color: transparent; +} +.control-group.warning .control-label, +.control-group.warning .help-block, +.control-group.warning .help-inline { + color: #c09853; +} +.control-group.warning .checkbox, +.control-group.warning .radio, +.control-group.warning input, +.control-group.warning select, +.control-group.warning textarea { + color: #c09853; +} +.control-group.warning input, +.control-group.warning select, +.control-group.warning textarea { + border-color: #c09853; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} +.control-group.warning input:focus, +.control-group.warning select:focus, +.control-group.warning textarea:focus { + border-color: #a47e3c; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e; + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e; +} +.control-group.warning .input-prepend .add-on, +.control-group.warning .input-append .add-on { + color: #c09853; + background-color: #fcf8e3; + border-color: #c09853; +} +.control-group.error .control-label, +.control-group.error .help-block, +.control-group.error .help-inline { + color: #b94a48; +} +.control-group.error .checkbox, +.control-group.error .radio, +.control-group.error input, +.control-group.error select, +.control-group.error textarea { + color: #b94a48; +} +.control-group.error input, +.control-group.error select, +.control-group.error textarea { + border-color: #b94a48; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} +.control-group.error input:focus, +.control-group.error select:focus, +.control-group.error textarea:focus { + border-color: #953b39; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392; + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392; +} +.control-group.error .input-prepend .add-on, +.control-group.error .input-append .add-on { + color: #b94a48; + background-color: #f2dede; + border-color: #b94a48; +} +.control-group.success .control-label, +.control-group.success .help-block, +.control-group.success .help-inline { + color: #468847; +} +.control-group.success .checkbox, +.control-group.success .radio, +.control-group.success input, +.control-group.success select, +.control-group.success textarea { + color: #468847; +} +.control-group.success input, +.control-group.success select, +.control-group.success textarea { + border-color: #468847; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} +.control-group.success input:focus, +.control-group.success select:focus, +.control-group.success textarea:focus { + border-color: #356635; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b; + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b; +} +.control-group.success .input-prepend .add-on, +.control-group.success .input-append .add-on { + color: #468847; + background-color: #dff0d8; + border-color: #468847; +} +.control-group.info .control-label, +.control-group.info .help-block, +.control-group.info .help-inline { + color: #3a87ad; +} +.control-group.info .checkbox, +.control-group.info .radio, +.control-group.info input, +.control-group.info select, +.control-group.info textarea { + color: #3a87ad; +} +.control-group.info input, +.control-group.info select, +.control-group.info textarea { + border-color: #3a87ad; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} +.control-group.info input:focus, +.control-group.info select:focus, +.control-group.info textarea:focus { + border-color: #2d6987; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3; + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7ab5d3; +} +.control-group.info .input-prepend .add-on, +.control-group.info .input-append .add-on { + color: #3a87ad; + background-color: #d9edf7; + border-color: #3a87ad; +} +input:focus:invalid, +textarea:focus:invalid, +select:focus:invalid { + color: #b94a48; + border-color: #ee5f5b; +} +input:focus:invalid:focus, +textarea:focus:invalid:focus, +select:focus:invalid:focus { + border-color: #e9322d; + -webkit-box-shadow: 0 0 6px #f8b9b7; + -moz-box-shadow: 0 0 6px #f8b9b7; + box-shadow: 0 0 6px #f8b9b7; +} +.form-actions { + padding: 20px 20px 21px; + margin-top: 21px; + margin-bottom: 21px; + background-color: #f5f5f5; + border-top: 1px solid #e5e5e5; + *zoom: 1; +} +.form-actions:before, +.form-actions:after { + display: table; + content: ""; + line-height: 0; +} +.form-actions:after { + clear: both; +} +.help-block, +.help-inline { + color: #595959; +} +.help-block { + display: block; + margin-bottom: 10.5px; +} +.help-inline { + display: inline-block; + *display: inline; + /* IE7 inline-block hack */ + *zoom: 1; + vertical-align: middle; + padding-left: 5px; +} +.input-append, +.input-prepend { + display: inline-block; + margin-bottom: 10.5px; + vertical-align: middle; + font-size: 0; + white-space: nowrap; +} +.input-append input, +.input-prepend input, +.input-append select, +.input-prepend select, +.input-append .uneditable-input, +.input-prepend .uneditable-input, +.input-append .dropdown-menu, +.input-prepend .dropdown-menu, +.input-append .popover, +.input-prepend .popover { + font-size: 16px; +} +.input-append input, +.input-prepend input, +.input-append select, +.input-prepend select, +.input-append .uneditable-input, +.input-prepend .uneditable-input { + position: relative; + margin-bottom: 0; + *margin-left: 0; + vertical-align: top; + -webkit-border-radius: 0 4px 4px 0; + -moz-border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; +} +.input-append input:focus, +.input-prepend input:focus, +.input-append select:focus, +.input-prepend select:focus, +.input-append .uneditable-input:focus, +.input-prepend .uneditable-input:focus { + z-index: 2; +} +.input-append .add-on, +.input-prepend .add-on { + display: inline-block; + width: auto; + height: 21px; + min-width: 16px; + padding: 4px 5px; + font-size: 16px; + font-weight: normal; + line-height: 21px; + text-align: center; + text-shadow: 0 1px 0 #ffffff; + background-color: #eeeeee; + border: 1px solid #ccc; +} +.input-append .add-on, +.input-prepend .add-on, +.input-append .btn, +.input-prepend .btn, +.input-append .btn-group > .dropdown-toggle, +.input-prepend .btn-group > .dropdown-toggle { + vertical-align: top; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} +.input-append .active, +.input-prepend .active { + background-color: #a9dba9; + border-color: #46a546; +} +.input-prepend .add-on, +.input-prepend .btn { + margin-right: -1px; +} +.input-prepend .add-on:first-child, +.input-prepend .btn:first-child { + -webkit-border-radius: 4px 0 0 4px; + -moz-border-radius: 4px 0 0 4px; + border-radius: 4px 0 0 4px; +} +.input-append input, +.input-append select, +.input-append .uneditable-input { + -webkit-border-radius: 4px 0 0 4px; + -moz-border-radius: 4px 0 0 4px; + border-radius: 4px 0 0 4px; +} +.input-append input + .btn-group .btn:last-child, +.input-append select + .btn-group .btn:last-child, +.input-append .uneditable-input + .btn-group .btn:last-child { + -webkit-border-radius: 0 4px 4px 0; + -moz-border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; +} +.input-append .add-on, +.input-append .btn, +.input-append .btn-group { + margin-left: -1px; +} +.input-append .add-on:last-child, +.input-append .btn:last-child, +.input-append .btn-group:last-child > .dropdown-toggle { + -webkit-border-radius: 0 4px 4px 0; + -moz-border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; +} +.input-prepend.input-append input, +.input-prepend.input-append select, +.input-prepend.input-append .uneditable-input { + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} +.input-prepend.input-append input + .btn-group .btn, +.input-prepend.input-append select + .btn-group .btn, +.input-prepend.input-append .uneditable-input + .btn-group .btn { + -webkit-border-radius: 0 4px 4px 0; + -moz-border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; +} +.input-prepend.input-append .add-on:first-child, +.input-prepend.input-append .btn:first-child { + margin-right: -1px; + -webkit-border-radius: 4px 0 0 4px; + -moz-border-radius: 4px 0 0 4px; + border-radius: 4px 0 0 4px; +} +.input-prepend.input-append .add-on:last-child, +.input-prepend.input-append .btn:last-child { + margin-left: -1px; + -webkit-border-radius: 0 4px 4px 0; + -moz-border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; +} +.input-prepend.input-append .btn-group:first-child { + margin-left: 0; +} +input.search-query { + padding-right: 14px; + padding-right: 4px \9; + padding-left: 14px; + padding-left: 4px \9; + /* IE7-8 doesn't have border-radius, so don't indent the padding */ + margin-bottom: 0; + -webkit-border-radius: 15px; + -moz-border-radius: 15px; + border-radius: 15px; +} +/* Allow for input prepend/append in search forms */ +.form-search .input-append .search-query, +.form-search .input-prepend .search-query { + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} +.form-search .input-append .search-query { + -webkit-border-radius: 14px 0 0 14px; + -moz-border-radius: 14px 0 0 14px; + border-radius: 14px 0 0 14px; +} +.form-search .input-append .btn { + -webkit-border-radius: 0 14px 14px 0; + -moz-border-radius: 0 14px 14px 0; + border-radius: 0 14px 14px 0; +} +.form-search .input-prepend .search-query { + -webkit-border-radius: 0 14px 14px 0; + -moz-border-radius: 0 14px 14px 0; + border-radius: 0 14px 14px 0; +} +.form-search .input-prepend .btn { + -webkit-border-radius: 14px 0 0 14px; + -moz-border-radius: 14px 0 0 14px; + border-radius: 14px 0 0 14px; +} +.form-search input, +.form-inline input, +.form-horizontal input, +.form-search textarea, +.form-inline textarea, +.form-horizontal textarea, +.form-search select, +.form-inline select, +.form-horizontal select, +.form-search .help-inline, +.form-inline .help-inline, +.form-horizontal .help-inline, +.form-search .uneditable-input, +.form-inline .uneditable-input, +.form-horizontal .uneditable-input, +.form-search .input-prepend, +.form-inline .input-prepend, +.form-horizontal .input-prepend, +.form-search .input-append, +.form-inline .input-append, +.form-horizontal .input-append { + display: inline-block; + *display: inline; + /* IE7 inline-block hack */ + *zoom: 1; + margin-bottom: 0; + vertical-align: middle; +} +.form-search .hide, +.form-inline .hide, +.form-horizontal .hide { + display: none; +} +.form-search label, +.form-inline label, +.form-search .btn-group, +.form-inline .btn-group { + display: inline-block; +} +.form-search .input-append, +.form-inline .input-append, +.form-search .input-prepend, +.form-inline .input-prepend { + margin-bottom: 0; +} +.form-search .radio, +.form-search .checkbox, +.form-inline .radio, +.form-inline .checkbox { + padding-left: 0; + margin-bottom: 0; + vertical-align: middle; +} +.form-search .radio input[type="radio"], +.form-search .checkbox input[type="checkbox"], +.form-inline .radio input[type="radio"], +.form-inline .checkbox input[type="checkbox"] { + float: left; + margin-right: 3px; + margin-left: 0; +} +.control-group { + margin-bottom: 10.5px; +} +legend + .control-group { + margin-top: 21px; + -webkit-margin-top-collapse: separate; +} +.form-horizontal .control-group { + margin-bottom: 21px; + *zoom: 1; +} +.form-horizontal .control-group:before, +.form-horizontal .control-group:after { + display: table; + content: ""; + line-height: 0; +} +.form-horizontal .control-group:after { + clear: both; +} +.form-horizontal .control-label { + float: left; + width: 160px; + padding-top: 5px; + text-align: right; +} +.form-horizontal .controls { + *display: inline-block; + *padding-left: 20px; + margin-left: 180px; + *margin-left: 0; +} +.form-horizontal .controls:first-child { + *padding-left: 180px; +} +.form-horizontal .help-block { + margin-bottom: 0; +} +.form-horizontal input + .help-block, +.form-horizontal select + .help-block, +.form-horizontal textarea + .help-block, +.form-horizontal .uneditable-input + .help-block, +.form-horizontal .input-prepend + .help-block, +.form-horizontal .input-append + .help-block { + margin-top: 10.5px; +} +.form-horizontal .form-actions { + padding-left: 180px; +} +table { + max-width: 100%; + background-color: transparent; + border-collapse: collapse; + border-spacing: 0; +} +.table { + width: 100%; + margin-bottom: 21px; +} +.table th, +.table td { + padding: 8px; + line-height: 21px; + text-align: left; + vertical-align: top; + border-top: 1px solid #dddddd; +} +.table th { + font-weight: bold; +} +.table thead th { + vertical-align: bottom; +} +.table caption + thead tr:first-child th, +.table caption + thead tr:first-child td, +.table colgroup + thead tr:first-child th, +.table colgroup + thead tr:first-child td, +.table thead:first-child tr:first-child th, +.table thead:first-child tr:first-child td { + border-top: 0; +} +.table tbody + tbody { + border-top: 2px solid #dddddd; +} +.table .table { + background-color: #ffffff; +} +.table-condensed th, +.table-condensed td { + padding: 4px 5px; +} +.table-bordered { + border: 1px solid #dddddd; + border-collapse: separate; + *border-collapse: collapse; + border-left: 0; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} +.table-bordered th, +.table-bordered td { + border-left: 1px solid #dddddd; +} +.table-bordered caption + thead tr:first-child th, +.table-bordered caption + tbody tr:first-child th, +.table-bordered caption + tbody tr:first-child td, +.table-bordered colgroup + thead tr:first-child th, +.table-bordered colgroup + tbody tr:first-child th, +.table-bordered colgroup + tbody tr:first-child td, +.table-bordered thead:first-child tr:first-child th, +.table-bordered tbody:first-child tr:first-child th, +.table-bordered tbody:first-child tr:first-child td { + border-top: 0; +} +.table-bordered thead:first-child tr:first-child > th:first-child, +.table-bordered tbody:first-child tr:first-child > td:first-child, +.table-bordered tbody:first-child tr:first-child > th:first-child { + -webkit-border-top-left-radius: 4px; + -moz-border-radius-topleft: 4px; + border-top-left-radius: 4px; +} +.table-bordered thead:first-child tr:first-child > th:last-child, +.table-bordered tbody:first-child tr:first-child > td:last-child, +.table-bordered tbody:first-child tr:first-child > th:last-child { + -webkit-border-top-right-radius: 4px; + -moz-border-radius-topright: 4px; + border-top-right-radius: 4px; +} +.table-bordered thead:last-child tr:last-child > th:first-child, +.table-bordered tbody:last-child tr:last-child > td:first-child, +.table-bordered tbody:last-child tr:last-child > th:first-child, +.table-bordered tfoot:last-child tr:last-child > td:first-child, +.table-bordered tfoot:last-child tr:last-child > th:first-child { + -webkit-border-bottom-left-radius: 4px; + -moz-border-radius-bottomleft: 4px; + border-bottom-left-radius: 4px; +} +.table-bordered thead:last-child tr:last-child > th:last-child, +.table-bordered tbody:last-child tr:last-child > td:last-child, +.table-bordered tbody:last-child tr:last-child > th:last-child, +.table-bordered tfoot:last-child tr:last-child > td:last-child, +.table-bordered tfoot:last-child tr:last-child > th:last-child { + -webkit-border-bottom-right-radius: 4px; + -moz-border-radius-bottomright: 4px; + border-bottom-right-radius: 4px; +} +.table-bordered tfoot + tbody:last-child tr:last-child td:first-child { + -webkit-border-bottom-left-radius: 0; + -moz-border-radius-bottomleft: 0; + border-bottom-left-radius: 0; +} +.table-bordered tfoot + tbody:last-child tr:last-child td:last-child { + -webkit-border-bottom-right-radius: 0; + -moz-border-radius-bottomright: 0; + border-bottom-right-radius: 0; +} +.table-bordered caption + thead tr:first-child th:first-child, +.table-bordered caption + tbody tr:first-child td:first-child, +.table-bordered colgroup + thead tr:first-child th:first-child, +.table-bordered colgroup + tbody tr:first-child td:first-child { + -webkit-border-top-left-radius: 4px; + -moz-border-radius-topleft: 4px; + border-top-left-radius: 4px; +} +.table-bordered caption + thead tr:first-child th:last-child, +.table-bordered caption + tbody tr:first-child td:last-child, +.table-bordered colgroup + thead tr:first-child th:last-child, +.table-bordered colgroup + tbody tr:first-child td:last-child { + -webkit-border-top-right-radius: 4px; + -moz-border-radius-topright: 4px; + border-top-right-radius: 4px; +} +.table-striped tbody > tr:nth-child(odd) > td, +.table-striped tbody > tr:nth-child(odd) > th { + background-color: #f9f9f9; +} +.table-hover tbody tr:hover > td, +.table-hover tbody tr:hover > th { + background-color: #f5f5f5; +} +table td[class*="span"], +table th[class*="span"], +.row-fluid table td[class*="span"], +.row-fluid table th[class*="span"] { + display: table-cell; + float: none; + margin-left: 0; +} +.table td.span1, +.table th.span1 { + float: none; + width: 44px; + margin-left: 0; +} +.table td.span2, +.table th.span2 { + float: none; + width: 124px; + margin-left: 0; +} +.table td.span3, +.table th.span3 { + float: none; + width: 204px; + margin-left: 0; +} +.table td.span4, +.table th.span4 { + float: none; + width: 284px; + margin-left: 0; +} +.table td.span5, +.table th.span5 { + float: none; + width: 364px; + margin-left: 0; +} +.table td.span6, +.table th.span6 { + float: none; + width: 444px; + margin-left: 0; +} +.table td.span7, +.table th.span7 { + float: none; + width: 524px; + margin-left: 0; +} +.table td.span8, +.table th.span8 { + float: none; + width: 604px; + margin-left: 0; +} +.table td.span9, +.table th.span9 { + float: none; + width: 684px; + margin-left: 0; +} +.table td.span10, +.table th.span10 { + float: none; + width: 764px; + margin-left: 0; +} +.table td.span11, +.table th.span11 { + float: none; + width: 844px; + margin-left: 0; +} +.table td.span12, +.table th.span12 { + float: none; + width: 924px; + margin-left: 0; +} +.table tbody tr.success > td { + background-color: #dff0d8; +} +.table tbody tr.error > td { + background-color: #f2dede; +} +.table tbody tr.warning > td { + background-color: #fcf8e3; +} +.table tbody tr.info > td { + background-color: #d9edf7; +} +.table-hover tbody tr.success:hover > td { + background-color: #d0e9c6; +} +.table-hover tbody tr.error:hover > td { + background-color: #ebcccc; +} +.table-hover tbody tr.warning:hover > td { + background-color: #faf2cc; +} +.table-hover tbody tr.info:hover > td { + background-color: #c4e3f3; +} +[class^="icon-"], +[class*=" icon-"] { + display: inline-block; + width: 14px; + height: 14px; + *margin-right: .3em; + line-height: 14px; + vertical-align: text-top; + background-image: url("../../img/glyphicons-halflings.png"); + background-position: 14px 14px; + background-repeat: no-repeat; + margin-top: 1px; +} +/* White icons with optional class, or on hover/focus/active states of certain elements */ +.icon-white, +.nav-pills > .active > a > [class^="icon-"], +.nav-pills > .active > a > [class*=" icon-"], +.nav-list > .active > a > [class^="icon-"], +.nav-list > .active > a > [class*=" icon-"], +.navbar-inverse .nav > .active > a > [class^="icon-"], +.navbar-inverse .nav > .active > a > [class*=" icon-"], +.dropdown-menu > li > a:hover > [class^="icon-"], +.dropdown-menu > li > a:focus > [class^="icon-"], +.dropdown-menu > li > a:hover > [class*=" icon-"], +.dropdown-menu > li > a:focus > [class*=" icon-"], +.dropdown-menu > .active > a > [class^="icon-"], +.dropdown-menu > .active > a > [class*=" icon-"], +.dropdown-submenu:hover > a > [class^="icon-"], +.dropdown-submenu:focus > a > [class^="icon-"], +.dropdown-submenu:hover > a > [class*=" icon-"], +.dropdown-submenu:focus > a > [class*=" icon-"] { + background-image: url("../../img/glyphicons-halflings-white.png"); +} +.icon-glass { + background-position: 0 0; +} +.icon-music { + background-position: -24px 0; +} +.icon-search { + background-position: -48px 0; +} +.icon-envelope { + background-position: -72px 0; +} +.icon-heart { + background-position: -96px 0; +} +.icon-star { + background-position: -120px 0; +} +.icon-star-empty { + background-position: -144px 0; +} +.icon-user { + background-position: -168px 0; +} +.icon-film { + background-position: -192px 0; +} +.icon-th-large { + background-position: -216px 0; +} +.icon-th { + background-position: -240px 0; +} +.icon-th-list { + background-position: -264px 0; +} +.icon-ok { + background-position: -288px 0; +} +.icon-remove { + background-position: -312px 0; +} +.icon-zoom-in { + background-position: -336px 0; +} +.icon-zoom-out { + background-position: -360px 0; +} +.icon-off { + background-position: -384px 0; +} +.icon-signal { + background-position: -408px 0; +} +.icon-cog { + background-position: -432px 0; +} +.icon-trash { + background-position: -456px 0; +} +.icon-home { + background-position: 0 -24px; +} +.icon-file { + background-position: -24px -24px; +} +.icon-time { + background-position: -48px -24px; +} +.icon-road { + background-position: -72px -24px; +} +.icon-download-alt { + background-position: -96px -24px; +} +.icon-download { + background-position: -120px -24px; +} +.icon-upload { + background-position: -144px -24px; +} +.icon-inbox { + background-position: -168px -24px; +} +.icon-play-circle { + background-position: -192px -24px; +} +.icon-repeat { + background-position: -216px -24px; +} +.icon-refresh { + background-position: -240px -24px; +} +.icon-list-alt { + background-position: -264px -24px; +} +.icon-lock { + background-position: -287px -24px; +} +.icon-flag { + background-position: -312px -24px; +} +.icon-headphones { + background-position: -336px -24px; +} +.icon-volume-off { + background-position: -360px -24px; +} +.icon-volume-down { + background-position: -384px -24px; +} +.icon-volume-up { + background-position: -408px -24px; +} +.icon-qrcode { + background-position: -432px -24px; +} +.icon-barcode { + background-position: -456px -24px; +} +.icon-tag { + background-position: 0 -48px; +} +.icon-tags { + background-position: -25px -48px; +} +.icon-book { + background-position: -48px -48px; +} +.icon-bookmark { + background-position: -72px -48px; +} +.icon-print { + background-position: -96px -48px; +} +.icon-camera { + background-position: -120px -48px; +} +.icon-font { + background-position: -144px -48px; +} +.icon-bold { + background-position: -167px -48px; +} +.icon-italic { + background-position: -192px -48px; +} +.icon-text-height { + background-position: -216px -48px; +} +.icon-text-width { + background-position: -240px -48px; +} +.icon-align-left { + background-position: -264px -48px; +} +.icon-align-center { + background-position: -288px -48px; +} +.icon-align-right { + background-position: -312px -48px; +} +.icon-align-justify { + background-position: -336px -48px; +} +.icon-list { + background-position: -360px -48px; +} +.icon-indent-left { + background-position: -384px -48px; +} +.icon-indent-right { + background-position: -408px -48px; +} +.icon-facetime-video { + background-position: -432px -48px; +} +.icon-picture { + background-position: -456px -48px; +} +.icon-pencil { + background-position: 0 -72px; +} +.icon-map-marker { + background-position: -24px -72px; +} +.icon-adjust { + background-position: -48px -72px; +} +.icon-tint { + background-position: -72px -72px; +} +.icon-edit { + background-position: -96px -72px; +} +.icon-share { + background-position: -120px -72px; +} +.icon-check { + background-position: -144px -72px; +} +.icon-move { + background-position: -168px -72px; +} +.icon-step-backward { + background-position: -192px -72px; +} +.icon-fast-backward { + background-position: -216px -72px; +} +.icon-backward { + background-position: -240px -72px; +} +.icon-play { + background-position: -264px -72px; +} +.icon-pause { + background-position: -288px -72px; +} +.icon-stop { + background-position: -312px -72px; +} +.icon-forward { + background-position: -336px -72px; +} +.icon-fast-forward { + background-position: -360px -72px; +} +.icon-step-forward { + background-position: -384px -72px; +} +.icon-eject { + background-position: -408px -72px; +} +.icon-chevron-left { + background-position: -432px -72px; +} +.icon-chevron-right { + background-position: -456px -72px; +} +.icon-plus-sign { + background-position: 0 -96px; +} +.icon-minus-sign { + background-position: -24px -96px; +} +.icon-remove-sign { + background-position: -48px -96px; +} +.icon-ok-sign { + background-position: -72px -96px; +} +.icon-question-sign { + background-position: -96px -96px; +} +.icon-info-sign { + background-position: -120px -96px; +} +.icon-screenshot { + background-position: -144px -96px; +} +.icon-remove-circle { + background-position: -168px -96px; +} +.icon-ok-circle { + background-position: -192px -96px; +} +.icon-ban-circle { + background-position: -216px -96px; +} +.icon-arrow-left { + background-position: -240px -96px; +} +.icon-arrow-right { + background-position: -264px -96px; +} +.icon-arrow-up { + background-position: -289px -96px; +} +.icon-arrow-down { + background-position: -312px -96px; +} +.icon-share-alt { + background-position: -336px -96px; +} +.icon-resize-full { + background-position: -360px -96px; +} +.icon-resize-small { + background-position: -384px -96px; +} +.icon-plus { + background-position: -408px -96px; +} +.icon-minus { + background-position: -433px -96px; +} +.icon-asterisk { + background-position: -456px -96px; +} +.icon-exclamation-sign { + background-position: 0 -120px; +} +.icon-gift { + background-position: -24px -120px; +} +.icon-leaf { + background-position: -48px -120px; +} +.icon-fire { + background-position: -72px -120px; +} +.icon-eye-open { + background-position: -96px -120px; +} +.icon-eye-close { + background-position: -120px -120px; +} +.icon-warning-sign { + background-position: -144px -120px; +} +.icon-plane { + background-position: -168px -120px; +} +.icon-calendar { + background-position: -192px -120px; +} +.icon-random { + background-position: -216px -120px; + width: 16px; +} +.icon-comment { + background-position: -240px -120px; +} +.icon-magnet { + background-position: -264px -120px; +} +.icon-chevron-up { + background-position: -288px -120px; +} +.icon-chevron-down { + background-position: -313px -119px; +} +.icon-retweet { + background-position: -336px -120px; +} +.icon-shopping-cart { + background-position: -360px -120px; +} +.icon-folder-close { + background-position: -384px -120px; + width: 16px; +} +.icon-folder-open { + background-position: -408px -120px; + width: 16px; +} +.icon-resize-vertical { + background-position: -432px -119px; +} +.icon-resize-horizontal { + background-position: -456px -118px; +} +.icon-hdd { + background-position: 0 -144px; +} +.icon-bullhorn { + background-position: -24px -144px; +} +.icon-bell { + background-position: -48px -144px; +} +.icon-certificate { + background-position: -72px -144px; +} +.icon-thumbs-up { + background-position: -96px -144px; +} +.icon-thumbs-down { + background-position: -120px -144px; +} +.icon-hand-right { + background-position: -144px -144px; +} +.icon-hand-left { + background-position: -168px -144px; +} +.icon-hand-up { + background-position: -192px -144px; +} +.icon-hand-down { + background-position: -216px -144px; +} +.icon-circle-arrow-right { + background-position: -240px -144px; +} +.icon-circle-arrow-left { + background-position: -264px -144px; +} +.icon-circle-arrow-up { + background-position: -288px -144px; +} +.icon-circle-arrow-down { + background-position: -312px -144px; +} +.icon-globe { + background-position: -336px -144px; +} +.icon-wrench { + background-position: -360px -144px; +} +.icon-tasks { + background-position: -384px -144px; +} +.icon-filter { + background-position: -408px -144px; +} +.icon-briefcase { + background-position: -432px -144px; +} +.icon-fullscreen { + background-position: -456px -144px; +} +.dropup, +.dropdown { + position: relative; +} +.dropdown-toggle { + *margin-bottom: -3px; +} +.dropdown-toggle:active, +.open .dropdown-toggle { + outline: 0; +} +.caret { + display: inline-block; + width: 0; + height: 0; + vertical-align: top; + border-top: 4px solid #000000; + border-right: 4px solid transparent; + border-left: 4px solid transparent; + content: ""; +} +.dropdown .caret { + margin-top: 8px; + margin-left: 2px; +} +.dropdown-menu { + position: absolute; + top: 100%; + left: 0; + z-index: 1000; + display: none; + float: left; + min-width: 160px; + padding: 5px 0; + margin: 2px 0 0; + list-style: none; + background-color: #ffffff; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, 0.2); + *border-right-width: 2px; + *border-bottom-width: 2px; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; + -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + -webkit-background-clip: padding-box; + -moz-background-clip: padding; + background-clip: padding-box; +} +.dropdown-menu.pull-right { + right: 0; + left: auto; +} +.dropdown-menu .divider { + *width: 100%; + height: 1px; + margin: 9.5px 1px; + *margin: -5px 0 5px; + overflow: hidden; + background-color: #e5e5e5; + border-bottom: 1px solid #ffffff; +} +.dropdown-menu > li > a { + display: block; + padding: 3px 20px; + clear: both; + font-weight: normal; + line-height: 21px; + color: #333333; + white-space: nowrap; +} +.dropdown-menu > li > a:hover, +.dropdown-menu > li > a:focus, +.dropdown-submenu:hover > a, +.dropdown-submenu:focus > a { + text-decoration: none; + color: #ffffff; + background-color: #0081c2; + background-image: -moz-linear-gradient(top, #0088cc, #0077b3); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0077b3)); + background-image: -webkit-linear-gradient(top, #0088cc, #0077b3); + background-image: -o-linear-gradient(top, #0088cc, #0077b3); + background-image: linear-gradient(to bottom, #0088cc, #0077b3); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0); +} +.dropdown-menu > .active > a, +.dropdown-menu > .active > a:hover, +.dropdown-menu > .active > a:focus { + color: #ffffff; + text-decoration: none; + outline: 0; + background-color: #0081c2; + background-image: -moz-linear-gradient(top, #0088cc, #0077b3); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0077b3)); + background-image: -webkit-linear-gradient(top, #0088cc, #0077b3); + background-image: -o-linear-gradient(top, #0088cc, #0077b3); + background-image: linear-gradient(to bottom, #0088cc, #0077b3); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0); +} +.dropdown-menu > .disabled > a, +.dropdown-menu > .disabled > a:hover, +.dropdown-menu > .disabled > a:focus { + color: #999999; +} +.dropdown-menu > .disabled > a:hover, +.dropdown-menu > .disabled > a:focus { + text-decoration: none; + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + cursor: default; +} +.open { + *z-index: 1000; +} +.open > .dropdown-menu { + display: block; +} +.pull-right > .dropdown-menu { + right: 0; + left: auto; +} +.dropup .caret, +.navbar-fixed-bottom .dropdown .caret { + border-top: 0; + border-bottom: 4px solid #000000; + content: ""; +} +.dropup .dropdown-menu, +.navbar-fixed-bottom .dropdown .dropdown-menu { + top: auto; + bottom: 100%; + margin-bottom: 1px; +} +.dropdown-submenu { + position: relative; +} +.dropdown-submenu > .dropdown-menu { + top: 0; + left: 100%; + margin-top: -6px; + margin-left: -1px; + -webkit-border-radius: 0 6px 6px 6px; + -moz-border-radius: 0 6px 6px 6px; + border-radius: 0 6px 6px 6px; +} +.dropdown-submenu:hover > .dropdown-menu { + display: block; +} +.dropup .dropdown-submenu > .dropdown-menu { + top: auto; + bottom: 0; + margin-top: 0; + margin-bottom: -2px; + -webkit-border-radius: 5px 5px 5px 0; + -moz-border-radius: 5px 5px 5px 0; + border-radius: 5px 5px 5px 0; +} +.dropdown-submenu > a:after { + display: block; + content: " "; + float: right; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; + border-width: 5px 0 5px 5px; + border-left-color: #cccccc; + margin-top: 5px; + margin-right: -10px; +} +.dropdown-submenu:hover > a:after { + border-left-color: #ffffff; +} +.dropdown-submenu.pull-left { + float: none; +} +.dropdown-submenu.pull-left > .dropdown-menu { + left: -100%; + margin-left: 10px; + -webkit-border-radius: 6px 0 6px 6px; + -moz-border-radius: 6px 0 6px 6px; + border-radius: 6px 0 6px 6px; +} +.dropdown .dropdown-menu .nav-header { + padding-left: 20px; + padding-right: 20px; +} +.typeahead { + z-index: 1051; + margin-top: 2px; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} +.well { + min-height: 20px; + padding: 19px; + margin-bottom: 20px; + background-color: #f5f5f5; + border: 1px solid #e3e3e3; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); +} +.well blockquote { + border-color: #ddd; + border-color: rgba(0, 0, 0, 0.15); +} +.well-large { + padding: 24px; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; +} +.well-small { + padding: 9px; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} +.fade { + opacity: 0; + -webkit-transition: opacity 0.15s linear; + -moz-transition: opacity 0.15s linear; + -o-transition: opacity 0.15s linear; + transition: opacity 0.15s linear; +} +.fade.in { + opacity: 1; +} +.collapse { + position: relative; + height: 0; + overflow: hidden; + -webkit-transition: height 0.35s ease; + -moz-transition: height 0.35s ease; + -o-transition: height 0.35s ease; + transition: height 0.35s ease; +} +.collapse.in { + height: auto; +} +.close { + float: right; + font-size: 20px; + font-weight: bold; + line-height: 21px; + color: #000000; + text-shadow: 0 1px 0 #ffffff; + opacity: 0.2; + filter: alpha(opacity=20); +} +.close:hover, +.close:focus { + color: #000000; + text-decoration: none; + cursor: pointer; + opacity: 0.4; + filter: alpha(opacity=40); +} +button.close { + padding: 0; + cursor: pointer; + background: transparent; + border: 0; + -webkit-appearance: none; +} +.btn { + display: inline-block; + *display: inline; + /* IE7 inline-block hack */ + *zoom: 1; + padding: 4px 12px; + margin-bottom: 0; + font-size: 16px; + line-height: 21px; + text-align: center; + vertical-align: middle; + cursor: pointer; + color: #333333; + text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75); + background-color: #f5f5f5; + background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#e6e6e6)); + background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -o-linear-gradient(top, #ffffff, #e6e6e6); + background-image: linear-gradient(to bottom, #ffffff, #e6e6e6); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe6e6e6', GradientType=0); + border-color: #e6e6e6 #e6e6e6 #bfbfbf; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + *background-color: #e6e6e6; + /* Darken IE7 buttons by default so they stand out more given they won't have borders */ + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + border: 1px solid #cccccc; + *border: 0; + border-bottom-color: #b3b3b3; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + *margin-left: .3em; + -webkit-box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05); + -moz-box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05); + box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05); +} +.btn:hover, +.btn:focus, +.btn:active, +.btn.active, +.btn.disabled, +.btn[disabled] { + color: #333333; + background-color: #e6e6e6; + *background-color: #d9d9d9; +} +.btn:active, +.btn.active { + background-color: #cccccc \9; +} +.btn:first-child { + *margin-left: 0; +} +.btn:hover, +.btn:focus { + color: #333333; + text-decoration: none; + background-position: 0 -15px; + -webkit-transition: background-position 0.1s linear; + -moz-transition: background-position 0.1s linear; + -o-transition: background-position 0.1s linear; + transition: background-position 0.1s linear; +} +.btn:focus { + outline: thin dotted #333; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +.btn.active, +.btn:active { + background-image: none; + outline: 0; + -webkit-box-shadow: inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05); + -moz-box-shadow: inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05); + box-shadow: inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05); +} +.btn.disabled, +.btn[disabled] { + cursor: default; + background-image: none; + opacity: 0.65; + filter: alpha(opacity=65); + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; +} +.btn-large { + padding: 11px 19px; + font-size: 20px; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; +} +.btn-large [class^="icon-"], +.btn-large [class*=" icon-"] { + margin-top: 4px; +} +.btn-small { + padding: 2px 10px; + font-size: 13.6px; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} +.btn-small [class^="icon-"], +.btn-small [class*=" icon-"] { + margin-top: 0; +} +.btn-mini [class^="icon-"], +.btn-mini [class*=" icon-"] { + margin-top: -1px; +} +.btn-mini { + padding: 0 6px; + font-size: 12px; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} +.btn-block { + display: block; + width: 100%; + padding-left: 0; + padding-right: 0; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +.btn-block + .btn-block { + margin-top: 5px; +} +input[type="submit"].btn-block, +input[type="reset"].btn-block, +input[type="button"].btn-block { + width: 100%; +} +.btn-primary.active, +.btn-warning.active, +.btn-danger.active, +.btn-success.active, +.btn-info.active, +.btn-inverse.active { + color: rgba(255, 255, 255, 0.75); +} +.btn-primary { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #006dcc; + background-image: -moz-linear-gradient(top, #0088cc, #0044cc); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc)); + background-image: -webkit-linear-gradient(top, #0088cc, #0044cc); + background-image: -o-linear-gradient(top, #0088cc, #0044cc); + background-image: linear-gradient(to bottom, #0088cc, #0044cc); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0044cc', GradientType=0); + border-color: #0044cc #0044cc #002a80; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + *background-color: #0044cc; + /* Darken IE7 buttons by default so they stand out more given they won't have borders */ + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); +} +.btn-primary:hover, +.btn-primary:focus, +.btn-primary:active, +.btn-primary.active, +.btn-primary.disabled, +.btn-primary[disabled] { + color: #ffffff; + background-color: #0044cc; + *background-color: #003bb3; +} +.btn-primary:active, +.btn-primary.active { + background-color: #003399 \9; +} +.btn-warning { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #faa732; + background-image: -moz-linear-gradient(top, #fbb450, #f89406); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406)); + background-image: -webkit-linear-gradient(top, #fbb450, #f89406); + background-image: -o-linear-gradient(top, #fbb450, #f89406); + background-image: linear-gradient(to bottom, #fbb450, #f89406); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450', endColorstr='#fff89406', GradientType=0); + border-color: #f89406 #f89406 #ad6704; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + *background-color: #f89406; + /* Darken IE7 buttons by default so they stand out more given they won't have borders */ + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); +} +.btn-warning:hover, +.btn-warning:focus, +.btn-warning:active, +.btn-warning.active, +.btn-warning.disabled, +.btn-warning[disabled] { + color: #ffffff; + background-color: #f89406; + *background-color: #df8505; +} +.btn-warning:active, +.btn-warning.active { + background-color: #c67605 \9; +} +.btn-danger { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #da4f49; + background-image: -moz-linear-gradient(top, #ee5f5b, #bd362f); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#bd362f)); + background-image: -webkit-linear-gradient(top, #ee5f5b, #bd362f); + background-image: -o-linear-gradient(top, #ee5f5b, #bd362f); + background-image: linear-gradient(to bottom, #ee5f5b, #bd362f); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b', endColorstr='#ffbd362f', GradientType=0); + border-color: #bd362f #bd362f #802420; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + *background-color: #bd362f; + /* Darken IE7 buttons by default so they stand out more given they won't have borders */ + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); +} +.btn-danger:hover, +.btn-danger:focus, +.btn-danger:active, +.btn-danger.active, +.btn-danger.disabled, +.btn-danger[disabled] { + color: #ffffff; + background-color: #bd362f; + *background-color: #a9302a; +} +.btn-danger:active, +.btn-danger.active { + background-color: #942a25 \9; +} +.btn-success { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #5bb75b; + background-image: -moz-linear-gradient(top, #62c462, #51a351); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#51a351)); + background-image: -webkit-linear-gradient(top, #62c462, #51a351); + background-image: -o-linear-gradient(top, #62c462, #51a351); + background-image: linear-gradient(to bottom, #62c462, #51a351); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462', endColorstr='#ff51a351', GradientType=0); + border-color: #51a351 #51a351 #387038; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + *background-color: #51a351; + /* Darken IE7 buttons by default so they stand out more given they won't have borders */ + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); +} +.btn-success:hover, +.btn-success:focus, +.btn-success:active, +.btn-success.active, +.btn-success.disabled, +.btn-success[disabled] { + color: #ffffff; + background-color: #51a351; + *background-color: #499249; +} +.btn-success:active, +.btn-success.active { + background-color: #408140 \9; +} +.btn-info { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #49afcd; + background-image: -moz-linear-gradient(top, #5bc0de, #2f96b4); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#2f96b4)); + background-image: -webkit-linear-gradient(top, #5bc0de, #2f96b4); + background-image: -o-linear-gradient(top, #5bc0de, #2f96b4); + background-image: linear-gradient(to bottom, #5bc0de, #2f96b4); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2f96b4', GradientType=0); + border-color: #2f96b4 #2f96b4 #1f6377; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + *background-color: #2f96b4; + /* Darken IE7 buttons by default so they stand out more given they won't have borders */ + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); +} +.btn-info:hover, +.btn-info:focus, +.btn-info:active, +.btn-info.active, +.btn-info.disabled, +.btn-info[disabled] { + color: #ffffff; + background-color: #2f96b4; + *background-color: #2a85a0; +} +.btn-info:active, +.btn-info.active { + background-color: #24748c \9; +} +.btn-inverse { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #363636; + background-image: -moz-linear-gradient(top, #444444, #222222); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#444444), to(#222222)); + background-image: -webkit-linear-gradient(top, #444444, #222222); + background-image: -o-linear-gradient(top, #444444, #222222); + background-image: linear-gradient(to bottom, #444444, #222222); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff444444', endColorstr='#ff222222', GradientType=0); + border-color: #222222 #222222 #000000; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + *background-color: #222222; + /* Darken IE7 buttons by default so they stand out more given they won't have borders */ + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); +} +.btn-inverse:hover, +.btn-inverse:focus, +.btn-inverse:active, +.btn-inverse.active, +.btn-inverse.disabled, +.btn-inverse[disabled] { + color: #ffffff; + background-color: #222222; + *background-color: #151515; +} +.btn-inverse:active, +.btn-inverse.active { + background-color: #080808 \9; +} +button.btn, +input[type="submit"].btn { + *padding-top: 3px; + *padding-bottom: 3px; +} +button.btn::-moz-focus-inner, +input[type="submit"].btn::-moz-focus-inner { + padding: 0; + border: 0; +} +button.btn.btn-large, +input[type="submit"].btn.btn-large { + *padding-top: 7px; + *padding-bottom: 7px; +} +button.btn.btn-small, +input[type="submit"].btn.btn-small { + *padding-top: 3px; + *padding-bottom: 3px; +} +button.btn.btn-mini, +input[type="submit"].btn.btn-mini { + *padding-top: 1px; + *padding-bottom: 1px; +} +.btn-link, +.btn-link:active, +.btn-link[disabled] { + background-color: transparent; + background-image: none; + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; +} +.btn-link { + border-color: transparent; + cursor: pointer; + color: #0088cc; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} +.btn-link:hover, +.btn-link:focus { + color: #005580; + text-decoration: underline; + background-color: transparent; +} +.btn-link[disabled]:hover, +.btn-link[disabled]:focus { + color: #333333; + text-decoration: none; +} +.btn-group { + position: relative; + display: inline-block; + *display: inline; + /* IE7 inline-block hack */ + *zoom: 1; + font-size: 0; + vertical-align: middle; + white-space: nowrap; + *margin-left: .3em; +} +.btn-group:first-child { + *margin-left: 0; +} +.btn-group + .btn-group { + margin-left: 5px; +} +.btn-toolbar { + font-size: 0; + margin-top: 10.5px; + margin-bottom: 10.5px; +} +.btn-toolbar > .btn + .btn, +.btn-toolbar > .btn-group + .btn, +.btn-toolbar > .btn + .btn-group { + margin-left: 5px; +} +.btn-group > .btn { + position: relative; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} +.btn-group > .btn + .btn { + margin-left: -1px; +} +.btn-group > .btn, +.btn-group > .dropdown-menu, +.btn-group > .popover { + font-size: 16px; +} +.btn-group > .btn-mini { + font-size: 12px; +} +.btn-group > .btn-small { + font-size: 13.6px; +} +.btn-group > .btn-large { + font-size: 20px; +} +.btn-group > .btn:first-child { + margin-left: 0; + -webkit-border-top-left-radius: 4px; + -moz-border-radius-topleft: 4px; + border-top-left-radius: 4px; + -webkit-border-bottom-left-radius: 4px; + -moz-border-radius-bottomleft: 4px; + border-bottom-left-radius: 4px; +} +.btn-group > .btn:last-child, +.btn-group > .dropdown-toggle { + -webkit-border-top-right-radius: 4px; + -moz-border-radius-topright: 4px; + border-top-right-radius: 4px; + -webkit-border-bottom-right-radius: 4px; + -moz-border-radius-bottomright: 4px; + border-bottom-right-radius: 4px; +} +.btn-group > .btn.large:first-child { + margin-left: 0; + -webkit-border-top-left-radius: 6px; + -moz-border-radius-topleft: 6px; + border-top-left-radius: 6px; + -webkit-border-bottom-left-radius: 6px; + -moz-border-radius-bottomleft: 6px; + border-bottom-left-radius: 6px; +} +.btn-group > .btn.large:last-child, +.btn-group > .large.dropdown-toggle { + -webkit-border-top-right-radius: 6px; + -moz-border-radius-topright: 6px; + border-top-right-radius: 6px; + -webkit-border-bottom-right-radius: 6px; + -moz-border-radius-bottomright: 6px; + border-bottom-right-radius: 6px; +} +.btn-group > .btn:hover, +.btn-group > .btn:focus, +.btn-group > .btn:active, +.btn-group > .btn.active { + z-index: 2; +} +.btn-group .dropdown-toggle:active, +.btn-group.open .dropdown-toggle { + outline: 0; +} +.btn-group > .btn + .dropdown-toggle { + padding-left: 8px; + padding-right: 8px; + -webkit-box-shadow: inset 1px 0 0 rgba(255,255,255,.125), inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05); + -moz-box-shadow: inset 1px 0 0 rgba(255,255,255,.125), inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05); + box-shadow: inset 1px 0 0 rgba(255,255,255,.125), inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05); + *padding-top: 5px; + *padding-bottom: 5px; +} +.btn-group > .btn-mini + .dropdown-toggle { + padding-left: 5px; + padding-right: 5px; + *padding-top: 2px; + *padding-bottom: 2px; +} +.btn-group > .btn-small + .dropdown-toggle { + *padding-top: 5px; + *padding-bottom: 4px; +} +.btn-group > .btn-large + .dropdown-toggle { + padding-left: 12px; + padding-right: 12px; + *padding-top: 7px; + *padding-bottom: 7px; +} +.btn-group.open .dropdown-toggle { + background-image: none; + -webkit-box-shadow: inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05); + -moz-box-shadow: inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05); + box-shadow: inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05); +} +.btn-group.open .btn.dropdown-toggle { + background-color: #e6e6e6; +} +.btn-group.open .btn-primary.dropdown-toggle { + background-color: #0044cc; +} +.btn-group.open .btn-warning.dropdown-toggle { + background-color: #f89406; +} +.btn-group.open .btn-danger.dropdown-toggle { + background-color: #bd362f; +} +.btn-group.open .btn-success.dropdown-toggle { + background-color: #51a351; +} +.btn-group.open .btn-info.dropdown-toggle { + background-color: #2f96b4; +} +.btn-group.open .btn-inverse.dropdown-toggle { + background-color: #222222; +} +.btn .caret { + margin-top: 8px; + margin-left: 0; +} +.btn-large .caret { + margin-top: 6px; +} +.btn-large .caret { + border-left-width: 5px; + border-right-width: 5px; + border-top-width: 5px; +} +.btn-mini .caret, +.btn-small .caret { + margin-top: 8px; +} +.dropup .btn-large .caret { + border-bottom-width: 5px; +} +.btn-primary .caret, +.btn-warning .caret, +.btn-danger .caret, +.btn-info .caret, +.btn-success .caret, +.btn-inverse .caret { + border-top-color: #ffffff; + border-bottom-color: #ffffff; +} +.btn-group-vertical { + display: inline-block; + *display: inline; + /* IE7 inline-block hack */ + *zoom: 1; +} +.btn-group-vertical > .btn { + display: block; + float: none; + max-width: 100%; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} +.btn-group-vertical > .btn + .btn { + margin-left: 0; + margin-top: -1px; +} +.btn-group-vertical > .btn:first-child { + -webkit-border-radius: 4px 4px 0 0; + -moz-border-radius: 4px 4px 0 0; + border-radius: 4px 4px 0 0; +} +.btn-group-vertical > .btn:last-child { + -webkit-border-radius: 0 0 4px 4px; + -moz-border-radius: 0 0 4px 4px; + border-radius: 0 0 4px 4px; +} +.btn-group-vertical > .btn-large:first-child { + -webkit-border-radius: 6px 6px 0 0; + -moz-border-radius: 6px 6px 0 0; + border-radius: 6px 6px 0 0; +} +.btn-group-vertical > .btn-large:last-child { + -webkit-border-radius: 0 0 6px 6px; + -moz-border-radius: 0 0 6px 6px; + border-radius: 0 0 6px 6px; +} +.alert { + padding: 8px 35px 8px 14px; + margin-bottom: 21px; + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); + background-color: #fcf8e3; + border: 1px solid #fbeed5; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} +.alert, +.alert h4 { + color: #c09853; +} +.alert h4 { + margin: 0; +} +.alert .close { + position: relative; + top: -2px; + right: -21px; + line-height: 21px; +} +.alert-success { + background-color: #dff0d8; + border-color: #d6e9c6; + color: #468847; +} +.alert-success h4 { + color: #468847; +} +.alert-danger, +.alert-error { + background-color: #f2dede; + border-color: #eed3d7; + color: #b94a48; +} +.alert-danger h4, +.alert-error h4 { + color: #b94a48; +} +.alert-info { + background-color: #d9edf7; + border-color: #bce8f1; + color: #3a87ad; +} +.alert-info h4 { + color: #3a87ad; +} +.alert-block { + padding-top: 14px; + padding-bottom: 14px; +} +.alert-block > p, +.alert-block > ul { + margin-bottom: 0; +} +.alert-block p + p { + margin-top: 5px; +} +.nav { + margin-left: 0; + margin-bottom: 21px; + list-style: none; +} +.nav > li > a { + display: block; +} +.nav > li > a:hover, +.nav > li > a:focus { + text-decoration: none; + background-color: #eeeeee; +} +.nav > li > a > img { + max-width: none; +} +.nav > .pull-right { + float: right; +} +.nav-header { + display: block; + padding: 3px 15px; + font-size: 11px; + font-weight: bold; + line-height: 21px; + color: #999999; + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); + text-transform: uppercase; +} +.nav li + .nav-header { + margin-top: 9px; +} +.nav-list { + padding-left: 15px; + padding-right: 15px; + margin-bottom: 0; +} +.nav-list > li > a, +.nav-list .nav-header { + margin-left: -15px; + margin-right: -15px; + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); +} +.nav-list > li > a { + padding: 3px 15px; +} +.nav-list > .active > a, +.nav-list > .active > a:hover, +.nav-list > .active > a:focus { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2); + background-color: #0088cc; +} +.nav-list [class^="icon-"], +.nav-list [class*=" icon-"] { + margin-right: 2px; +} +.nav-list .divider { + *width: 100%; + height: 1px; + margin: 9.5px 1px; + *margin: -5px 0 5px; + overflow: hidden; + background-color: #e5e5e5; + border-bottom: 1px solid #ffffff; +} +.nav-tabs, +.nav-pills { + *zoom: 1; +} +.nav-tabs:before, +.nav-pills:before, +.nav-tabs:after, +.nav-pills:after { + display: table; + content: ""; + line-height: 0; +} +.nav-tabs:after, +.nav-pills:after { + clear: both; +} +.nav-tabs > li, +.nav-pills > li { + float: left; +} +.nav-tabs > li > a, +.nav-pills > li > a { + padding-right: 12px; + padding-left: 12px; + margin-right: 2px; + line-height: 14px; +} +.nav-tabs { + border-bottom: 1px solid #ddd; +} +.nav-tabs > li { + margin-bottom: -1px; +} +.nav-tabs > li > a { + padding-top: 8px; + padding-bottom: 8px; + line-height: 21px; + border: 1px solid transparent; + -webkit-border-radius: 4px 4px 0 0; + -moz-border-radius: 4px 4px 0 0; + border-radius: 4px 4px 0 0; +} +.nav-tabs > li > a:hover, +.nav-tabs > li > a:focus { + border-color: #eeeeee #eeeeee #dddddd; +} +.nav-tabs > .active > a, +.nav-tabs > .active > a:hover, +.nav-tabs > .active > a:focus { + color: #555555; + background-color: #ffffff; + border: 1px solid #ddd; + border-bottom-color: transparent; + cursor: default; +} +.nav-pills > li > a { + padding-top: 8px; + padding-bottom: 8px; + margin-top: 2px; + margin-bottom: 2px; + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + border-radius: 5px; +} +.nav-pills > .active > a, +.nav-pills > .active > a:hover, +.nav-pills > .active > a:focus { + color: #ffffff; + background-color: #0088cc; +} +.nav-stacked > li { + float: none; +} +.nav-stacked > li > a { + margin-right: 0; +} +.nav-tabs.nav-stacked { + border-bottom: 0; +} +.nav-tabs.nav-stacked > li > a { + border: 1px solid #ddd; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} +.nav-tabs.nav-stacked > li:first-child > a { + -webkit-border-top-right-radius: 4px; + -moz-border-radius-topright: 4px; + border-top-right-radius: 4px; + -webkit-border-top-left-radius: 4px; + -moz-border-radius-topleft: 4px; + border-top-left-radius: 4px; +} +.nav-tabs.nav-stacked > li:last-child > a { + -webkit-border-bottom-right-radius: 4px; + -moz-border-radius-bottomright: 4px; + border-bottom-right-radius: 4px; + -webkit-border-bottom-left-radius: 4px; + -moz-border-radius-bottomleft: 4px; + border-bottom-left-radius: 4px; +} +.nav-tabs.nav-stacked > li > a:hover, +.nav-tabs.nav-stacked > li > a:focus { + border-color: #ddd; + z-index: 2; +} +.nav-pills.nav-stacked > li > a { + margin-bottom: 3px; +} +.nav-pills.nav-stacked > li:last-child > a { + margin-bottom: 1px; +} +.nav-tabs .dropdown-menu { + -webkit-border-radius: 0 0 6px 6px; + -moz-border-radius: 0 0 6px 6px; + border-radius: 0 0 6px 6px; +} +.nav-pills .dropdown-menu { + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; +} +.nav .dropdown-toggle .caret { + border-top-color: #0088cc; + border-bottom-color: #0088cc; + margin-top: 6px; +} +.nav .dropdown-toggle:hover .caret, +.nav .dropdown-toggle:focus .caret { + border-top-color: #005580; + border-bottom-color: #005580; +} +/* move down carets for tabs */ +.nav-tabs .dropdown-toggle .caret { + margin-top: 8px; +} +.nav .active .dropdown-toggle .caret { + border-top-color: #fff; + border-bottom-color: #fff; +} +.nav-tabs .active .dropdown-toggle .caret { + border-top-color: #555555; + border-bottom-color: #555555; +} +.nav > .dropdown.active > a:hover, +.nav > .dropdown.active > a:focus { + cursor: pointer; +} +.nav-tabs .open .dropdown-toggle, +.nav-pills .open .dropdown-toggle, +.nav > li.dropdown.open.active > a:hover, +.nav > li.dropdown.open.active > a:focus { + color: #ffffff; + background-color: #999999; + border-color: #999999; +} +.nav li.dropdown.open .caret, +.nav li.dropdown.open.active .caret, +.nav li.dropdown.open a:hover .caret, +.nav li.dropdown.open a:focus .caret { + border-top-color: #ffffff; + border-bottom-color: #ffffff; + opacity: 1; + filter: alpha(opacity=100); +} +.tabs-stacked .open > a:hover, +.tabs-stacked .open > a:focus { + border-color: #999999; +} +.tabbable { + *zoom: 1; +} +.tabbable:before, +.tabbable:after { + display: table; + content: ""; + line-height: 0; +} +.tabbable:after { + clear: both; +} +.tab-content { + overflow: auto; +} +.tabs-below > .nav-tabs, +.tabs-right > .nav-tabs, +.tabs-left > .nav-tabs { + border-bottom: 0; +} +.tab-content > .tab-pane, +.pill-content > .pill-pane { + display: none; +} +.tab-content > .active, +.pill-content > .active { + display: block; +} +.tabs-below > .nav-tabs { + border-top: 1px solid #ddd; +} +.tabs-below > .nav-tabs > li { + margin-top: -1px; + margin-bottom: 0; +} +.tabs-below > .nav-tabs > li > a { + -webkit-border-radius: 0 0 4px 4px; + -moz-border-radius: 0 0 4px 4px; + border-radius: 0 0 4px 4px; +} +.tabs-below > .nav-tabs > li > a:hover, +.tabs-below > .nav-tabs > li > a:focus { + border-bottom-color: transparent; + border-top-color: #ddd; +} +.tabs-below > .nav-tabs > .active > a, +.tabs-below > .nav-tabs > .active > a:hover, +.tabs-below > .nav-tabs > .active > a:focus { + border-color: transparent #ddd #ddd #ddd; +} +.tabs-left > .nav-tabs > li, +.tabs-right > .nav-tabs > li { + float: none; +} +.tabs-left > .nav-tabs > li > a, +.tabs-right > .nav-tabs > li > a { + min-width: 74px; + margin-right: 0; + margin-bottom: 3px; +} +.tabs-left > .nav-tabs { + float: left; + margin-right: 19px; + border-right: 1px solid #ddd; +} +.tabs-left > .nav-tabs > li > a { + margin-right: -1px; + -webkit-border-radius: 4px 0 0 4px; + -moz-border-radius: 4px 0 0 4px; + border-radius: 4px 0 0 4px; +} +.tabs-left > .nav-tabs > li > a:hover, +.tabs-left > .nav-tabs > li > a:focus { + border-color: #eeeeee #dddddd #eeeeee #eeeeee; +} +.tabs-left > .nav-tabs .active > a, +.tabs-left > .nav-tabs .active > a:hover, +.tabs-left > .nav-tabs .active > a:focus { + border-color: #ddd transparent #ddd #ddd; + *border-right-color: #ffffff; +} +.tabs-right > .nav-tabs { + float: right; + margin-left: 19px; + border-left: 1px solid #ddd; +} +.tabs-right > .nav-tabs > li > a { + margin-left: -1px; + -webkit-border-radius: 0 4px 4px 0; + -moz-border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; +} +.tabs-right > .nav-tabs > li > a:hover, +.tabs-right > .nav-tabs > li > a:focus { + border-color: #eeeeee #eeeeee #eeeeee #dddddd; +} +.tabs-right > .nav-tabs .active > a, +.tabs-right > .nav-tabs .active > a:hover, +.tabs-right > .nav-tabs .active > a:focus { + border-color: #ddd #ddd #ddd transparent; + *border-left-color: #ffffff; +} +.nav > .disabled > a { + color: #999999; +} +.nav > .disabled > a:hover, +.nav > .disabled > a:focus { + text-decoration: none; + background-color: transparent; + cursor: default; +} +.navbar { + overflow: visible; + margin-bottom: 21px; + *position: relative; + *z-index: 2; +} +.navbar-inner { + min-height: 40px; + padding-left: 20px; + padding-right: 20px; + background-color: #fafafa; + background-image: -moz-linear-gradient(top, #ffffff, #f2f2f2); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#f2f2f2)); + background-image: -webkit-linear-gradient(top, #ffffff, #f2f2f2); + background-image: -o-linear-gradient(top, #ffffff, #f2f2f2); + background-image: linear-gradient(to bottom, #ffffff, #f2f2f2); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff2f2f2', GradientType=0); + border: 1px solid #d4d4d4; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065); + -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065); + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065); + *zoom: 1; +} +.navbar-inner:before, +.navbar-inner:after { + display: table; + content: ""; + line-height: 0; +} +.navbar-inner:after { + clear: both; +} +.navbar .container { + width: auto; +} +.nav-collapse.collapse { + height: auto; + overflow: visible; +} +.navbar .brand { + float: left; + display: block; + padding: 9.5px 20px 9.5px; + margin-left: -20px; + font-size: 20px; + font-weight: 200; + color: #777777; + text-shadow: 0 1px 0 #ffffff; +} +.navbar .brand:hover, +.navbar .brand:focus { + text-decoration: none; +} +.navbar-text { + margin-bottom: 0; + line-height: 40px; + color: #777777; +} +.navbar-link { + color: #777777; +} +.navbar-link:hover, +.navbar-link:focus { + color: #333333; +} +.navbar .divider-vertical { + height: 40px; + margin: 0 9px; + border-left: 1px solid #f2f2f2; + border-right: 1px solid #ffffff; +} +.navbar .btn, +.navbar .btn-group { + margin-top: 5px; +} +.navbar .btn-group .btn, +.navbar .input-prepend .btn, +.navbar .input-append .btn, +.navbar .input-prepend .btn-group, +.navbar .input-append .btn-group { + margin-top: 0; +} +.navbar-form { + margin-bottom: 0; + *zoom: 1; +} +.navbar-form:before, +.navbar-form:after { + display: table; + content: ""; + line-height: 0; +} +.navbar-form:after { + clear: both; +} +.navbar-form input, +.navbar-form select, +.navbar-form .radio, +.navbar-form .checkbox { + margin-top: 5px; +} +.navbar-form input, +.navbar-form select, +.navbar-form .btn { + display: inline-block; + margin-bottom: 0; +} +.navbar-form input[type="image"], +.navbar-form input[type="checkbox"], +.navbar-form input[type="radio"] { + margin-top: 3px; +} +.navbar-form .input-append, +.navbar-form .input-prepend { + margin-top: 5px; + white-space: nowrap; +} +.navbar-form .input-append input, +.navbar-form .input-prepend input { + margin-top: 0; +} +.navbar-search { + position: relative; + float: left; + margin-top: 5px; + margin-bottom: 0; +} +.navbar-search .search-query { + margin-bottom: 0; + padding: 4px 14px; + font-family: 'Source Sans Pro', sans-serif; + font-size: 13px; + font-weight: normal; + line-height: 1; + -webkit-border-radius: 15px; + -moz-border-radius: 15px; + border-radius: 15px; +} +.navbar-static-top { + position: static; + margin-bottom: 0; +} +.navbar-static-top .navbar-inner { + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} +.navbar-fixed-top, +.navbar-fixed-bottom { + position: fixed; + right: 0; + left: 0; + z-index: 1030; + margin-bottom: 0; +} +.navbar-fixed-top .navbar-inner, +.navbar-static-top .navbar-inner { + border-width: 0 0 1px; +} +.navbar-fixed-bottom .navbar-inner { + border-width: 1px 0 0; +} +.navbar-fixed-top .navbar-inner, +.navbar-fixed-bottom .navbar-inner { + padding-left: 0; + padding-right: 0; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} +.navbar-static-top .container, +.navbar-fixed-top .container, +.navbar-fixed-bottom .container { + width: 940px; +} +.navbar-fixed-top { + top: 0; +} +.navbar-fixed-top .navbar-inner, +.navbar-static-top .navbar-inner { + -webkit-box-shadow: 0 1px 10px rgba(0,0,0,.1); + -moz-box-shadow: 0 1px 10px rgba(0,0,0,.1); + box-shadow: 0 1px 10px rgba(0,0,0,.1); +} +.navbar-fixed-bottom { + bottom: 0; +} +.navbar-fixed-bottom .navbar-inner { + -webkit-box-shadow: 0 -1px 10px rgba(0,0,0,.1); + -moz-box-shadow: 0 -1px 10px rgba(0,0,0,.1); + box-shadow: 0 -1px 10px rgba(0,0,0,.1); +} +.navbar .nav { + position: relative; + left: 0; + display: block; + float: left; + margin: 0 10px 0 0; +} +.navbar .nav.pull-right { + float: right; + margin-right: 0; +} +.navbar .nav > li { + float: left; +} +.navbar .nav > li > a { + float: none; + padding: 9.5px 15px 9.5px; + color: #777777; + text-decoration: none; + text-shadow: 0 1px 0 #ffffff; +} +.navbar .nav .dropdown-toggle .caret { + margin-top: 8px; +} +.navbar .nav > li > a:focus, +.navbar .nav > li > a:hover { + background-color: transparent; + color: #333333; + text-decoration: none; +} +.navbar .nav > .active > a, +.navbar .nav > .active > a:hover, +.navbar .nav > .active > a:focus { + color: #555555; + text-decoration: none; + background-color: #e5e5e5; + -webkit-box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.125); + -moz-box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.125); + box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.125); +} +.navbar .btn-navbar { + display: none; + float: right; + padding: 7px 10px; + margin-left: 5px; + margin-right: 5px; + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #ededed; + background-image: -moz-linear-gradient(top, #f2f2f2, #e5e5e5); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f2f2f2), to(#e5e5e5)); + background-image: -webkit-linear-gradient(top, #f2f2f2, #e5e5e5); + background-image: -o-linear-gradient(top, #f2f2f2, #e5e5e5); + background-image: linear-gradient(to bottom, #f2f2f2, #e5e5e5); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2f2f2', endColorstr='#ffe5e5e5', GradientType=0); + border-color: #e5e5e5 #e5e5e5 #bfbfbf; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + *background-color: #e5e5e5; + /* Darken IE7 buttons by default so they stand out more given they won't have borders */ + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + -webkit-box-shadow: inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.075); + -moz-box-shadow: inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.075); + box-shadow: inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.075); +} +.navbar .btn-navbar:hover, +.navbar .btn-navbar:focus, +.navbar .btn-navbar:active, +.navbar .btn-navbar.active, +.navbar .btn-navbar.disabled, +.navbar .btn-navbar[disabled] { + color: #ffffff; + background-color: #e5e5e5; + *background-color: #d9d9d9; +} +.navbar .btn-navbar:active, +.navbar .btn-navbar.active { + background-color: #cccccc \9; +} +.navbar .btn-navbar .icon-bar { + display: block; + width: 18px; + height: 2px; + background-color: #f5f5f5; + -webkit-border-radius: 1px; + -moz-border-radius: 1px; + border-radius: 1px; + -webkit-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25); + -moz-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25); + box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25); +} +.btn-navbar .icon-bar + .icon-bar { + margin-top: 3px; +} +.navbar .nav > li > .dropdown-menu:before { + content: ''; + display: inline-block; + border-left: 7px solid transparent; + border-right: 7px solid transparent; + border-bottom: 7px solid #ccc; + border-bottom-color: rgba(0, 0, 0, 0.2); + position: absolute; + top: -7px; + left: 9px; +} +.navbar .nav > li > .dropdown-menu:after { + content: ''; + display: inline-block; + border-left: 6px solid transparent; + border-right: 6px solid transparent; + border-bottom: 6px solid #ffffff; + position: absolute; + top: -6px; + left: 10px; +} +.navbar-fixed-bottom .nav > li > .dropdown-menu:before { + border-top: 7px solid #ccc; + border-top-color: rgba(0, 0, 0, 0.2); + border-bottom: 0; + bottom: -7px; + top: auto; +} +.navbar-fixed-bottom .nav > li > .dropdown-menu:after { + border-top: 6px solid #ffffff; + border-bottom: 0; + bottom: -6px; + top: auto; +} +.navbar .nav li.dropdown > a:hover .caret, +.navbar .nav li.dropdown > a:focus .caret { + border-top-color: #333333; + border-bottom-color: #333333; +} +.navbar .nav li.dropdown.open > .dropdown-toggle, +.navbar .nav li.dropdown.active > .dropdown-toggle, +.navbar .nav li.dropdown.open.active > .dropdown-toggle { + background-color: #e5e5e5; + color: #555555; +} +.navbar .nav li.dropdown > .dropdown-toggle .caret { + border-top-color: #777777; + border-bottom-color: #777777; +} +.navbar .nav li.dropdown.open > .dropdown-toggle .caret, +.navbar .nav li.dropdown.active > .dropdown-toggle .caret, +.navbar .nav li.dropdown.open.active > .dropdown-toggle .caret { + border-top-color: #555555; + border-bottom-color: #555555; +} +.navbar .pull-right > li > .dropdown-menu, +.navbar .nav > li > .dropdown-menu.pull-right { + left: auto; + right: 0; +} +.navbar .pull-right > li > .dropdown-menu:before, +.navbar .nav > li > .dropdown-menu.pull-right:before { + left: auto; + right: 12px; +} +.navbar .pull-right > li > .dropdown-menu:after, +.navbar .nav > li > .dropdown-menu.pull-right:after { + left: auto; + right: 13px; +} +.navbar .pull-right > li > .dropdown-menu .dropdown-menu, +.navbar .nav > li > .dropdown-menu.pull-right .dropdown-menu { + left: auto; + right: 100%; + margin-left: 0; + margin-right: -1px; + -webkit-border-radius: 6px 0 6px 6px; + -moz-border-radius: 6px 0 6px 6px; + border-radius: 6px 0 6px 6px; +} +.navbar-inverse .navbar-inner { + background-color: #1b1b1b; + background-image: -moz-linear-gradient(top, #222222, #111111); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#222222), to(#111111)); + background-image: -webkit-linear-gradient(top, #222222, #111111); + background-image: -o-linear-gradient(top, #222222, #111111); + background-image: linear-gradient(to bottom, #222222, #111111); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222', endColorstr='#ff111111', GradientType=0); + border-color: #252525; +} +.navbar-inverse .brand, +.navbar-inverse .nav > li > a { + color: #999999; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); +} +.navbar-inverse .brand:hover, +.navbar-inverse .nav > li > a:hover, +.navbar-inverse .brand:focus, +.navbar-inverse .nav > li > a:focus { + color: #ffffff; +} +.navbar-inverse .brand { + color: #999999; +} +.navbar-inverse .navbar-text { + color: #999999; +} +.navbar-inverse .nav > li > a:focus, +.navbar-inverse .nav > li > a:hover { + background-color: transparent; + color: #ffffff; +} +.navbar-inverse .nav .active > a, +.navbar-inverse .nav .active > a:hover, +.navbar-inverse .nav .active > a:focus { + color: #ffffff; + background-color: #111111; +} +.navbar-inverse .navbar-link { + color: #999999; +} +.navbar-inverse .navbar-link:hover, +.navbar-inverse .navbar-link:focus { + color: #ffffff; +} +.navbar-inverse .divider-vertical { + border-left-color: #111111; + border-right-color: #222222; +} +.navbar-inverse .nav li.dropdown.open > .dropdown-toggle, +.navbar-inverse .nav li.dropdown.active > .dropdown-toggle, +.navbar-inverse .nav li.dropdown.open.active > .dropdown-toggle { + background-color: #111111; + color: #ffffff; +} +.navbar-inverse .nav li.dropdown > a:hover .caret, +.navbar-inverse .nav li.dropdown > a:focus .caret { + border-top-color: #ffffff; + border-bottom-color: #ffffff; +} +.navbar-inverse .nav li.dropdown > .dropdown-toggle .caret { + border-top-color: #999999; + border-bottom-color: #999999; +} +.navbar-inverse .nav li.dropdown.open > .dropdown-toggle .caret, +.navbar-inverse .nav li.dropdown.active > .dropdown-toggle .caret, +.navbar-inverse .nav li.dropdown.open.active > .dropdown-toggle .caret { + border-top-color: #ffffff; + border-bottom-color: #ffffff; +} +.navbar-inverse .navbar-search .search-query { + color: #ffffff; + background-color: #515151; + border-color: #111111; + -webkit-box-shadow: inset 0 1px 2px rgba(0,0,0,.1), 0 1px 0 rgba(255,255,255,.15); + -moz-box-shadow: inset 0 1px 2px rgba(0,0,0,.1), 0 1px 0 rgba(255,255,255,.15); + box-shadow: inset 0 1px 2px rgba(0,0,0,.1), 0 1px 0 rgba(255,255,255,.15); + -webkit-transition: none; + -moz-transition: none; + -o-transition: none; + transition: none; +} +.navbar-inverse .navbar-search .search-query:-moz-placeholder { + color: #cccccc; +} +.navbar-inverse .navbar-search .search-query:-ms-input-placeholder { + color: #cccccc; +} +.navbar-inverse .navbar-search .search-query::-webkit-input-placeholder { + color: #cccccc; +} +.navbar-inverse .navbar-search .search-query:focus, +.navbar-inverse .navbar-search .search-query.focused { + padding: 5px 15px; + color: #333333; + text-shadow: 0 1px 0 #ffffff; + background-color: #ffffff; + border: 0; + -webkit-box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); + -moz-box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); + box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); + outline: 0; +} +.navbar-inverse .btn-navbar { + color: #ffffff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #0e0e0e; + background-image: -moz-linear-gradient(top, #151515, #040404); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#151515), to(#040404)); + background-image: -webkit-linear-gradient(top, #151515, #040404); + background-image: -o-linear-gradient(top, #151515, #040404); + background-image: linear-gradient(to bottom, #151515, #040404); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff151515', endColorstr='#ff040404', GradientType=0); + border-color: #040404 #040404 #000000; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + *background-color: #040404; + /* Darken IE7 buttons by default so they stand out more given they won't have borders */ + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); +} +.navbar-inverse .btn-navbar:hover, +.navbar-inverse .btn-navbar:focus, +.navbar-inverse .btn-navbar:active, +.navbar-inverse .btn-navbar.active, +.navbar-inverse .btn-navbar.disabled, +.navbar-inverse .btn-navbar[disabled] { + color: #ffffff; + background-color: #040404; + *background-color: #000000; +} +.navbar-inverse .btn-navbar:active, +.navbar-inverse .btn-navbar.active { + background-color: #000000 \9; +} +.breadcrumb { + padding: 8px 15px; + margin: 0 0 21px; + list-style: none; + background-color: #f5f5f5; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} +.breadcrumb > li { + display: inline-block; + *display: inline; + /* IE7 inline-block hack */ + *zoom: 1; + text-shadow: 0 1px 0 #ffffff; +} +.breadcrumb > li > .divider { + padding: 0 5px; + color: #ccc; +} +.breadcrumb > .active { + color: #999999; +} +.pagination { + margin: 21px 0; +} +.pagination ul { + display: inline-block; + *display: inline; + /* IE7 inline-block hack */ + *zoom: 1; + margin-left: 0; + margin-bottom: 0; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); +} +.pagination ul > li { + display: inline; +} +.pagination ul > li > a, +.pagination ul > li > span { + float: left; + padding: 4px 12px; + line-height: 21px; + text-decoration: none; + background-color: #ffffff; + border: 1px solid #dddddd; + border-left-width: 0; +} +.pagination ul > li > a:hover, +.pagination ul > li > a:focus, +.pagination ul > .active > a, +.pagination ul > .active > span { + background-color: #f5f5f5; +} +.pagination ul > .active > a, +.pagination ul > .active > span { + color: #999999; + cursor: default; +} +.pagination ul > .disabled > span, +.pagination ul > .disabled > a, +.pagination ul > .disabled > a:hover, +.pagination ul > .disabled > a:focus { + color: #999999; + background-color: transparent; + cursor: default; +} +.pagination ul > li:first-child > a, +.pagination ul > li:first-child > span { + border-left-width: 1px; + -webkit-border-top-left-radius: 4px; + -moz-border-radius-topleft: 4px; + border-top-left-radius: 4px; + -webkit-border-bottom-left-radius: 4px; + -moz-border-radius-bottomleft: 4px; + border-bottom-left-radius: 4px; +} +.pagination ul > li:last-child > a, +.pagination ul > li:last-child > span { + -webkit-border-top-right-radius: 4px; + -moz-border-radius-topright: 4px; + border-top-right-radius: 4px; + -webkit-border-bottom-right-radius: 4px; + -moz-border-radius-bottomright: 4px; + border-bottom-right-radius: 4px; +} +.pagination-centered { + text-align: center; +} +.pagination-right { + text-align: right; +} +.pagination-large ul > li > a, +.pagination-large ul > li > span { + padding: 11px 19px; + font-size: 20px; +} +.pagination-large ul > li:first-child > a, +.pagination-large ul > li:first-child > span { + -webkit-border-top-left-radius: 6px; + -moz-border-radius-topleft: 6px; + border-top-left-radius: 6px; + -webkit-border-bottom-left-radius: 6px; + -moz-border-radius-bottomleft: 6px; + border-bottom-left-radius: 6px; +} +.pagination-large ul > li:last-child > a, +.pagination-large ul > li:last-child > span { + -webkit-border-top-right-radius: 6px; + -moz-border-radius-topright: 6px; + border-top-right-radius: 6px; + -webkit-border-bottom-right-radius: 6px; + -moz-border-radius-bottomright: 6px; + border-bottom-right-radius: 6px; +} +.pagination-mini ul > li:first-child > a, +.pagination-small ul > li:first-child > a, +.pagination-mini ul > li:first-child > span, +.pagination-small ul > li:first-child > span { + -webkit-border-top-left-radius: 3px; + -moz-border-radius-topleft: 3px; + border-top-left-radius: 3px; + -webkit-border-bottom-left-radius: 3px; + -moz-border-radius-bottomleft: 3px; + border-bottom-left-radius: 3px; +} +.pagination-mini ul > li:last-child > a, +.pagination-small ul > li:last-child > a, +.pagination-mini ul > li:last-child > span, +.pagination-small ul > li:last-child > span { + -webkit-border-top-right-radius: 3px; + -moz-border-radius-topright: 3px; + border-top-right-radius: 3px; + -webkit-border-bottom-right-radius: 3px; + -moz-border-radius-bottomright: 3px; + border-bottom-right-radius: 3px; +} +.pagination-small ul > li > a, +.pagination-small ul > li > span { + padding: 2px 10px; + font-size: 13.6px; +} +.pagination-mini ul > li > a, +.pagination-mini ul > li > span { + padding: 0 6px; + font-size: 12px; +} +.pager { + margin: 21px 0; + list-style: none; + text-align: center; + *zoom: 1; +} +.pager:before, +.pager:after { + display: table; + content: ""; + line-height: 0; +} +.pager:after { + clear: both; +} +.pager li { + display: inline; +} +.pager li > a, +.pager li > span { + display: inline-block; + padding: 5px 14px; + background-color: #fff; + border: 1px solid #ddd; + -webkit-border-radius: 15px; + -moz-border-radius: 15px; + border-radius: 15px; +} +.pager li > a:hover, +.pager li > a:focus { + text-decoration: none; + background-color: #f5f5f5; +} +.pager .next > a, +.pager .next > span { + float: right; +} +.pager .previous > a, +.pager .previous > span { + float: left; +} +.pager .disabled > a, +.pager .disabled > a:hover, +.pager .disabled > a:focus, +.pager .disabled > span { + color: #999999; + background-color: #fff; + cursor: default; +} +.modal-backdrop { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1040; + background-color: #000000; +} +.modal-backdrop.fade { + opacity: 0; +} +.modal-backdrop, +.modal-backdrop.fade.in { + opacity: 0.8; + filter: alpha(opacity=80); +} +.modal { + position: fixed; + top: 10%; + left: 50%; + z-index: 1050; + width: 560px; + margin-left: -280px; + background-color: #ffffff; + border: 1px solid #999; + border: 1px solid rgba(0, 0, 0, 0.3); + *border: 1px solid #999; + /* IE6-7 */ + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; + -webkit-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); + -moz-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); + box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); + -webkit-background-clip: padding-box; + -moz-background-clip: padding-box; + background-clip: padding-box; + outline: none; +} +.modal.fade { + -webkit-transition: opacity .3s linear, top .3s ease-out; + -moz-transition: opacity .3s linear, top .3s ease-out; + -o-transition: opacity .3s linear, top .3s ease-out; + transition: opacity .3s linear, top .3s ease-out; + top: -25%; +} +.modal.fade.in { + top: 10%; +} +.modal-header { + padding: 9px 15px; + border-bottom: 1px solid #eee; +} +.modal-header .close { + margin-top: 2px; +} +.modal-header h3 { + margin: 0; + line-height: 30px; +} +.modal-body { + position: relative; + overflow-y: auto; + max-height: 400px; + padding: 15px; +} +.modal-form { + margin-bottom: 0; +} +.modal-footer { + padding: 14px 15px 15px; + margin-bottom: 0; + text-align: right; + background-color: #f5f5f5; + border-top: 1px solid #ddd; + -webkit-border-radius: 0 0 6px 6px; + -moz-border-radius: 0 0 6px 6px; + border-radius: 0 0 6px 6px; + -webkit-box-shadow: inset 0 1px 0 #ffffff; + -moz-box-shadow: inset 0 1px 0 #ffffff; + box-shadow: inset 0 1px 0 #ffffff; + *zoom: 1; +} +.modal-footer:before, +.modal-footer:after { + display: table; + content: ""; + line-height: 0; +} +.modal-footer:after { + clear: both; +} +.modal-footer .btn + .btn { + margin-left: 5px; + margin-bottom: 0; +} +.modal-footer .btn-group .btn + .btn { + margin-left: -1px; +} +.modal-footer .btn-block + .btn-block { + margin-left: 0; +} +.tooltip { + position: absolute; + z-index: 1030; + display: block; + visibility: visible; + font-size: 11px; + line-height: 1.4; + opacity: 0; + filter: alpha(opacity=0); +} +.tooltip.in { + opacity: 0.8; + filter: alpha(opacity=80); +} +.tooltip.top { + margin-top: -3px; + padding: 5px 0; +} +.tooltip.right { + margin-left: 3px; + padding: 0 5px; +} +.tooltip.bottom { + margin-top: 3px; + padding: 5px 0; +} +.tooltip.left { + margin-left: -3px; + padding: 0 5px; +} +.tooltip-inner { + max-width: 200px; + padding: 8px; + color: #ffffff; + text-align: center; + text-decoration: none; + background-color: #000000; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} +.tooltip-arrow { + position: absolute; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} +.tooltip.top .tooltip-arrow { + bottom: 0; + left: 50%; + margin-left: -5px; + border-width: 5px 5px 0; + border-top-color: #000000; +} +.tooltip.right .tooltip-arrow { + top: 50%; + left: 0; + margin-top: -5px; + border-width: 5px 5px 5px 0; + border-right-color: #000000; +} +.tooltip.left .tooltip-arrow { + top: 50%; + right: 0; + margin-top: -5px; + border-width: 5px 0 5px 5px; + border-left-color: #000000; +} +.tooltip.bottom .tooltip-arrow { + top: 0; + left: 50%; + margin-left: -5px; + border-width: 0 5px 5px; + border-bottom-color: #000000; +} +.popover { + position: absolute; + top: 0; + left: 0; + z-index: 1010; + display: none; + max-width: 276px; + padding: 1px; + text-align: left; + background-color: #ffffff; + -webkit-background-clip: padding-box; + -moz-background-clip: padding; + background-clip: padding-box; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, 0.2); + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; + -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + white-space: normal; +} +.popover.top { + margin-top: -10px; +} +.popover.right { + margin-left: 10px; +} +.popover.bottom { + margin-top: 10px; +} +.popover.left { + margin-left: -10px; +} +.popover-title { + margin: 0; + padding: 8px 14px; + font-size: 14px; + font-weight: normal; + line-height: 18px; + background-color: #f7f7f7; + border-bottom: 1px solid #ebebeb; + -webkit-border-radius: 5px 5px 0 0; + -moz-border-radius: 5px 5px 0 0; + border-radius: 5px 5px 0 0; +} +.popover-title:empty { + display: none; +} +.popover-content { + padding: 9px 14px; +} +.popover .arrow, +.popover .arrow:after { + position: absolute; + display: block; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} +.popover .arrow { + border-width: 11px; +} +.popover .arrow:after { + border-width: 10px; + content: ""; +} +.popover.top .arrow { + left: 50%; + margin-left: -11px; + border-bottom-width: 0; + border-top-color: #999; + border-top-color: rgba(0, 0, 0, 0.25); + bottom: -11px; +} +.popover.top .arrow:after { + bottom: 1px; + margin-left: -10px; + border-bottom-width: 0; + border-top-color: #ffffff; +} +.popover.right .arrow { + top: 50%; + left: -11px; + margin-top: -11px; + border-left-width: 0; + border-right-color: #999; + border-right-color: rgba(0, 0, 0, 0.25); +} +.popover.right .arrow:after { + left: 1px; + bottom: -10px; + border-left-width: 0; + border-right-color: #ffffff; +} +.popover.bottom .arrow { + left: 50%; + margin-left: -11px; + border-top-width: 0; + border-bottom-color: #999; + border-bottom-color: rgba(0, 0, 0, 0.25); + top: -11px; +} +.popover.bottom .arrow:after { + top: 1px; + margin-left: -10px; + border-top-width: 0; + border-bottom-color: #ffffff; +} +.popover.left .arrow { + top: 50%; + right: -11px; + margin-top: -11px; + border-right-width: 0; + border-left-color: #999; + border-left-color: rgba(0, 0, 0, 0.25); +} +.popover.left .arrow:after { + right: 1px; + border-right-width: 0; + border-left-color: #ffffff; + bottom: -10px; +} +.thumbnails { + margin-left: -20px; + list-style: none; + *zoom: 1; +} +.thumbnails:before, +.thumbnails:after { + display: table; + content: ""; + line-height: 0; +} +.thumbnails:after { + clear: both; +} +.row-fluid .thumbnails { + margin-left: 0; +} +.thumbnails > li { + float: left; + margin-bottom: 21px; + margin-left: 20px; +} +.thumbnail { + display: block; + padding: 4px; + line-height: 21px; + border: 1px solid #ddd; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055); + -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055); + -webkit-transition: all 0.2s ease-in-out; + -moz-transition: all 0.2s ease-in-out; + -o-transition: all 0.2s ease-in-out; + transition: all 0.2s ease-in-out; +} +a.thumbnail:hover, +a.thumbnail:focus { + border-color: #0088cc; + -webkit-box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25); + -moz-box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25); + box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25); +} +.thumbnail > img { + display: block; + max-width: 100%; + margin-left: auto; + margin-right: auto; +} +.thumbnail .caption { + padding: 9px; + color: #555555; +} +.media, +.media-body { + overflow: hidden; + *overflow: visible; + zoom: 1; +} +.media, +.media .media { + margin-top: 15px; +} +.media:first-child { + margin-top: 0; +} +.media-object { + display: block; +} +.media-heading { + margin: 0 0 5px; +} +.media > .pull-left { + margin-right: 10px; +} +.media > .pull-right { + margin-left: 10px; +} +.media-list { + margin-left: 0; + list-style: none; +} +.label, +.badge { + display: inline-block; + padding: 2px 4px; + font-size: 13.536px; + font-weight: bold; + line-height: 14px; + color: #ffffff; + vertical-align: baseline; + white-space: nowrap; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #999999; +} +.label { + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} +.badge { + padding-left: 9px; + padding-right: 9px; + -webkit-border-radius: 9px; + -moz-border-radius: 9px; + border-radius: 9px; +} +.label:empty, +.badge:empty { + display: none; +} +a.label:hover, +a.label:focus, +a.badge:hover, +a.badge:focus { + color: #ffffff; + text-decoration: none; + cursor: pointer; +} +.label-important, +.badge-important { + background-color: #b94a48; +} +.label-important[href], +.badge-important[href] { + background-color: #953b39; +} +.label-warning, +.badge-warning { + background-color: #f89406; +} +.label-warning[href], +.badge-warning[href] { + background-color: #c67605; +} +.label-success, +.badge-success { + background-color: #468847; +} +.label-success[href], +.badge-success[href] { + background-color: #356635; +} +.label-info, +.badge-info { + background-color: #3a87ad; +} +.label-info[href], +.badge-info[href] { + background-color: #2d6987; +} +.label-inverse, +.badge-inverse { + background-color: #333333; +} +.label-inverse[href], +.badge-inverse[href] { + background-color: #1a1a1a; +} +.btn .label, +.btn .badge { + position: relative; + top: -1px; +} +.btn-mini .label, +.btn-mini .badge { + top: 0; +} +@-webkit-keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} +@-moz-keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} +@-ms-keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} +@-o-keyframes progress-bar-stripes { + from { + background-position: 0 0; + } + to { + background-position: 40px 0; + } +} +@keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} +.progress { + overflow: hidden; + height: 21px; + margin-bottom: 21px; + background-color: #f7f7f7; + background-image: -moz-linear-gradient(top, #f5f5f5, #f9f9f9); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f5f5f5), to(#f9f9f9)); + background-image: -webkit-linear-gradient(top, #f5f5f5, #f9f9f9); + background-image: -o-linear-gradient(top, #f5f5f5, #f9f9f9); + background-image: linear-gradient(to bottom, #f5f5f5, #f9f9f9); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#fff9f9f9', GradientType=0); + -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); + -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} +.progress .bar { + width: 0%; + height: 100%; + color: #ffffff; + float: left; + font-size: 12px; + text-align: center; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); + background-color: #0e90d2; + background-image: -moz-linear-gradient(top, #149bdf, #0480be); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#149bdf), to(#0480be)); + background-image: -webkit-linear-gradient(top, #149bdf, #0480be); + background-image: -o-linear-gradient(top, #149bdf, #0480be); + background-image: linear-gradient(to bottom, #149bdf, #0480be); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff149bdf', endColorstr='#ff0480be', GradientType=0); + -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); + -moz-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + -webkit-transition: width 0.6s ease; + -moz-transition: width 0.6s ease; + -o-transition: width 0.6s ease; + transition: width 0.6s ease; +} +.progress .bar + .bar { + -webkit-box-shadow: inset 1px 0 0 rgba(0,0,0,.15), inset 0 -1px 0 rgba(0,0,0,.15); + -moz-box-shadow: inset 1px 0 0 rgba(0,0,0,.15), inset 0 -1px 0 rgba(0,0,0,.15); + box-shadow: inset 1px 0 0 rgba(0,0,0,.15), inset 0 -1px 0 rgba(0,0,0,.15); +} +.progress-striped .bar { + background-color: #149bdf; + background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + -webkit-background-size: 40px 40px; + -moz-background-size: 40px 40px; + -o-background-size: 40px 40px; + background-size: 40px 40px; +} +.progress.active .bar { + -webkit-animation: progress-bar-stripes 2s linear infinite; + -moz-animation: progress-bar-stripes 2s linear infinite; + -ms-animation: progress-bar-stripes 2s linear infinite; + -o-animation: progress-bar-stripes 2s linear infinite; + animation: progress-bar-stripes 2s linear infinite; +} +.progress-danger .bar, +.progress .bar-danger { + background-color: #dd514c; + background-image: -moz-linear-gradient(top, #ee5f5b, #c43c35); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#c43c35)); + background-image: -webkit-linear-gradient(top, #ee5f5b, #c43c35); + background-image: -o-linear-gradient(top, #ee5f5b, #c43c35); + background-image: linear-gradient(to bottom, #ee5f5b, #c43c35); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b', endColorstr='#ffc43c35', GradientType=0); +} +.progress-danger.progress-striped .bar, +.progress-striped .bar-danger { + background-color: #ee5f5b; + background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} +.progress-success .bar, +.progress .bar-success { + background-color: #5eb95e; + background-image: -moz-linear-gradient(top, #62c462, #57a957); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#57a957)); + background-image: -webkit-linear-gradient(top, #62c462, #57a957); + background-image: -o-linear-gradient(top, #62c462, #57a957); + background-image: linear-gradient(to bottom, #62c462, #57a957); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462', endColorstr='#ff57a957', GradientType=0); +} +.progress-success.progress-striped .bar, +.progress-striped .bar-success { + background-color: #62c462; + background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} +.progress-info .bar, +.progress .bar-info { + background-color: #4bb1cf; + background-image: -moz-linear-gradient(top, #5bc0de, #339bb9); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#339bb9)); + background-image: -webkit-linear-gradient(top, #5bc0de, #339bb9); + background-image: -o-linear-gradient(top, #5bc0de, #339bb9); + background-image: linear-gradient(to bottom, #5bc0de, #339bb9); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff339bb9', GradientType=0); +} +.progress-info.progress-striped .bar, +.progress-striped .bar-info { + background-color: #5bc0de; + background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} +.progress-warning .bar, +.progress .bar-warning { + background-color: #faa732; + background-image: -moz-linear-gradient(top, #fbb450, #f89406); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406)); + background-image: -webkit-linear-gradient(top, #fbb450, #f89406); + background-image: -o-linear-gradient(top, #fbb450, #f89406); + background-image: linear-gradient(to bottom, #fbb450, #f89406); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450', endColorstr='#fff89406', GradientType=0); +} +.progress-warning.progress-striped .bar, +.progress-striped .bar-warning { + background-color: #fbb450; + background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); +} +.accordion { + margin-bottom: 21px; +} +.accordion-group { + margin-bottom: 2px; + border: 1px solid #e5e5e5; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} +.accordion-heading { + border-bottom: 0; +} +.accordion-heading .accordion-toggle { + display: block; + padding: 8px 15px; +} +.accordion-toggle { + cursor: pointer; +} +.accordion-inner { + padding: 9px 15px; + border-top: 1px solid #e5e5e5; +} +.carousel { + position: relative; + margin-bottom: 21px; + line-height: 1; +} +.carousel-inner { + overflow: hidden; + width: 100%; + position: relative; +} +.carousel-inner > .item { + display: none; + position: relative; + -webkit-transition: 0.6s ease-in-out left; + -moz-transition: 0.6s ease-in-out left; + -o-transition: 0.6s ease-in-out left; + transition: 0.6s ease-in-out left; +} +.carousel-inner > .item > img, +.carousel-inner > .item > a > img { + display: block; + line-height: 1; +} +.carousel-inner > .active, +.carousel-inner > .next, +.carousel-inner > .prev { + display: block; +} +.carousel-inner > .active { + left: 0; +} +.carousel-inner > .next, +.carousel-inner > .prev { + position: absolute; + top: 0; + width: 100%; +} +.carousel-inner > .next { + left: 100%; +} +.carousel-inner > .prev { + left: -100%; +} +.carousel-inner > .next.left, +.carousel-inner > .prev.right { + left: 0; +} +.carousel-inner > .active.left { + left: -100%; +} +.carousel-inner > .active.right { + left: 100%; +} +.carousel-control { + position: absolute; + top: 40%; + left: 15px; + width: 40px; + height: 40px; + margin-top: -20px; + font-size: 60px; + font-weight: 100; + line-height: 30px; + color: #ffffff; + text-align: center; + background: #222222; + border: 3px solid #ffffff; + -webkit-border-radius: 23px; + -moz-border-radius: 23px; + border-radius: 23px; + opacity: 0.5; + filter: alpha(opacity=50); +} +.carousel-control.right { + left: auto; + right: 15px; +} +.carousel-control:hover, +.carousel-control:focus { + color: #ffffff; + text-decoration: none; + opacity: 0.9; + filter: alpha(opacity=90); +} +.carousel-indicators { + position: absolute; + top: 15px; + right: 15px; + z-index: 5; + margin: 0; + list-style: none; +} +.carousel-indicators li { + display: block; + float: left; + width: 10px; + height: 10px; + margin-left: 5px; + text-indent: -999px; + background-color: #ccc; + background-color: rgba(255, 255, 255, 0.25); + border-radius: 5px; +} +.carousel-indicators .active { + background-color: #fff; +} +.carousel-caption { + position: absolute; + left: 0; + right: 0; + bottom: 0; + padding: 15px; + background: #333333; + background: rgba(0, 0, 0, 0.75); +} +.carousel-caption h4, +.carousel-caption p { + color: #ffffff; + line-height: 21px; +} +.carousel-caption h4 { + margin: 0 0 5px; +} +.carousel-caption p { + margin-bottom: 0; +} +.hero-unit { + padding: 60px; + margin-bottom: 30px; + font-size: 18px; + font-weight: 200; + line-height: 31.5px; + color: inherit; + background-color: #eeeeee; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; +} +.hero-unit h1 { + margin-bottom: 0; + font-size: 60px; + line-height: 1; + color: inherit; + letter-spacing: -1px; +} +.hero-unit li { + line-height: 31.5px; +} +.pull-right { + float: right; +} +.pull-left { + float: left; +} +.hide { + display: none; +} +.show { + display: block; +} +.invisible { + visibility: hidden; +} +.affix { + position: fixed; +} +/*! + * Bootstrap Responsive v2.3.1 + * + * Copyright 2012 Twitter, Inc + * Licensed under the Apache License v2.0 + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Designed and built with all the love in the world @twitter by @mdo and @fat. + */ +@-ms-viewport { + width: device-width; +} +.hidden { + display: none; + visibility: hidden; +} +.visible-phone { + display: none !important; +} +.visible-tablet { + display: none !important; +} +.hidden-desktop { + display: none !important; +} +.visible-desktop { + display: inherit !important; +} +@media (min-width: 768px) and (max-width: 979px) { + .hidden-desktop { + display: inherit !important; + } + .visible-desktop { + display: none !important ; + } + .visible-tablet { + display: inherit !important; + } + .hidden-tablet { + display: none !important; + } +} +@media (max-width: 767px) { + .hidden-desktop { + display: inherit !important; + } + .visible-desktop { + display: none !important; + } + .visible-phone { + display: inherit !important; + } + .hidden-phone { + display: none !important; + } +} +.visible-print { + display: none !important; +} +@media print { + .visible-print { + display: inherit !important; + } + .hidden-print { + display: none !important; + } +} +@media (min-width: 1200px) { + .row { + margin-left: -30px; + *zoom: 1; + } + .row:before, + .row:after { + display: table; + content: ""; + line-height: 0; + } + .row:after { + clear: both; + } + [class*="span"] { + float: left; + min-height: 1px; + margin-left: 30px; + } + .container, + .navbar-static-top .container, + .navbar-fixed-top .container, + .navbar-fixed-bottom .container { + width: 1170px; + } + .span12 { + width: 1170px; + } + .span11 { + width: 1070px; + } + .span10 { + width: 970px; + } + .span9 { + width: 870px; + } + .span8 { + width: 770px; + } + .span7 { + width: 670px; + } + .span6 { + width: 570px; + } + .span5 { + width: 470px; + } + .span4 { + width: 370px; + } + .span3 { + width: 270px; + } + .span2 { + width: 170px; + } + .span1 { + width: 70px; + } + .offset12 { + margin-left: 1230px; + } + .offset11 { + margin-left: 1130px; + } + .offset10 { + margin-left: 1030px; + } + .offset9 { + margin-left: 930px; + } + .offset8 { + margin-left: 830px; + } + .offset7 { + margin-left: 730px; + } + .offset6 { + margin-left: 630px; + } + .offset5 { + margin-left: 530px; + } + .offset4 { + margin-left: 430px; + } + .offset3 { + margin-left: 330px; + } + .offset2 { + margin-left: 230px; + } + .offset1 { + margin-left: 130px; + } + .row-fluid { + width: 100%; + *zoom: 1; + } + .row-fluid:before, + .row-fluid:after { + display: table; + content: ""; + line-height: 0; + } + .row-fluid:after { + clear: both; + } + .row-fluid [class*="span"] { + display: block; + width: 100%; + min-height: 31px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + float: left; + margin-left: 2.56410256%; + *margin-left: 2.51091107%; + } + .row-fluid [class*="span"]:first-child { + margin-left: 0; + } + .row-fluid .controls-row [class*="span"] + [class*="span"] { + margin-left: 2.56410256%; + } + .row-fluid .span12 { + width: 100%; + *width: 99.94680851%; + } + .row-fluid .span11 { + width: 91.45299145%; + *width: 91.39979996%; + } + .row-fluid .span10 { + width: 82.90598291%; + *width: 82.85279142%; + } + .row-fluid .span9 { + width: 74.35897436%; + *width: 74.30578287%; + } + .row-fluid .span8 { + width: 65.81196581%; + *width: 65.75877432%; + } + .row-fluid .span7 { + width: 57.26495726%; + *width: 57.21176578%; + } + .row-fluid .span6 { + width: 48.71794872%; + *width: 48.66475723%; + } + .row-fluid .span5 { + width: 40.17094017%; + *width: 40.11774868%; + } + .row-fluid .span4 { + width: 31.62393162%; + *width: 31.57074013%; + } + .row-fluid .span3 { + width: 23.07692308%; + *width: 23.02373159%; + } + .row-fluid .span2 { + width: 14.52991453%; + *width: 14.47672304%; + } + .row-fluid .span1 { + width: 5.98290598%; + *width: 5.92971449%; + } + .row-fluid .offset12 { + margin-left: 105.12820513%; + *margin-left: 105.02182215%; + } + .row-fluid .offset12:first-child { + margin-left: 102.56410256%; + *margin-left: 102.45771959%; + } + .row-fluid .offset11 { + margin-left: 96.58119658%; + *margin-left: 96.4748136%; + } + .row-fluid .offset11:first-child { + margin-left: 94.01709402%; + *margin-left: 93.91071104%; + } + .row-fluid .offset10 { + margin-left: 88.03418803%; + *margin-left: 87.92780506%; + } + .row-fluid .offset10:first-child { + margin-left: 85.47008547%; + *margin-left: 85.36370249%; + } + .row-fluid .offset9 { + margin-left: 79.48717949%; + *margin-left: 79.38079651%; + } + .row-fluid .offset9:first-child { + margin-left: 76.92307692%; + *margin-left: 76.81669394%; + } + .row-fluid .offset8 { + margin-left: 70.94017094%; + *margin-left: 70.83378796%; + } + .row-fluid .offset8:first-child { + margin-left: 68.37606838%; + *margin-left: 68.2696854%; + } + .row-fluid .offset7 { + margin-left: 62.39316239%; + *margin-left: 62.28677941%; + } + .row-fluid .offset7:first-child { + margin-left: 59.82905983%; + *margin-left: 59.72267685%; + } + .row-fluid .offset6 { + margin-left: 53.84615385%; + *margin-left: 53.73977087%; + } + .row-fluid .offset6:first-child { + margin-left: 51.28205128%; + *margin-left: 51.1756683%; + } + .row-fluid .offset5 { + margin-left: 45.2991453%; + *margin-left: 45.19276232%; + } + .row-fluid .offset5:first-child { + margin-left: 42.73504274%; + *margin-left: 42.62865976%; + } + .row-fluid .offset4 { + margin-left: 36.75213675%; + *margin-left: 36.64575377%; + } + .row-fluid .offset4:first-child { + margin-left: 34.18803419%; + *margin-left: 34.08165121%; + } + .row-fluid .offset3 { + margin-left: 28.20512821%; + *margin-left: 28.09874523%; + } + .row-fluid .offset3:first-child { + margin-left: 25.64102564%; + *margin-left: 25.53464266%; + } + .row-fluid .offset2 { + margin-left: 19.65811966%; + *margin-left: 19.55173668%; + } + .row-fluid .offset2:first-child { + margin-left: 17.09401709%; + *margin-left: 16.98763412%; + } + .row-fluid .offset1 { + margin-left: 11.11111111%; + *margin-left: 11.00472813%; + } + .row-fluid .offset1:first-child { + margin-left: 8.54700855%; + *margin-left: 8.44062557%; + } + input, + textarea, + .uneditable-input { + margin-left: 0; + } + .controls-row [class*="span"] + [class*="span"] { + margin-left: 30px; + } + input.span12, + textarea.span12, + .uneditable-input.span12 { + width: 1156px; + } + input.span11, + textarea.span11, + .uneditable-input.span11 { + width: 1056px; + } + input.span10, + textarea.span10, + .uneditable-input.span10 { + width: 956px; + } + input.span9, + textarea.span9, + .uneditable-input.span9 { + width: 856px; + } + input.span8, + textarea.span8, + .uneditable-input.span8 { + width: 756px; + } + input.span7, + textarea.span7, + .uneditable-input.span7 { + width: 656px; + } + input.span6, + textarea.span6, + .uneditable-input.span6 { + width: 556px; + } + input.span5, + textarea.span5, + .uneditable-input.span5 { + width: 456px; + } + input.span4, + textarea.span4, + .uneditable-input.span4 { + width: 356px; + } + input.span3, + textarea.span3, + .uneditable-input.span3 { + width: 256px; + } + input.span2, + textarea.span2, + .uneditable-input.span2 { + width: 156px; + } + input.span1, + textarea.span1, + .uneditable-input.span1 { + width: 56px; + } + .thumbnails { + margin-left: -30px; + } + .thumbnails > li { + margin-left: 30px; + } + .row-fluid .thumbnails { + margin-left: 0; + } +} +@media (min-width: 768px) and (max-width: 979px) { + .row { + margin-left: -20px; + *zoom: 1; + } + .row:before, + .row:after { + display: table; + content: ""; + line-height: 0; + } + .row:after { + clear: both; + } + [class*="span"] { + float: left; + min-height: 1px; + margin-left: 20px; + } + .container, + .navbar-static-top .container, + .navbar-fixed-top .container, + .navbar-fixed-bottom .container { + width: 724px; + } + .span12 { + width: 724px; + } + .span11 { + width: 662px; + } + .span10 { + width: 600px; + } + .span9 { + width: 538px; + } + .span8 { + width: 476px; + } + .span7 { + width: 414px; + } + .span6 { + width: 352px; + } + .span5 { + width: 290px; + } + .span4 { + width: 228px; + } + .span3 { + width: 166px; + } + .span2 { + width: 104px; + } + .span1 { + width: 42px; + } + .offset12 { + margin-left: 764px; + } + .offset11 { + margin-left: 702px; + } + .offset10 { + margin-left: 640px; + } + .offset9 { + margin-left: 578px; + } + .offset8 { + margin-left: 516px; + } + .offset7 { + margin-left: 454px; + } + .offset6 { + margin-left: 392px; + } + .offset5 { + margin-left: 330px; + } + .offset4 { + margin-left: 268px; + } + .offset3 { + margin-left: 206px; + } + .offset2 { + margin-left: 144px; + } + .offset1 { + margin-left: 82px; + } + .row-fluid { + width: 100%; + *zoom: 1; + } + .row-fluid:before, + .row-fluid:after { + display: table; + content: ""; + line-height: 0; + } + .row-fluid:after { + clear: both; + } + .row-fluid [class*="span"] { + display: block; + width: 100%; + min-height: 31px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + float: left; + margin-left: 2.76243094%; + *margin-left: 2.70923945%; + } + .row-fluid [class*="span"]:first-child { + margin-left: 0; + } + .row-fluid .controls-row [class*="span"] + [class*="span"] { + margin-left: 2.76243094%; + } + .row-fluid .span12 { + width: 100%; + *width: 99.94680851%; + } + .row-fluid .span11 { + width: 91.43646409%; + *width: 91.3832726%; + } + .row-fluid .span10 { + width: 82.87292818%; + *width: 82.81973669%; + } + .row-fluid .span9 { + width: 74.30939227%; + *width: 74.25620078%; + } + .row-fluid .span8 { + width: 65.74585635%; + *width: 65.69266486%; + } + .row-fluid .span7 { + width: 57.18232044%; + *width: 57.12912895%; + } + .row-fluid .span6 { + width: 48.61878453%; + *width: 48.56559304%; + } + .row-fluid .span5 { + width: 40.05524862%; + *width: 40.00205713%; + } + .row-fluid .span4 { + width: 31.49171271%; + *width: 31.43852122%; + } + .row-fluid .span3 { + width: 22.9281768%; + *width: 22.87498531%; + } + .row-fluid .span2 { + width: 14.36464088%; + *width: 14.31144939%; + } + .row-fluid .span1 { + width: 5.80110497%; + *width: 5.74791348%; + } + .row-fluid .offset12 { + margin-left: 105.52486188%; + *margin-left: 105.4184789%; + } + .row-fluid .offset12:first-child { + margin-left: 102.76243094%; + *margin-left: 102.65604796%; + } + .row-fluid .offset11 { + margin-left: 96.96132597%; + *margin-left: 96.85494299%; + } + .row-fluid .offset11:first-child { + margin-left: 94.19889503%; + *margin-left: 94.09251205%; + } + .row-fluid .offset10 { + margin-left: 88.39779006%; + *margin-left: 88.29140708%; + } + .row-fluid .offset10:first-child { + margin-left: 85.63535912%; + *margin-left: 85.52897614%; + } + .row-fluid .offset9 { + margin-left: 79.83425414%; + *margin-left: 79.72787116%; + } + .row-fluid .offset9:first-child { + margin-left: 77.0718232%; + *margin-left: 76.96544023%; + } + .row-fluid .offset8 { + margin-left: 71.27071823%; + *margin-left: 71.16433525%; + } + .row-fluid .offset8:first-child { + margin-left: 68.50828729%; + *margin-left: 68.40190431%; + } + .row-fluid .offset7 { + margin-left: 62.70718232%; + *margin-left: 62.60079934%; + } + .row-fluid .offset7:first-child { + margin-left: 59.94475138%; + *margin-left: 59.8383684%; + } + .row-fluid .offset6 { + margin-left: 54.14364641%; + *margin-left: 54.03726343%; + } + .row-fluid .offset6:first-child { + margin-left: 51.38121547%; + *margin-left: 51.27483249%; + } + .row-fluid .offset5 { + margin-left: 45.5801105%; + *margin-left: 45.47372752%; + } + .row-fluid .offset5:first-child { + margin-left: 42.81767956%; + *margin-left: 42.71129658%; + } + .row-fluid .offset4 { + margin-left: 37.01657459%; + *margin-left: 36.91019161%; + } + .row-fluid .offset4:first-child { + margin-left: 34.25414365%; + *margin-left: 34.14776067%; + } + .row-fluid .offset3 { + margin-left: 28.45303867%; + *margin-left: 28.3466557%; + } + .row-fluid .offset3:first-child { + margin-left: 25.69060773%; + *margin-left: 25.58422476%; + } + .row-fluid .offset2 { + margin-left: 19.88950276%; + *margin-left: 19.78311978%; + } + .row-fluid .offset2:first-child { + margin-left: 17.12707182%; + *margin-left: 17.02068884%; + } + .row-fluid .offset1 { + margin-left: 11.32596685%; + *margin-left: 11.21958387%; + } + .row-fluid .offset1:first-child { + margin-left: 8.56353591%; + *margin-left: 8.45715293%; + } + input, + textarea, + .uneditable-input { + margin-left: 0; + } + .controls-row [class*="span"] + [class*="span"] { + margin-left: 20px; + } + input.span12, + textarea.span12, + .uneditable-input.span12 { + width: 710px; + } + input.span11, + textarea.span11, + .uneditable-input.span11 { + width: 648px; + } + input.span10, + textarea.span10, + .uneditable-input.span10 { + width: 586px; + } + input.span9, + textarea.span9, + .uneditable-input.span9 { + width: 524px; + } + input.span8, + textarea.span8, + .uneditable-input.span8 { + width: 462px; + } + input.span7, + textarea.span7, + .uneditable-input.span7 { + width: 400px; + } + input.span6, + textarea.span6, + .uneditable-input.span6 { + width: 338px; + } + input.span5, + textarea.span5, + .uneditable-input.span5 { + width: 276px; + } + input.span4, + textarea.span4, + .uneditable-input.span4 { + width: 214px; + } + input.span3, + textarea.span3, + .uneditable-input.span3 { + width: 152px; + } + input.span2, + textarea.span2, + .uneditable-input.span2 { + width: 90px; + } + input.span1, + textarea.span1, + .uneditable-input.span1 { + width: 28px; + } +} +@media (max-width: 767px) { + body { + padding-left: 20px; + padding-right: 20px; + } + .navbar-fixed-top, + .navbar-fixed-bottom, + .navbar-static-top { + margin-left: -20px; + margin-right: -20px; + } + .container-fluid { + padding: 0; + } + .dl-horizontal dt { + float: none; + clear: none; + width: auto; + text-align: left; + } + .dl-horizontal dd { + margin-left: 0; + } + .container { + width: auto; + } + .row-fluid { + width: 100%; + } + .row, + .thumbnails { + margin-left: 0; + } + .thumbnails > li { + float: none; + margin-left: 0; + } + [class*="span"], + .uneditable-input[class*="span"], + .row-fluid [class*="span"] { + float: none; + display: block; + width: 100%; + margin-left: 0; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + } + .span12, + .row-fluid .span12 { + width: 100%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + } + .row-fluid [class*="offset"]:first-child { + margin-left: 0; + } + .input-large, + .input-xlarge, + .input-xxlarge, + input[class*="span"], + select[class*="span"], + textarea[class*="span"], + .uneditable-input { + display: block; + width: 100%; + min-height: 31px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + } + .input-prepend input, + .input-append input, + .input-prepend input[class*="span"], + .input-append input[class*="span"] { + display: inline-block; + width: auto; + } + .controls-row [class*="span"] + [class*="span"] { + margin-left: 0; + } + .modal { + position: fixed; + top: 20px; + left: 20px; + right: 20px; + width: auto; + margin: 0; + } + .modal.fade { + top: -100px; + } + .modal.fade.in { + top: 20px; + } +} +@media (max-width: 480px) { + .nav-collapse { + -webkit-transform: translate3d(0, 0, 0); + } + .page-header h1 small { + display: block; + line-height: 21px; + } + input[type="checkbox"], + input[type="radio"] { + border: 1px solid #ccc; + } + .form-horizontal .control-label { + float: none; + width: auto; + padding-top: 0; + text-align: left; + } + .form-horizontal .controls { + margin-left: 0; + } + .form-horizontal .control-list { + padding-top: 0; + } + .form-horizontal .form-actions { + padding-left: 10px; + padding-right: 10px; + } + .media .pull-left, + .media .pull-right { + float: none; + display: block; + margin-bottom: 10px; + } + .media-object { + margin-right: 0; + margin-left: 0; + } + .modal { + top: 10px; + left: 10px; + right: 10px; + } + .modal-header .close { + padding: 10px; + margin: -10px; + } + .carousel-caption { + position: static; + } +} +@media (max-width: 979px) { + body { + padding-top: 0; + } + .navbar-fixed-top, + .navbar-fixed-bottom { + position: static; + } + .navbar-fixed-top { + margin-bottom: 21px; + } + .navbar-fixed-bottom { + margin-top: 21px; + } + .navbar-fixed-top .navbar-inner, + .navbar-fixed-bottom .navbar-inner { + padding: 5px; + } + .navbar .container { + width: auto; + padding: 0; + } + .navbar .brand { + padding-left: 10px; + padding-right: 10px; + margin: 0 0 0 -5px; + } + .nav-collapse { + clear: both; + } + .nav-collapse .nav { + float: none; + margin: 0 0 10.5px; + } + .nav-collapse .nav > li { + float: none; + } + .nav-collapse .nav > li > a { + margin-bottom: 2px; + } + .nav-collapse .nav > .divider-vertical { + display: none; + } + .nav-collapse .nav .nav-header { + color: #777777; + text-shadow: none; + } + .nav-collapse .nav > li > a, + .nav-collapse .dropdown-menu a { + padding: 9px 15px; + font-weight: bold; + color: #777777; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; + } + .nav-collapse .btn { + padding: 4px 10px 4px; + font-weight: normal; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + } + .nav-collapse .dropdown-menu li + li a { + margin-bottom: 2px; + } + .nav-collapse .nav > li > a:hover, + .nav-collapse .nav > li > a:focus, + .nav-collapse .dropdown-menu a:hover, + .nav-collapse .dropdown-menu a:focus { + background-color: #f2f2f2; + } + .navbar-inverse .nav-collapse .nav > li > a, + .navbar-inverse .nav-collapse .dropdown-menu a { + color: #999999; + } + .navbar-inverse .nav-collapse .nav > li > a:hover, + .navbar-inverse .nav-collapse .nav > li > a:focus, + .navbar-inverse .nav-collapse .dropdown-menu a:hover, + .navbar-inverse .nav-collapse .dropdown-menu a:focus { + background-color: #111111; + } + .nav-collapse.in .btn-group { + margin-top: 5px; + padding: 0; + } + .nav-collapse .dropdown-menu { + position: static; + top: auto; + left: auto; + float: none; + display: none; + max-width: none; + margin: 0 15px; + padding: 0; + background-color: transparent; + border: none; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; + } + .nav-collapse .open > .dropdown-menu { + display: block; + } + .nav-collapse .dropdown-menu:before, + .nav-collapse .dropdown-menu:after { + display: none; + } + .nav-collapse .dropdown-menu .divider { + display: none; + } + .nav-collapse .nav > li > .dropdown-menu:before, + .nav-collapse .nav > li > .dropdown-menu:after { + display: none; + } + .nav-collapse .navbar-form, + .nav-collapse .navbar-search { + float: none; + padding: 10.5px 15px; + margin: 10.5px 0; + border-top: 1px solid #f2f2f2; + border-bottom: 1px solid #f2f2f2; + -webkit-box-shadow: inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.1); + -moz-box-shadow: inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.1); + box-shadow: inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.1); + } + .navbar-inverse .nav-collapse .navbar-form, + .navbar-inverse .nav-collapse .navbar-search { + border-top-color: #111111; + border-bottom-color: #111111; + } + .navbar .nav-collapse .nav.pull-right { + float: none; + margin-left: 0; + } + .nav-collapse, + .nav-collapse.collapse { + overflow: hidden; + height: 0; + } + .navbar .btn-navbar { + display: block; + } + .navbar-static .navbar-inner { + padding-left: 10px; + padding-right: 10px; + } +} +@media (min-width: 979px + 1) { + .nav-collapse.collapse { + height: auto !important; + overflow: visible !important; + } +} diff --git a/_static/css/spc-extend.css b/_static/css/spc-extend.css new file mode 100644 index 000000000..264a62bfb --- /dev/null +++ b/_static/css/spc-extend.css @@ -0,0 +1,92 @@ +body { + background-color: white; +} +.container { + width: 90%; +} +.underline { + border-bottom: 1.5px solid #eeeeee; +} +.header { + margin-top: 15px; + margin-bottom: 15px; + margin-left: 0px; + margin-right: 0px; +} +.spc-navbar { + margin-top: 15px; + margin-bottom: 5px; + margin-left: 0px; + margin-right: 0px; +} +.spc-navbar .nav-pills { + margin-bottom: 0px; + font-size: 12px; +} +.spc-navbar .nav-pills > li > a { + padding-top: 2.5px; + padding-bottom: 2.5px; +} +.spc-page-title h1, +.spc-page-title h2, +.spc-page-title h3, +.spc-page-title h4 { + font-weight: normal; + border-bottom: 1.5px solid #eeeeee; +} +.tags .btn { + border: none; + font-size: 9.5px; + font-weight: bold; +} +.spc-search-result-title h1, +.spc-search-result-title h2, +.spc-search-result-title h3, +.spc-search-result-title h4 { + font-weight: normal; +} +.spc-snippet-header { + margin-bottom: 5px; +} +.spc-snippet-info { + padding-top: 10px; +} +.spc-snippet-info .dl-horizontal { + margin: 5px; +} +.spc-snippet-info .dl-horizontal dt { + font-weight: normal; +} +.spc-snippet-body { + padding: 10px; +} +.spc-snippet-body .accordion-group { + border: none; +} +.spc-snippet-body .accordion-heading { + text-transform: uppercase; + font-size: 14px; + border-bottom: 1px solid #e5e5e5; +} +.spc-snippet-body .accordion-heading .accordion-toggle { + padding-top: 10px; + padding-bottom: 5px; +} +.spc-rightsidebar { + color: #555555; +} +.spc-rightsidebar .navigation { + padding: 2px 10px; + font-size: 11.9px; +} +.spc-rightsidebar .navigation .nav-title { + font-weight: bold; + text-transform: uppercase; +} +.spc-rightsidebar .navigation li { + margin: 5px; +} +.footer { + padding: 5px; + font-size: small; +} diff --git a/_static/doctools.js b/_static/doctools.js new file mode 100644 index 000000000..b33f87fcb --- /dev/null +++ b/_static/doctools.js @@ -0,0 +1,314 @@ +/* + * doctools.js + * ~~~~~~~~~~~ + * + * Sphinx JavaScript utilities for all documentation. + * + * :copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +/** + * select a different prefix for underscore + */ +$u = _.noConflict(); + +/** + * make the code below compatible with browsers without + * an installed firebug like debugger +if (!window.console || !console.firebug) { + var names = ["log", "debug", "info", "warn", "error", "assert", "dir", + "dirxml", "group", "groupEnd", "time", "timeEnd", "count", "trace", + "profile", "profileEnd"]; + window.console = {}; + for (var i = 0; i < names.length; ++i) + window.console[names[i]] = function() {}; +} + */ + +/** + * small helper function to urldecode strings + */ +jQuery.urldecode = function(x) { + return decodeURIComponent(x).replace(/\+/g, ' '); +}; + +/** + * small helper function to urlencode strings + */ +jQuery.urlencode = encodeURIComponent; + +/** + * This function returns the parsed url parameters of the + * current request. Multiple values per key are supported, + * it will always return arrays of strings for the value parts. + */ +jQuery.getQueryParameters = function(s) { + if (typeof s === 'undefined') + s = document.location.search; + var parts = s.substr(s.indexOf('?') + 1).split('&'); + var result = {}; + for (var i = 0; i < parts.length; i++) { + var tmp = parts[i].split('=', 2); + var key = jQuery.urldecode(tmp[0]); + var value = jQuery.urldecode(tmp[1]); + if (key in result) + result[key].push(value); + else + result[key] = [value]; + } + return result; +}; + +/** + * highlight a given string on a jquery object by wrapping it in + * span elements with the given class name. + */ +jQuery.fn.highlightText = function(text, className) { + function highlight(node, addItems) { + if (node.nodeType === 3) { + var val = node.nodeValue; + var pos = val.toLowerCase().indexOf(text); + if (pos >= 0 && + !jQuery(node.parentNode).hasClass(className) && + !jQuery(node.parentNode).hasClass("nohighlight")) { + var span; + var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg"); + if (isInSVG) { + span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); + } else { + span = document.createElement("span"); + span.className = className; + } + span.appendChild(document.createTextNode(val.substr(pos, text.length))); + node.parentNode.insertBefore(span, node.parentNode.insertBefore( + document.createTextNode(val.substr(pos + text.length)), + node.nextSibling)); + node.nodeValue = val.substr(0, pos); + if (isInSVG) { + var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect"); + var bbox = node.parentElement.getBBox(); + rect.x.baseVal.value = bbox.x; + rect.y.baseVal.value = bbox.y; + rect.width.baseVal.value = bbox.width; + rect.height.baseVal.value = bbox.height; + rect.setAttribute('class', className); + addItems.push({ + "parent": node.parentNode, + "target": rect}); + } + } + } + else if (!jQuery(node).is("button, select, textarea")) { + jQuery.each(node.childNodes, function() { + highlight(this, addItems); + }); + } + } + var addItems = []; + var result = this.each(function() { + highlight(this, addItems); + }); + for (var i = 0; i < addItems.length; ++i) { + jQuery(addItems[i].parent).before(addItems[i].target); + } + return result; +}; + +/* + * backward compatibility for jQuery.browser + * This will be supported until firefox bug is fixed. + */ +if (!jQuery.browser) { + jQuery.uaMatch = function(ua) { + ua = ua.toLowerCase(); + + var match = /(chrome)[ \/]([\w.]+)/.exec(ua) || + /(webkit)[ \/]([\w.]+)/.exec(ua) || + /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || + /(msie) ([\w.]+)/.exec(ua) || + ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || + []; + + return { + browser: match[ 1 ] || "", + version: match[ 2 ] || "0" + }; + }; + jQuery.browser = {}; + jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true; +} + +/** + * Small JavaScript module for the documentation. + */ +var Documentation = { + + init : function() { + this.fixFirefoxAnchorBug(); + this.highlightSearchWords(); + this.initIndexTable(); + if (DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) { + this.initOnKeyListeners(); + } + }, + + /** + * i18n support + */ + TRANSLATIONS : {}, + PLURAL_EXPR : function(n) { return n === 1 ? 0 : 1; }, + LOCALE : 'unknown', + + // gettext and ngettext don't access this so that the functions + // can safely bound to a different name (_ = Documentation.gettext) + gettext : function(string) { + var translated = Documentation.TRANSLATIONS[string]; + if (typeof translated === 'undefined') + return string; + return (typeof translated === 'string') ? translated : translated[0]; + }, + + ngettext : function(singular, plural, n) { + var translated = Documentation.TRANSLATIONS[singular]; + if (typeof translated === 'undefined') + return (n == 1) ? singular : plural; + return translated[Documentation.PLURALEXPR(n)]; + }, + + addTranslations : function(catalog) { + for (var key in catalog.messages) + this.TRANSLATIONS[key] = catalog.messages[key]; + this.PLURAL_EXPR = new Function('n', 'return +(' + catalog.plural_expr + ')'); + this.LOCALE = catalog.locale; + }, + + /** + * add context elements like header anchor links + */ + addContextElements : function() { + $('div[id] > :header:first').each(function() { + $('\u00B6'). + attr('href', '#' + this.id). + attr('title', _('Permalink to this headline')). + appendTo(this); + }); + $('dt[id]').each(function() { + $('\u00B6'). + attr('href', '#' + this.id). + attr('title', _('Permalink to this definition')). + appendTo(this); + }); + }, + + /** + * workaround a firefox stupidity + * see: https://bugzilla.mozilla.org/show_bug.cgi?id=645075 + */ + fixFirefoxAnchorBug : function() { + if (document.location.hash && $.browser.mozilla) + window.setTimeout(function() { + document.location.href += ''; + }, 10); + }, + + /** + * highlight the search words provided in the url in the text + */ + highlightSearchWords : function() { + var params = $.getQueryParameters(); + var terms = (params.highlight) ? params.highlight[0].split(/\s+/) : []; + if (terms.length) { + var body = $('div.body'); + if (!body.length) { + body = $('body'); + } + window.setTimeout(function() { + $.each(terms, function() { + body.highlightText(this.toLowerCase(), 'highlighted'); + }); + }, 10); + $('') + .appendTo($('#searchbox')); + } + }, + + /** + * init the domain index toggle buttons + */ + initIndexTable : function() { + var togglers = $('img.toggler').click(function() { + var src = $(this).attr('src'); + var idnum = $(this).attr('id').substr(7); + $('tr.cg-' + idnum).toggle(); + if (src.substr(-9) === 'minus.png') + $(this).attr('src', src.substr(0, src.length-9) + 'plus.png'); + else + $(this).attr('src', src.substr(0, src.length-8) + 'minus.png'); + }).css('display', ''); + if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) { + togglers.click(); + } + }, + + /** + * helper function to hide the search marks again + */ + hideSearchWords : function() { + $('#searchbox .highlight-link').fadeOut(300); + $('span.highlighted').removeClass('highlighted'); + }, + + /** + * make the url absolute + */ + makeURL : function(relativeURL) { + return DOCUMENTATION_OPTIONS.URL_ROOT + '/' + relativeURL; + }, + + /** + * get the current relative url + */ + getCurrentURL : function() { + var path = document.location.pathname; + var parts = path.split(/\//); + $.each(DOCUMENTATION_OPTIONS.URL_ROOT.split(/\//), function() { + if (this === '..') + parts.pop(); + }); + var url = parts.join('/'); + return path.substring(url.lastIndexOf('/') + 1, path.length - 1); + }, + + initOnKeyListeners: function() { + $(document).keyup(function(event) { + var activeElementType = document.activeElement.tagName; + // don't navigate when in search box or textarea + if (activeElementType !== 'TEXTAREA' && activeElementType !== 'INPUT' && activeElementType !== 'SELECT') { + switch (event.keyCode) { + case 37: // left + var prevHref = $('link[rel="prev"]').prop('href'); + if (prevHref) { + window.location.href = prevHref; + return false; + } + case 39: // right + var nextHref = $('link[rel="next"]').prop('href'); + if (nextHref) { + window.location.href = nextHref; + return false; + } + } + } + }); + } +}; + +// quick alias for translations +_ = Documentation.gettext; + +$(document).ready(function() { + Documentation.init(); +}); diff --git a/_static/documentation_options.js b/_static/documentation_options.js new file mode 100644 index 000000000..3ceefbbee --- /dev/null +++ b/_static/documentation_options.js @@ -0,0 +1,11 @@ +var DOCUMENTATION_OPTIONS = { + URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'), + VERSION: 'unknown', + LANGUAGE: 'None', + COLLAPSE_INDEX: false, + BUILDER: 'html', + FILE_SUFFIX: '.html', + HAS_SOURCE: true, + SOURCELINK_SUFFIX: '.txt', + NAVIGATION_WITH_KEYS: false +}; \ No newline at end of file diff --git a/_static/enthought.css b/_static/enthought.css new file mode 100644 index 000000000..32ddcacb7 --- /dev/null +++ b/_static/enthought.css @@ -0,0 +1,412 @@ +/* -*- css -*- + * + * sphinxdoc.css_t + * ~~~~~~~~~~~~~~~ + * + * Sphinx stylesheet -- sphinxdoc theme. Originally created by + * Armin Ronacher for Werkzeug. + * + * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +@import url("basic.css"); + +/* @import url("css/scipy-central.css"); */ + +@font-face { + font-family: 'Source Code Pro'; + font-weight: 400; + font-style: normal; + font-stretch: normal; + src: local('Source Code Pro'), local('SourceCodePro-Regular'), url('fonts/SourceCodePro-Regular.otf.woff') format('woff'); +} + +@font-face { + font-family: 'Source Code Pro'; + font-weight: 600; + font-style: normal; + font-stretch: normal; + src: local('Source Code Pro Semibold'), local('SourceCodePro-Semibold'), url('fonts/SourceCodePro-Semibold.otf.woff') format('woff'); +} + +@font-face{ + font-family: 'Source Sans Pro'; + font-weight: 400; + font-style: normal; + font-stretch: normal; + src: local('Source Sans Pro'), local('SourceSansPro-Regular'), url('fonts/SourceSansPro-Regular.otf.woff') format('woff'); +} + +@font-face{ + font-family: 'Source Sans Pro'; + font-weight: 600; + font-style: normal; + font-stretch: normal; + src: local('Source Sans Pro Semibold'), local('SourceSansPro-Semibold'), url('fonts/SourceSansPro-Semibold.otf.woff') format('woff'); +} + +@font-face{ + font-family: 'Source Sans Pro'; + font-weight: 400; + font-style: italic; + font-stretch: normal; + src: local('Source Sans Pro Italic'), local('SourceSansPro-It'), url('fonts/SourceSansPro-It.otf.woff') format('woff'); +} + +@font-face{ + font-family: 'Source Sans Pro'; + font-weight: 600; + font-style: italic; + font-stretch: normal; + src: local('Source Sans Pro Semibold Italic'), local('SourceSansPro-SemiboldIt'), url('fonts/SourceSansPro-SemiboldIt.otf.woff') format('woff'); +} + +/* + * General tweaks + */ + +div.container-navbar-bottom { + margin-top: 0; +} + +div.container-navbar-bottom div.spc-navbar { + margin-top: 0; +} + +div.spc-navbar { + margin: 0; +} + +tt { + color: inherit; + font: inherit; +} + +tt.literal { + font-family: 'Source Code Pro', monospace; + padding-left: 2px; + background-color: rgb(242, 242, 242); +} + +a tt.literal { + border-bottom: none; + background-color: inherit; +} + +tt.xref { + border-bottom: none; + background-color: inherit; + font-weight: inherit; + padding-left: 0px; +} + +tt.descclassname { + font-family: 'Source Code Pro', monospace; +} + +tt.descname { + font-family: 'Source Code Pro', monospace; + font-size: 20px; +} + +code { + color: inherit; + font: inherit; + padding: 2px 0px; + border: inherit; +} + +code.literal { + font-family: monospace; + padding-left: 2px; + background-color: rgb(242, 242, 242); +} + +a code.literal { + border: none; + background-color: inherit; +} + +code.xref { + font-family: inherit; + border-bottom: none; + background-color: inherit; + padding-left: 0px; +} + +code.descname { + font-size: 16px; +} + +dl.class, +dl.function, +dl.data +{ + border-top: 2px solid #888; + padding-top: 1px; +} + +dl.attribute, +dl.classmethod, +dl.staticmethod, +dl.method +{ + border-top: 1px solid #aaa; + padding-top: 1px; +} + +dl.attribute > dt > tt.descname, +dl.classmethod > dt > tt.descname, +dl.staticmethod > dt > tt.descname, +dl.method > dt > tt.descname +{ + font-size: inherit; +} + +dl.class > dt, +dl.attribute > dt, +dl.data > dt, +dl.function > dt, +dl.exception > dt, +dl.classmethod > dt, +dl.staticmethod > dt, +dl.method > dt +{ + font-weight: normal; + /* Fake a hanging indent. If the text has to linewrap, it will indent more + * than the following
. + */ + text-indent: -60px; + padding-left: 60px; +} + +pre { + border-radius: 0; + border: none; + font-family: 'Source Code Pro', monospace; +} + +.nav-pills > .active > a, +.nav-pills > .active > a:hover, +.nav-pills > .active > a:focus { + color: #ffffff; + background-color: #253370; +} + +.nav-pills.pull-right > li { + float: right; +} + +a { + color: #253370; + text-decoration: none; +} + +/* + * Field lists + */ + +table.field-list { + border-collapse: collapse; + border-spacing: 5px; + margin-left: 1px; + border-left: 5px solid #DFDFDF !important; +} + +table.field-list th.field-name { + display: block; + text-align: left; + padding: 1px 8px 1px 5px; + white-space: nowrap; + background-color: #DFDFDF; +} + +table.field-list td.field-body { + display: block; + border-left: none !important; + padding-left: 1em; +} + +table.field-list td.field-body > ul { + padding-left: 1em; +} + +table.field-list td.field-body > p, +table.field-list td.field-body > ul > li > p +{ + display: inline; +} + +table.field-list td.field-body > p > strong { + font-style: normal; +} + +td.field-body blockquote { + border-left: none; + margin: 0; + padding-left: 30px; +} + +td.field-body blockquote p, +dl.class blockquote p, +dl.function blockquote p, +dl.classmethod blockquote p, +dl.staticmethod blockquote p, +dl.method blockquote p +{ + font-family: inherit; + font-size: inherit; + font-weight: inherit; + line-height: inherit; +} + + +/* + * Sidebars and top logo + */ + +div.sphinxsidebarwrapper { + overflow: hidden; +} + +div.sphinxsidebarwrapper p.logo { + text-align: center; +} + +div.spc-rightsidebar h3 { + font-size: 120%; + line-height: inherit; + border-bottom: 1px solid #656565; +} + +div.spc-rightsidebar h4 { + font-size: 110%; + line-height: inherit; + border-bottom: 1px solid #656565; +} + +form.search > input[type="text"] { + width: auto; +} + +/* + * Headers + */ + +h1 a { color: #333333; } +h2 a { color: #333333; } +h3 a { color: #333333; } +h4 a { color: #333333; } +h5 a { color: #333333; } +h6 a { color: #333333; } + +h1 tt { font: inherit; border-bottom: none; } +h2 tt { font: inherit; border-bottom: none; } +h3 tt { font: inherit; border-bottom: none; } +h4 tt { font: inherit; border-bottom: none; } +h5 tt { font: inherit; border-bottom: none; } +h6 tt { font: inherit; border-bottom: none; } + +div#spc-section-body h1, h2, h3, h4, h5, h6{ + color: #444444; + font-weight: normal; + border-bottom: 0px solid #656565; + margin-bottom: 0.5em; +} +div#spc-section-body h1 { font-size: 200%; font-weight: bold; color: #333333; } +div#spc-section-body h2 { font-size: 160%; font-weight: bold; } +div#spc-section-body h3 { font-size: 140%; } +div#spc-section-body h4 { font-size: 120%; border-bottom: none; } +div#spc-section-body h5 { font-size: 110%; border-bottom: none; } +div#spc-section-body h6 { font-size: 100%; border-bottom: none; } + +/* Styling for hyperlinks */ +div#spc-section-body a{ + text-decoration: none; +} +div#spc-section-body a:hover{ + text-decoration: underline; +} + +/* Styling for images and figures: images are inline, figures are centered */ +div#spc-section-body .align-center{ + text-align: center; +} + +p.rubric { + color: #333333; + font-size: 120%; + font-weight: normal; + border-bottom: 1px solid #DDDDDD; +} + +.highlight-python pre { + border-top: 1px solid #656565; + border-bottom: 1px solid #656565; +} + +/* Docutils uses this tag for footnote backrefs. By coincidence, Bootstrap also + * uses this class and assigns an obnoxious color to it. Use a less obnoxious + * color. + */ +td.label { + background-color: rgb(242, 242, 242); +} + +/* + * Tables + */ + +table.citation { + border: none; +} + +table.docutils td, table.docutils th { + border: none; +} + +table.docutils { + margin-bottom: 9.5px; +} + + +/* + * Admonitions + */ + +p.admonition-title { + display: inline; +} + +p.admonition-title:after { + content: ":"; +} + +div.seealso { + background-color: #ffc; + border: 1px solid #ff6; +} + +div.seealso dt { + float: left; + clear: left; + min-width: 4em; + padding-right: 1em; +} + +div.seealso dd { + margin-top: 0; + margin-bottom: 0; +} + +div.warning { + background-color: #ffe4e4; + border: 1px solid #f66; +} + +div.note { + background-color: #eee; + border: 1px solid #ccc; +} \ No newline at end of file diff --git a/_static/file.png b/_static/file.png new file mode 100644 index 0000000000000000000000000000000000000000..a858a410e4faa62ce324d814e4b816fff83a6fb3 GIT binary patch literal 286 zcmV+(0pb3MP)s`hMrGg#P~ix$^RISR_I47Y|r1 z_CyJOe}D1){SET-^Amu_i71Lt6eYfZjRyw@I6OQAIXXHDfiX^GbOlHe=Ae4>0m)d(f|Me07*qoM6N<$f}vM^LjV8( literal 0 HcmV?d00001 diff --git a/_static/fonts/SourceCodePro-Regular.otf.woff b/_static/fonts/SourceCodePro-Regular.otf.woff new file mode 100644 index 0000000000000000000000000000000000000000..46e7bd058b145d1524eb58999a1a38b48bd4dea4 GIT binary patch literal 57712 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 literal 0 HcmV?d00001 diff --git a/_static/fonts/SourceSansPro-It.otf.woff b/_static/fonts/SourceSansPro-It.otf.woff new file mode 100644 index 0000000000000000000000000000000000000000..9cae9b4b9ba35f8241580f8f86e05827f64ff356 GIT binary patch literal 53852 zcmZU)W0WR6)GmCtr)}G|ZQHiZ>1o@xF>T{++cu|dP1~Ht*Ux#s_xw3o*R?9y**mFJ zR;sd-$}dF;2}LzEMF3zy4}bsw06+wP06+l3-?9HEhzP5S0|5PK-x)mrF&aAsC@LXA z1OOmR0RSm90KiCpUs6q_n5wkox1JIJfD8u!V7(tyo;gX1iAw+gYk8xvyVBlhsg~|H{r)mvz(?M-zz4 zZF0^ILaLO({7VECjC~g#l`qo*{|TbrID)TWEy{2wTby0cUihm9MT~yLZ=0k z*r%*F>CZ#|3g`_O!iaR7$z76{mX$8de6^S{oq3(fv?cLsePB&3f01Q|(JNf3r@cX& zo=e9_KgIVlaSmlzP{dGNgnt{}$M(woYv<>|#{BEzS<{y>lm3gILAm)IIIQYcyV=+V_g*oKo<;8;W+j`dZTCeb2y7E`)J*x# z?YV0nHrJ{1v2+f_py}1+lea!{jJAv@BaNP&t6SXB@k!IL5NPm7W0>|azvZr+emA<+ zd;Q4Vq3aQ%ZS=>$>NlwDv2>2yuJ*IKMbod|Zf=9yY;6;9R1@5hS{S7Ji^#T;mX?k& zrI+j(io0cI2>g{{i+*%5l1nC=Jhf6eNeI`Z;7HF9H)=Y{X`?_HiBU&)!!-xCt=c{z zp|qi3GA%Et!gozRSY33SYhped@47vWqWRBwPTM{0kH&fI^ka<|F{g=CT?bBC&120; zbsJM>T?3z#3B%Mc{#tI?N8v(VX+D1%U1aOU&CeQ#$Y_q-9}se>z%Bxg|Rkz6yZb2_E=dlSyiPo_ipa7WQc zF_}l>%5^X?3N$YJn~+j-%UT{ybpJnk zNd&LLf3L$U%kyS8VmmL+b?rzec(oJGHpheKq#Hg>Y`sVKl7r76xmC6aaTJ`22=9Fc zO;@r{9>Y$&VBh=fcO15C?}JCS4T{#>TlpL=ZzZbQPg}M$P&@4%*a+|NTie_FXpktD zi`Y~>XMS@;EHEkQhx3;hWL;$M4C zPMp{$y1FOU2@1anVVL`pS@vXRHTZi1X7;6J9vNL^$&IRSqIa_tc6VWs?3y!wq##{{fHU*y;$|PT*wAGIq zgs1zP=Zo#3v%wLu_UJ}%piNFlTS@Q~-eIdmm71hqc9Vjx(Cj*)HfD?FaL1f|@Lx}J+sG6Z3#FJ?=ay{n4k6Z8d7A*Wa6>Dwg4MW`EdH!m*s?dM^4bW`|s z&QqL+FWUG6AQVb#d_hT&enl;!Pe3C$XKo7===h(drPZxL1`nG15JLJ?i%4L!0@D-S zD|`qm#;j;X_o+_NWe!c2{%>L|Q?S1YI4hYNS+J_KIc-_H`kmbTp z85*TS{uH_B&A5zPq(_85X)p2dp4;5V{awk)9pf?Fl@l35zuupuOC+&CiUnBU#92JO zdp5U!cLuS0?458;>@6;l3};qIF!bOVcOrViKJgCRei>KN+A1IU*1IiHipTX#-A7C& z<9PSQ{8~l2;@dj{QNsOcU$D5|;J7Q>5Z)fyP3!o_sVm$Q%oa6wH)?2Cq=mkFyEt6y zTFAsL#?kzh`mFRXa>Hyfu4^Q7G9O7!*g}c^_zsv#StV<5(VZ{ST&NEkjWy1%&xjZQj+oEC3li*u-GAvG|i3CwSo_sj68Z1njVNZ^vbiGN=v?R z>hu`-rg-u~u^#lyhl}|-jR>011yYLr{n%_eMV?Dpd=}%{oAXaF3;)p4|K7jLX`XIs z;us^n*BZ(7;Godbt8dC%(v(s}cJoqNVxS^yKfvL#Njk@f{xO8q!+=UlW%NU2{*IaX z(GUVc7Kjwm-;W%oEa5F8y|?nM2Ue&bQw3LS zZLzQEukF9v7HiFZ{|QN@+kYE~5GvAH<1FKQ%0OxT3d+5PxaVSuYZ+031LwS5&i6D* z�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⪤d~1{g{kMTUU4 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 r{KsP#d6i9j{Y$~-QBI|#hs7e|GhWgym@DK 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 k#&V4N^f*%@W(!qelX4V1n4 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 LKG44a?VOkjIFJon literal 0 HcmV?d00001 diff --git a/_static/fonts/SourceSansPro-Semibold.otf.woff b/_static/fonts/SourceSansPro-Semibold.otf.woff new file mode 100644 index 0000000000000000000000000000000000000000..475c49971499e548091c83a5fbc1dffce9b6da1d GIT binary patch literal 134540 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

LIW=YP*YbEl0fBkhm0ESEauK%Z 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 literal 0 HcmV?d00001 diff --git a/_static/fonts/SourceSansPro-SemiboldIt.otf.woff b/_static/fonts/SourceSansPro-SemiboldIt.otf.woff new file mode 100644 index 0000000000000000000000000000000000000000..62c2997d3f3b600f1c319c5901f293cdfa01090c GIT binary patch literal 54260 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 literal 0 HcmV?d00001 diff --git a/_static/img/contents.png b/_static/img/contents.png new file mode 100644 index 0000000000000000000000000000000000000000..7fb82154a1748d507925865d3fbf7508d62483e5 GIT binary patch literal 202 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 C5^xg$ literal 0 HcmV?d00001 diff --git a/_static/img/favicon.ico b/_static/img/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..6ff4b1d2f0390ad13f32193b425a9c7a0368459d GIT binary patch literal 4286 zcmchYF^|(g5QR5#kus@ho9neq?Ve*k z4ygkS?(tG#{oC{X9QR|l&-8|tEj6&N3{uYqy&!sedyP5P!1JfB1>Om?hO{2oG4?8L z_5z+{H`nOCmzYnQdD=Vup|(A#c^%-hADz!K_(O>Ow(D-*L--4@)&0`et&M>nL%C8% z2)`Di`G<~y&$CfFb=15){jbG@vJS<8KZAHSYToDKvo^4KHfsBNPCjdBUS0Y7HSm11 zmz}?l|KWp|?z!$a#j}+(eM8?v*AIsFExC1YJ`UyDxDNan{!7SZYR}x8+8MJf?H!7XbZ_#1~=c;DsnzrK4apPt_j&V_f| z-+Jq{L~N|pK>oM)7xMY#0~_1CFMPSykMmFtHFMo}_9}yFX9N7Ba~(eWz&c%?_RN?X z-|8xvY@8 rLhfqYlT~G;kZe-Si$O8Ej*uU1qhe5*Y$C?YrRe?TY{9E0a1r?fajpt` literal 0 HcmV?d00001 diff --git a/_static/img/glyphicons-halflings-white.png b/_static/img/glyphicons-halflings-white.png new file mode 100644 index 0000000000000000000000000000000000000000..3bf6484a29d8da269f9bc874b25493a45fae3bae GIT binary patch literal 8777 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 B7G?kd literal 0 HcmV?d00001 diff --git a/_static/img/glyphicons-halflings.png b/_static/img/glyphicons-halflings.png new file mode 100644 index 0000000000000000000000000000000000000000..a9969993201f9cee63cf9f49217646347297b643 GIT binary patch literal 12799 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 literal 0 HcmV?d00001 diff --git a/_static/img/navigation.png b/_static/img/navigation.png new file mode 100644 index 0000000000000000000000000000000000000000..1081dc1439fb984dfa7ef627afe3c7dc476fdbce GIT binary patch literal 218 zcmeAS@N?(olHy`uVBq!ia0vp^j6iI|!3HFkf4uMuBv2gW?!>U}oXkrghqJ&VvY3H^ zTNs2H8D`Cq01C2~c>21s-(chw7$R|bZ|_0D0|q>YSbqDzW^|HYIk%*-&O)*Wip zr1+)TIKaUJCb)3nLQ_*yOiaw=qF!qFketMoIjjxu+*ELIq3^F;|~!D}xJWooB?$cBKq{?z$?hNo|cNFAPo` zO&OTQC}y}+m@%P=WhA$rVnnh{7SX(PoVYv)TAFz|#t}bjGsZt@H(&cIJk_o%TK$P8 z&W+_ANA|oaG-UsCh~PsE`m$&m=VJlYpSd>1NRj}UnXH$GV0woD@cLQj6Rf^=McCnB zg+*<5$0ufhHcqTUQ*f2Fs7ya!*`Tg8=_L@pu5UEdC?Ry0lRXBRtnC}?^b)ls@VafK zO%*|@Ha-=PlK$X9<|fzB!opOt@XO$X`Jt~6aj!psv%Ex;MJIGlWF>?xz2uIpJiAKi zUU(I#D$IsD3p&`sc{@Nbp8*)XG|-*Sjba<3B*P`0w9?g%FRbS|WtQ;(0x-bSj(V~G z?4Y5JcE!V7`lxm!L-?}b+%G>94EO7IpV&3_4tR)Wogv4hEE$zEEmhH_5p^s$wHroE z_p-^!FhfOEG0*xDLq_h$1t?7264baJFYhw2^8Bu3sI^Uzy^-#PG;NcZMrou5t1a~!Fx&L zqTF*i=y!*63|dySwPRn6=THf65kfW}3iGuN^tGOo+2(TI1ubJI`x>N2qH}6(;WgLl z#tqoCOiY-u4SJrP5{p%@gmkIg(nPtn9%DBmp{~1FXHu%CKrikbOKtgyB*o)S8D6y^ zd?Z02c4RIuOX!iQ^4JKKth?L+w!SSv^kMTOXQ2;*||OP8`L)bqdZ6=hG3znjBdyouQC- z2{|0(#LB*c*Y@m8w%x8e$)!>@aePd#Gz1{;x;$GLm$RD-;+&E4OTfN^l#oYz+pl{9 ztGltv^dIhTL5CU(YkuB>N{G0Dv}w(IDcts{ZL`6#^nPt?i$=m{D23_}=zdLGKobnbKO&IVDbBU4*Z5%!iq zwd4gy;B|a{6z_Y3ZCGELzjfi*#NXQob_F!etMeQXTP?>q_esTGgPMt!lYSi3Dk%lV zFj`-5x050$NZ}(6i)}hQkYTlvY8Qp-Z!otT;M%|JB!Kwrq1mAuVh-s8J`2(Z^xnPt zR!4ECmU?MGIlsK8xxEj(F5}*c?K-Fx9TCTsJOoyoO->S0t0Ql}USDv~FFZ_3wlL+k zE`6{8Ds$jwATl0?qVdNe@X*G7G+tihnA%zLE@dA$P0U95A6u*S^xk5 literal 0 HcmV?d00001 diff --git a/_static/jquery-3.4.1.js b/_static/jquery-3.4.1.js new file mode 100644 index 000000000..773ad95c5 --- /dev/null +++ b/_static/jquery-3.4.1.js @@ -0,0 +1,10598 @@ +/*! + * jQuery JavaScript Library v3.4.1 + * https://jquery.com/ + * + * Includes Sizzle.js + * https://sizzlejs.com/ + * + * Copyright JS Foundation and other contributors + * Released under the MIT license + * https://jquery.org/license + * + * Date: 2019-05-01T21:04Z + */ +( function( global, factory ) { + + "use strict"; + + if ( typeof module === "object" && typeof module.exports === "object" ) { + + // For CommonJS and CommonJS-like environments where a proper `window` + // is present, execute the factory and get jQuery. + // For environments that do not have a `window` with a `document` + // (such as Node.js), expose a factory as module.exports. + // This accentuates the need for the creation of a real `window`. + // e.g. var jQuery = require("jquery")(window); + // See ticket #14549 for more info. + module.exports = global.document ? + factory( global, true ) : + function( w ) { + if ( !w.document ) { + throw new Error( "jQuery requires a window with a document" ); + } + return factory( w ); + }; + } else { + factory( global ); + } + +// Pass this if window is not defined yet +} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) { + +// Edge <= 12 - 13+, Firefox <=18 - 45+, IE 10 - 11, Safari 5.1 - 9+, iOS 6 - 9.1 +// throw exceptions when non-strict code (e.g., ASP.NET 4.5) accesses strict mode +// arguments.callee.caller (trac-13335). But as of jQuery 3.0 (2016), strict mode should be common +// enough that all such attempts are guarded in a try block. +"use strict"; + +var arr = []; + +var document = window.document; + +var getProto = Object.getPrototypeOf; + +var slice = arr.slice; + +var concat = arr.concat; + +var push = arr.push; + +var indexOf = arr.indexOf; + +var class2type = {}; + +var toString = class2type.toString; + +var hasOwn = class2type.hasOwnProperty; + +var fnToString = hasOwn.toString; + +var ObjectFunctionString = fnToString.call( Object ); + +var support = {}; + +var isFunction = function isFunction( obj ) { + + // Support: Chrome <=57, Firefox <=52 + // In some browsers, typeof returns "function" for HTML elements + // (i.e., `typeof document.createElement( "object" ) === "function"`). + // We don't want to classify *any* DOM node as a function. + return typeof obj === "function" && typeof obj.nodeType !== "number"; + }; + + +var isWindow = function isWindow( obj ) { + return obj != null && obj === obj.window; + }; + + + + + var preservedScriptAttributes = { + type: true, + src: true, + nonce: true, + noModule: true + }; + + function DOMEval( code, node, doc ) { + doc = doc || document; + + var i, val, + script = doc.createElement( "script" ); + + script.text = code; + if ( node ) { + for ( i in preservedScriptAttributes ) { + + // Support: Firefox 64+, Edge 18+ + // Some browsers don't support the "nonce" property on scripts. + // On the other hand, just using `getAttribute` is not enough as + // the `nonce` attribute is reset to an empty string whenever it + // becomes browsing-context connected. + // See https://github.com/whatwg/html/issues/2369 + // See https://html.spec.whatwg.org/#nonce-attributes + // The `node.getAttribute` check was added for the sake of + // `jQuery.globalEval` so that it can fake a nonce-containing node + // via an object. + val = node[ i ] || node.getAttribute && node.getAttribute( i ); + if ( val ) { + script.setAttribute( i, val ); + } + } + } + doc.head.appendChild( script ).parentNode.removeChild( script ); + } + + +function toType( obj ) { + if ( obj == null ) { + return obj + ""; + } + + // Support: Android <=2.3 only (functionish RegExp) + return typeof obj === "object" || typeof obj === "function" ? + class2type[ toString.call( obj ) ] || "object" : + typeof obj; +} +/* global Symbol */ +// Defining this global in .eslintrc.json would create a danger of using the global +// unguarded in another place, it seems safer to define global only for this module + + + +var + version = "3.4.1", + + // Define a local copy of jQuery + jQuery = function( selector, context ) { + + // The jQuery object is actually just the init constructor 'enhanced' + // Need init if jQuery is called (just allow error to be thrown if not included) + return new jQuery.fn.init( selector, context ); + }, + + // Support: Android <=4.0 only + // Make sure we trim BOM and NBSP + rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g; + +jQuery.fn = jQuery.prototype = { + + // The current version of jQuery being used + jquery: version, + + constructor: jQuery, + + // The default length of a jQuery object is 0 + length: 0, + + toArray: function() { + return slice.call( this ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + + // Return all the elements in a clean array + if ( num == null ) { + return slice.call( this ); + } + + // Return just the one element from the set + return num < 0 ? this[ num + this.length ] : this[ num ]; + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems ) { + + // Build a new jQuery matched element set + var ret = jQuery.merge( this.constructor(), elems ); + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + each: function( callback ) { + return jQuery.each( this, callback ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map( this, function( elem, i ) { + return callback.call( elem, i, elem ); + } ) ); + }, + + slice: function() { + return this.pushStack( slice.apply( this, arguments ) ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + eq: function( i ) { + var len = this.length, + j = +i + ( i < 0 ? len : 0 ); + return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] ); + }, + + end: function() { + return this.prevObject || this.constructor(); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: push, + sort: arr.sort, + splice: arr.splice +}; + +jQuery.extend = jQuery.fn.extend = function() { + var options, name, src, copy, copyIsArray, clone, + target = arguments[ 0 ] || {}, + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + + // Skip the boolean and the target + target = arguments[ i ] || {}; + i++; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !isFunction( target ) ) { + target = {}; + } + + // Extend jQuery itself if only one argument is passed + if ( i === length ) { + target = this; + i--; + } + + for ( ; i < length; i++ ) { + + // Only deal with non-null/undefined values + if ( ( options = arguments[ i ] ) != null ) { + + // Extend the base object + for ( name in options ) { + copy = options[ name ]; + + // Prevent Object.prototype pollution + // Prevent never-ending loop + if ( name === "__proto__" || target === copy ) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject( copy ) || + ( copyIsArray = Array.isArray( copy ) ) ) ) { + src = target[ name ]; + + // Ensure proper type for the source value + if ( copyIsArray && !Array.isArray( src ) ) { + clone = []; + } else if ( !copyIsArray && !jQuery.isPlainObject( src ) ) { + clone = {}; + } else { + clone = src; + } + copyIsArray = false; + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +jQuery.extend( { + + // Unique for each copy of jQuery on the page + expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), + + // Assume jQuery is ready without the ready module + isReady: true, + + error: function( msg ) { + throw new Error( msg ); + }, + + noop: function() {}, + + isPlainObject: function( obj ) { + var proto, Ctor; + + // Detect obvious negatives + // Use toString instead of jQuery.type to catch host objects + if ( !obj || toString.call( obj ) !== "[object Object]" ) { + return false; + } + + proto = getProto( obj ); + + // Objects with no prototype (e.g., `Object.create( null )`) are plain + if ( !proto ) { + return true; + } + + // Objects with prototype are plain iff they were constructed by a global Object function + Ctor = hasOwn.call( proto, "constructor" ) && proto.constructor; + return typeof Ctor === "function" && fnToString.call( Ctor ) === ObjectFunctionString; + }, + + isEmptyObject: function( obj ) { + var name; + + for ( name in obj ) { + return false; + } + return true; + }, + + // Evaluates a script in a global context + globalEval: function( code, options ) { + DOMEval( code, { nonce: options && options.nonce } ); + }, + + each: function( obj, callback ) { + var length, i = 0; + + if ( isArrayLike( obj ) ) { + length = obj.length; + for ( ; i < length; i++ ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } else { + for ( i in obj ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } + + return obj; + }, + + // Support: Android <=4.0 only + trim: function( text ) { + return text == null ? + "" : + ( text + "" ).replace( rtrim, "" ); + }, + + // results is for internal usage only + makeArray: function( arr, results ) { + var ret = results || []; + + if ( arr != null ) { + if ( isArrayLike( Object( arr ) ) ) { + jQuery.merge( ret, + typeof arr === "string" ? + [ arr ] : arr + ); + } else { + push.call( ret, arr ); + } + } + + return ret; + }, + + inArray: function( elem, arr, i ) { + return arr == null ? -1 : indexOf.call( arr, elem, i ); + }, + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + merge: function( first, second ) { + var len = +second.length, + j = 0, + i = first.length; + + for ( ; j < len; j++ ) { + first[ i++ ] = second[ j ]; + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, invert ) { + var callbackInverse, + matches = [], + i = 0, + length = elems.length, + callbackExpect = !invert; + + // Go through the array, only saving the items + // that pass the validator function + for ( ; i < length; i++ ) { + callbackInverse = !callback( elems[ i ], i ); + if ( callbackInverse !== callbackExpect ) { + matches.push( elems[ i ] ); + } + } + + return matches; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var length, value, + i = 0, + ret = []; + + // Go through the array, translating each of the items to their new values + if ( isArrayLike( elems ) ) { + length = elems.length; + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + + // Go through every key on the object, + } else { + for ( i in elems ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + } + + // Flatten any nested arrays + return concat.apply( [], ret ); + }, + + // A global GUID counter for objects + guid: 1, + + // jQuery.support is not used in Core but other projects attach their + // properties to it so it needs to exist. + support: support +} ); + +if ( typeof Symbol === "function" ) { + jQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ]; +} + +// Populate the class2type map +jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ), +function( i, name ) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); +} ); + +function isArrayLike( obj ) { + + // Support: real iOS 8.2 only (not reproducible in simulator) + // `in` check used to prevent JIT error (gh-2145) + // hasOwn isn't used here due to false negatives + // regarding Nodelist length in IE + var length = !!obj && "length" in obj && obj.length, + type = toType( obj ); + + if ( isFunction( obj ) || isWindow( obj ) ) { + return false; + } + + return type === "array" || length === 0 || + typeof length === "number" && length > 0 && ( length - 1 ) in obj; +} +var Sizzle = +/*! + * Sizzle CSS Selector Engine v2.3.4 + * https://sizzlejs.com/ + * + * Copyright JS Foundation and other contributors + * Released under the MIT license + * https://js.foundation/ + * + * Date: 2019-04-08 + */ +(function( window ) { + +var i, + support, + Expr, + getText, + isXML, + tokenize, + compile, + select, + outermostContext, + sortInput, + hasDuplicate, + + // Local document vars + setDocument, + document, + docElem, + documentIsHTML, + rbuggyQSA, + rbuggyMatches, + matches, + contains, + + // Instance-specific data + expando = "sizzle" + 1 * new Date(), + preferredDoc = window.document, + dirruns = 0, + done = 0, + classCache = createCache(), + tokenCache = createCache(), + compilerCache = createCache(), + nonnativeSelectorCache = createCache(), + sortOrder = function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + } + return 0; + }, + + // Instance methods + hasOwn = ({}).hasOwnProperty, + arr = [], + pop = arr.pop, + push_native = arr.push, + push = arr.push, + slice = arr.slice, + // Use a stripped-down indexOf as it's faster than native + // https://jsperf.com/thor-indexof-vs-for/5 + indexOf = function( list, elem ) { + var i = 0, + len = list.length; + for ( ; i < len; i++ ) { + if ( list[i] === elem ) { + return i; + } + } + return -1; + }, + + booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped", + + // Regular expressions + + // http://www.w3.org/TR/css3-selectors/#whitespace + whitespace = "[\\x20\\t\\r\\n\\f]", + + // http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier + identifier = "(?:\\\\.|[\\w-]|[^\0-\\xa0])+", + + // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors + attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace + + // Operator (capture 2) + "*([*^$|!~]?=)" + whitespace + + // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]" + "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace + + "*\\]", + + pseudos = ":(" + identifier + ")(?:\\((" + + // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments: + // 1. quoted (capture 3; capture 4 or capture 5) + "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" + + // 2. simple (capture 6) + "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" + + // 3. anything else (capture 2) + ".*" + + ")\\)|)", + + // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter + rwhitespace = new RegExp( whitespace + "+", "g" ), + rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), + + rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), + rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ), + rdescend = new RegExp( whitespace + "|>" ), + + rpseudo = new RegExp( pseudos ), + ridentifier = new RegExp( "^" + identifier + "$" ), + + matchExpr = { + "ID": new RegExp( "^#(" + identifier + ")" ), + "CLASS": new RegExp( "^\\.(" + identifier + ")" ), + "TAG": new RegExp( "^(" + identifier + "|[*])" ), + "ATTR": new RegExp( "^" + attributes ), + "PSEUDO": new RegExp( "^" + pseudos ), + "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace + + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), + "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), + // For use in libraries implementing .is() + // We use this for POS matching in `select` + "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + + whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) + }, + + rhtml = /HTML$/i, + rinputs = /^(?:input|select|textarea|button)$/i, + rheader = /^h\d$/i, + + rnative = /^[^{]+\{\s*\[native \w/, + + // Easily-parseable/retrievable ID or TAG or CLASS selectors + rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, + + rsibling = /[+~]/, + + // CSS escapes + // http://www.w3.org/TR/CSS21/syndata.html#escaped-characters + runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ), + funescape = function( _, escaped, escapedWhitespace ) { + var high = "0x" + escaped - 0x10000; + // NaN means non-codepoint + // Support: Firefox<24 + // Workaround erroneous numeric interpretation of +"0x" + return high !== high || escapedWhitespace ? + escaped : + high < 0 ? + // BMP codepoint + String.fromCharCode( high + 0x10000 ) : + // Supplemental Plane codepoint (surrogate pair) + String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); + }, + + // CSS string/identifier serialization + // https://drafts.csswg.org/cssom/#common-serializing-idioms + rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g, + fcssescape = function( ch, asCodePoint ) { + if ( asCodePoint ) { + + // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER + if ( ch === "\0" ) { + return "\uFFFD"; + } + + // Control characters and (dependent upon position) numbers get escaped as code points + return ch.slice( 0, -1 ) + "\\" + ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " "; + } + + // Other potentially-special ASCII characters get backslash-escaped + return "\\" + ch; + }, + + // Used for iframes + // See setDocument() + // Removing the function wrapper causes a "Permission Denied" + // error in IE + unloadHandler = function() { + setDocument(); + }, + + inDisabledFieldset = addCombinator( + function( elem ) { + return elem.disabled === true && elem.nodeName.toLowerCase() === "fieldset"; + }, + { dir: "parentNode", next: "legend" } + ); + +// Optimize for push.apply( _, NodeList ) +try { + push.apply( + (arr = slice.call( preferredDoc.childNodes )), + preferredDoc.childNodes + ); + // Support: Android<4.0 + // Detect silently failing push.apply + arr[ preferredDoc.childNodes.length ].nodeType; +} catch ( e ) { + push = { apply: arr.length ? + + // Leverage slice if possible + function( target, els ) { + push_native.apply( target, slice.call(els) ); + } : + + // Support: IE<9 + // Otherwise append directly + function( target, els ) { + var j = target.length, + i = 0; + // Can't trust NodeList.length + while ( (target[j++] = els[i++]) ) {} + target.length = j - 1; + } + }; +} + +function Sizzle( selector, context, results, seed ) { + var m, i, elem, nid, match, groups, newSelector, + newContext = context && context.ownerDocument, + + // nodeType defaults to 9, since context defaults to document + nodeType = context ? context.nodeType : 9; + + results = results || []; + + // Return early from calls with invalid selector or context + if ( typeof selector !== "string" || !selector || + nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) { + + return results; + } + + // Try to shortcut find operations (as opposed to filters) in HTML documents + if ( !seed ) { + + if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) { + setDocument( context ); + } + context = context || document; + + if ( documentIsHTML ) { + + // If the selector is sufficiently simple, try using a "get*By*" DOM method + // (excepting DocumentFragment context, where the methods don't exist) + if ( nodeType !== 11 && (match = rquickExpr.exec( selector )) ) { + + // ID selector + if ( (m = match[1]) ) { + + // Document context + if ( nodeType === 9 ) { + if ( (elem = context.getElementById( m )) ) { + + // Support: IE, Opera, Webkit + // TODO: identify versions + // getElementById can match elements by name instead of ID + if ( elem.id === m ) { + results.push( elem ); + return results; + } + } else { + return results; + } + + // Element context + } else { + + // Support: IE, Opera, Webkit + // TODO: identify versions + // getElementById can match elements by name instead of ID + if ( newContext && (elem = newContext.getElementById( m )) && + contains( context, elem ) && + elem.id === m ) { + + results.push( elem ); + return results; + } + } + + // Type selector + } else if ( match[2] ) { + push.apply( results, context.getElementsByTagName( selector ) ); + return results; + + // Class selector + } else if ( (m = match[3]) && support.getElementsByClassName && + context.getElementsByClassName ) { + + push.apply( results, context.getElementsByClassName( m ) ); + return results; + } + } + + // Take advantage of querySelectorAll + if ( support.qsa && + !nonnativeSelectorCache[ selector + " " ] && + (!rbuggyQSA || !rbuggyQSA.test( selector )) && + + // Support: IE 8 only + // Exclude object elements + (nodeType !== 1 || context.nodeName.toLowerCase() !== "object") ) { + + newSelector = selector; + newContext = context; + + // qSA considers elements outside a scoping root when evaluating child or + // descendant combinators, which is not what we want. + // In such cases, we work around the behavior by prefixing every selector in the + // list with an ID selector referencing the scope context. + // Thanks to Andrew Dupont for this technique. + if ( nodeType === 1 && rdescend.test( selector ) ) { + + // Capture the context ID, setting it first if necessary + if ( (nid = context.getAttribute( "id" )) ) { + nid = nid.replace( rcssescape, fcssescape ); + } else { + context.setAttribute( "id", (nid = expando) ); + } + + // Prefix every selector in the list + groups = tokenize( selector ); + i = groups.length; + while ( i-- ) { + groups[i] = "#" + nid + " " + toSelector( groups[i] ); + } + newSelector = groups.join( "," ); + + // Expand context for sibling selectors + newContext = rsibling.test( selector ) && testContext( context.parentNode ) || + context; + } + + try { + push.apply( results, + newContext.querySelectorAll( newSelector ) + ); + return results; + } catch ( qsaError ) { + nonnativeSelectorCache( selector, true ); + } finally { + if ( nid === expando ) { + context.removeAttribute( "id" ); + } + } + } + } + } + + // All others + return select( selector.replace( rtrim, "$1" ), context, results, seed ); +} + +/** + * Create key-value caches of limited size + * @returns {function(string, object)} Returns the Object data after storing it on itself with + * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) + * deleting the oldest entry + */ +function createCache() { + var keys = []; + + function cache( key, value ) { + // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) + if ( keys.push( key + " " ) > Expr.cacheLength ) { + // Only keep the most recent entries + delete cache[ keys.shift() ]; + } + return (cache[ key + " " ] = value); + } + return cache; +} + +/** + * Mark a function for special use by Sizzle + * @param {Function} fn The function to mark + */ +function markFunction( fn ) { + fn[ expando ] = true; + return fn; +} + +/** + * Support testing using an element + * @param {Function} fn Passed the created element and returns a boolean result + */ +function assert( fn ) { + var el = document.createElement("fieldset"); + + try { + return !!fn( el ); + } catch (e) { + return false; + } finally { + // Remove from its parent by default + if ( el.parentNode ) { + el.parentNode.removeChild( el ); + } + // release memory in IE + el = null; + } +} + +/** + * Adds the same handler for all of the specified attrs + * @param {String} attrs Pipe-separated list of attributes + * @param {Function} handler The method that will be applied + */ +function addHandle( attrs, handler ) { + var arr = attrs.split("|"), + i = arr.length; + + while ( i-- ) { + Expr.attrHandle[ arr[i] ] = handler; + } +} + +/** + * Checks document order of two siblings + * @param {Element} a + * @param {Element} b + * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b + */ +function siblingCheck( a, b ) { + var cur = b && a, + diff = cur && a.nodeType === 1 && b.nodeType === 1 && + a.sourceIndex - b.sourceIndex; + + // Use IE sourceIndex if available on both nodes + if ( diff ) { + return diff; + } + + // Check if b follows a + if ( cur ) { + while ( (cur = cur.nextSibling) ) { + if ( cur === b ) { + return -1; + } + } + } + + return a ? 1 : -1; +} + +/** + * Returns a function to use in pseudos for input types + * @param {String} type + */ +function createInputPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for buttons + * @param {String} type + */ +function createButtonPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for :enabled/:disabled + * @param {Boolean} disabled true for :disabled; false for :enabled + */ +function createDisabledPseudo( disabled ) { + + // Known :disabled false positives: fieldset[disabled] > legend:nth-of-type(n+2) :can-disable + return function( elem ) { + + // Only certain elements can match :enabled or :disabled + // https://html.spec.whatwg.org/multipage/scripting.html#selector-enabled + // https://html.spec.whatwg.org/multipage/scripting.html#selector-disabled + if ( "form" in elem ) { + + // Check for inherited disabledness on relevant non-disabled elements: + // * listed form-associated elements in a disabled fieldset + // https://html.spec.whatwg.org/multipage/forms.html#category-listed + // https://html.spec.whatwg.org/multipage/forms.html#concept-fe-disabled + // * option elements in a disabled optgroup + // https://html.spec.whatwg.org/multipage/forms.html#concept-option-disabled + // All such elements have a "form" property. + if ( elem.parentNode && elem.disabled === false ) { + + // Option elements defer to a parent optgroup if present + if ( "label" in elem ) { + if ( "label" in elem.parentNode ) { + return elem.parentNode.disabled === disabled; + } else { + return elem.disabled === disabled; + } + } + + // Support: IE 6 - 11 + // Use the isDisabled shortcut property to check for disabled fieldset ancestors + return elem.isDisabled === disabled || + + // Where there is no isDisabled, check manually + /* jshint -W018 */ + elem.isDisabled !== !disabled && + inDisabledFieldset( elem ) === disabled; + } + + return elem.disabled === disabled; + + // Try to winnow out elements that can't be disabled before trusting the disabled property. + // Some victims get caught in our net (label, legend, menu, track), but it shouldn't + // even exist on them, let alone have a boolean value. + } else if ( "label" in elem ) { + return elem.disabled === disabled; + } + + // Remaining elements are neither :enabled nor :disabled + return false; + }; +} + +/** + * Returns a function to use in pseudos for positionals + * @param {Function} fn + */ +function createPositionalPseudo( fn ) { + return markFunction(function( argument ) { + argument = +argument; + return markFunction(function( seed, matches ) { + var j, + matchIndexes = fn( [], seed.length, argument ), + i = matchIndexes.length; + + // Match elements found at the specified indexes + while ( i-- ) { + if ( seed[ (j = matchIndexes[i]) ] ) { + seed[j] = !(matches[j] = seed[j]); + } + } + }); + }); +} + +/** + * Checks a node for validity as a Sizzle context + * @param {Element|Object=} context + * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value + */ +function testContext( context ) { + return context && typeof context.getElementsByTagName !== "undefined" && context; +} + +// Expose support vars for convenience +support = Sizzle.support = {}; + +/** + * Detects XML nodes + * @param {Element|Object} elem An element or a document + * @returns {Boolean} True iff elem is a non-HTML XML node + */ +isXML = Sizzle.isXML = function( elem ) { + var namespace = elem.namespaceURI, + docElem = (elem.ownerDocument || elem).documentElement; + + // Support: IE <=8 + // Assume HTML when documentElement doesn't yet exist, such as inside loading iframes + // https://bugs.jquery.com/ticket/4833 + return !rhtml.test( namespace || docElem && docElem.nodeName || "HTML" ); +}; + +/** + * Sets document-related variables once based on the current document + * @param {Element|Object} [doc] An element or document object to use to set the document + * @returns {Object} Returns the current document + */ +setDocument = Sizzle.setDocument = function( node ) { + var hasCompare, subWindow, + doc = node ? node.ownerDocument || node : preferredDoc; + + // Return early if doc is invalid or already selected + if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) { + return document; + } + + // Update global variables + document = doc; + docElem = document.documentElement; + documentIsHTML = !isXML( document ); + + // Support: IE 9-11, Edge + // Accessing iframe documents after unload throws "permission denied" errors (jQuery #13936) + if ( preferredDoc !== document && + (subWindow = document.defaultView) && subWindow.top !== subWindow ) { + + // Support: IE 11, Edge + if ( subWindow.addEventListener ) { + subWindow.addEventListener( "unload", unloadHandler, false ); + + // Support: IE 9 - 10 only + } else if ( subWindow.attachEvent ) { + subWindow.attachEvent( "onunload", unloadHandler ); + } + } + + /* Attributes + ---------------------------------------------------------------------- */ + + // Support: IE<8 + // Verify that getAttribute really returns attributes and not properties + // (excepting IE8 booleans) + support.attributes = assert(function( el ) { + el.className = "i"; + return !el.getAttribute("className"); + }); + + /* getElement(s)By* + ---------------------------------------------------------------------- */ + + // Check if getElementsByTagName("*") returns only elements + support.getElementsByTagName = assert(function( el ) { + el.appendChild( document.createComment("") ); + return !el.getElementsByTagName("*").length; + }); + + // Support: IE<9 + support.getElementsByClassName = rnative.test( document.getElementsByClassName ); + + // Support: IE<10 + // Check if getElementById returns elements by name + // The broken getElementById methods don't pick up programmatically-set names, + // so use a roundabout getElementsByName test + support.getById = assert(function( el ) { + docElem.appendChild( el ).id = expando; + return !document.getElementsByName || !document.getElementsByName( expando ).length; + }); + + // ID filter and find + if ( support.getById ) { + Expr.filter["ID"] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + return elem.getAttribute("id") === attrId; + }; + }; + Expr.find["ID"] = function( id, context ) { + if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { + var elem = context.getElementById( id ); + return elem ? [ elem ] : []; + } + }; + } else { + Expr.filter["ID"] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + var node = typeof elem.getAttributeNode !== "undefined" && + elem.getAttributeNode("id"); + return node && node.value === attrId; + }; + }; + + // Support: IE 6 - 7 only + // getElementById is not reliable as a find shortcut + Expr.find["ID"] = function( id, context ) { + if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { + var node, i, elems, + elem = context.getElementById( id ); + + if ( elem ) { + + // Verify the id attribute + node = elem.getAttributeNode("id"); + if ( node && node.value === id ) { + return [ elem ]; + } + + // Fall back on getElementsByName + elems = context.getElementsByName( id ); + i = 0; + while ( (elem = elems[i++]) ) { + node = elem.getAttributeNode("id"); + if ( node && node.value === id ) { + return [ elem ]; + } + } + } + + return []; + } + }; + } + + // Tag + Expr.find["TAG"] = support.getElementsByTagName ? + function( tag, context ) { + if ( typeof context.getElementsByTagName !== "undefined" ) { + return context.getElementsByTagName( tag ); + + // DocumentFragment nodes don't have gEBTN + } else if ( support.qsa ) { + return context.querySelectorAll( tag ); + } + } : + + function( tag, context ) { + var elem, + tmp = [], + i = 0, + // By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too + results = context.getElementsByTagName( tag ); + + // Filter out possible comments + if ( tag === "*" ) { + while ( (elem = results[i++]) ) { + if ( elem.nodeType === 1 ) { + tmp.push( elem ); + } + } + + return tmp; + } + return results; + }; + + // Class + Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) { + if ( typeof context.getElementsByClassName !== "undefined" && documentIsHTML ) { + return context.getElementsByClassName( className ); + } + }; + + /* QSA/matchesSelector + ---------------------------------------------------------------------- */ + + // QSA and matchesSelector support + + // matchesSelector(:active) reports false when true (IE9/Opera 11.5) + rbuggyMatches = []; + + // qSa(:focus) reports false when true (Chrome 21) + // We allow this because of a bug in IE8/9 that throws an error + // whenever `document.activeElement` is accessed on an iframe + // So, we allow :focus to pass through QSA all the time to avoid the IE error + // See https://bugs.jquery.com/ticket/13378 + rbuggyQSA = []; + + if ( (support.qsa = rnative.test( document.querySelectorAll )) ) { + // Build QSA regex + // Regex strategy adopted from Diego Perini + assert(function( el ) { + // Select is set to empty string on purpose + // This is to test IE's treatment of not explicitly + // setting a boolean content attribute, + // since its presence should be enough + // https://bugs.jquery.com/ticket/12359 + docElem.appendChild( el ).innerHTML = "" + + ""; + + // Support: IE8, Opera 11-12.16 + // Nothing should be selected when empty strings follow ^= or $= or *= + // The test attribute must be unknown in Opera but "safe" for WinRT + // https://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section + if ( el.querySelectorAll("[msallowcapture^='']").length ) { + rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); + } + + // Support: IE8 + // Boolean attributes and "value" are not treated correctly + if ( !el.querySelectorAll("[selected]").length ) { + rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); + } + + // Support: Chrome<29, Android<4.4, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.8+ + if ( !el.querySelectorAll( "[id~=" + expando + "-]" ).length ) { + rbuggyQSA.push("~="); + } + + // Webkit/Opera - :checked should return selected option elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + // IE8 throws error here and will not see later tests + if ( !el.querySelectorAll(":checked").length ) { + rbuggyQSA.push(":checked"); + } + + // Support: Safari 8+, iOS 8+ + // https://bugs.webkit.org/show_bug.cgi?id=136851 + // In-page `selector#id sibling-combinator selector` fails + if ( !el.querySelectorAll( "a#" + expando + "+*" ).length ) { + rbuggyQSA.push(".#.+[+~]"); + } + }); + + assert(function( el ) { + el.innerHTML = "" + + ""; + + // Support: Windows 8 Native Apps + // The type and name attributes are restricted during .innerHTML assignment + var input = document.createElement("input"); + input.setAttribute( "type", "hidden" ); + el.appendChild( input ).setAttribute( "name", "D" ); + + // Support: IE8 + // Enforce case-sensitivity of name attribute + if ( el.querySelectorAll("[name=d]").length ) { + rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" ); + } + + // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) + // IE8 throws error here and will not see later tests + if ( el.querySelectorAll(":enabled").length !== 2 ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Support: IE9-11+ + // IE's :disabled selector does not pick up the children of disabled fieldsets + docElem.appendChild( el ).disabled = true; + if ( el.querySelectorAll(":disabled").length !== 2 ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Opera 10-11 does not throw on post-comma invalid pseudos + el.querySelectorAll("*,:x"); + rbuggyQSA.push(",.*:"); + }); + } + + if ( (support.matchesSelector = rnative.test( (matches = docElem.matches || + docElem.webkitMatchesSelector || + docElem.mozMatchesSelector || + docElem.oMatchesSelector || + docElem.msMatchesSelector) )) ) { + + assert(function( el ) { + // Check to see if it's possible to do matchesSelector + // on a disconnected node (IE 9) + support.disconnectedMatch = matches.call( el, "*" ); + + // This should fail with an exception + // Gecko does not error, returns false instead + matches.call( el, "[s!='']:x" ); + rbuggyMatches.push( "!=", pseudos ); + }); + } + + rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") ); + rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") ); + + /* Contains + ---------------------------------------------------------------------- */ + hasCompare = rnative.test( docElem.compareDocumentPosition ); + + // Element contains another + // Purposefully self-exclusive + // As in, an element does not contain itself + contains = hasCompare || rnative.test( docElem.contains ) ? + function( a, b ) { + var adown = a.nodeType === 9 ? a.documentElement : a, + bup = b && b.parentNode; + return a === bup || !!( bup && bup.nodeType === 1 && ( + adown.contains ? + adown.contains( bup ) : + a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 + )); + } : + function( a, b ) { + if ( b ) { + while ( (b = b.parentNode) ) { + if ( b === a ) { + return true; + } + } + } + return false; + }; + + /* Sorting + ---------------------------------------------------------------------- */ + + // Document order sorting + sortOrder = hasCompare ? + function( a, b ) { + + // Flag for duplicate removal + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + // Sort on method existence if only one input has compareDocumentPosition + var compare = !a.compareDocumentPosition - !b.compareDocumentPosition; + if ( compare ) { + return compare; + } + + // Calculate position if both inputs belong to the same document + compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ? + a.compareDocumentPosition( b ) : + + // Otherwise we know they are disconnected + 1; + + // Disconnected nodes + if ( compare & 1 || + (!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) { + + // Choose the first element that is related to our preferred document + if ( a === document || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) { + return -1; + } + if ( b === document || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) { + return 1; + } + + // Maintain original order + return sortInput ? + ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : + 0; + } + + return compare & 4 ? -1 : 1; + } : + function( a, b ) { + // Exit early if the nodes are identical + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + var cur, + i = 0, + aup = a.parentNode, + bup = b.parentNode, + ap = [ a ], + bp = [ b ]; + + // Parentless nodes are either documents or disconnected + if ( !aup || !bup ) { + return a === document ? -1 : + b === document ? 1 : + aup ? -1 : + bup ? 1 : + sortInput ? + ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : + 0; + + // If the nodes are siblings, we can do a quick check + } else if ( aup === bup ) { + return siblingCheck( a, b ); + } + + // Otherwise we need full lists of their ancestors for comparison + cur = a; + while ( (cur = cur.parentNode) ) { + ap.unshift( cur ); + } + cur = b; + while ( (cur = cur.parentNode) ) { + bp.unshift( cur ); + } + + // Walk down the tree looking for a discrepancy + while ( ap[i] === bp[i] ) { + i++; + } + + return i ? + // Do a sibling check if the nodes have a common ancestor + siblingCheck( ap[i], bp[i] ) : + + // Otherwise nodes in our document sort first + ap[i] === preferredDoc ? -1 : + bp[i] === preferredDoc ? 1 : + 0; + }; + + return document; +}; + +Sizzle.matches = function( expr, elements ) { + return Sizzle( expr, null, null, elements ); +}; + +Sizzle.matchesSelector = function( elem, expr ) { + // Set document vars if needed + if ( ( elem.ownerDocument || elem ) !== document ) { + setDocument( elem ); + } + + if ( support.matchesSelector && documentIsHTML && + !nonnativeSelectorCache[ expr + " " ] && + ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && + ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { + + try { + var ret = matches.call( elem, expr ); + + // IE 9's matchesSelector returns false on disconnected nodes + if ( ret || support.disconnectedMatch || + // As well, disconnected nodes are said to be in a document + // fragment in IE 9 + elem.document && elem.document.nodeType !== 11 ) { + return ret; + } + } catch (e) { + nonnativeSelectorCache( expr, true ); + } + } + + return Sizzle( expr, document, null, [ elem ] ).length > 0; +}; + +Sizzle.contains = function( context, elem ) { + // Set document vars if needed + if ( ( context.ownerDocument || context ) !== document ) { + setDocument( context ); + } + return contains( context, elem ); +}; + +Sizzle.attr = function( elem, name ) { + // Set document vars if needed + if ( ( elem.ownerDocument || elem ) !== document ) { + setDocument( elem ); + } + + var fn = Expr.attrHandle[ name.toLowerCase() ], + // Don't get fooled by Object.prototype properties (jQuery #13807) + val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ? + fn( elem, name, !documentIsHTML ) : + undefined; + + return val !== undefined ? + val : + support.attributes || !documentIsHTML ? + elem.getAttribute( name ) : + (val = elem.getAttributeNode(name)) && val.specified ? + val.value : + null; +}; + +Sizzle.escape = function( sel ) { + return (sel + "").replace( rcssescape, fcssescape ); +}; + +Sizzle.error = function( msg ) { + throw new Error( "Syntax error, unrecognized expression: " + msg ); +}; + +/** + * Document sorting and removing duplicates + * @param {ArrayLike} results + */ +Sizzle.uniqueSort = function( results ) { + var elem, + duplicates = [], + j = 0, + i = 0; + + // Unless we *know* we can detect duplicates, assume their presence + hasDuplicate = !support.detectDuplicates; + sortInput = !support.sortStable && results.slice( 0 ); + results.sort( sortOrder ); + + if ( hasDuplicate ) { + while ( (elem = results[i++]) ) { + if ( elem === results[ i ] ) { + j = duplicates.push( i ); + } + } + while ( j-- ) { + results.splice( duplicates[ j ], 1 ); + } + } + + // Clear input after sorting to release objects + // See https://github.com/jquery/sizzle/pull/225 + sortInput = null; + + return results; +}; + +/** + * Utility function for retrieving the text value of an array of DOM nodes + * @param {Array|Element} elem + */ +getText = Sizzle.getText = function( elem ) { + var node, + ret = "", + i = 0, + nodeType = elem.nodeType; + + if ( !nodeType ) { + // If no nodeType, this is expected to be an array + while ( (node = elem[i++]) ) { + // Do not traverse comment nodes + ret += getText( node ); + } + } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { + // Use textContent for elements + // innerText usage removed for consistency of new lines (jQuery #11153) + if ( typeof elem.textContent === "string" ) { + return elem.textContent; + } else { + // Traverse its children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + ret += getText( elem ); + } + } + } else if ( nodeType === 3 || nodeType === 4 ) { + return elem.nodeValue; + } + // Do not include comment or processing instruction nodes + + return ret; +}; + +Expr = Sizzle.selectors = { + + // Can be adjusted by the user + cacheLength: 50, + + createPseudo: markFunction, + + match: matchExpr, + + attrHandle: {}, + + find: {}, + + relative: { + ">": { dir: "parentNode", first: true }, + " ": { dir: "parentNode" }, + "+": { dir: "previousSibling", first: true }, + "~": { dir: "previousSibling" } + }, + + preFilter: { + "ATTR": function( match ) { + match[1] = match[1].replace( runescape, funescape ); + + // Move the given value to match[3] whether quoted or unquoted + match[3] = ( match[3] || match[4] || match[5] || "" ).replace( runescape, funescape ); + + if ( match[2] === "~=" ) { + match[3] = " " + match[3] + " "; + } + + return match.slice( 0, 4 ); + }, + + "CHILD": function( match ) { + /* matches from matchExpr["CHILD"] + 1 type (only|nth|...) + 2 what (child|of-type) + 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) + 4 xn-component of xn+y argument ([+-]?\d*n|) + 5 sign of xn-component + 6 x of xn-component + 7 sign of y-component + 8 y of y-component + */ + match[1] = match[1].toLowerCase(); + + if ( match[1].slice( 0, 3 ) === "nth" ) { + // nth-* requires argument + if ( !match[3] ) { + Sizzle.error( match[0] ); + } + + // numeric x and y parameters for Expr.filter.CHILD + // remember that false/true cast respectively to 0/1 + match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) ); + match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" ); + + // other types prohibit arguments + } else if ( match[3] ) { + Sizzle.error( match[0] ); + } + + return match; + }, + + "PSEUDO": function( match ) { + var excess, + unquoted = !match[6] && match[2]; + + if ( matchExpr["CHILD"].test( match[0] ) ) { + return null; + } + + // Accept quoted arguments as-is + if ( match[3] ) { + match[2] = match[4] || match[5] || ""; + + // Strip excess characters from unquoted arguments + } else if ( unquoted && rpseudo.test( unquoted ) && + // Get excess from tokenize (recursively) + (excess = tokenize( unquoted, true )) && + // advance to the next closing parenthesis + (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) { + + // excess is a negative index + match[0] = match[0].slice( 0, excess ); + match[2] = unquoted.slice( 0, excess ); + } + + // Return only captures needed by the pseudo filter method (type and argument) + return match.slice( 0, 3 ); + } + }, + + filter: { + + "TAG": function( nodeNameSelector ) { + var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); + return nodeNameSelector === "*" ? + function() { return true; } : + function( elem ) { + return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; + }; + }, + + "CLASS": function( className ) { + var pattern = classCache[ className + " " ]; + + return pattern || + (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) && + classCache( className, function( elem ) { + return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== "undefined" && elem.getAttribute("class") || "" ); + }); + }, + + "ATTR": function( name, operator, check ) { + return function( elem ) { + var result = Sizzle.attr( elem, name ); + + if ( result == null ) { + return operator === "!="; + } + if ( !operator ) { + return true; + } + + result += ""; + + return operator === "=" ? result === check : + operator === "!=" ? result !== check : + operator === "^=" ? check && result.indexOf( check ) === 0 : + operator === "*=" ? check && result.indexOf( check ) > -1 : + operator === "$=" ? check && result.slice( -check.length ) === check : + operator === "~=" ? ( " " + result.replace( rwhitespace, " " ) + " " ).indexOf( check ) > -1 : + operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : + false; + }; + }, + + "CHILD": function( type, what, argument, first, last ) { + var simple = type.slice( 0, 3 ) !== "nth", + forward = type.slice( -4 ) !== "last", + ofType = what === "of-type"; + + return first === 1 && last === 0 ? + + // Shortcut for :nth-*(n) + function( elem ) { + return !!elem.parentNode; + } : + + function( elem, context, xml ) { + var cache, uniqueCache, outerCache, node, nodeIndex, start, + dir = simple !== forward ? "nextSibling" : "previousSibling", + parent = elem.parentNode, + name = ofType && elem.nodeName.toLowerCase(), + useCache = !xml && !ofType, + diff = false; + + if ( parent ) { + + // :(first|last|only)-(child|of-type) + if ( simple ) { + while ( dir ) { + node = elem; + while ( (node = node[ dir ]) ) { + if ( ofType ? + node.nodeName.toLowerCase() === name : + node.nodeType === 1 ) { + + return false; + } + } + // Reverse direction for :only-* (if we haven't yet done so) + start = dir = type === "only" && !start && "nextSibling"; + } + return true; + } + + start = [ forward ? parent.firstChild : parent.lastChild ]; + + // non-xml :nth-child(...) stores cache data on `parent` + if ( forward && useCache ) { + + // Seek `elem` from a previously-cached index + + // ...in a gzip-friendly way + node = parent; + outerCache = node[ expando ] || (node[ expando ] = {}); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + (outerCache[ node.uniqueID ] = {}); + + cache = uniqueCache[ type ] || []; + nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; + diff = nodeIndex && cache[ 2 ]; + node = nodeIndex && parent.childNodes[ nodeIndex ]; + + while ( (node = ++nodeIndex && node && node[ dir ] || + + // Fallback to seeking `elem` from the start + (diff = nodeIndex = 0) || start.pop()) ) { + + // When found, cache indexes on `parent` and break + if ( node.nodeType === 1 && ++diff && node === elem ) { + uniqueCache[ type ] = [ dirruns, nodeIndex, diff ]; + break; + } + } + + } else { + // Use previously-cached element index if available + if ( useCache ) { + // ...in a gzip-friendly way + node = elem; + outerCache = node[ expando ] || (node[ expando ] = {}); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + (outerCache[ node.uniqueID ] = {}); + + cache = uniqueCache[ type ] || []; + nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; + diff = nodeIndex; + } + + // xml :nth-child(...) + // or :nth-last-child(...) or :nth(-last)?-of-type(...) + if ( diff === false ) { + // Use the same loop as above to seek `elem` from the start + while ( (node = ++nodeIndex && node && node[ dir ] || + (diff = nodeIndex = 0) || start.pop()) ) { + + if ( ( ofType ? + node.nodeName.toLowerCase() === name : + node.nodeType === 1 ) && + ++diff ) { + + // Cache the index of each encountered element + if ( useCache ) { + outerCache = node[ expando ] || (node[ expando ] = {}); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + (outerCache[ node.uniqueID ] = {}); + + uniqueCache[ type ] = [ dirruns, diff ]; + } + + if ( node === elem ) { + break; + } + } + } + } + } + + // Incorporate the offset, then check against cycle size + diff -= last; + return diff === first || ( diff % first === 0 && diff / first >= 0 ); + } + }; + }, + + "PSEUDO": function( pseudo, argument ) { + // pseudo-class names are case-insensitive + // http://www.w3.org/TR/selectors/#pseudo-classes + // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters + // Remember that setFilters inherits from pseudos + var args, + fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || + Sizzle.error( "unsupported pseudo: " + pseudo ); + + // The user may use createPseudo to indicate that + // arguments are needed to create the filter function + // just as Sizzle does + if ( fn[ expando ] ) { + return fn( argument ); + } + + // But maintain support for old signatures + if ( fn.length > 1 ) { + args = [ pseudo, pseudo, "", argument ]; + return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? + markFunction(function( seed, matches ) { + var idx, + matched = fn( seed, argument ), + i = matched.length; + while ( i-- ) { + idx = indexOf( seed, matched[i] ); + seed[ idx ] = !( matches[ idx ] = matched[i] ); + } + }) : + function( elem ) { + return fn( elem, 0, args ); + }; + } + + return fn; + } + }, + + pseudos: { + // Potentially complex pseudos + "not": markFunction(function( selector ) { + // Trim the selector passed to compile + // to avoid treating leading and trailing + // spaces as combinators + var input = [], + results = [], + matcher = compile( selector.replace( rtrim, "$1" ) ); + + return matcher[ expando ] ? + markFunction(function( seed, matches, context, xml ) { + var elem, + unmatched = matcher( seed, null, xml, [] ), + i = seed.length; + + // Match elements unmatched by `matcher` + while ( i-- ) { + if ( (elem = unmatched[i]) ) { + seed[i] = !(matches[i] = elem); + } + } + }) : + function( elem, context, xml ) { + input[0] = elem; + matcher( input, null, xml, results ); + // Don't keep the element (issue #299) + input[0] = null; + return !results.pop(); + }; + }), + + "has": markFunction(function( selector ) { + return function( elem ) { + return Sizzle( selector, elem ).length > 0; + }; + }), + + "contains": markFunction(function( text ) { + text = text.replace( runescape, funescape ); + return function( elem ) { + return ( elem.textContent || getText( elem ) ).indexOf( text ) > -1; + }; + }), + + // "Whether an element is represented by a :lang() selector + // is based solely on the element's language value + // being equal to the identifier C, + // or beginning with the identifier C immediately followed by "-". + // The matching of C against the element's language value is performed case-insensitively. + // The identifier C does not have to be a valid language name." + // http://www.w3.org/TR/selectors/#lang-pseudo + "lang": markFunction( function( lang ) { + // lang value must be a valid identifier + if ( !ridentifier.test(lang || "") ) { + Sizzle.error( "unsupported lang: " + lang ); + } + lang = lang.replace( runescape, funescape ).toLowerCase(); + return function( elem ) { + var elemLang; + do { + if ( (elemLang = documentIsHTML ? + elem.lang : + elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) { + + elemLang = elemLang.toLowerCase(); + return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; + } + } while ( (elem = elem.parentNode) && elem.nodeType === 1 ); + return false; + }; + }), + + // Miscellaneous + "target": function( elem ) { + var hash = window.location && window.location.hash; + return hash && hash.slice( 1 ) === elem.id; + }, + + "root": function( elem ) { + return elem === docElem; + }, + + "focus": function( elem ) { + return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex); + }, + + // Boolean properties + "enabled": createDisabledPseudo( false ), + "disabled": createDisabledPseudo( true ), + + "checked": function( elem ) { + // In CSS3, :checked should return both checked and selected elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + var nodeName = elem.nodeName.toLowerCase(); + return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected); + }, + + "selected": function( elem ) { + // Accessing this property makes selected-by-default + // options in Safari work properly + if ( elem.parentNode ) { + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + // Contents + "empty": function( elem ) { + // http://www.w3.org/TR/selectors/#empty-pseudo + // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5), + // but not by others (comment: 8; processing instruction: 7; etc.) + // nodeType < 6 works because attributes (2) do not appear as children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + if ( elem.nodeType < 6 ) { + return false; + } + } + return true; + }, + + "parent": function( elem ) { + return !Expr.pseudos["empty"]( elem ); + }, + + // Element/input types + "header": function( elem ) { + return rheader.test( elem.nodeName ); + }, + + "input": function( elem ) { + return rinputs.test( elem.nodeName ); + }, + + "button": function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === "button" || name === "button"; + }, + + "text": function( elem ) { + var attr; + return elem.nodeName.toLowerCase() === "input" && + elem.type === "text" && + + // Support: IE<8 + // New HTML5 attribute values (e.g., "search") appear with elem.type === "text" + ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text" ); + }, + + // Position-in-collection + "first": createPositionalPseudo(function() { + return [ 0 ]; + }), + + "last": createPositionalPseudo(function( matchIndexes, length ) { + return [ length - 1 ]; + }), + + "eq": createPositionalPseudo(function( matchIndexes, length, argument ) { + return [ argument < 0 ? argument + length : argument ]; + }), + + "even": createPositionalPseudo(function( matchIndexes, length ) { + var i = 0; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "odd": createPositionalPseudo(function( matchIndexes, length ) { + var i = 1; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "lt": createPositionalPseudo(function( matchIndexes, length, argument ) { + var i = argument < 0 ? + argument + length : + argument > length ? + length : + argument; + for ( ; --i >= 0; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "gt": createPositionalPseudo(function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; ++i < length; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }) + } +}; + +Expr.pseudos["nth"] = Expr.pseudos["eq"]; + +// Add button/input type pseudos +for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { + Expr.pseudos[ i ] = createInputPseudo( i ); +} +for ( i in { submit: true, reset: true } ) { + Expr.pseudos[ i ] = createButtonPseudo( i ); +} + +// Easy API for creating new setFilters +function setFilters() {} +setFilters.prototype = Expr.filters = Expr.pseudos; +Expr.setFilters = new setFilters(); + +tokenize = Sizzle.tokenize = function( selector, parseOnly ) { + var matched, match, tokens, type, + soFar, groups, preFilters, + cached = tokenCache[ selector + " " ]; + + if ( cached ) { + return parseOnly ? 0 : cached.slice( 0 ); + } + + soFar = selector; + groups = []; + preFilters = Expr.preFilter; + + while ( soFar ) { + + // Comma and first run + if ( !matched || (match = rcomma.exec( soFar )) ) { + if ( match ) { + // Don't consume trailing commas as valid + soFar = soFar.slice( match[0].length ) || soFar; + } + groups.push( (tokens = []) ); + } + + matched = false; + + // Combinators + if ( (match = rcombinators.exec( soFar )) ) { + matched = match.shift(); + tokens.push({ + value: matched, + // Cast descendant combinators to space + type: match[0].replace( rtrim, " " ) + }); + soFar = soFar.slice( matched.length ); + } + + // Filters + for ( type in Expr.filter ) { + if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || + (match = preFilters[ type ]( match ))) ) { + matched = match.shift(); + tokens.push({ + value: matched, + type: type, + matches: match + }); + soFar = soFar.slice( matched.length ); + } + } + + if ( !matched ) { + break; + } + } + + // Return the length of the invalid excess + // if we're just parsing + // Otherwise, throw an error or return tokens + return parseOnly ? + soFar.length : + soFar ? + Sizzle.error( selector ) : + // Cache the tokens + tokenCache( selector, groups ).slice( 0 ); +}; + +function toSelector( tokens ) { + var i = 0, + len = tokens.length, + selector = ""; + for ( ; i < len; i++ ) { + selector += tokens[i].value; + } + return selector; +} + +function addCombinator( matcher, combinator, base ) { + var dir = combinator.dir, + skip = combinator.next, + key = skip || dir, + checkNonElements = base && key === "parentNode", + doneName = done++; + + return combinator.first ? + // Check against closest ancestor/preceding element + function( elem, context, xml ) { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + return matcher( elem, context, xml ); + } + } + return false; + } : + + // Check against all ancestor/preceding elements + function( elem, context, xml ) { + var oldCache, uniqueCache, outerCache, + newCache = [ dirruns, doneName ]; + + // We can't set arbitrary data on XML nodes, so they don't benefit from combinator caching + if ( xml ) { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + if ( matcher( elem, context, xml ) ) { + return true; + } + } + } + } else { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + outerCache = elem[ expando ] || (elem[ expando ] = {}); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ elem.uniqueID ] || (outerCache[ elem.uniqueID ] = {}); + + if ( skip && skip === elem.nodeName.toLowerCase() ) { + elem = elem[ dir ] || elem; + } else if ( (oldCache = uniqueCache[ key ]) && + oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) { + + // Assign to newCache so results back-propagate to previous elements + return (newCache[ 2 ] = oldCache[ 2 ]); + } else { + // Reuse newcache so results back-propagate to previous elements + uniqueCache[ key ] = newCache; + + // A match means we're done; a fail means we have to keep checking + if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) { + return true; + } + } + } + } + } + return false; + }; +} + +function elementMatcher( matchers ) { + return matchers.length > 1 ? + function( elem, context, xml ) { + var i = matchers.length; + while ( i-- ) { + if ( !matchers[i]( elem, context, xml ) ) { + return false; + } + } + return true; + } : + matchers[0]; +} + +function multipleContexts( selector, contexts, results ) { + var i = 0, + len = contexts.length; + for ( ; i < len; i++ ) { + Sizzle( selector, contexts[i], results ); + } + return results; +} + +function condense( unmatched, map, filter, context, xml ) { + var elem, + newUnmatched = [], + i = 0, + len = unmatched.length, + mapped = map != null; + + for ( ; i < len; i++ ) { + if ( (elem = unmatched[i]) ) { + if ( !filter || filter( elem, context, xml ) ) { + newUnmatched.push( elem ); + if ( mapped ) { + map.push( i ); + } + } + } + } + + return newUnmatched; +} + +function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { + if ( postFilter && !postFilter[ expando ] ) { + postFilter = setMatcher( postFilter ); + } + if ( postFinder && !postFinder[ expando ] ) { + postFinder = setMatcher( postFinder, postSelector ); + } + return markFunction(function( seed, results, context, xml ) { + var temp, i, elem, + preMap = [], + postMap = [], + preexisting = results.length, + + // Get initial elements from seed or context + elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ), + + // Prefilter to get matcher input, preserving a map for seed-results synchronization + matcherIn = preFilter && ( seed || !selector ) ? + condense( elems, preMap, preFilter, context, xml ) : + elems, + + matcherOut = matcher ? + // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, + postFinder || ( seed ? preFilter : preexisting || postFilter ) ? + + // ...intermediate processing is necessary + [] : + + // ...otherwise use results directly + results : + matcherIn; + + // Find primary matches + if ( matcher ) { + matcher( matcherIn, matcherOut, context, xml ); + } + + // Apply postFilter + if ( postFilter ) { + temp = condense( matcherOut, postMap ); + postFilter( temp, [], context, xml ); + + // Un-match failing elements by moving them back to matcherIn + i = temp.length; + while ( i-- ) { + if ( (elem = temp[i]) ) { + matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem); + } + } + } + + if ( seed ) { + if ( postFinder || preFilter ) { + if ( postFinder ) { + // Get the final matcherOut by condensing this intermediate into postFinder contexts + temp = []; + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) ) { + // Restore matcherIn since elem is not yet a final match + temp.push( (matcherIn[i] = elem) ); + } + } + postFinder( null, (matcherOut = []), temp, xml ); + } + + // Move matched elements from seed to results to keep them synchronized + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) && + (temp = postFinder ? indexOf( seed, elem ) : preMap[i]) > -1 ) { + + seed[temp] = !(results[temp] = elem); + } + } + } + + // Add elements to results, through postFinder if defined + } else { + matcherOut = condense( + matcherOut === results ? + matcherOut.splice( preexisting, matcherOut.length ) : + matcherOut + ); + if ( postFinder ) { + postFinder( null, results, matcherOut, xml ); + } else { + push.apply( results, matcherOut ); + } + } + }); +} + +function matcherFromTokens( tokens ) { + var checkContext, matcher, j, + len = tokens.length, + leadingRelative = Expr.relative[ tokens[0].type ], + implicitRelative = leadingRelative || Expr.relative[" "], + i = leadingRelative ? 1 : 0, + + // The foundational matcher ensures that elements are reachable from top-level context(s) + matchContext = addCombinator( function( elem ) { + return elem === checkContext; + }, implicitRelative, true ), + matchAnyContext = addCombinator( function( elem ) { + return indexOf( checkContext, elem ) > -1; + }, implicitRelative, true ), + matchers = [ function( elem, context, xml ) { + var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( + (checkContext = context).nodeType ? + matchContext( elem, context, xml ) : + matchAnyContext( elem, context, xml ) ); + // Avoid hanging onto element (issue #299) + checkContext = null; + return ret; + } ]; + + for ( ; i < len; i++ ) { + if ( (matcher = Expr.relative[ tokens[i].type ]) ) { + matchers = [ addCombinator(elementMatcher( matchers ), matcher) ]; + } else { + matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches ); + + // Return special upon seeing a positional matcher + if ( matcher[ expando ] ) { + // Find the next relative operator (if any) for proper handling + j = ++i; + for ( ; j < len; j++ ) { + if ( Expr.relative[ tokens[j].type ] ) { + break; + } + } + return setMatcher( + i > 1 && elementMatcher( matchers ), + i > 1 && toSelector( + // If the preceding token was a descendant combinator, insert an implicit any-element `*` + tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" }) + ).replace( rtrim, "$1" ), + matcher, + i < j && matcherFromTokens( tokens.slice( i, j ) ), + j < len && matcherFromTokens( (tokens = tokens.slice( j )) ), + j < len && toSelector( tokens ) + ); + } + matchers.push( matcher ); + } + } + + return elementMatcher( matchers ); +} + +function matcherFromGroupMatchers( elementMatchers, setMatchers ) { + var bySet = setMatchers.length > 0, + byElement = elementMatchers.length > 0, + superMatcher = function( seed, context, xml, results, outermost ) { + var elem, j, matcher, + matchedCount = 0, + i = "0", + unmatched = seed && [], + setMatched = [], + contextBackup = outermostContext, + // We must always have either seed elements or outermost context + elems = seed || byElement && Expr.find["TAG"]( "*", outermost ), + // Use integer dirruns iff this is the outermost matcher + dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1), + len = elems.length; + + if ( outermost ) { + outermostContext = context === document || context || outermost; + } + + // Add elements passing elementMatchers directly to results + // Support: IE<9, Safari + // Tolerate NodeList properties (IE: "length"; Safari: ) matching elements by id + for ( ; i !== len && (elem = elems[i]) != null; i++ ) { + if ( byElement && elem ) { + j = 0; + if ( !context && elem.ownerDocument !== document ) { + setDocument( elem ); + xml = !documentIsHTML; + } + while ( (matcher = elementMatchers[j++]) ) { + if ( matcher( elem, context || document, xml) ) { + results.push( elem ); + break; + } + } + if ( outermost ) { + dirruns = dirrunsUnique; + } + } + + // Track unmatched elements for set filters + if ( bySet ) { + // They will have gone through all possible matchers + if ( (elem = !matcher && elem) ) { + matchedCount--; + } + + // Lengthen the array for every element, matched or not + if ( seed ) { + unmatched.push( elem ); + } + } + } + + // `i` is now the count of elements visited above, and adding it to `matchedCount` + // makes the latter nonnegative. + matchedCount += i; + + // Apply set filters to unmatched elements + // NOTE: This can be skipped if there are no unmatched elements (i.e., `matchedCount` + // equals `i`), unless we didn't visit _any_ elements in the above loop because we have + // no element matchers and no seed. + // Incrementing an initially-string "0" `i` allows `i` to remain a string only in that + // case, which will result in a "00" `matchedCount` that differs from `i` but is also + // numerically zero. + if ( bySet && i !== matchedCount ) { + j = 0; + while ( (matcher = setMatchers[j++]) ) { + matcher( unmatched, setMatched, context, xml ); + } + + if ( seed ) { + // Reintegrate element matches to eliminate the need for sorting + if ( matchedCount > 0 ) { + while ( i-- ) { + if ( !(unmatched[i] || setMatched[i]) ) { + setMatched[i] = pop.call( results ); + } + } + } + + // Discard index placeholder values to get only actual matches + setMatched = condense( setMatched ); + } + + // Add matches to results + push.apply( results, setMatched ); + + // Seedless set matches succeeding multiple successful matchers stipulate sorting + if ( outermost && !seed && setMatched.length > 0 && + ( matchedCount + setMatchers.length ) > 1 ) { + + Sizzle.uniqueSort( results ); + } + } + + // Override manipulation of globals by nested matchers + if ( outermost ) { + dirruns = dirrunsUnique; + outermostContext = contextBackup; + } + + return unmatched; + }; + + return bySet ? + markFunction( superMatcher ) : + superMatcher; +} + +compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) { + var i, + setMatchers = [], + elementMatchers = [], + cached = compilerCache[ selector + " " ]; + + if ( !cached ) { + // Generate a function of recursive functions that can be used to check each element + if ( !match ) { + match = tokenize( selector ); + } + i = match.length; + while ( i-- ) { + cached = matcherFromTokens( match[i] ); + if ( cached[ expando ] ) { + setMatchers.push( cached ); + } else { + elementMatchers.push( cached ); + } + } + + // Cache the compiled function + cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) ); + + // Save selector and tokenization + cached.selector = selector; + } + return cached; +}; + +/** + * A low-level selection function that works with Sizzle's compiled + * selector functions + * @param {String|Function} selector A selector or a pre-compiled + * selector function built with Sizzle.compile + * @param {Element} context + * @param {Array} [results] + * @param {Array} [seed] A set of elements to match against + */ +select = Sizzle.select = function( selector, context, results, seed ) { + var i, tokens, token, type, find, + compiled = typeof selector === "function" && selector, + match = !seed && tokenize( (selector = compiled.selector || selector) ); + + results = results || []; + + // Try to minimize operations if there is only one selector in the list and no seed + // (the latter of which guarantees us context) + if ( match.length === 1 ) { + + // Reduce context if the leading compound selector is an ID + tokens = match[0] = match[0].slice( 0 ); + if ( tokens.length > 2 && (token = tokens[0]).type === "ID" && + context.nodeType === 9 && documentIsHTML && Expr.relative[ tokens[1].type ] ) { + + context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0]; + if ( !context ) { + return results; + + // Precompiled matchers will still verify ancestry, so step up a level + } else if ( compiled ) { + context = context.parentNode; + } + + selector = selector.slice( tokens.shift().value.length ); + } + + // Fetch a seed set for right-to-left matching + i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length; + while ( i-- ) { + token = tokens[i]; + + // Abort if we hit a combinator + if ( Expr.relative[ (type = token.type) ] ) { + break; + } + if ( (find = Expr.find[ type ]) ) { + // Search, expanding context for leading sibling combinators + if ( (seed = find( + token.matches[0].replace( runescape, funescape ), + rsibling.test( tokens[0].type ) && testContext( context.parentNode ) || context + )) ) { + + // If seed is empty or no tokens remain, we can return early + tokens.splice( i, 1 ); + selector = seed.length && toSelector( tokens ); + if ( !selector ) { + push.apply( results, seed ); + return results; + } + + break; + } + } + } + } + + // Compile and execute a filtering function if one is not provided + // Provide `match` to avoid retokenization if we modified the selector above + ( compiled || compile( selector, match ) )( + seed, + context, + !documentIsHTML, + results, + !context || rsibling.test( selector ) && testContext( context.parentNode ) || context + ); + return results; +}; + +// One-time assignments + +// Sort stability +support.sortStable = expando.split("").sort( sortOrder ).join("") === expando; + +// Support: Chrome 14-35+ +// Always assume duplicates if they aren't passed to the comparison function +support.detectDuplicates = !!hasDuplicate; + +// Initialize against the default document +setDocument(); + +// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27) +// Detached nodes confoundingly follow *each other* +support.sortDetached = assert(function( el ) { + // Should return 1, but returns 4 (following) + return el.compareDocumentPosition( document.createElement("fieldset") ) & 1; +}); + +// Support: IE<8 +// Prevent attribute/property "interpolation" +// https://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx +if ( !assert(function( el ) { + el.innerHTML = ""; + return el.firstChild.getAttribute("href") === "#" ; +}) ) { + addHandle( "type|href|height|width", function( elem, name, isXML ) { + if ( !isXML ) { + return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 ); + } + }); +} + +// Support: IE<9 +// Use defaultValue in place of getAttribute("value") +if ( !support.attributes || !assert(function( el ) { + el.innerHTML = ""; + el.firstChild.setAttribute( "value", "" ); + return el.firstChild.getAttribute( "value" ) === ""; +}) ) { + addHandle( "value", function( elem, name, isXML ) { + if ( !isXML && elem.nodeName.toLowerCase() === "input" ) { + return elem.defaultValue; + } + }); +} + +// Support: IE<9 +// Use getAttributeNode to fetch booleans when getAttribute lies +if ( !assert(function( el ) { + return el.getAttribute("disabled") == null; +}) ) { + addHandle( booleans, function( elem, name, isXML ) { + var val; + if ( !isXML ) { + return elem[ name ] === true ? name.toLowerCase() : + (val = elem.getAttributeNode( name )) && val.specified ? + val.value : + null; + } + }); +} + +return Sizzle; + +})( window ); + + + +jQuery.find = Sizzle; +jQuery.expr = Sizzle.selectors; + +// Deprecated +jQuery.expr[ ":" ] = jQuery.expr.pseudos; +jQuery.uniqueSort = jQuery.unique = Sizzle.uniqueSort; +jQuery.text = Sizzle.getText; +jQuery.isXMLDoc = Sizzle.isXML; +jQuery.contains = Sizzle.contains; +jQuery.escapeSelector = Sizzle.escape; + + + + +var dir = function( elem, dir, until ) { + var matched = [], + truncate = until !== undefined; + + while ( ( elem = elem[ dir ] ) && elem.nodeType !== 9 ) { + if ( elem.nodeType === 1 ) { + if ( truncate && jQuery( elem ).is( until ) ) { + break; + } + matched.push( elem ); + } + } + return matched; +}; + + +var siblings = function( n, elem ) { + var matched = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + matched.push( n ); + } + } + + return matched; +}; + + +var rneedsContext = jQuery.expr.match.needsContext; + + + +function nodeName( elem, name ) { + + return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); + +}; +var rsingleTag = ( /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i ); + + + +// Implement the identical functionality for filter and not +function winnow( elements, qualifier, not ) { + if ( isFunction( qualifier ) ) { + return jQuery.grep( elements, function( elem, i ) { + return !!qualifier.call( elem, i, elem ) !== not; + } ); + } + + // Single element + if ( qualifier.nodeType ) { + return jQuery.grep( elements, function( elem ) { + return ( elem === qualifier ) !== not; + } ); + } + + // Arraylike of elements (jQuery, arguments, Array) + if ( typeof qualifier !== "string" ) { + return jQuery.grep( elements, function( elem ) { + return ( indexOf.call( qualifier, elem ) > -1 ) !== not; + } ); + } + + // Filtered directly for both simple and complex selectors + return jQuery.filter( qualifier, elements, not ); +} + +jQuery.filter = function( expr, elems, not ) { + var elem = elems[ 0 ]; + + if ( not ) { + expr = ":not(" + expr + ")"; + } + + if ( elems.length === 1 && elem.nodeType === 1 ) { + return jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : []; + } + + return jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { + return elem.nodeType === 1; + } ) ); +}; + +jQuery.fn.extend( { + find: function( selector ) { + var i, ret, + len = this.length, + self = this; + + if ( typeof selector !== "string" ) { + return this.pushStack( jQuery( selector ).filter( function() { + for ( i = 0; i < len; i++ ) { + if ( jQuery.contains( self[ i ], this ) ) { + return true; + } + } + } ) ); + } + + ret = this.pushStack( [] ); + + for ( i = 0; i < len; i++ ) { + jQuery.find( selector, self[ i ], ret ); + } + + return len > 1 ? jQuery.uniqueSort( ret ) : ret; + }, + filter: function( selector ) { + return this.pushStack( winnow( this, selector || [], false ) ); + }, + not: function( selector ) { + return this.pushStack( winnow( this, selector || [], true ) ); + }, + is: function( selector ) { + return !!winnow( + this, + + // If this is a positional/relative selector, check membership in the returned set + // so $("p:first").is("p:last") won't return true for a doc with two "p". + typeof selector === "string" && rneedsContext.test( selector ) ? + jQuery( selector ) : + selector || [], + false + ).length; + } +} ); + + +// Initialize a jQuery object + + +// A central reference to the root jQuery(document) +var rootjQuery, + + // A simple way to check for HTML strings + // Prioritize #id over to avoid XSS via location.hash (#9521) + // Strict HTML recognition (#11290: must start with <) + // Shortcut simple #id case for speed + rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/, + + init = jQuery.fn.init = function( selector, context, root ) { + var match, elem; + + // HANDLE: $(""), $(null), $(undefined), $(false) + if ( !selector ) { + return this; + } + + // Method init() accepts an alternate rootjQuery + // so migrate can support jQuery.sub (gh-2101) + root = root || rootjQuery; + + // Handle HTML strings + if ( typeof selector === "string" ) { + if ( selector[ 0 ] === "<" && + selector[ selector.length - 1 ] === ">" && + selector.length >= 3 ) { + + // Assume that strings that start and end with <> are HTML and skip the regex check + match = [ null, selector, null ]; + + } else { + match = rquickExpr.exec( selector ); + } + + // Match html or make sure no context is specified for #id + if ( match && ( match[ 1 ] || !context ) ) { + + // HANDLE: $(html) -> $(array) + if ( match[ 1 ] ) { + context = context instanceof jQuery ? context[ 0 ] : context; + + // Option to run scripts is true for back-compat + // Intentionally let the error be thrown if parseHTML is not present + jQuery.merge( this, jQuery.parseHTML( + match[ 1 ], + context && context.nodeType ? context.ownerDocument || context : document, + true + ) ); + + // HANDLE: $(html, props) + if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) { + for ( match in context ) { + + // Properties of context are called as methods if possible + if ( isFunction( this[ match ] ) ) { + this[ match ]( context[ match ] ); + + // ...and otherwise set as attributes + } else { + this.attr( match, context[ match ] ); + } + } + } + + return this; + + // HANDLE: $(#id) + } else { + elem = document.getElementById( match[ 2 ] ); + + if ( elem ) { + + // Inject the element directly into the jQuery object + this[ 0 ] = elem; + this.length = 1; + } + return this; + } + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return ( context || root ).find( selector ); + + // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return this.constructor( context ).find( selector ); + } + + // HANDLE: $(DOMElement) + } else if ( selector.nodeType ) { + this[ 0 ] = selector; + this.length = 1; + return this; + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( isFunction( selector ) ) { + return root.ready !== undefined ? + root.ready( selector ) : + + // Execute immediately if ready is not present + selector( jQuery ); + } + + return jQuery.makeArray( selector, this ); + }; + +// Give the init function the jQuery prototype for later instantiation +init.prototype = jQuery.fn; + +// Initialize central reference +rootjQuery = jQuery( document ); + + +var rparentsprev = /^(?:parents|prev(?:Until|All))/, + + // Methods guaranteed to produce a unique set when starting from a unique set + guaranteedUnique = { + children: true, + contents: true, + next: true, + prev: true + }; + +jQuery.fn.extend( { + has: function( target ) { + var targets = jQuery( target, this ), + l = targets.length; + + return this.filter( function() { + var i = 0; + for ( ; i < l; i++ ) { + if ( jQuery.contains( this, targets[ i ] ) ) { + return true; + } + } + } ); + }, + + closest: function( selectors, context ) { + var cur, + i = 0, + l = this.length, + matched = [], + targets = typeof selectors !== "string" && jQuery( selectors ); + + // Positional selectors never match, since there's no _selection_ context + if ( !rneedsContext.test( selectors ) ) { + for ( ; i < l; i++ ) { + for ( cur = this[ i ]; cur && cur !== context; cur = cur.parentNode ) { + + // Always skip document fragments + if ( cur.nodeType < 11 && ( targets ? + targets.index( cur ) > -1 : + + // Don't pass non-elements to Sizzle + cur.nodeType === 1 && + jQuery.find.matchesSelector( cur, selectors ) ) ) { + + matched.push( cur ); + break; + } + } + } + } + + return this.pushStack( matched.length > 1 ? jQuery.uniqueSort( matched ) : matched ); + }, + + // Determine the position of an element within the set + index: function( elem ) { + + // No argument, return index in parent + if ( !elem ) { + return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1; + } + + // Index in selector + if ( typeof elem === "string" ) { + return indexOf.call( jQuery( elem ), this[ 0 ] ); + } + + // Locate the position of the desired element + return indexOf.call( this, + + // If it receives a jQuery object, the first element is used + elem.jquery ? elem[ 0 ] : elem + ); + }, + + add: function( selector, context ) { + return this.pushStack( + jQuery.uniqueSort( + jQuery.merge( this.get(), jQuery( selector, context ) ) + ) + ); + }, + + addBack: function( selector ) { + return this.add( selector == null ? + this.prevObject : this.prevObject.filter( selector ) + ); + } +} ); + +function sibling( cur, dir ) { + while ( ( cur = cur[ dir ] ) && cur.nodeType !== 1 ) {} + return cur; +} + +jQuery.each( { + parent: function( elem ) { + var parent = elem.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + parents: function( elem ) { + return dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, i, until ) { + return dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return sibling( elem, "nextSibling" ); + }, + prev: function( elem ) { + return sibling( elem, "previousSibling" ); + }, + nextAll: function( elem ) { + return dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, i, until ) { + return dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, i, until ) { + return dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return siblings( ( elem.parentNode || {} ).firstChild, elem ); + }, + children: function( elem ) { + return siblings( elem.firstChild ); + }, + contents: function( elem ) { + if ( typeof elem.contentDocument !== "undefined" ) { + return elem.contentDocument; + } + + // Support: IE 9 - 11 only, iOS 7 only, Android Browser <=4.3 only + // Treat the template element as a regular one in browsers that + // don't support it. + if ( nodeName( elem, "template" ) ) { + elem = elem.content || elem; + } + + return jQuery.merge( [], elem.childNodes ); + } +}, function( name, fn ) { + jQuery.fn[ name ] = function( until, selector ) { + var matched = jQuery.map( this, fn, until ); + + if ( name.slice( -5 ) !== "Until" ) { + selector = until; + } + + if ( selector && typeof selector === "string" ) { + matched = jQuery.filter( selector, matched ); + } + + if ( this.length > 1 ) { + + // Remove duplicates + if ( !guaranteedUnique[ name ] ) { + jQuery.uniqueSort( matched ); + } + + // Reverse order for parents* and prev-derivatives + if ( rparentsprev.test( name ) ) { + matched.reverse(); + } + } + + return this.pushStack( matched ); + }; +} ); +var rnothtmlwhite = ( /[^\x20\t\r\n\f]+/g ); + + + +// Convert String-formatted options into Object-formatted ones +function createOptions( options ) { + var object = {}; + jQuery.each( options.match( rnothtmlwhite ) || [], function( _, flag ) { + object[ flag ] = true; + } ); + return object; +} + +/* + * Create a callback list using the following parameters: + * + * options: an optional list of space-separated options that will change how + * the callback list behaves or a more traditional option object + * + * By default a callback list will act like an event callback list and can be + * "fired" multiple times. + * + * Possible options: + * + * once: will ensure the callback list can only be fired once (like a Deferred) + * + * memory: will keep track of previous values and will call any callback added + * after the list has been fired right away with the latest "memorized" + * values (like a Deferred) + * + * unique: will ensure a callback can only be added once (no duplicate in the list) + * + * stopOnFalse: interrupt callings when a callback returns false + * + */ +jQuery.Callbacks = function( options ) { + + // Convert options from String-formatted to Object-formatted if needed + // (we check in cache first) + options = typeof options === "string" ? + createOptions( options ) : + jQuery.extend( {}, options ); + + var // Flag to know if list is currently firing + firing, + + // Last fire value for non-forgettable lists + memory, + + // Flag to know if list was already fired + fired, + + // Flag to prevent firing + locked, + + // Actual callback list + list = [], + + // Queue of execution data for repeatable lists + queue = [], + + // Index of currently firing callback (modified by add/remove as needed) + firingIndex = -1, + + // Fire callbacks + fire = function() { + + // Enforce single-firing + locked = locked || options.once; + + // Execute callbacks for all pending executions, + // respecting firingIndex overrides and runtime changes + fired = firing = true; + for ( ; queue.length; firingIndex = -1 ) { + memory = queue.shift(); + while ( ++firingIndex < list.length ) { + + // Run callback and check for early termination + if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false && + options.stopOnFalse ) { + + // Jump to end and forget the data so .add doesn't re-fire + firingIndex = list.length; + memory = false; + } + } + } + + // Forget the data if we're done with it + if ( !options.memory ) { + memory = false; + } + + firing = false; + + // Clean up if we're done firing for good + if ( locked ) { + + // Keep an empty list if we have data for future add calls + if ( memory ) { + list = []; + + // Otherwise, this object is spent + } else { + list = ""; + } + } + }, + + // Actual Callbacks object + self = { + + // Add a callback or a collection of callbacks to the list + add: function() { + if ( list ) { + + // If we have memory from a past run, we should fire after adding + if ( memory && !firing ) { + firingIndex = list.length - 1; + queue.push( memory ); + } + + ( function add( args ) { + jQuery.each( args, function( _, arg ) { + if ( isFunction( arg ) ) { + if ( !options.unique || !self.has( arg ) ) { + list.push( arg ); + } + } else if ( arg && arg.length && toType( arg ) !== "string" ) { + + // Inspect recursively + add( arg ); + } + } ); + } )( arguments ); + + if ( memory && !firing ) { + fire(); + } + } + return this; + }, + + // Remove a callback from the list + remove: function() { + jQuery.each( arguments, function( _, arg ) { + var index; + while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { + list.splice( index, 1 ); + + // Handle firing indexes + if ( index <= firingIndex ) { + firingIndex--; + } + } + } ); + return this; + }, + + // Check if a given callback is in the list. + // If no argument is given, return whether or not list has callbacks attached. + has: function( fn ) { + return fn ? + jQuery.inArray( fn, list ) > -1 : + list.length > 0; + }, + + // Remove all callbacks from the list + empty: function() { + if ( list ) { + list = []; + } + return this; + }, + + // Disable .fire and .add + // Abort any current/pending executions + // Clear all callbacks and values + disable: function() { + locked = queue = []; + list = memory = ""; + return this; + }, + disabled: function() { + return !list; + }, + + // Disable .fire + // Also disable .add unless we have memory (since it would have no effect) + // Abort any pending executions + lock: function() { + locked = queue = []; + if ( !memory && !firing ) { + list = memory = ""; + } + return this; + }, + locked: function() { + return !!locked; + }, + + // Call all callbacks with the given context and arguments + fireWith: function( context, args ) { + if ( !locked ) { + args = args || []; + args = [ context, args.slice ? args.slice() : args ]; + queue.push( args ); + if ( !firing ) { + fire(); + } + } + return this; + }, + + // Call all the callbacks with the given arguments + fire: function() { + self.fireWith( this, arguments ); + return this; + }, + + // To know if the callbacks have already been called at least once + fired: function() { + return !!fired; + } + }; + + return self; +}; + + +function Identity( v ) { + return v; +} +function Thrower( ex ) { + throw ex; +} + +function adoptValue( value, resolve, reject, noValue ) { + var method; + + try { + + // Check for promise aspect first to privilege synchronous behavior + if ( value && isFunction( ( method = value.promise ) ) ) { + method.call( value ).done( resolve ).fail( reject ); + + // Other thenables + } else if ( value && isFunction( ( method = value.then ) ) ) { + method.call( value, resolve, reject ); + + // Other non-thenables + } else { + + // Control `resolve` arguments by letting Array#slice cast boolean `noValue` to integer: + // * false: [ value ].slice( 0 ) => resolve( value ) + // * true: [ value ].slice( 1 ) => resolve() + resolve.apply( undefined, [ value ].slice( noValue ) ); + } + + // For Promises/A+, convert exceptions into rejections + // Since jQuery.when doesn't unwrap thenables, we can skip the extra checks appearing in + // Deferred#then to conditionally suppress rejection. + } catch ( value ) { + + // Support: Android 4.0 only + // Strict mode functions invoked without .call/.apply get global-object context + reject.apply( undefined, [ value ] ); + } +} + +jQuery.extend( { + + Deferred: function( func ) { + var tuples = [ + + // action, add listener, callbacks, + // ... .then handlers, argument index, [final state] + [ "notify", "progress", jQuery.Callbacks( "memory" ), + jQuery.Callbacks( "memory" ), 2 ], + [ "resolve", "done", jQuery.Callbacks( "once memory" ), + jQuery.Callbacks( "once memory" ), 0, "resolved" ], + [ "reject", "fail", jQuery.Callbacks( "once memory" ), + jQuery.Callbacks( "once memory" ), 1, "rejected" ] + ], + state = "pending", + promise = { + state: function() { + return state; + }, + always: function() { + deferred.done( arguments ).fail( arguments ); + return this; + }, + "catch": function( fn ) { + return promise.then( null, fn ); + }, + + // Keep pipe for back-compat + pipe: function( /* fnDone, fnFail, fnProgress */ ) { + var fns = arguments; + + return jQuery.Deferred( function( newDefer ) { + jQuery.each( tuples, function( i, tuple ) { + + // Map tuples (progress, done, fail) to arguments (done, fail, progress) + var fn = isFunction( fns[ tuple[ 4 ] ] ) && fns[ tuple[ 4 ] ]; + + // deferred.progress(function() { bind to newDefer or newDefer.notify }) + // deferred.done(function() { bind to newDefer or newDefer.resolve }) + // deferred.fail(function() { bind to newDefer or newDefer.reject }) + deferred[ tuple[ 1 ] ]( function() { + var returned = fn && fn.apply( this, arguments ); + if ( returned && isFunction( returned.promise ) ) { + returned.promise() + .progress( newDefer.notify ) + .done( newDefer.resolve ) + .fail( newDefer.reject ); + } else { + newDefer[ tuple[ 0 ] + "With" ]( + this, + fn ? [ returned ] : arguments + ); + } + } ); + } ); + fns = null; + } ).promise(); + }, + then: function( onFulfilled, onRejected, onProgress ) { + var maxDepth = 0; + function resolve( depth, deferred, handler, special ) { + return function() { + var that = this, + args = arguments, + mightThrow = function() { + var returned, then; + + // Support: Promises/A+ section 2.3.3.3.3 + // https://promisesaplus.com/#point-59 + // Ignore double-resolution attempts + if ( depth < maxDepth ) { + return; + } + + returned = handler.apply( that, args ); + + // Support: Promises/A+ section 2.3.1 + // https://promisesaplus.com/#point-48 + if ( returned === deferred.promise() ) { + throw new TypeError( "Thenable self-resolution" ); + } + + // Support: Promises/A+ sections 2.3.3.1, 3.5 + // https://promisesaplus.com/#point-54 + // https://promisesaplus.com/#point-75 + // Retrieve `then` only once + then = returned && + + // Support: Promises/A+ section 2.3.4 + // https://promisesaplus.com/#point-64 + // Only check objects and functions for thenability + ( typeof returned === "object" || + typeof returned === "function" ) && + returned.then; + + // Handle a returned thenable + if ( isFunction( then ) ) { + + // Special processors (notify) just wait for resolution + if ( special ) { + then.call( + returned, + resolve( maxDepth, deferred, Identity, special ), + resolve( maxDepth, deferred, Thrower, special ) + ); + + // Normal processors (resolve) also hook into progress + } else { + + // ...and disregard older resolution values + maxDepth++; + + then.call( + returned, + resolve( maxDepth, deferred, Identity, special ), + resolve( maxDepth, deferred, Thrower, special ), + resolve( maxDepth, deferred, Identity, + deferred.notifyWith ) + ); + } + + // Handle all other returned values + } else { + + // Only substitute handlers pass on context + // and multiple values (non-spec behavior) + if ( handler !== Identity ) { + that = undefined; + args = [ returned ]; + } + + // Process the value(s) + // Default process is resolve + ( special || deferred.resolveWith )( that, args ); + } + }, + + // Only normal processors (resolve) catch and reject exceptions + process = special ? + mightThrow : + function() { + try { + mightThrow(); + } catch ( e ) { + + if ( jQuery.Deferred.exceptionHook ) { + jQuery.Deferred.exceptionHook( e, + process.stackTrace ); + } + + // Support: Promises/A+ section 2.3.3.3.4.1 + // https://promisesaplus.com/#point-61 + // Ignore post-resolution exceptions + if ( depth + 1 >= maxDepth ) { + + // Only substitute handlers pass on context + // and multiple values (non-spec behavior) + if ( handler !== Thrower ) { + that = undefined; + args = [ e ]; + } + + deferred.rejectWith( that, args ); + } + } + }; + + // Support: Promises/A+ section 2.3.3.3.1 + // https://promisesaplus.com/#point-57 + // Re-resolve promises immediately to dodge false rejection from + // subsequent errors + if ( depth ) { + process(); + } else { + + // Call an optional hook to record the stack, in case of exception + // since it's otherwise lost when execution goes async + if ( jQuery.Deferred.getStackHook ) { + process.stackTrace = jQuery.Deferred.getStackHook(); + } + window.setTimeout( process ); + } + }; + } + + return jQuery.Deferred( function( newDefer ) { + + // progress_handlers.add( ... ) + tuples[ 0 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onProgress ) ? + onProgress : + Identity, + newDefer.notifyWith + ) + ); + + // fulfilled_handlers.add( ... ) + tuples[ 1 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onFulfilled ) ? + onFulfilled : + Identity + ) + ); + + // rejected_handlers.add( ... ) + tuples[ 2 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onRejected ) ? + onRejected : + Thrower + ) + ); + } ).promise(); + }, + + // Get a promise for this deferred + // If obj is provided, the promise aspect is added to the object + promise: function( obj ) { + return obj != null ? jQuery.extend( obj, promise ) : promise; + } + }, + deferred = {}; + + // Add list-specific methods + jQuery.each( tuples, function( i, tuple ) { + var list = tuple[ 2 ], + stateString = tuple[ 5 ]; + + // promise.progress = list.add + // promise.done = list.add + // promise.fail = list.add + promise[ tuple[ 1 ] ] = list.add; + + // Handle state + if ( stateString ) { + list.add( + function() { + + // state = "resolved" (i.e., fulfilled) + // state = "rejected" + state = stateString; + }, + + // rejected_callbacks.disable + // fulfilled_callbacks.disable + tuples[ 3 - i ][ 2 ].disable, + + // rejected_handlers.disable + // fulfilled_handlers.disable + tuples[ 3 - i ][ 3 ].disable, + + // progress_callbacks.lock + tuples[ 0 ][ 2 ].lock, + + // progress_handlers.lock + tuples[ 0 ][ 3 ].lock + ); + } + + // progress_handlers.fire + // fulfilled_handlers.fire + // rejected_handlers.fire + list.add( tuple[ 3 ].fire ); + + // deferred.notify = function() { deferred.notifyWith(...) } + // deferred.resolve = function() { deferred.resolveWith(...) } + // deferred.reject = function() { deferred.rejectWith(...) } + deferred[ tuple[ 0 ] ] = function() { + deferred[ tuple[ 0 ] + "With" ]( this === deferred ? undefined : this, arguments ); + return this; + }; + + // deferred.notifyWith = list.fireWith + // deferred.resolveWith = list.fireWith + // deferred.rejectWith = list.fireWith + deferred[ tuple[ 0 ] + "With" ] = list.fireWith; + } ); + + // Make the deferred a promise + promise.promise( deferred ); + + // Call given func if any + if ( func ) { + func.call( deferred, deferred ); + } + + // All done! + return deferred; + }, + + // Deferred helper + when: function( singleValue ) { + var + + // count of uncompleted subordinates + remaining = arguments.length, + + // count of unprocessed arguments + i = remaining, + + // subordinate fulfillment data + resolveContexts = Array( i ), + resolveValues = slice.call( arguments ), + + // the master Deferred + master = jQuery.Deferred(), + + // subordinate callback factory + updateFunc = function( i ) { + return function( value ) { + resolveContexts[ i ] = this; + resolveValues[ i ] = arguments.length > 1 ? slice.call( arguments ) : value; + if ( !( --remaining ) ) { + master.resolveWith( resolveContexts, resolveValues ); + } + }; + }; + + // Single- and empty arguments are adopted like Promise.resolve + if ( remaining <= 1 ) { + adoptValue( singleValue, master.done( updateFunc( i ) ).resolve, master.reject, + !remaining ); + + // Use .then() to unwrap secondary thenables (cf. gh-3000) + if ( master.state() === "pending" || + isFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) { + + return master.then(); + } + } + + // Multiple arguments are aggregated like Promise.all array elements + while ( i-- ) { + adoptValue( resolveValues[ i ], updateFunc( i ), master.reject ); + } + + return master.promise(); + } +} ); + + +// These usually indicate a programmer mistake during development, +// warn about them ASAP rather than swallowing them by default. +var rerrorNames = /^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/; + +jQuery.Deferred.exceptionHook = function( error, stack ) { + + // Support: IE 8 - 9 only + // Console exists when dev tools are open, which can happen at any time + if ( window.console && window.console.warn && error && rerrorNames.test( error.name ) ) { + window.console.warn( "jQuery.Deferred exception: " + error.message, error.stack, stack ); + } +}; + + + + +jQuery.readyException = function( error ) { + window.setTimeout( function() { + throw error; + } ); +}; + + + + +// The deferred used on DOM ready +var readyList = jQuery.Deferred(); + +jQuery.fn.ready = function( fn ) { + + readyList + .then( fn ) + + // Wrap jQuery.readyException in a function so that the lookup + // happens at the time of error handling instead of callback + // registration. + .catch( function( error ) { + jQuery.readyException( error ); + } ); + + return this; +}; + +jQuery.extend( { + + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, + + // A counter to track how many items to wait for before + // the ready event fires. See #6781 + readyWait: 1, + + // Handle when the DOM is ready + ready: function( wait ) { + + // Abort if there are pending holds or we're already ready + if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { + return; + } + + // Remember that the DOM is ready + jQuery.isReady = true; + + // If a normal DOM Ready event fired, decrement, and wait if need be + if ( wait !== true && --jQuery.readyWait > 0 ) { + return; + } + + // If there are functions bound, to execute + readyList.resolveWith( document, [ jQuery ] ); + } +} ); + +jQuery.ready.then = readyList.then; + +// The ready event handler and self cleanup method +function completed() { + document.removeEventListener( "DOMContentLoaded", completed ); + window.removeEventListener( "load", completed ); + jQuery.ready(); +} + +// Catch cases where $(document).ready() is called +// after the browser event has already occurred. +// Support: IE <=9 - 10 only +// Older IE sometimes signals "interactive" too soon +if ( document.readyState === "complete" || + ( document.readyState !== "loading" && !document.documentElement.doScroll ) ) { + + // Handle it asynchronously to allow scripts the opportunity to delay ready + window.setTimeout( jQuery.ready ); + +} else { + + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", completed ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", completed ); +} + + + + +// Multifunctional method to get and set values of a collection +// The value/s can optionally be executed if it's a function +var access = function( elems, fn, key, value, chainable, emptyGet, raw ) { + var i = 0, + len = elems.length, + bulk = key == null; + + // Sets many values + if ( toType( key ) === "object" ) { + chainable = true; + for ( i in key ) { + access( elems, fn, i, key[ i ], true, emptyGet, raw ); + } + + // Sets one value + } else if ( value !== undefined ) { + chainable = true; + + if ( !isFunction( value ) ) { + raw = true; + } + + if ( bulk ) { + + // Bulk operations run against the entire set + if ( raw ) { + fn.call( elems, value ); + fn = null; + + // ...except when executing function values + } else { + bulk = fn; + fn = function( elem, key, value ) { + return bulk.call( jQuery( elem ), value ); + }; + } + } + + if ( fn ) { + for ( ; i < len; i++ ) { + fn( + elems[ i ], key, raw ? + value : + value.call( elems[ i ], i, fn( elems[ i ], key ) ) + ); + } + } + } + + if ( chainable ) { + return elems; + } + + // Gets + if ( bulk ) { + return fn.call( elems ); + } + + return len ? fn( elems[ 0 ], key ) : emptyGet; +}; + + +// Matches dashed string for camelizing +var rmsPrefix = /^-ms-/, + rdashAlpha = /-([a-z])/g; + +// Used by camelCase as callback to replace() +function fcamelCase( all, letter ) { + return letter.toUpperCase(); +} + +// Convert dashed to camelCase; used by the css and data modules +// Support: IE <=9 - 11, Edge 12 - 15 +// Microsoft forgot to hump their vendor prefix (#9572) +function camelCase( string ) { + return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); +} +var acceptData = function( owner ) { + + // Accepts only: + // - Node + // - Node.ELEMENT_NODE + // - Node.DOCUMENT_NODE + // - Object + // - Any + return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType ); +}; + + + + +function Data() { + this.expando = jQuery.expando + Data.uid++; +} + +Data.uid = 1; + +Data.prototype = { + + cache: function( owner ) { + + // Check if the owner object already has a cache + var value = owner[ this.expando ]; + + // If not, create one + if ( !value ) { + value = {}; + + // We can accept data for non-element nodes in modern browsers, + // but we should not, see #8335. + // Always return an empty object. + if ( acceptData( owner ) ) { + + // If it is a node unlikely to be stringify-ed or looped over + // use plain assignment + if ( owner.nodeType ) { + owner[ this.expando ] = value; + + // Otherwise secure it in a non-enumerable property + // configurable must be true to allow the property to be + // deleted when data is removed + } else { + Object.defineProperty( owner, this.expando, { + value: value, + configurable: true + } ); + } + } + } + + return value; + }, + set: function( owner, data, value ) { + var prop, + cache = this.cache( owner ); + + // Handle: [ owner, key, value ] args + // Always use camelCase key (gh-2257) + if ( typeof data === "string" ) { + cache[ camelCase( data ) ] = value; + + // Handle: [ owner, { properties } ] args + } else { + + // Copy the properties one-by-one to the cache object + for ( prop in data ) { + cache[ camelCase( prop ) ] = data[ prop ]; + } + } + return cache; + }, + get: function( owner, key ) { + return key === undefined ? + this.cache( owner ) : + + // Always use camelCase key (gh-2257) + owner[ this.expando ] && owner[ this.expando ][ camelCase( key ) ]; + }, + access: function( owner, key, value ) { + + // In cases where either: + // + // 1. No key was specified + // 2. A string key was specified, but no value provided + // + // Take the "read" path and allow the get method to determine + // which value to return, respectively either: + // + // 1. The entire cache object + // 2. The data stored at the key + // + if ( key === undefined || + ( ( key && typeof key === "string" ) && value === undefined ) ) { + + return this.get( owner, key ); + } + + // When the key is not a string, or both a key and value + // are specified, set or extend (existing objects) with either: + // + // 1. An object of properties + // 2. A key and value + // + this.set( owner, key, value ); + + // Since the "set" path can have two possible entry points + // return the expected data based on which path was taken[*] + return value !== undefined ? value : key; + }, + remove: function( owner, key ) { + var i, + cache = owner[ this.expando ]; + + if ( cache === undefined ) { + return; + } + + if ( key !== undefined ) { + + // Support array or space separated string of keys + if ( Array.isArray( key ) ) { + + // If key is an array of keys... + // We always set camelCase keys, so remove that. + key = key.map( camelCase ); + } else { + key = camelCase( key ); + + // If a key with the spaces exists, use it. + // Otherwise, create an array by matching non-whitespace + key = key in cache ? + [ key ] : + ( key.match( rnothtmlwhite ) || [] ); + } + + i = key.length; + + while ( i-- ) { + delete cache[ key[ i ] ]; + } + } + + // Remove the expando if there's no more data + if ( key === undefined || jQuery.isEmptyObject( cache ) ) { + + // Support: Chrome <=35 - 45 + // Webkit & Blink performance suffers when deleting properties + // from DOM nodes, so set to undefined instead + // https://bugs.chromium.org/p/chromium/issues/detail?id=378607 (bug restricted) + if ( owner.nodeType ) { + owner[ this.expando ] = undefined; + } else { + delete owner[ this.expando ]; + } + } + }, + hasData: function( owner ) { + var cache = owner[ this.expando ]; + return cache !== undefined && !jQuery.isEmptyObject( cache ); + } +}; +var dataPriv = new Data(); + +var dataUser = new Data(); + + + +// Implementation Summary +// +// 1. Enforce API surface and semantic compatibility with 1.9.x branch +// 2. Improve the module's maintainability by reducing the storage +// paths to a single mechanism. +// 3. Use the same single mechanism to support "private" and "user" data. +// 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData) +// 5. Avoid exposing implementation details on user objects (eg. expando properties) +// 6. Provide a clear path for implementation upgrade to WeakMap in 2014 + +var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, + rmultiDash = /[A-Z]/g; + +function getData( data ) { + if ( data === "true" ) { + return true; + } + + if ( data === "false" ) { + return false; + } + + if ( data === "null" ) { + return null; + } + + // Only convert to a number if it doesn't change the string + if ( data === +data + "" ) { + return +data; + } + + if ( rbrace.test( data ) ) { + return JSON.parse( data ); + } + + return data; +} + +function dataAttr( elem, key, data ) { + var name; + + // If nothing was found internally, try to fetch any + // data from the HTML5 data-* attribute + if ( data === undefined && elem.nodeType === 1 ) { + name = "data-" + key.replace( rmultiDash, "-$&" ).toLowerCase(); + data = elem.getAttribute( name ); + + if ( typeof data === "string" ) { + try { + data = getData( data ); + } catch ( e ) {} + + // Make sure we set the data so it isn't changed later + dataUser.set( elem, key, data ); + } else { + data = undefined; + } + } + return data; +} + +jQuery.extend( { + hasData: function( elem ) { + return dataUser.hasData( elem ) || dataPriv.hasData( elem ); + }, + + data: function( elem, name, data ) { + return dataUser.access( elem, name, data ); + }, + + removeData: function( elem, name ) { + dataUser.remove( elem, name ); + }, + + // TODO: Now that all calls to _data and _removeData have been replaced + // with direct calls to dataPriv methods, these can be deprecated. + _data: function( elem, name, data ) { + return dataPriv.access( elem, name, data ); + }, + + _removeData: function( elem, name ) { + dataPriv.remove( elem, name ); + } +} ); + +jQuery.fn.extend( { + data: function( key, value ) { + var i, name, data, + elem = this[ 0 ], + attrs = elem && elem.attributes; + + // Gets all values + if ( key === undefined ) { + if ( this.length ) { + data = dataUser.get( elem ); + + if ( elem.nodeType === 1 && !dataPriv.get( elem, "hasDataAttrs" ) ) { + i = attrs.length; + while ( i-- ) { + + // Support: IE 11 only + // The attrs elements can be null (#14894) + if ( attrs[ i ] ) { + name = attrs[ i ].name; + if ( name.indexOf( "data-" ) === 0 ) { + name = camelCase( name.slice( 5 ) ); + dataAttr( elem, name, data[ name ] ); + } + } + } + dataPriv.set( elem, "hasDataAttrs", true ); + } + } + + return data; + } + + // Sets multiple values + if ( typeof key === "object" ) { + return this.each( function() { + dataUser.set( this, key ); + } ); + } + + return access( this, function( value ) { + var data; + + // The calling jQuery object (element matches) is not empty + // (and therefore has an element appears at this[ 0 ]) and the + // `value` parameter was not undefined. An empty jQuery object + // will result in `undefined` for elem = this[ 0 ] which will + // throw an exception if an attempt to read a data cache is made. + if ( elem && value === undefined ) { + + // Attempt to get data from the cache + // The key will always be camelCased in Data + data = dataUser.get( elem, key ); + if ( data !== undefined ) { + return data; + } + + // Attempt to "discover" the data in + // HTML5 custom data-* attrs + data = dataAttr( elem, key ); + if ( data !== undefined ) { + return data; + } + + // We tried really hard, but the data doesn't exist. + return; + } + + // Set the data... + this.each( function() { + + // We always store the camelCased key + dataUser.set( this, key, value ); + } ); + }, null, value, arguments.length > 1, null, true ); + }, + + removeData: function( key ) { + return this.each( function() { + dataUser.remove( this, key ); + } ); + } +} ); + + +jQuery.extend( { + queue: function( elem, type, data ) { + var queue; + + if ( elem ) { + type = ( type || "fx" ) + "queue"; + queue = dataPriv.get( elem, type ); + + // Speed up dequeue by getting out quickly if this is just a lookup + if ( data ) { + if ( !queue || Array.isArray( data ) ) { + queue = dataPriv.access( elem, type, jQuery.makeArray( data ) ); + } else { + queue.push( data ); + } + } + return queue || []; + } + }, + + dequeue: function( elem, type ) { + type = type || "fx"; + + var queue = jQuery.queue( elem, type ), + startLength = queue.length, + fn = queue.shift(), + hooks = jQuery._queueHooks( elem, type ), + next = function() { + jQuery.dequeue( elem, type ); + }; + + // If the fx queue is dequeued, always remove the progress sentinel + if ( fn === "inprogress" ) { + fn = queue.shift(); + startLength--; + } + + if ( fn ) { + + // Add a progress sentinel to prevent the fx queue from being + // automatically dequeued + if ( type === "fx" ) { + queue.unshift( "inprogress" ); + } + + // Clear up the last queue stop function + delete hooks.stop; + fn.call( elem, next, hooks ); + } + + if ( !startLength && hooks ) { + hooks.empty.fire(); + } + }, + + // Not public - generate a queueHooks object, or return the current one + _queueHooks: function( elem, type ) { + var key = type + "queueHooks"; + return dataPriv.get( elem, key ) || dataPriv.access( elem, key, { + empty: jQuery.Callbacks( "once memory" ).add( function() { + dataPriv.remove( elem, [ type + "queue", key ] ); + } ) + } ); + } +} ); + +jQuery.fn.extend( { + queue: function( type, data ) { + var setter = 2; + + if ( typeof type !== "string" ) { + data = type; + type = "fx"; + setter--; + } + + if ( arguments.length < setter ) { + return jQuery.queue( this[ 0 ], type ); + } + + return data === undefined ? + this : + this.each( function() { + var queue = jQuery.queue( this, type, data ); + + // Ensure a hooks for this queue + jQuery._queueHooks( this, type ); + + if ( type === "fx" && queue[ 0 ] !== "inprogress" ) { + jQuery.dequeue( this, type ); + } + } ); + }, + dequeue: function( type ) { + return this.each( function() { + jQuery.dequeue( this, type ); + } ); + }, + clearQueue: function( type ) { + return this.queue( type || "fx", [] ); + }, + + // Get a promise resolved when queues of a certain type + // are emptied (fx is the type by default) + promise: function( type, obj ) { + var tmp, + count = 1, + defer = jQuery.Deferred(), + elements = this, + i = this.length, + resolve = function() { + if ( !( --count ) ) { + defer.resolveWith( elements, [ elements ] ); + } + }; + + if ( typeof type !== "string" ) { + obj = type; + type = undefined; + } + type = type || "fx"; + + while ( i-- ) { + tmp = dataPriv.get( elements[ i ], type + "queueHooks" ); + if ( tmp && tmp.empty ) { + count++; + tmp.empty.add( resolve ); + } + } + resolve(); + return defer.promise( obj ); + } +} ); +var pnum = ( /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/ ).source; + +var rcssNum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" ); + + +var cssExpand = [ "Top", "Right", "Bottom", "Left" ]; + +var documentElement = document.documentElement; + + + + var isAttached = function( elem ) { + return jQuery.contains( elem.ownerDocument, elem ); + }, + composed = { composed: true }; + + // Support: IE 9 - 11+, Edge 12 - 18+, iOS 10.0 - 10.2 only + // Check attachment across shadow DOM boundaries when possible (gh-3504) + // Support: iOS 10.0-10.2 only + // Early iOS 10 versions support `attachShadow` but not `getRootNode`, + // leading to errors. We need to check for `getRootNode`. + if ( documentElement.getRootNode ) { + isAttached = function( elem ) { + return jQuery.contains( elem.ownerDocument, elem ) || + elem.getRootNode( composed ) === elem.ownerDocument; + }; + } +var isHiddenWithinTree = function( elem, el ) { + + // isHiddenWithinTree might be called from jQuery#filter function; + // in that case, element will be second argument + elem = el || elem; + + // Inline style trumps all + return elem.style.display === "none" || + elem.style.display === "" && + + // Otherwise, check computed style + // Support: Firefox <=43 - 45 + // Disconnected elements can have computed display: none, so first confirm that elem is + // in the document. + isAttached( elem ) && + + jQuery.css( elem, "display" ) === "none"; + }; + +var swap = function( elem, options, callback, args ) { + var ret, name, + old = {}; + + // Remember the old values, and insert the new ones + for ( name in options ) { + old[ name ] = elem.style[ name ]; + elem.style[ name ] = options[ name ]; + } + + ret = callback.apply( elem, args || [] ); + + // Revert the old values + for ( name in options ) { + elem.style[ name ] = old[ name ]; + } + + return ret; +}; + + + + +function adjustCSS( elem, prop, valueParts, tween ) { + var adjusted, scale, + maxIterations = 20, + currentValue = tween ? + function() { + return tween.cur(); + } : + function() { + return jQuery.css( elem, prop, "" ); + }, + initial = currentValue(), + unit = valueParts && valueParts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ), + + // Starting value computation is required for potential unit mismatches + initialInUnit = elem.nodeType && + ( jQuery.cssNumber[ prop ] || unit !== "px" && +initial ) && + rcssNum.exec( jQuery.css( elem, prop ) ); + + if ( initialInUnit && initialInUnit[ 3 ] !== unit ) { + + // Support: Firefox <=54 + // Halve the iteration target value to prevent interference from CSS upper bounds (gh-2144) + initial = initial / 2; + + // Trust units reported by jQuery.css + unit = unit || initialInUnit[ 3 ]; + + // Iteratively approximate from a nonzero starting point + initialInUnit = +initial || 1; + + while ( maxIterations-- ) { + + // Evaluate and update our best guess (doubling guesses that zero out). + // Finish if the scale equals or crosses 1 (making the old*new product non-positive). + jQuery.style( elem, prop, initialInUnit + unit ); + if ( ( 1 - scale ) * ( 1 - ( scale = currentValue() / initial || 0.5 ) ) <= 0 ) { + maxIterations = 0; + } + initialInUnit = initialInUnit / scale; + + } + + initialInUnit = initialInUnit * 2; + jQuery.style( elem, prop, initialInUnit + unit ); + + // Make sure we update the tween properties later on + valueParts = valueParts || []; + } + + if ( valueParts ) { + initialInUnit = +initialInUnit || +initial || 0; + + // Apply relative offset (+=/-=) if specified + adjusted = valueParts[ 1 ] ? + initialInUnit + ( valueParts[ 1 ] + 1 ) * valueParts[ 2 ] : + +valueParts[ 2 ]; + if ( tween ) { + tween.unit = unit; + tween.start = initialInUnit; + tween.end = adjusted; + } + } + return adjusted; +} + + +var defaultDisplayMap = {}; + +function getDefaultDisplay( elem ) { + var temp, + doc = elem.ownerDocument, + nodeName = elem.nodeName, + display = defaultDisplayMap[ nodeName ]; + + if ( display ) { + return display; + } + + temp = doc.body.appendChild( doc.createElement( nodeName ) ); + display = jQuery.css( temp, "display" ); + + temp.parentNode.removeChild( temp ); + + if ( display === "none" ) { + display = "block"; + } + defaultDisplayMap[ nodeName ] = display; + + return display; +} + +function showHide( elements, show ) { + var display, elem, + values = [], + index = 0, + length = elements.length; + + // Determine new display value for elements that need to change + for ( ; index < length; index++ ) { + elem = elements[ index ]; + if ( !elem.style ) { + continue; + } + + display = elem.style.display; + if ( show ) { + + // Since we force visibility upon cascade-hidden elements, an immediate (and slow) + // check is required in this first loop unless we have a nonempty display value (either + // inline or about-to-be-restored) + if ( display === "none" ) { + values[ index ] = dataPriv.get( elem, "display" ) || null; + if ( !values[ index ] ) { + elem.style.display = ""; + } + } + if ( elem.style.display === "" && isHiddenWithinTree( elem ) ) { + values[ index ] = getDefaultDisplay( elem ); + } + } else { + if ( display !== "none" ) { + values[ index ] = "none"; + + // Remember what we're overwriting + dataPriv.set( elem, "display", display ); + } + } + } + + // Set the display of the elements in a second loop to avoid constant reflow + for ( index = 0; index < length; index++ ) { + if ( values[ index ] != null ) { + elements[ index ].style.display = values[ index ]; + } + } + + return elements; +} + +jQuery.fn.extend( { + show: function() { + return showHide( this, true ); + }, + hide: function() { + return showHide( this ); + }, + toggle: function( state ) { + if ( typeof state === "boolean" ) { + return state ? this.show() : this.hide(); + } + + return this.each( function() { + if ( isHiddenWithinTree( this ) ) { + jQuery( this ).show(); + } else { + jQuery( this ).hide(); + } + } ); + } +} ); +var rcheckableType = ( /^(?:checkbox|radio)$/i ); + +var rtagName = ( /<([a-z][^\/\0>\x20\t\r\n\f]*)/i ); + +var rscriptType = ( /^$|^module$|\/(?:java|ecma)script/i ); + + + +// We have to close these tags to support XHTML (#13200) +var wrapMap = { + + // Support: IE <=9 only + option: [ 1, "" ], + + // XHTML parsers do not magically insert elements in the + // same way that tag soup parsers do. So we cannot shorten + // this by omitting or other required elements. + thead: [ 1, "", "
" ], + col: [ 2, "", "
" ], + tr: [ 2, "", "
" ], + td: [ 3, "", "
" ], + + _default: [ 0, "", "" ] +}; + +// Support: IE <=9 only +wrapMap.optgroup = wrapMap.option; + +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + + +function getAll( context, tag ) { + + // Support: IE <=9 - 11 only + // Use typeof to avoid zero-argument method invocation on host objects (#15151) + var ret; + + if ( typeof context.getElementsByTagName !== "undefined" ) { + ret = context.getElementsByTagName( tag || "*" ); + + } else if ( typeof context.querySelectorAll !== "undefined" ) { + ret = context.querySelectorAll( tag || "*" ); + + } else { + ret = []; + } + + if ( tag === undefined || tag && nodeName( context, tag ) ) { + return jQuery.merge( [ context ], ret ); + } + + return ret; +} + + +// Mark scripts as having already been evaluated +function setGlobalEval( elems, refElements ) { + var i = 0, + l = elems.length; + + for ( ; i < l; i++ ) { + dataPriv.set( + elems[ i ], + "globalEval", + !refElements || dataPriv.get( refElements[ i ], "globalEval" ) + ); + } +} + + +var rhtml = /<|&#?\w+;/; + +function buildFragment( elems, context, scripts, selection, ignored ) { + var elem, tmp, tag, wrap, attached, j, + fragment = context.createDocumentFragment(), + nodes = [], + i = 0, + l = elems.length; + + for ( ; i < l; i++ ) { + elem = elems[ i ]; + + if ( elem || elem === 0 ) { + + // Add nodes directly + if ( toType( elem ) === "object" ) { + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); + + // Convert non-html into a text node + } else if ( !rhtml.test( elem ) ) { + nodes.push( context.createTextNode( elem ) ); + + // Convert html into DOM nodes + } else { + tmp = tmp || fragment.appendChild( context.createElement( "div" ) ); + + // Deserialize a standard representation + tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase(); + wrap = wrapMap[ tag ] || wrapMap._default; + tmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ]; + + // Descend through wrappers to the right content + j = wrap[ 0 ]; + while ( j-- ) { + tmp = tmp.lastChild; + } + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( nodes, tmp.childNodes ); + + // Remember the top-level container + tmp = fragment.firstChild; + + // Ensure the created nodes are orphaned (#12392) + tmp.textContent = ""; + } + } + } + + // Remove wrapper from fragment + fragment.textContent = ""; + + i = 0; + while ( ( elem = nodes[ i++ ] ) ) { + + // Skip elements already in the context collection (trac-4087) + if ( selection && jQuery.inArray( elem, selection ) > -1 ) { + if ( ignored ) { + ignored.push( elem ); + } + continue; + } + + attached = isAttached( elem ); + + // Append to fragment + tmp = getAll( fragment.appendChild( elem ), "script" ); + + // Preserve script evaluation history + if ( attached ) { + setGlobalEval( tmp ); + } + + // Capture executables + if ( scripts ) { + j = 0; + while ( ( elem = tmp[ j++ ] ) ) { + if ( rscriptType.test( elem.type || "" ) ) { + scripts.push( elem ); + } + } + } + } + + return fragment; +} + + +( function() { + var fragment = document.createDocumentFragment(), + div = fragment.appendChild( document.createElement( "div" ) ), + input = document.createElement( "input" ); + + // Support: Android 4.0 - 4.3 only + // Check state lost if the name is set (#11217) + // Support: Windows Web Apps (WWA) + // `name` and `type` must use .setAttribute for WWA (#14901) + input.setAttribute( "type", "radio" ); + input.setAttribute( "checked", "checked" ); + input.setAttribute( "name", "t" ); + + div.appendChild( input ); + + // Support: Android <=4.1 only + // Older WebKit doesn't clone checked state correctly in fragments + support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked; + + // Support: IE <=11 only + // Make sure textarea (and checkbox) defaultValue is properly cloned + div.innerHTML = ""; + support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; +} )(); + + +var + rkeyEvent = /^key/, + rmouseEvent = /^(?:mouse|pointer|contextmenu|drag|drop)|click/, + rtypenamespace = /^([^.]*)(?:\.(.+)|)/; + +function returnTrue() { + return true; +} + +function returnFalse() { + return false; +} + +// Support: IE <=9 - 11+ +// focus() and blur() are asynchronous, except when they are no-op. +// So expect focus to be synchronous when the element is already active, +// and blur to be synchronous when the element is not already active. +// (focus and blur are always synchronous in other supported browsers, +// this just defines when we can count on it). +function expectSync( elem, type ) { + return ( elem === safeActiveElement() ) === ( type === "focus" ); +} + +// Support: IE <=9 only +// Accessing document.activeElement can throw unexpectedly +// https://bugs.jquery.com/ticket/13393 +function safeActiveElement() { + try { + return document.activeElement; + } catch ( err ) { } +} + +function on( elem, types, selector, data, fn, one ) { + var origFn, type; + + // Types can be a map of types/handlers + if ( typeof types === "object" ) { + + // ( types-Object, selector, data ) + if ( typeof selector !== "string" ) { + + // ( types-Object, data ) + data = data || selector; + selector = undefined; + } + for ( type in types ) { + on( elem, type, selector, data, types[ type ], one ); + } + return elem; + } + + if ( data == null && fn == null ) { + + // ( types, fn ) + fn = selector; + data = selector = undefined; + } else if ( fn == null ) { + if ( typeof selector === "string" ) { + + // ( types, selector, fn ) + fn = data; + data = undefined; + } else { + + // ( types, data, fn ) + fn = data; + data = selector; + selector = undefined; + } + } + if ( fn === false ) { + fn = returnFalse; + } else if ( !fn ) { + return elem; + } + + if ( one === 1 ) { + origFn = fn; + fn = function( event ) { + + // Can use an empty set, since event contains the info + jQuery().off( event ); + return origFn.apply( this, arguments ); + }; + + // Use same guid so caller can remove using origFn + fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); + } + return elem.each( function() { + jQuery.event.add( this, types, fn, data, selector ); + } ); +} + +/* + * Helper functions for managing events -- not part of the public interface. + * Props to Dean Edwards' addEvent library for many of the ideas. + */ +jQuery.event = { + + global: {}, + + add: function( elem, types, handler, data, selector ) { + + var handleObjIn, eventHandle, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = dataPriv.get( elem ); + + // Don't attach events to noData or text/comment nodes (but allow plain objects) + if ( !elemData ) { + return; + } + + // Caller can pass in an object of custom data in lieu of the handler + if ( handler.handler ) { + handleObjIn = handler; + handler = handleObjIn.handler; + selector = handleObjIn.selector; + } + + // Ensure that invalid selectors throw exceptions at attach time + // Evaluate against documentElement in case elem is a non-element node (e.g., document) + if ( selector ) { + jQuery.find.matchesSelector( documentElement, selector ); + } + + // Make sure that the handler has a unique ID, used to find/remove it later + if ( !handler.guid ) { + handler.guid = jQuery.guid++; + } + + // Init the element's event structure and main handler, if this is the first + if ( !( events = elemData.events ) ) { + events = elemData.events = {}; + } + if ( !( eventHandle = elemData.handle ) ) { + eventHandle = elemData.handle = function( e ) { + + // Discard the second event of a jQuery.event.trigger() and + // when an event is called after a page has unloaded + return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ? + jQuery.event.dispatch.apply( elem, arguments ) : undefined; + }; + } + + // Handle multiple events separated by a space + types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[ t ] ) || []; + type = origType = tmp[ 1 ]; + namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); + + // There *must* be a type, no attaching namespace-only handlers + if ( !type ) { + continue; + } + + // If event changes its type, use the special event handlers for the changed type + special = jQuery.event.special[ type ] || {}; + + // If selector defined, determine special event api type, otherwise given type + type = ( selector ? special.delegateType : special.bindType ) || type; + + // Update special based on newly reset type + special = jQuery.event.special[ type ] || {}; + + // handleObj is passed to all event handlers + handleObj = jQuery.extend( { + type: type, + origType: origType, + data: data, + handler: handler, + guid: handler.guid, + selector: selector, + needsContext: selector && jQuery.expr.match.needsContext.test( selector ), + namespace: namespaces.join( "." ) + }, handleObjIn ); + + // Init the event handler queue if we're the first + if ( !( handlers = events[ type ] ) ) { + handlers = events[ type ] = []; + handlers.delegateCount = 0; + + // Only use addEventListener if the special events handler returns false + if ( !special.setup || + special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle ); + } + } + } + + if ( special.add ) { + special.add.call( elem, handleObj ); + + if ( !handleObj.handler.guid ) { + handleObj.handler.guid = handler.guid; + } + } + + // Add to the element's handler list, delegates in front + if ( selector ) { + handlers.splice( handlers.delegateCount++, 0, handleObj ); + } else { + handlers.push( handleObj ); + } + + // Keep track of which events have ever been used, for event optimization + jQuery.event.global[ type ] = true; + } + + }, + + // Detach an event or set of events from an element + remove: function( elem, types, handler, selector, mappedTypes ) { + + var j, origCount, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = dataPriv.hasData( elem ) && dataPriv.get( elem ); + + if ( !elemData || !( events = elemData.events ) ) { + return; + } + + // Once for each type.namespace in types; type may be omitted + types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[ t ] ) || []; + type = origType = tmp[ 1 ]; + namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); + + // Unbind all events (on this namespace, if provided) for the element + if ( !type ) { + for ( type in events ) { + jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); + } + continue; + } + + special = jQuery.event.special[ type ] || {}; + type = ( selector ? special.delegateType : special.bindType ) || type; + handlers = events[ type ] || []; + tmp = tmp[ 2 ] && + new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ); + + // Remove matching events + origCount = j = handlers.length; + while ( j-- ) { + handleObj = handlers[ j ]; + + if ( ( mappedTypes || origType === handleObj.origType ) && + ( !handler || handler.guid === handleObj.guid ) && + ( !tmp || tmp.test( handleObj.namespace ) ) && + ( !selector || selector === handleObj.selector || + selector === "**" && handleObj.selector ) ) { + handlers.splice( j, 1 ); + + if ( handleObj.selector ) { + handlers.delegateCount--; + } + if ( special.remove ) { + special.remove.call( elem, handleObj ); + } + } + } + + // Remove generic event handler if we removed something and no more handlers exist + // (avoids potential for endless recursion during removal of special event handlers) + if ( origCount && !handlers.length ) { + if ( !special.teardown || + special.teardown.call( elem, namespaces, elemData.handle ) === false ) { + + jQuery.removeEvent( elem, type, elemData.handle ); + } + + delete events[ type ]; + } + } + + // Remove data and the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + dataPriv.remove( elem, "handle events" ); + } + }, + + dispatch: function( nativeEvent ) { + + // Make a writable jQuery.Event from the native event object + var event = jQuery.event.fix( nativeEvent ); + + var i, j, ret, matched, handleObj, handlerQueue, + args = new Array( arguments.length ), + handlers = ( dataPriv.get( this, "events" ) || {} )[ event.type ] || [], + special = jQuery.event.special[ event.type ] || {}; + + // Use the fix-ed jQuery.Event rather than the (read-only) native event + args[ 0 ] = event; + + for ( i = 1; i < arguments.length; i++ ) { + args[ i ] = arguments[ i ]; + } + + event.delegateTarget = this; + + // Call the preDispatch hook for the mapped type, and let it bail if desired + if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { + return; + } + + // Determine handlers + handlerQueue = jQuery.event.handlers.call( this, event, handlers ); + + // Run delegates first; they may want to stop propagation beneath us + i = 0; + while ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) { + event.currentTarget = matched.elem; + + j = 0; + while ( ( handleObj = matched.handlers[ j++ ] ) && + !event.isImmediatePropagationStopped() ) { + + // If the event is namespaced, then each handler is only invoked if it is + // specially universal or its namespaces are a superset of the event's. + if ( !event.rnamespace || handleObj.namespace === false || + event.rnamespace.test( handleObj.namespace ) ) { + + event.handleObj = handleObj; + event.data = handleObj.data; + + ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle || + handleObj.handler ).apply( matched.elem, args ); + + if ( ret !== undefined ) { + if ( ( event.result = ret ) === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + } + } + } + + // Call the postDispatch hook for the mapped type + if ( special.postDispatch ) { + special.postDispatch.call( this, event ); + } + + return event.result; + }, + + handlers: function( event, handlers ) { + var i, handleObj, sel, matchedHandlers, matchedSelectors, + handlerQueue = [], + delegateCount = handlers.delegateCount, + cur = event.target; + + // Find delegate handlers + if ( delegateCount && + + // Support: IE <=9 + // Black-hole SVG instance trees (trac-13180) + cur.nodeType && + + // Support: Firefox <=42 + // Suppress spec-violating clicks indicating a non-primary pointer button (trac-3861) + // https://www.w3.org/TR/DOM-Level-3-Events/#event-type-click + // Support: IE 11 only + // ...but not arrow key "clicks" of radio inputs, which can have `button` -1 (gh-2343) + !( event.type === "click" && event.button >= 1 ) ) { + + for ( ; cur !== this; cur = cur.parentNode || this ) { + + // Don't check non-elements (#13208) + // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) + if ( cur.nodeType === 1 && !( event.type === "click" && cur.disabled === true ) ) { + matchedHandlers = []; + matchedSelectors = {}; + for ( i = 0; i < delegateCount; i++ ) { + handleObj = handlers[ i ]; + + // Don't conflict with Object.prototype properties (#13203) + sel = handleObj.selector + " "; + + if ( matchedSelectors[ sel ] === undefined ) { + matchedSelectors[ sel ] = handleObj.needsContext ? + jQuery( sel, this ).index( cur ) > -1 : + jQuery.find( sel, this, null, [ cur ] ).length; + } + if ( matchedSelectors[ sel ] ) { + matchedHandlers.push( handleObj ); + } + } + if ( matchedHandlers.length ) { + handlerQueue.push( { elem: cur, handlers: matchedHandlers } ); + } + } + } + } + + // Add the remaining (directly-bound) handlers + cur = this; + if ( delegateCount < handlers.length ) { + handlerQueue.push( { elem: cur, handlers: handlers.slice( delegateCount ) } ); + } + + return handlerQueue; + }, + + addProp: function( name, hook ) { + Object.defineProperty( jQuery.Event.prototype, name, { + enumerable: true, + configurable: true, + + get: isFunction( hook ) ? + function() { + if ( this.originalEvent ) { + return hook( this.originalEvent ); + } + } : + function() { + if ( this.originalEvent ) { + return this.originalEvent[ name ]; + } + }, + + set: function( value ) { + Object.defineProperty( this, name, { + enumerable: true, + configurable: true, + writable: true, + value: value + } ); + } + } ); + }, + + fix: function( originalEvent ) { + return originalEvent[ jQuery.expando ] ? + originalEvent : + new jQuery.Event( originalEvent ); + }, + + special: { + load: { + + // Prevent triggered image.load events from bubbling to window.load + noBubble: true + }, + click: { + + // Utilize native event to ensure correct state for checkable inputs + setup: function( data ) { + + // For mutual compressibility with _default, replace `this` access with a local var. + // `|| data` is dead code meant only to preserve the variable through minification. + var el = this || data; + + // Claim the first handler + if ( rcheckableType.test( el.type ) && + el.click && nodeName( el, "input" ) ) { + + // dataPriv.set( el, "click", ... ) + leverageNative( el, "click", returnTrue ); + } + + // Return false to allow normal processing in the caller + return false; + }, + trigger: function( data ) { + + // For mutual compressibility with _default, replace `this` access with a local var. + // `|| data` is dead code meant only to preserve the variable through minification. + var el = this || data; + + // Force setup before triggering a click + if ( rcheckableType.test( el.type ) && + el.click && nodeName( el, "input" ) ) { + + leverageNative( el, "click" ); + } + + // Return non-false to allow normal event-path propagation + return true; + }, + + // For cross-browser consistency, suppress native .click() on links + // Also prevent it if we're currently inside a leveraged native-event stack + _default: function( event ) { + var target = event.target; + return rcheckableType.test( target.type ) && + target.click && nodeName( target, "input" ) && + dataPriv.get( target, "click" ) || + nodeName( target, "a" ); + } + }, + + beforeunload: { + postDispatch: function( event ) { + + // Support: Firefox 20+ + // Firefox doesn't alert if the returnValue field is not set. + if ( event.result !== undefined && event.originalEvent ) { + event.originalEvent.returnValue = event.result; + } + } + } + } +}; + +// Ensure the presence of an event listener that handles manually-triggered +// synthetic events by interrupting progress until reinvoked in response to +// *native* events that it fires directly, ensuring that state changes have +// already occurred before other listeners are invoked. +function leverageNative( el, type, expectSync ) { + + // Missing expectSync indicates a trigger call, which must force setup through jQuery.event.add + if ( !expectSync ) { + if ( dataPriv.get( el, type ) === undefined ) { + jQuery.event.add( el, type, returnTrue ); + } + return; + } + + // Register the controller as a special universal handler for all event namespaces + dataPriv.set( el, type, false ); + jQuery.event.add( el, type, { + namespace: false, + handler: function( event ) { + var notAsync, result, + saved = dataPriv.get( this, type ); + + if ( ( event.isTrigger & 1 ) && this[ type ] ) { + + // Interrupt processing of the outer synthetic .trigger()ed event + // Saved data should be false in such cases, but might be a leftover capture object + // from an async native handler (gh-4350) + if ( !saved.length ) { + + // Store arguments for use when handling the inner native event + // There will always be at least one argument (an event object), so this array + // will not be confused with a leftover capture object. + saved = slice.call( arguments ); + dataPriv.set( this, type, saved ); + + // Trigger the native event and capture its result + // Support: IE <=9 - 11+ + // focus() and blur() are asynchronous + notAsync = expectSync( this, type ); + this[ type ](); + result = dataPriv.get( this, type ); + if ( saved !== result || notAsync ) { + dataPriv.set( this, type, false ); + } else { + result = {}; + } + if ( saved !== result ) { + + // Cancel the outer synthetic event + event.stopImmediatePropagation(); + event.preventDefault(); + return result.value; + } + + // If this is an inner synthetic event for an event with a bubbling surrogate + // (focus or blur), assume that the surrogate already propagated from triggering the + // native event and prevent that from happening again here. + // This technically gets the ordering wrong w.r.t. to `.trigger()` (in which the + // bubbling surrogate propagates *after* the non-bubbling base), but that seems + // less bad than duplication. + } else if ( ( jQuery.event.special[ type ] || {} ).delegateType ) { + event.stopPropagation(); + } + + // If this is a native event triggered above, everything is now in order + // Fire an inner synthetic event with the original arguments + } else if ( saved.length ) { + + // ...and capture the result + dataPriv.set( this, type, { + value: jQuery.event.trigger( + + // Support: IE <=9 - 11+ + // Extend with the prototype to reset the above stopImmediatePropagation() + jQuery.extend( saved[ 0 ], jQuery.Event.prototype ), + saved.slice( 1 ), + this + ) + } ); + + // Abort handling of the native event + event.stopImmediatePropagation(); + } + } + } ); +} + +jQuery.removeEvent = function( elem, type, handle ) { + + // This "if" is needed for plain objects + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle ); + } +}; + +jQuery.Event = function( src, props ) { + + // Allow instantiation without the 'new' keyword + if ( !( this instanceof jQuery.Event ) ) { + return new jQuery.Event( src, props ); + } + + // Event object + if ( src && src.type ) { + this.originalEvent = src; + this.type = src.type; + + // Events bubbling up the document may have been marked as prevented + // by a handler lower down the tree; reflect the correct value. + this.isDefaultPrevented = src.defaultPrevented || + src.defaultPrevented === undefined && + + // Support: Android <=2.3 only + src.returnValue === false ? + returnTrue : + returnFalse; + + // Create target properties + // Support: Safari <=6 - 7 only + // Target should not be a text node (#504, #13143) + this.target = ( src.target && src.target.nodeType === 3 ) ? + src.target.parentNode : + src.target; + + this.currentTarget = src.currentTarget; + this.relatedTarget = src.relatedTarget; + + // Event type + } else { + this.type = src; + } + + // Put explicitly provided properties onto the event object + if ( props ) { + jQuery.extend( this, props ); + } + + // Create a timestamp if incoming event doesn't have one + this.timeStamp = src && src.timeStamp || Date.now(); + + // Mark it as fixed + this[ jQuery.expando ] = true; +}; + +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// https://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +jQuery.Event.prototype = { + constructor: jQuery.Event, + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse, + isSimulated: false, + + preventDefault: function() { + var e = this.originalEvent; + + this.isDefaultPrevented = returnTrue; + + if ( e && !this.isSimulated ) { + e.preventDefault(); + } + }, + stopPropagation: function() { + var e = this.originalEvent; + + this.isPropagationStopped = returnTrue; + + if ( e && !this.isSimulated ) { + e.stopPropagation(); + } + }, + stopImmediatePropagation: function() { + var e = this.originalEvent; + + this.isImmediatePropagationStopped = returnTrue; + + if ( e && !this.isSimulated ) { + e.stopImmediatePropagation(); + } + + this.stopPropagation(); + } +}; + +// Includes all common event props including KeyEvent and MouseEvent specific props +jQuery.each( { + altKey: true, + bubbles: true, + cancelable: true, + changedTouches: true, + ctrlKey: true, + detail: true, + eventPhase: true, + metaKey: true, + pageX: true, + pageY: true, + shiftKey: true, + view: true, + "char": true, + code: true, + charCode: true, + key: true, + keyCode: true, + button: true, + buttons: true, + clientX: true, + clientY: true, + offsetX: true, + offsetY: true, + pointerId: true, + pointerType: true, + screenX: true, + screenY: true, + targetTouches: true, + toElement: true, + touches: true, + + which: function( event ) { + var button = event.button; + + // Add which for key events + if ( event.which == null && rkeyEvent.test( event.type ) ) { + return event.charCode != null ? event.charCode : event.keyCode; + } + + // Add which for click: 1 === left; 2 === middle; 3 === right + if ( !event.which && button !== undefined && rmouseEvent.test( event.type ) ) { + if ( button & 1 ) { + return 1; + } + + if ( button & 2 ) { + return 3; + } + + if ( button & 4 ) { + return 2; + } + + return 0; + } + + return event.which; + } +}, jQuery.event.addProp ); + +jQuery.each( { focus: "focusin", blur: "focusout" }, function( type, delegateType ) { + jQuery.event.special[ type ] = { + + // Utilize native event if possible so blur/focus sequence is correct + setup: function() { + + // Claim the first handler + // dataPriv.set( this, "focus", ... ) + // dataPriv.set( this, "blur", ... ) + leverageNative( this, type, expectSync ); + + // Return false to allow normal processing in the caller + return false; + }, + trigger: function() { + + // Force setup before trigger + leverageNative( this, type ); + + // Return non-false to allow normal event-path propagation + return true; + }, + + delegateType: delegateType + }; +} ); + +// Create mouseenter/leave events using mouseover/out and event-time checks +// so that event delegation works in jQuery. +// Do the same for pointerenter/pointerleave and pointerover/pointerout +// +// Support: Safari 7 only +// Safari sends mouseenter too often; see: +// https://bugs.chromium.org/p/chromium/issues/detail?id=470258 +// for the description of the bug (it existed in older Chrome versions as well). +jQuery.each( { + mouseenter: "mouseover", + mouseleave: "mouseout", + pointerenter: "pointerover", + pointerleave: "pointerout" +}, function( orig, fix ) { + jQuery.event.special[ orig ] = { + delegateType: fix, + bindType: fix, + + handle: function( event ) { + var ret, + target = this, + related = event.relatedTarget, + handleObj = event.handleObj; + + // For mouseenter/leave call the handler if related is outside the target. + // NB: No relatedTarget if the mouse left/entered the browser window + if ( !related || ( related !== target && !jQuery.contains( target, related ) ) ) { + event.type = handleObj.origType; + ret = handleObj.handler.apply( this, arguments ); + event.type = fix; + } + return ret; + } + }; +} ); + +jQuery.fn.extend( { + + on: function( types, selector, data, fn ) { + return on( this, types, selector, data, fn ); + }, + one: function( types, selector, data, fn ) { + return on( this, types, selector, data, fn, 1 ); + }, + off: function( types, selector, fn ) { + var handleObj, type; + if ( types && types.preventDefault && types.handleObj ) { + + // ( event ) dispatched jQuery.Event + handleObj = types.handleObj; + jQuery( types.delegateTarget ).off( + handleObj.namespace ? + handleObj.origType + "." + handleObj.namespace : + handleObj.origType, + handleObj.selector, + handleObj.handler + ); + return this; + } + if ( typeof types === "object" ) { + + // ( types-object [, selector] ) + for ( type in types ) { + this.off( type, selector, types[ type ] ); + } + return this; + } + if ( selector === false || typeof selector === "function" ) { + + // ( types [, fn] ) + fn = selector; + selector = undefined; + } + if ( fn === false ) { + fn = returnFalse; + } + return this.each( function() { + jQuery.event.remove( this, types, fn, selector ); + } ); + } +} ); + + +var + + /* eslint-disable max-len */ + + // See https://github.com/eslint/eslint/issues/3229 + rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([a-z][^\/\0>\x20\t\r\n\f]*)[^>]*)\/>/gi, + + /* eslint-enable */ + + // Support: IE <=10 - 11, Edge 12 - 13 only + // In IE/Edge using regex groups here causes severe slowdowns. + // See https://connect.microsoft.com/IE/feedback/details/1736512/ + rnoInnerhtml = /\s*$/g; + +// Prefer a tbody over its parent table for containing new rows +function manipulationTarget( elem, content ) { + if ( nodeName( elem, "table" ) && + nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ) { + + return jQuery( elem ).children( "tbody" )[ 0 ] || elem; + } + + return elem; +} + +// Replace/restore the type attribute of script elements for safe DOM manipulation +function disableScript( elem ) { + elem.type = ( elem.getAttribute( "type" ) !== null ) + "/" + elem.type; + return elem; +} +function restoreScript( elem ) { + if ( ( elem.type || "" ).slice( 0, 5 ) === "true/" ) { + elem.type = elem.type.slice( 5 ); + } else { + elem.removeAttribute( "type" ); + } + + return elem; +} + +function cloneCopyEvent( src, dest ) { + var i, l, type, pdataOld, pdataCur, udataOld, udataCur, events; + + if ( dest.nodeType !== 1 ) { + return; + } + + // 1. Copy private data: events, handlers, etc. + if ( dataPriv.hasData( src ) ) { + pdataOld = dataPriv.access( src ); + pdataCur = dataPriv.set( dest, pdataOld ); + events = pdataOld.events; + + if ( events ) { + delete pdataCur.handle; + pdataCur.events = {}; + + for ( type in events ) { + for ( i = 0, l = events[ type ].length; i < l; i++ ) { + jQuery.event.add( dest, type, events[ type ][ i ] ); + } + } + } + } + + // 2. Copy user data + if ( dataUser.hasData( src ) ) { + udataOld = dataUser.access( src ); + udataCur = jQuery.extend( {}, udataOld ); + + dataUser.set( dest, udataCur ); + } +} + +// Fix IE bugs, see support tests +function fixInput( src, dest ) { + var nodeName = dest.nodeName.toLowerCase(); + + // Fails to persist the checked state of a cloned checkbox or radio button. + if ( nodeName === "input" && rcheckableType.test( src.type ) ) { + dest.checked = src.checked; + + // Fails to return the selected option to the default selected state when cloning options + } else if ( nodeName === "input" || nodeName === "textarea" ) { + dest.defaultValue = src.defaultValue; + } +} + +function domManip( collection, args, callback, ignored ) { + + // Flatten any nested arrays + args = concat.apply( [], args ); + + var fragment, first, scripts, hasScripts, node, doc, + i = 0, + l = collection.length, + iNoClone = l - 1, + value = args[ 0 ], + valueIsFunction = isFunction( value ); + + // We can't cloneNode fragments that contain checked, in WebKit + if ( valueIsFunction || + ( l > 1 && typeof value === "string" && + !support.checkClone && rchecked.test( value ) ) ) { + return collection.each( function( index ) { + var self = collection.eq( index ); + if ( valueIsFunction ) { + args[ 0 ] = value.call( this, index, self.html() ); + } + domManip( self, args, callback, ignored ); + } ); + } + + if ( l ) { + fragment = buildFragment( args, collection[ 0 ].ownerDocument, false, collection, ignored ); + first = fragment.firstChild; + + if ( fragment.childNodes.length === 1 ) { + fragment = first; + } + + // Require either new content or an interest in ignored elements to invoke the callback + if ( first || ignored ) { + scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); + hasScripts = scripts.length; + + // Use the original fragment for the last item + // instead of the first because it can end up + // being emptied incorrectly in certain situations (#8070). + for ( ; i < l; i++ ) { + node = fragment; + + if ( i !== iNoClone ) { + node = jQuery.clone( node, true, true ); + + // Keep references to cloned scripts for later restoration + if ( hasScripts ) { + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( scripts, getAll( node, "script" ) ); + } + } + + callback.call( collection[ i ], node, i ); + } + + if ( hasScripts ) { + doc = scripts[ scripts.length - 1 ].ownerDocument; + + // Reenable scripts + jQuery.map( scripts, restoreScript ); + + // Evaluate executable scripts on first document insertion + for ( i = 0; i < hasScripts; i++ ) { + node = scripts[ i ]; + if ( rscriptType.test( node.type || "" ) && + !dataPriv.access( node, "globalEval" ) && + jQuery.contains( doc, node ) ) { + + if ( node.src && ( node.type || "" ).toLowerCase() !== "module" ) { + + // Optional AJAX dependency, but won't run scripts if not present + if ( jQuery._evalUrl && !node.noModule ) { + jQuery._evalUrl( node.src, { + nonce: node.nonce || node.getAttribute( "nonce" ) + } ); + } + } else { + DOMEval( node.textContent.replace( rcleanScript, "" ), node, doc ); + } + } + } + } + } + } + + return collection; +} + +function remove( elem, selector, keepData ) { + var node, + nodes = selector ? jQuery.filter( selector, elem ) : elem, + i = 0; + + for ( ; ( node = nodes[ i ] ) != null; i++ ) { + if ( !keepData && node.nodeType === 1 ) { + jQuery.cleanData( getAll( node ) ); + } + + if ( node.parentNode ) { + if ( keepData && isAttached( node ) ) { + setGlobalEval( getAll( node, "script" ) ); + } + node.parentNode.removeChild( node ); + } + } + + return elem; +} + +jQuery.extend( { + htmlPrefilter: function( html ) { + return html.replace( rxhtmlTag, "<$1>" ); + }, + + clone: function( elem, dataAndEvents, deepDataAndEvents ) { + var i, l, srcElements, destElements, + clone = elem.cloneNode( true ), + inPage = isAttached( elem ); + + // Fix IE cloning issues + if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) && + !jQuery.isXMLDoc( elem ) ) { + + // We eschew Sizzle here for performance reasons: https://jsperf.com/getall-vs-sizzle/2 + destElements = getAll( clone ); + srcElements = getAll( elem ); + + for ( i = 0, l = srcElements.length; i < l; i++ ) { + fixInput( srcElements[ i ], destElements[ i ] ); + } + } + + // Copy the events from the original to the clone + if ( dataAndEvents ) { + if ( deepDataAndEvents ) { + srcElements = srcElements || getAll( elem ); + destElements = destElements || getAll( clone ); + + for ( i = 0, l = srcElements.length; i < l; i++ ) { + cloneCopyEvent( srcElements[ i ], destElements[ i ] ); + } + } else { + cloneCopyEvent( elem, clone ); + } + } + + // Preserve script evaluation history + destElements = getAll( clone, "script" ); + if ( destElements.length > 0 ) { + setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); + } + + // Return the cloned set + return clone; + }, + + cleanData: function( elems ) { + var data, elem, type, + special = jQuery.event.special, + i = 0; + + for ( ; ( elem = elems[ i ] ) !== undefined; i++ ) { + if ( acceptData( elem ) ) { + if ( ( data = elem[ dataPriv.expando ] ) ) { + if ( data.events ) { + for ( type in data.events ) { + if ( special[ type ] ) { + jQuery.event.remove( elem, type ); + + // This is a shortcut to avoid jQuery.event.remove's overhead + } else { + jQuery.removeEvent( elem, type, data.handle ); + } + } + } + + // Support: Chrome <=35 - 45+ + // Assign undefined instead of using delete, see Data#remove + elem[ dataPriv.expando ] = undefined; + } + if ( elem[ dataUser.expando ] ) { + + // Support: Chrome <=35 - 45+ + // Assign undefined instead of using delete, see Data#remove + elem[ dataUser.expando ] = undefined; + } + } + } + } +} ); + +jQuery.fn.extend( { + detach: function( selector ) { + return remove( this, selector, true ); + }, + + remove: function( selector ) { + return remove( this, selector ); + }, + + text: function( value ) { + return access( this, function( value ) { + return value === undefined ? + jQuery.text( this ) : + this.empty().each( function() { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + this.textContent = value; + } + } ); + }, null, value, arguments.length ); + }, + + append: function() { + return domManip( this, arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.appendChild( elem ); + } + } ); + }, + + prepend: function() { + return domManip( this, arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.insertBefore( elem, target.firstChild ); + } + } ); + }, + + before: function() { + return domManip( this, arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this ); + } + } ); + }, + + after: function() { + return domManip( this, arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this.nextSibling ); + } + } ); + }, + + empty: function() { + var elem, + i = 0; + + for ( ; ( elem = this[ i ] ) != null; i++ ) { + if ( elem.nodeType === 1 ) { + + // Prevent memory leaks + jQuery.cleanData( getAll( elem, false ) ); + + // Remove any remaining nodes + elem.textContent = ""; + } + } + + return this; + }, + + clone: function( dataAndEvents, deepDataAndEvents ) { + dataAndEvents = dataAndEvents == null ? false : dataAndEvents; + deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; + + return this.map( function() { + return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); + } ); + }, + + html: function( value ) { + return access( this, function( value ) { + var elem = this[ 0 ] || {}, + i = 0, + l = this.length; + + if ( value === undefined && elem.nodeType === 1 ) { + return elem.innerHTML; + } + + // See if we can take a shortcut and just use innerHTML + if ( typeof value === "string" && !rnoInnerhtml.test( value ) && + !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) { + + value = jQuery.htmlPrefilter( value ); + + try { + for ( ; i < l; i++ ) { + elem = this[ i ] || {}; + + // Remove element nodes and prevent memory leaks + if ( elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem, false ) ); + elem.innerHTML = value; + } + } + + elem = 0; + + // If using innerHTML throws an exception, use the fallback method + } catch ( e ) {} + } + + if ( elem ) { + this.empty().append( value ); + } + }, null, value, arguments.length ); + }, + + replaceWith: function() { + var ignored = []; + + // Make the changes, replacing each non-ignored context element with the new content + return domManip( this, arguments, function( elem ) { + var parent = this.parentNode; + + if ( jQuery.inArray( this, ignored ) < 0 ) { + jQuery.cleanData( getAll( this ) ); + if ( parent ) { + parent.replaceChild( elem, this ); + } + } + + // Force callback invocation + }, ignored ); + } +} ); + +jQuery.each( { + appendTo: "append", + prependTo: "prepend", + insertBefore: "before", + insertAfter: "after", + replaceAll: "replaceWith" +}, function( name, original ) { + jQuery.fn[ name ] = function( selector ) { + var elems, + ret = [], + insert = jQuery( selector ), + last = insert.length - 1, + i = 0; + + for ( ; i <= last; i++ ) { + elems = i === last ? this : this.clone( true ); + jQuery( insert[ i ] )[ original ]( elems ); + + // Support: Android <=4.0 only, PhantomJS 1 only + // .get() because push.apply(_, arraylike) throws on ancient WebKit + push.apply( ret, elems.get() ); + } + + return this.pushStack( ret ); + }; +} ); +var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" ); + +var getStyles = function( elem ) { + + // Support: IE <=11 only, Firefox <=30 (#15098, #14150) + // IE throws on elements created in popups + // FF meanwhile throws on frame elements through "defaultView.getComputedStyle" + var view = elem.ownerDocument.defaultView; + + if ( !view || !view.opener ) { + view = window; + } + + return view.getComputedStyle( elem ); + }; + +var rboxStyle = new RegExp( cssExpand.join( "|" ), "i" ); + + + +( function() { + + // Executing both pixelPosition & boxSizingReliable tests require only one layout + // so they're executed at the same time to save the second computation. + function computeStyleTests() { + + // This is a singleton, we need to execute it only once + if ( !div ) { + return; + } + + container.style.cssText = "position:absolute;left:-11111px;width:60px;" + + "margin-top:1px;padding:0;border:0"; + div.style.cssText = + "position:relative;display:block;box-sizing:border-box;overflow:scroll;" + + "margin:auto;border:1px;padding:1px;" + + "width:60%;top:1%"; + documentElement.appendChild( container ).appendChild( div ); + + var divStyle = window.getComputedStyle( div ); + pixelPositionVal = divStyle.top !== "1%"; + + // Support: Android 4.0 - 4.3 only, Firefox <=3 - 44 + reliableMarginLeftVal = roundPixelMeasures( divStyle.marginLeft ) === 12; + + // Support: Android 4.0 - 4.3 only, Safari <=9.1 - 10.1, iOS <=7.0 - 9.3 + // Some styles come back with percentage values, even though they shouldn't + div.style.right = "60%"; + pixelBoxStylesVal = roundPixelMeasures( divStyle.right ) === 36; + + // Support: IE 9 - 11 only + // Detect misreporting of content dimensions for box-sizing:border-box elements + boxSizingReliableVal = roundPixelMeasures( divStyle.width ) === 36; + + // Support: IE 9 only + // Detect overflow:scroll screwiness (gh-3699) + // Support: Chrome <=64 + // Don't get tricked when zoom affects offsetWidth (gh-4029) + div.style.position = "absolute"; + scrollboxSizeVal = roundPixelMeasures( div.offsetWidth / 3 ) === 12; + + documentElement.removeChild( container ); + + // Nullify the div so it wouldn't be stored in the memory and + // it will also be a sign that checks already performed + div = null; + } + + function roundPixelMeasures( measure ) { + return Math.round( parseFloat( measure ) ); + } + + var pixelPositionVal, boxSizingReliableVal, scrollboxSizeVal, pixelBoxStylesVal, + reliableMarginLeftVal, + container = document.createElement( "div" ), + div = document.createElement( "div" ); + + // Finish early in limited (non-browser) environments + if ( !div.style ) { + return; + } + + // Support: IE <=9 - 11 only + // Style of cloned element affects source element cloned (#8908) + div.style.backgroundClip = "content-box"; + div.cloneNode( true ).style.backgroundClip = ""; + support.clearCloneStyle = div.style.backgroundClip === "content-box"; + + jQuery.extend( support, { + boxSizingReliable: function() { + computeStyleTests(); + return boxSizingReliableVal; + }, + pixelBoxStyles: function() { + computeStyleTests(); + return pixelBoxStylesVal; + }, + pixelPosition: function() { + computeStyleTests(); + return pixelPositionVal; + }, + reliableMarginLeft: function() { + computeStyleTests(); + return reliableMarginLeftVal; + }, + scrollboxSize: function() { + computeStyleTests(); + return scrollboxSizeVal; + } + } ); +} )(); + + +function curCSS( elem, name, computed ) { + var width, minWidth, maxWidth, ret, + + // Support: Firefox 51+ + // Retrieving style before computed somehow + // fixes an issue with getting wrong values + // on detached elements + style = elem.style; + + computed = computed || getStyles( elem ); + + // getPropertyValue is needed for: + // .css('filter') (IE 9 only, #12537) + // .css('--customProperty) (#3144) + if ( computed ) { + ret = computed.getPropertyValue( name ) || computed[ name ]; + + if ( ret === "" && !isAttached( elem ) ) { + ret = jQuery.style( elem, name ); + } + + // A tribute to the "awesome hack by Dean Edwards" + // Android Browser returns percentage for some values, + // but width seems to be reliably pixels. + // This is against the CSSOM draft spec: + // https://drafts.csswg.org/cssom/#resolved-values + if ( !support.pixelBoxStyles() && rnumnonpx.test( ret ) && rboxStyle.test( name ) ) { + + // Remember the original values + width = style.width; + minWidth = style.minWidth; + maxWidth = style.maxWidth; + + // Put in the new values to get a computed value out + style.minWidth = style.maxWidth = style.width = ret; + ret = computed.width; + + // Revert the changed values + style.width = width; + style.minWidth = minWidth; + style.maxWidth = maxWidth; + } + } + + return ret !== undefined ? + + // Support: IE <=9 - 11 only + // IE returns zIndex value as an integer. + ret + "" : + ret; +} + + +function addGetHookIf( conditionFn, hookFn ) { + + // Define the hook, we'll check on the first run if it's really needed. + return { + get: function() { + if ( conditionFn() ) { + + // Hook not needed (or it's not possible to use it due + // to missing dependency), remove it. + delete this.get; + return; + } + + // Hook needed; redefine it so that the support test is not executed again. + return ( this.get = hookFn ).apply( this, arguments ); + } + }; +} + + +var cssPrefixes = [ "Webkit", "Moz", "ms" ], + emptyStyle = document.createElement( "div" ).style, + vendorProps = {}; + +// Return a vendor-prefixed property or undefined +function vendorPropName( name ) { + + // Check for vendor prefixed names + var capName = name[ 0 ].toUpperCase() + name.slice( 1 ), + i = cssPrefixes.length; + + while ( i-- ) { + name = cssPrefixes[ i ] + capName; + if ( name in emptyStyle ) { + return name; + } + } +} + +// Return a potentially-mapped jQuery.cssProps or vendor prefixed property +function finalPropName( name ) { + var final = jQuery.cssProps[ name ] || vendorProps[ name ]; + + if ( final ) { + return final; + } + if ( name in emptyStyle ) { + return name; + } + return vendorProps[ name ] = vendorPropName( name ) || name; +} + + +var + + // Swappable if display is none or starts with table + // except "table", "table-cell", or "table-caption" + // See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display + rdisplayswap = /^(none|table(?!-c[ea]).+)/, + rcustomProp = /^--/, + cssShow = { position: "absolute", visibility: "hidden", display: "block" }, + cssNormalTransform = { + letterSpacing: "0", + fontWeight: "400" + }; + +function setPositiveNumber( elem, value, subtract ) { + + // Any relative (+/-) values have already been + // normalized at this point + var matches = rcssNum.exec( value ); + return matches ? + + // Guard against undefined "subtract", e.g., when used as in cssHooks + Math.max( 0, matches[ 2 ] - ( subtract || 0 ) ) + ( matches[ 3 ] || "px" ) : + value; +} + +function boxModelAdjustment( elem, dimension, box, isBorderBox, styles, computedVal ) { + var i = dimension === "width" ? 1 : 0, + extra = 0, + delta = 0; + + // Adjustment may not be necessary + if ( box === ( isBorderBox ? "border" : "content" ) ) { + return 0; + } + + for ( ; i < 4; i += 2 ) { + + // Both box models exclude margin + if ( box === "margin" ) { + delta += jQuery.css( elem, box + cssExpand[ i ], true, styles ); + } + + // If we get here with a content-box, we're seeking "padding" or "border" or "margin" + if ( !isBorderBox ) { + + // Add padding + delta += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + + // For "border" or "margin", add border + if ( box !== "padding" ) { + delta += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + + // But still keep track of it otherwise + } else { + extra += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + + // If we get here with a border-box (content + padding + border), we're seeking "content" or + // "padding" or "margin" + } else { + + // For "content", subtract padding + if ( box === "content" ) { + delta -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + } + + // For "content" or "padding", subtract border + if ( box !== "margin" ) { + delta -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + } + } + + // Account for positive content-box scroll gutter when requested by providing computedVal + if ( !isBorderBox && computedVal >= 0 ) { + + // offsetWidth/offsetHeight is a rounded sum of content, padding, scroll gutter, and border + // Assuming integer scroll gutter, subtract the rest and round down + delta += Math.max( 0, Math.ceil( + elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] - + computedVal - + delta - + extra - + 0.5 + + // If offsetWidth/offsetHeight is unknown, then we can't determine content-box scroll gutter + // Use an explicit zero to avoid NaN (gh-3964) + ) ) || 0; + } + + return delta; +} + +function getWidthOrHeight( elem, dimension, extra ) { + + // Start with computed style + var styles = getStyles( elem ), + + // To avoid forcing a reflow, only fetch boxSizing if we need it (gh-4322). + // Fake content-box until we know it's needed to know the true value. + boxSizingNeeded = !support.boxSizingReliable() || extra, + isBorderBox = boxSizingNeeded && + jQuery.css( elem, "boxSizing", false, styles ) === "border-box", + valueIsBorderBox = isBorderBox, + + val = curCSS( elem, dimension, styles ), + offsetProp = "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ); + + // Support: Firefox <=54 + // Return a confounding non-pixel value or feign ignorance, as appropriate. + if ( rnumnonpx.test( val ) ) { + if ( !extra ) { + return val; + } + val = "auto"; + } + + + // Fall back to offsetWidth/offsetHeight when value is "auto" + // This happens for inline elements with no explicit setting (gh-3571) + // Support: Android <=4.1 - 4.3 only + // Also use offsetWidth/offsetHeight for misreported inline dimensions (gh-3602) + // Support: IE 9-11 only + // Also use offsetWidth/offsetHeight for when box sizing is unreliable + // We use getClientRects() to check for hidden/disconnected. + // In those cases, the computed value can be trusted to be border-box + if ( ( !support.boxSizingReliable() && isBorderBox || + val === "auto" || + !parseFloat( val ) && jQuery.css( elem, "display", false, styles ) === "inline" ) && + elem.getClientRects().length ) { + + isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box"; + + // Where available, offsetWidth/offsetHeight approximate border box dimensions. + // Where not available (e.g., SVG), assume unreliable box-sizing and interpret the + // retrieved value as a content box dimension. + valueIsBorderBox = offsetProp in elem; + if ( valueIsBorderBox ) { + val = elem[ offsetProp ]; + } + } + + // Normalize "" and auto + val = parseFloat( val ) || 0; + + // Adjust for the element's box model + return ( val + + boxModelAdjustment( + elem, + dimension, + extra || ( isBorderBox ? "border" : "content" ), + valueIsBorderBox, + styles, + + // Provide the current computed size to request scroll gutter calculation (gh-3589) + val + ) + ) + "px"; +} + +jQuery.extend( { + + // Add in style property hooks for overriding the default + // behavior of getting and setting a style property + cssHooks: { + opacity: { + get: function( elem, computed ) { + if ( computed ) { + + // We should always get a number back from opacity + var ret = curCSS( elem, "opacity" ); + return ret === "" ? "1" : ret; + } + } + } + }, + + // Don't automatically add "px" to these possibly-unitless properties + cssNumber: { + "animationIterationCount": true, + "columnCount": true, + "fillOpacity": true, + "flexGrow": true, + "flexShrink": true, + "fontWeight": true, + "gridArea": true, + "gridColumn": true, + "gridColumnEnd": true, + "gridColumnStart": true, + "gridRow": true, + "gridRowEnd": true, + "gridRowStart": true, + "lineHeight": true, + "opacity": true, + "order": true, + "orphans": true, + "widows": true, + "zIndex": true, + "zoom": true + }, + + // Add in properties whose names you wish to fix before + // setting or getting the value + cssProps: {}, + + // Get and set the style property on a DOM Node + style: function( elem, name, value, extra ) { + + // Don't set styles on text and comment nodes + if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) { + return; + } + + // Make sure that we're working with the right name + var ret, type, hooks, + origName = camelCase( name ), + isCustomProp = rcustomProp.test( name ), + style = elem.style; + + // Make sure that we're working with the right name. We don't + // want to query the value if it is a CSS custom property + // since they are user-defined. + if ( !isCustomProp ) { + name = finalPropName( origName ); + } + + // Gets hook for the prefixed version, then unprefixed version + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // Check if we're setting a value + if ( value !== undefined ) { + type = typeof value; + + // Convert "+=" or "-=" to relative numbers (#7345) + if ( type === "string" && ( ret = rcssNum.exec( value ) ) && ret[ 1 ] ) { + value = adjustCSS( elem, name, ret ); + + // Fixes bug #9237 + type = "number"; + } + + // Make sure that null and NaN values aren't set (#7116) + if ( value == null || value !== value ) { + return; + } + + // If a number was passed in, add the unit (except for certain CSS properties) + // The isCustomProp check can be removed in jQuery 4.0 when we only auto-append + // "px" to a few hardcoded values. + if ( type === "number" && !isCustomProp ) { + value += ret && ret[ 3 ] || ( jQuery.cssNumber[ origName ] ? "" : "px" ); + } + + // background-* props affect original clone's values + if ( !support.clearCloneStyle && value === "" && name.indexOf( "background" ) === 0 ) { + style[ name ] = "inherit"; + } + + // If a hook was provided, use that value, otherwise just set the specified value + if ( !hooks || !( "set" in hooks ) || + ( value = hooks.set( elem, value, extra ) ) !== undefined ) { + + if ( isCustomProp ) { + style.setProperty( name, value ); + } else { + style[ name ] = value; + } + } + + } else { + + // If a hook was provided get the non-computed value from there + if ( hooks && "get" in hooks && + ( ret = hooks.get( elem, false, extra ) ) !== undefined ) { + + return ret; + } + + // Otherwise just get the value from the style object + return style[ name ]; + } + }, + + css: function( elem, name, extra, styles ) { + var val, num, hooks, + origName = camelCase( name ), + isCustomProp = rcustomProp.test( name ); + + // Make sure that we're working with the right name. We don't + // want to modify the value if it is a CSS custom property + // since they are user-defined. + if ( !isCustomProp ) { + name = finalPropName( origName ); + } + + // Try prefixed name followed by the unprefixed name + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // If a hook was provided get the computed value from there + if ( hooks && "get" in hooks ) { + val = hooks.get( elem, true, extra ); + } + + // Otherwise, if a way to get the computed value exists, use that + if ( val === undefined ) { + val = curCSS( elem, name, styles ); + } + + // Convert "normal" to computed value + if ( val === "normal" && name in cssNormalTransform ) { + val = cssNormalTransform[ name ]; + } + + // Make numeric if forced or a qualifier was provided and val looks numeric + if ( extra === "" || extra ) { + num = parseFloat( val ); + return extra === true || isFinite( num ) ? num || 0 : val; + } + + return val; + } +} ); + +jQuery.each( [ "height", "width" ], function( i, dimension ) { + jQuery.cssHooks[ dimension ] = { + get: function( elem, computed, extra ) { + if ( computed ) { + + // Certain elements can have dimension info if we invisibly show them + // but it must have a current display style that would benefit + return rdisplayswap.test( jQuery.css( elem, "display" ) ) && + + // Support: Safari 8+ + // Table columns in Safari have non-zero offsetWidth & zero + // getBoundingClientRect().width unless display is changed. + // Support: IE <=11 only + // Running getBoundingClientRect on a disconnected node + // in IE throws an error. + ( !elem.getClientRects().length || !elem.getBoundingClientRect().width ) ? + swap( elem, cssShow, function() { + return getWidthOrHeight( elem, dimension, extra ); + } ) : + getWidthOrHeight( elem, dimension, extra ); + } + }, + + set: function( elem, value, extra ) { + var matches, + styles = getStyles( elem ), + + // Only read styles.position if the test has a chance to fail + // to avoid forcing a reflow. + scrollboxSizeBuggy = !support.scrollboxSize() && + styles.position === "absolute", + + // To avoid forcing a reflow, only fetch boxSizing if we need it (gh-3991) + boxSizingNeeded = scrollboxSizeBuggy || extra, + isBorderBox = boxSizingNeeded && + jQuery.css( elem, "boxSizing", false, styles ) === "border-box", + subtract = extra ? + boxModelAdjustment( + elem, + dimension, + extra, + isBorderBox, + styles + ) : + 0; + + // Account for unreliable border-box dimensions by comparing offset* to computed and + // faking a content-box to get border and padding (gh-3699) + if ( isBorderBox && scrollboxSizeBuggy ) { + subtract -= Math.ceil( + elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] - + parseFloat( styles[ dimension ] ) - + boxModelAdjustment( elem, dimension, "border", false, styles ) - + 0.5 + ); + } + + // Convert to pixels if value adjustment is needed + if ( subtract && ( matches = rcssNum.exec( value ) ) && + ( matches[ 3 ] || "px" ) !== "px" ) { + + elem.style[ dimension ] = value; + value = jQuery.css( elem, dimension ); + } + + return setPositiveNumber( elem, value, subtract ); + } + }; +} ); + +jQuery.cssHooks.marginLeft = addGetHookIf( support.reliableMarginLeft, + function( elem, computed ) { + if ( computed ) { + return ( parseFloat( curCSS( elem, "marginLeft" ) ) || + elem.getBoundingClientRect().left - + swap( elem, { marginLeft: 0 }, function() { + return elem.getBoundingClientRect().left; + } ) + ) + "px"; + } + } +); + +// These hooks are used by animate to expand properties +jQuery.each( { + margin: "", + padding: "", + border: "Width" +}, function( prefix, suffix ) { + jQuery.cssHooks[ prefix + suffix ] = { + expand: function( value ) { + var i = 0, + expanded = {}, + + // Assumes a single number if not a string + parts = typeof value === "string" ? value.split( " " ) : [ value ]; + + for ( ; i < 4; i++ ) { + expanded[ prefix + cssExpand[ i ] + suffix ] = + parts[ i ] || parts[ i - 2 ] || parts[ 0 ]; + } + + return expanded; + } + }; + + if ( prefix !== "margin" ) { + jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber; + } +} ); + +jQuery.fn.extend( { + css: function( name, value ) { + return access( this, function( elem, name, value ) { + var styles, len, + map = {}, + i = 0; + + if ( Array.isArray( name ) ) { + styles = getStyles( elem ); + len = name.length; + + for ( ; i < len; i++ ) { + map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles ); + } + + return map; + } + + return value !== undefined ? + jQuery.style( elem, name, value ) : + jQuery.css( elem, name ); + }, name, value, arguments.length > 1 ); + } +} ); + + +function Tween( elem, options, prop, end, easing ) { + return new Tween.prototype.init( elem, options, prop, end, easing ); +} +jQuery.Tween = Tween; + +Tween.prototype = { + constructor: Tween, + init: function( elem, options, prop, end, easing, unit ) { + this.elem = elem; + this.prop = prop; + this.easing = easing || jQuery.easing._default; + this.options = options; + this.start = this.now = this.cur(); + this.end = end; + this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" ); + }, + cur: function() { + var hooks = Tween.propHooks[ this.prop ]; + + return hooks && hooks.get ? + hooks.get( this ) : + Tween.propHooks._default.get( this ); + }, + run: function( percent ) { + var eased, + hooks = Tween.propHooks[ this.prop ]; + + if ( this.options.duration ) { + this.pos = eased = jQuery.easing[ this.easing ]( + percent, this.options.duration * percent, 0, 1, this.options.duration + ); + } else { + this.pos = eased = percent; + } + this.now = ( this.end - this.start ) * eased + this.start; + + if ( this.options.step ) { + this.options.step.call( this.elem, this.now, this ); + } + + if ( hooks && hooks.set ) { + hooks.set( this ); + } else { + Tween.propHooks._default.set( this ); + } + return this; + } +}; + +Tween.prototype.init.prototype = Tween.prototype; + +Tween.propHooks = { + _default: { + get: function( tween ) { + var result; + + // Use a property on the element directly when it is not a DOM element, + // or when there is no matching style property that exists. + if ( tween.elem.nodeType !== 1 || + tween.elem[ tween.prop ] != null && tween.elem.style[ tween.prop ] == null ) { + return tween.elem[ tween.prop ]; + } + + // Passing an empty string as a 3rd parameter to .css will automatically + // attempt a parseFloat and fallback to a string if the parse fails. + // Simple values such as "10px" are parsed to Float; + // complex values such as "rotate(1rad)" are returned as-is. + result = jQuery.css( tween.elem, tween.prop, "" ); + + // Empty strings, null, undefined and "auto" are converted to 0. + return !result || result === "auto" ? 0 : result; + }, + set: function( tween ) { + + // Use step hook for back compat. + // Use cssHook if its there. + // Use .style if available and use plain properties where available. + if ( jQuery.fx.step[ tween.prop ] ) { + jQuery.fx.step[ tween.prop ]( tween ); + } else if ( tween.elem.nodeType === 1 && ( + jQuery.cssHooks[ tween.prop ] || + tween.elem.style[ finalPropName( tween.prop ) ] != null ) ) { + jQuery.style( tween.elem, tween.prop, tween.now + tween.unit ); + } else { + tween.elem[ tween.prop ] = tween.now; + } + } + } +}; + +// Support: IE <=9 only +// Panic based approach to setting things on disconnected nodes +Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = { + set: function( tween ) { + if ( tween.elem.nodeType && tween.elem.parentNode ) { + tween.elem[ tween.prop ] = tween.now; + } + } +}; + +jQuery.easing = { + linear: function( p ) { + return p; + }, + swing: function( p ) { + return 0.5 - Math.cos( p * Math.PI ) / 2; + }, + _default: "swing" +}; + +jQuery.fx = Tween.prototype.init; + +// Back compat <1.8 extension point +jQuery.fx.step = {}; + + + + +var + fxNow, inProgress, + rfxtypes = /^(?:toggle|show|hide)$/, + rrun = /queueHooks$/; + +function schedule() { + if ( inProgress ) { + if ( document.hidden === false && window.requestAnimationFrame ) { + window.requestAnimationFrame( schedule ); + } else { + window.setTimeout( schedule, jQuery.fx.interval ); + } + + jQuery.fx.tick(); + } +} + +// Animations created synchronously will run synchronously +function createFxNow() { + window.setTimeout( function() { + fxNow = undefined; + } ); + return ( fxNow = Date.now() ); +} + +// Generate parameters to create a standard animation +function genFx( type, includeWidth ) { + var which, + i = 0, + attrs = { height: type }; + + // If we include width, step value is 1 to do all cssExpand values, + // otherwise step value is 2 to skip over Left and Right + includeWidth = includeWidth ? 1 : 0; + for ( ; i < 4; i += 2 - includeWidth ) { + which = cssExpand[ i ]; + attrs[ "margin" + which ] = attrs[ "padding" + which ] = type; + } + + if ( includeWidth ) { + attrs.opacity = attrs.width = type; + } + + return attrs; +} + +function createTween( value, prop, animation ) { + var tween, + collection = ( Animation.tweeners[ prop ] || [] ).concat( Animation.tweeners[ "*" ] ), + index = 0, + length = collection.length; + for ( ; index < length; index++ ) { + if ( ( tween = collection[ index ].call( animation, prop, value ) ) ) { + + // We're done with this property + return tween; + } + } +} + +function defaultPrefilter( elem, props, opts ) { + var prop, value, toggle, hooks, oldfire, propTween, restoreDisplay, display, + isBox = "width" in props || "height" in props, + anim = this, + orig = {}, + style = elem.style, + hidden = elem.nodeType && isHiddenWithinTree( elem ), + dataShow = dataPriv.get( elem, "fxshow" ); + + // Queue-skipping animations hijack the fx hooks + if ( !opts.queue ) { + hooks = jQuery._queueHooks( elem, "fx" ); + if ( hooks.unqueued == null ) { + hooks.unqueued = 0; + oldfire = hooks.empty.fire; + hooks.empty.fire = function() { + if ( !hooks.unqueued ) { + oldfire(); + } + }; + } + hooks.unqueued++; + + anim.always( function() { + + // Ensure the complete handler is called before this completes + anim.always( function() { + hooks.unqueued--; + if ( !jQuery.queue( elem, "fx" ).length ) { + hooks.empty.fire(); + } + } ); + } ); + } + + // Detect show/hide animations + for ( prop in props ) { + value = props[ prop ]; + if ( rfxtypes.test( value ) ) { + delete props[ prop ]; + toggle = toggle || value === "toggle"; + if ( value === ( hidden ? "hide" : "show" ) ) { + + // Pretend to be hidden if this is a "show" and + // there is still data from a stopped show/hide + if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) { + hidden = true; + + // Ignore all other no-op show/hide data + } else { + continue; + } + } + orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop ); + } + } + + // Bail out if this is a no-op like .hide().hide() + propTween = !jQuery.isEmptyObject( props ); + if ( !propTween && jQuery.isEmptyObject( orig ) ) { + return; + } + + // Restrict "overflow" and "display" styles during box animations + if ( isBox && elem.nodeType === 1 ) { + + // Support: IE <=9 - 11, Edge 12 - 15 + // Record all 3 overflow attributes because IE does not infer the shorthand + // from identically-valued overflowX and overflowY and Edge just mirrors + // the overflowX value there. + opts.overflow = [ style.overflow, style.overflowX, style.overflowY ]; + + // Identify a display type, preferring old show/hide data over the CSS cascade + restoreDisplay = dataShow && dataShow.display; + if ( restoreDisplay == null ) { + restoreDisplay = dataPriv.get( elem, "display" ); + } + display = jQuery.css( elem, "display" ); + if ( display === "none" ) { + if ( restoreDisplay ) { + display = restoreDisplay; + } else { + + // Get nonempty value(s) by temporarily forcing visibility + showHide( [ elem ], true ); + restoreDisplay = elem.style.display || restoreDisplay; + display = jQuery.css( elem, "display" ); + showHide( [ elem ] ); + } + } + + // Animate inline elements as inline-block + if ( display === "inline" || display === "inline-block" && restoreDisplay != null ) { + if ( jQuery.css( elem, "float" ) === "none" ) { + + // Restore the original display value at the end of pure show/hide animations + if ( !propTween ) { + anim.done( function() { + style.display = restoreDisplay; + } ); + if ( restoreDisplay == null ) { + display = style.display; + restoreDisplay = display === "none" ? "" : display; + } + } + style.display = "inline-block"; + } + } + } + + if ( opts.overflow ) { + style.overflow = "hidden"; + anim.always( function() { + style.overflow = opts.overflow[ 0 ]; + style.overflowX = opts.overflow[ 1 ]; + style.overflowY = opts.overflow[ 2 ]; + } ); + } + + // Implement show/hide animations + propTween = false; + for ( prop in orig ) { + + // General show/hide setup for this element animation + if ( !propTween ) { + if ( dataShow ) { + if ( "hidden" in dataShow ) { + hidden = dataShow.hidden; + } + } else { + dataShow = dataPriv.access( elem, "fxshow", { display: restoreDisplay } ); + } + + // Store hidden/visible for toggle so `.stop().toggle()` "reverses" + if ( toggle ) { + dataShow.hidden = !hidden; + } + + // Show elements before animating them + if ( hidden ) { + showHide( [ elem ], true ); + } + + /* eslint-disable no-loop-func */ + + anim.done( function() { + + /* eslint-enable no-loop-func */ + + // The final step of a "hide" animation is actually hiding the element + if ( !hidden ) { + showHide( [ elem ] ); + } + dataPriv.remove( elem, "fxshow" ); + for ( prop in orig ) { + jQuery.style( elem, prop, orig[ prop ] ); + } + } ); + } + + // Per-property setup + propTween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim ); + if ( !( prop in dataShow ) ) { + dataShow[ prop ] = propTween.start; + if ( hidden ) { + propTween.end = propTween.start; + propTween.start = 0; + } + } + } +} + +function propFilter( props, specialEasing ) { + var index, name, easing, value, hooks; + + // camelCase, specialEasing and expand cssHook pass + for ( index in props ) { + name = camelCase( index ); + easing = specialEasing[ name ]; + value = props[ index ]; + if ( Array.isArray( value ) ) { + easing = value[ 1 ]; + value = props[ index ] = value[ 0 ]; + } + + if ( index !== name ) { + props[ name ] = value; + delete props[ index ]; + } + + hooks = jQuery.cssHooks[ name ]; + if ( hooks && "expand" in hooks ) { + value = hooks.expand( value ); + delete props[ name ]; + + // Not quite $.extend, this won't overwrite existing keys. + // Reusing 'index' because we have the correct "name" + for ( index in value ) { + if ( !( index in props ) ) { + props[ index ] = value[ index ]; + specialEasing[ index ] = easing; + } + } + } else { + specialEasing[ name ] = easing; + } + } +} + +function Animation( elem, properties, options ) { + var result, + stopped, + index = 0, + length = Animation.prefilters.length, + deferred = jQuery.Deferred().always( function() { + + // Don't match elem in the :animated selector + delete tick.elem; + } ), + tick = function() { + if ( stopped ) { + return false; + } + var currentTime = fxNow || createFxNow(), + remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ), + + // Support: Android 2.3 only + // Archaic crash bug won't allow us to use `1 - ( 0.5 || 0 )` (#12497) + temp = remaining / animation.duration || 0, + percent = 1 - temp, + index = 0, + length = animation.tweens.length; + + for ( ; index < length; index++ ) { + animation.tweens[ index ].run( percent ); + } + + deferred.notifyWith( elem, [ animation, percent, remaining ] ); + + // If there's more to do, yield + if ( percent < 1 && length ) { + return remaining; + } + + // If this was an empty animation, synthesize a final progress notification + if ( !length ) { + deferred.notifyWith( elem, [ animation, 1, 0 ] ); + } + + // Resolve the animation and report its conclusion + deferred.resolveWith( elem, [ animation ] ); + return false; + }, + animation = deferred.promise( { + elem: elem, + props: jQuery.extend( {}, properties ), + opts: jQuery.extend( true, { + specialEasing: {}, + easing: jQuery.easing._default + }, options ), + originalProperties: properties, + originalOptions: options, + startTime: fxNow || createFxNow(), + duration: options.duration, + tweens: [], + createTween: function( prop, end ) { + var tween = jQuery.Tween( elem, animation.opts, prop, end, + animation.opts.specialEasing[ prop ] || animation.opts.easing ); + animation.tweens.push( tween ); + return tween; + }, + stop: function( gotoEnd ) { + var index = 0, + + // If we are going to the end, we want to run all the tweens + // otherwise we skip this part + length = gotoEnd ? animation.tweens.length : 0; + if ( stopped ) { + return this; + } + stopped = true; + for ( ; index < length; index++ ) { + animation.tweens[ index ].run( 1 ); + } + + // Resolve when we played the last frame; otherwise, reject + if ( gotoEnd ) { + deferred.notifyWith( elem, [ animation, 1, 0 ] ); + deferred.resolveWith( elem, [ animation, gotoEnd ] ); + } else { + deferred.rejectWith( elem, [ animation, gotoEnd ] ); + } + return this; + } + } ), + props = animation.props; + + propFilter( props, animation.opts.specialEasing ); + + for ( ; index < length; index++ ) { + result = Animation.prefilters[ index ].call( animation, elem, props, animation.opts ); + if ( result ) { + if ( isFunction( result.stop ) ) { + jQuery._queueHooks( animation.elem, animation.opts.queue ).stop = + result.stop.bind( result ); + } + return result; + } + } + + jQuery.map( props, createTween, animation ); + + if ( isFunction( animation.opts.start ) ) { + animation.opts.start.call( elem, animation ); + } + + // Attach callbacks from options + animation + .progress( animation.opts.progress ) + .done( animation.opts.done, animation.opts.complete ) + .fail( animation.opts.fail ) + .always( animation.opts.always ); + + jQuery.fx.timer( + jQuery.extend( tick, { + elem: elem, + anim: animation, + queue: animation.opts.queue + } ) + ); + + return animation; +} + +jQuery.Animation = jQuery.extend( Animation, { + + tweeners: { + "*": [ function( prop, value ) { + var tween = this.createTween( prop, value ); + adjustCSS( tween.elem, prop, rcssNum.exec( value ), tween ); + return tween; + } ] + }, + + tweener: function( props, callback ) { + if ( isFunction( props ) ) { + callback = props; + props = [ "*" ]; + } else { + props = props.match( rnothtmlwhite ); + } + + var prop, + index = 0, + length = props.length; + + for ( ; index < length; index++ ) { + prop = props[ index ]; + Animation.tweeners[ prop ] = Animation.tweeners[ prop ] || []; + Animation.tweeners[ prop ].unshift( callback ); + } + }, + + prefilters: [ defaultPrefilter ], + + prefilter: function( callback, prepend ) { + if ( prepend ) { + Animation.prefilters.unshift( callback ); + } else { + Animation.prefilters.push( callback ); + } + } +} ); + +jQuery.speed = function( speed, easing, fn ) { + var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : { + complete: fn || !fn && easing || + isFunction( speed ) && speed, + duration: speed, + easing: fn && easing || easing && !isFunction( easing ) && easing + }; + + // Go to the end state if fx are off + if ( jQuery.fx.off ) { + opt.duration = 0; + + } else { + if ( typeof opt.duration !== "number" ) { + if ( opt.duration in jQuery.fx.speeds ) { + opt.duration = jQuery.fx.speeds[ opt.duration ]; + + } else { + opt.duration = jQuery.fx.speeds._default; + } + } + } + + // Normalize opt.queue - true/undefined/null -> "fx" + if ( opt.queue == null || opt.queue === true ) { + opt.queue = "fx"; + } + + // Queueing + opt.old = opt.complete; + + opt.complete = function() { + if ( isFunction( opt.old ) ) { + opt.old.call( this ); + } + + if ( opt.queue ) { + jQuery.dequeue( this, opt.queue ); + } + }; + + return opt; +}; + +jQuery.fn.extend( { + fadeTo: function( speed, to, easing, callback ) { + + // Show any hidden elements after setting opacity to 0 + return this.filter( isHiddenWithinTree ).css( "opacity", 0 ).show() + + // Animate to the value specified + .end().animate( { opacity: to }, speed, easing, callback ); + }, + animate: function( prop, speed, easing, callback ) { + var empty = jQuery.isEmptyObject( prop ), + optall = jQuery.speed( speed, easing, callback ), + doAnimation = function() { + + // Operate on a copy of prop so per-property easing won't be lost + var anim = Animation( this, jQuery.extend( {}, prop ), optall ); + + // Empty animations, or finishing resolves immediately + if ( empty || dataPriv.get( this, "finish" ) ) { + anim.stop( true ); + } + }; + doAnimation.finish = doAnimation; + + return empty || optall.queue === false ? + this.each( doAnimation ) : + this.queue( optall.queue, doAnimation ); + }, + stop: function( type, clearQueue, gotoEnd ) { + var stopQueue = function( hooks ) { + var stop = hooks.stop; + delete hooks.stop; + stop( gotoEnd ); + }; + + if ( typeof type !== "string" ) { + gotoEnd = clearQueue; + clearQueue = type; + type = undefined; + } + if ( clearQueue && type !== false ) { + this.queue( type || "fx", [] ); + } + + return this.each( function() { + var dequeue = true, + index = type != null && type + "queueHooks", + timers = jQuery.timers, + data = dataPriv.get( this ); + + if ( index ) { + if ( data[ index ] && data[ index ].stop ) { + stopQueue( data[ index ] ); + } + } else { + for ( index in data ) { + if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) { + stopQueue( data[ index ] ); + } + } + } + + for ( index = timers.length; index--; ) { + if ( timers[ index ].elem === this && + ( type == null || timers[ index ].queue === type ) ) { + + timers[ index ].anim.stop( gotoEnd ); + dequeue = false; + timers.splice( index, 1 ); + } + } + + // Start the next in the queue if the last step wasn't forced. + // Timers currently will call their complete callbacks, which + // will dequeue but only if they were gotoEnd. + if ( dequeue || !gotoEnd ) { + jQuery.dequeue( this, type ); + } + } ); + }, + finish: function( type ) { + if ( type !== false ) { + type = type || "fx"; + } + return this.each( function() { + var index, + data = dataPriv.get( this ), + queue = data[ type + "queue" ], + hooks = data[ type + "queueHooks" ], + timers = jQuery.timers, + length = queue ? queue.length : 0; + + // Enable finishing flag on private data + data.finish = true; + + // Empty the queue first + jQuery.queue( this, type, [] ); + + if ( hooks && hooks.stop ) { + hooks.stop.call( this, true ); + } + + // Look for any active animations, and finish them + for ( index = timers.length; index--; ) { + if ( timers[ index ].elem === this && timers[ index ].queue === type ) { + timers[ index ].anim.stop( true ); + timers.splice( index, 1 ); + } + } + + // Look for any animations in the old queue and finish them + for ( index = 0; index < length; index++ ) { + if ( queue[ index ] && queue[ index ].finish ) { + queue[ index ].finish.call( this ); + } + } + + // Turn off finishing flag + delete data.finish; + } ); + } +} ); + +jQuery.each( [ "toggle", "show", "hide" ], function( i, name ) { + var cssFn = jQuery.fn[ name ]; + jQuery.fn[ name ] = function( speed, easing, callback ) { + return speed == null || typeof speed === "boolean" ? + cssFn.apply( this, arguments ) : + this.animate( genFx( name, true ), speed, easing, callback ); + }; +} ); + +// Generate shortcuts for custom animations +jQuery.each( { + slideDown: genFx( "show" ), + slideUp: genFx( "hide" ), + slideToggle: genFx( "toggle" ), + fadeIn: { opacity: "show" }, + fadeOut: { opacity: "hide" }, + fadeToggle: { opacity: "toggle" } +}, function( name, props ) { + jQuery.fn[ name ] = function( speed, easing, callback ) { + return this.animate( props, speed, easing, callback ); + }; +} ); + +jQuery.timers = []; +jQuery.fx.tick = function() { + var timer, + i = 0, + timers = jQuery.timers; + + fxNow = Date.now(); + + for ( ; i < timers.length; i++ ) { + timer = timers[ i ]; + + // Run the timer and safely remove it when done (allowing for external removal) + if ( !timer() && timers[ i ] === timer ) { + timers.splice( i--, 1 ); + } + } + + if ( !timers.length ) { + jQuery.fx.stop(); + } + fxNow = undefined; +}; + +jQuery.fx.timer = function( timer ) { + jQuery.timers.push( timer ); + jQuery.fx.start(); +}; + +jQuery.fx.interval = 13; +jQuery.fx.start = function() { + if ( inProgress ) { + return; + } + + inProgress = true; + schedule(); +}; + +jQuery.fx.stop = function() { + inProgress = null; +}; + +jQuery.fx.speeds = { + slow: 600, + fast: 200, + + // Default speed + _default: 400 +}; + + +// Based off of the plugin by Clint Helfers, with permission. +// https://web.archive.org/web/20100324014747/http://blindsignals.com/index.php/2009/07/jquery-delay/ +jQuery.fn.delay = function( time, type ) { + time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; + type = type || "fx"; + + return this.queue( type, function( next, hooks ) { + var timeout = window.setTimeout( next, time ); + hooks.stop = function() { + window.clearTimeout( timeout ); + }; + } ); +}; + + +( function() { + var input = document.createElement( "input" ), + select = document.createElement( "select" ), + opt = select.appendChild( document.createElement( "option" ) ); + + input.type = "checkbox"; + + // Support: Android <=4.3 only + // Default value for a checkbox should be "on" + support.checkOn = input.value !== ""; + + // Support: IE <=11 only + // Must access selectedIndex to make default options select + support.optSelected = opt.selected; + + // Support: IE <=11 only + // An input loses its value after becoming a radio + input = document.createElement( "input" ); + input.value = "t"; + input.type = "radio"; + support.radioValue = input.value === "t"; +} )(); + + +var boolHook, + attrHandle = jQuery.expr.attrHandle; + +jQuery.fn.extend( { + attr: function( name, value ) { + return access( this, jQuery.attr, name, value, arguments.length > 1 ); + }, + + removeAttr: function( name ) { + return this.each( function() { + jQuery.removeAttr( this, name ); + } ); + } +} ); + +jQuery.extend( { + attr: function( elem, name, value ) { + var ret, hooks, + nType = elem.nodeType; + + // Don't get/set attributes on text, comment and attribute nodes + if ( nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + // Fallback to prop when attributes are not supported + if ( typeof elem.getAttribute === "undefined" ) { + return jQuery.prop( elem, name, value ); + } + + // Attribute hooks are determined by the lowercase version + // Grab necessary hook if one is defined + if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { + hooks = jQuery.attrHooks[ name.toLowerCase() ] || + ( jQuery.expr.match.bool.test( name ) ? boolHook : undefined ); + } + + if ( value !== undefined ) { + if ( value === null ) { + jQuery.removeAttr( elem, name ); + return; + } + + if ( hooks && "set" in hooks && + ( ret = hooks.set( elem, value, name ) ) !== undefined ) { + return ret; + } + + elem.setAttribute( name, value + "" ); + return value; + } + + if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { + return ret; + } + + ret = jQuery.find.attr( elem, name ); + + // Non-existent attributes return null, we normalize to undefined + return ret == null ? undefined : ret; + }, + + attrHooks: { + type: { + set: function( elem, value ) { + if ( !support.radioValue && value === "radio" && + nodeName( elem, "input" ) ) { + var val = elem.value; + elem.setAttribute( "type", value ); + if ( val ) { + elem.value = val; + } + return value; + } + } + } + }, + + removeAttr: function( elem, value ) { + var name, + i = 0, + + // Attribute names can contain non-HTML whitespace characters + // https://html.spec.whatwg.org/multipage/syntax.html#attributes-2 + attrNames = value && value.match( rnothtmlwhite ); + + if ( attrNames && elem.nodeType === 1 ) { + while ( ( name = attrNames[ i++ ] ) ) { + elem.removeAttribute( name ); + } + } + } +} ); + +// Hooks for boolean attributes +boolHook = { + set: function( elem, value, name ) { + if ( value === false ) { + + // Remove boolean attributes when set to false + jQuery.removeAttr( elem, name ); + } else { + elem.setAttribute( name, name ); + } + return name; + } +}; + +jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( i, name ) { + var getter = attrHandle[ name ] || jQuery.find.attr; + + attrHandle[ name ] = function( elem, name, isXML ) { + var ret, handle, + lowercaseName = name.toLowerCase(); + + if ( !isXML ) { + + // Avoid an infinite loop by temporarily removing this function from the getter + handle = attrHandle[ lowercaseName ]; + attrHandle[ lowercaseName ] = ret; + ret = getter( elem, name, isXML ) != null ? + lowercaseName : + null; + attrHandle[ lowercaseName ] = handle; + } + return ret; + }; +} ); + + + + +var rfocusable = /^(?:input|select|textarea|button)$/i, + rclickable = /^(?:a|area)$/i; + +jQuery.fn.extend( { + prop: function( name, value ) { + return access( this, jQuery.prop, name, value, arguments.length > 1 ); + }, + + removeProp: function( name ) { + return this.each( function() { + delete this[ jQuery.propFix[ name ] || name ]; + } ); + } +} ); + +jQuery.extend( { + prop: function( elem, name, value ) { + var ret, hooks, + nType = elem.nodeType; + + // Don't get/set properties on text, comment and attribute nodes + if ( nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { + + // Fix name and attach hooks + name = jQuery.propFix[ name ] || name; + hooks = jQuery.propHooks[ name ]; + } + + if ( value !== undefined ) { + if ( hooks && "set" in hooks && + ( ret = hooks.set( elem, value, name ) ) !== undefined ) { + return ret; + } + + return ( elem[ name ] = value ); + } + + if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { + return ret; + } + + return elem[ name ]; + }, + + propHooks: { + tabIndex: { + get: function( elem ) { + + // Support: IE <=9 - 11 only + // elem.tabIndex doesn't always return the + // correct value when it hasn't been explicitly set + // https://web.archive.org/web/20141116233347/http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ + // Use proper attribute retrieval(#12072) + var tabindex = jQuery.find.attr( elem, "tabindex" ); + + if ( tabindex ) { + return parseInt( tabindex, 10 ); + } + + if ( + rfocusable.test( elem.nodeName ) || + rclickable.test( elem.nodeName ) && + elem.href + ) { + return 0; + } + + return -1; + } + } + }, + + propFix: { + "for": "htmlFor", + "class": "className" + } +} ); + +// Support: IE <=11 only +// Accessing the selectedIndex property +// forces the browser to respect setting selected +// on the option +// The getter ensures a default option is selected +// when in an optgroup +// eslint rule "no-unused-expressions" is disabled for this code +// since it considers such accessions noop +if ( !support.optSelected ) { + jQuery.propHooks.selected = { + get: function( elem ) { + + /* eslint no-unused-expressions: "off" */ + + var parent = elem.parentNode; + if ( parent && parent.parentNode ) { + parent.parentNode.selectedIndex; + } + return null; + }, + set: function( elem ) { + + /* eslint no-unused-expressions: "off" */ + + var parent = elem.parentNode; + if ( parent ) { + parent.selectedIndex; + + if ( parent.parentNode ) { + parent.parentNode.selectedIndex; + } + } + } + }; +} + +jQuery.each( [ + "tabIndex", + "readOnly", + "maxLength", + "cellSpacing", + "cellPadding", + "rowSpan", + "colSpan", + "useMap", + "frameBorder", + "contentEditable" +], function() { + jQuery.propFix[ this.toLowerCase() ] = this; +} ); + + + + + // Strip and collapse whitespace according to HTML spec + // https://infra.spec.whatwg.org/#strip-and-collapse-ascii-whitespace + function stripAndCollapse( value ) { + var tokens = value.match( rnothtmlwhite ) || []; + return tokens.join( " " ); + } + + +function getClass( elem ) { + return elem.getAttribute && elem.getAttribute( "class" ) || ""; +} + +function classesToArray( value ) { + if ( Array.isArray( value ) ) { + return value; + } + if ( typeof value === "string" ) { + return value.match( rnothtmlwhite ) || []; + } + return []; +} + +jQuery.fn.extend( { + addClass: function( value ) { + var classes, elem, cur, curValue, clazz, j, finalValue, + i = 0; + + if ( isFunction( value ) ) { + return this.each( function( j ) { + jQuery( this ).addClass( value.call( this, j, getClass( this ) ) ); + } ); + } + + classes = classesToArray( value ); + + if ( classes.length ) { + while ( ( elem = this[ i++ ] ) ) { + curValue = getClass( elem ); + cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); + + if ( cur ) { + j = 0; + while ( ( clazz = classes[ j++ ] ) ) { + if ( cur.indexOf( " " + clazz + " " ) < 0 ) { + cur += clazz + " "; + } + } + + // Only assign if different to avoid unneeded rendering. + finalValue = stripAndCollapse( cur ); + if ( curValue !== finalValue ) { + elem.setAttribute( "class", finalValue ); + } + } + } + } + + return this; + }, + + removeClass: function( value ) { + var classes, elem, cur, curValue, clazz, j, finalValue, + i = 0; + + if ( isFunction( value ) ) { + return this.each( function( j ) { + jQuery( this ).removeClass( value.call( this, j, getClass( this ) ) ); + } ); + } + + if ( !arguments.length ) { + return this.attr( "class", "" ); + } + + classes = classesToArray( value ); + + if ( classes.length ) { + while ( ( elem = this[ i++ ] ) ) { + curValue = getClass( elem ); + + // This expression is here for better compressibility (see addClass) + cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); + + if ( cur ) { + j = 0; + while ( ( clazz = classes[ j++ ] ) ) { + + // Remove *all* instances + while ( cur.indexOf( " " + clazz + " " ) > -1 ) { + cur = cur.replace( " " + clazz + " ", " " ); + } + } + + // Only assign if different to avoid unneeded rendering. + finalValue = stripAndCollapse( cur ); + if ( curValue !== finalValue ) { + elem.setAttribute( "class", finalValue ); + } + } + } + } + + return this; + }, + + toggleClass: function( value, stateVal ) { + var type = typeof value, + isValidValue = type === "string" || Array.isArray( value ); + + if ( typeof stateVal === "boolean" && isValidValue ) { + return stateVal ? this.addClass( value ) : this.removeClass( value ); + } + + if ( isFunction( value ) ) { + return this.each( function( i ) { + jQuery( this ).toggleClass( + value.call( this, i, getClass( this ), stateVal ), + stateVal + ); + } ); + } + + return this.each( function() { + var className, i, self, classNames; + + if ( isValidValue ) { + + // Toggle individual class names + i = 0; + self = jQuery( this ); + classNames = classesToArray( value ); + + while ( ( className = classNames[ i++ ] ) ) { + + // Check each className given, space separated list + if ( self.hasClass( className ) ) { + self.removeClass( className ); + } else { + self.addClass( className ); + } + } + + // Toggle whole class name + } else if ( value === undefined || type === "boolean" ) { + className = getClass( this ); + if ( className ) { + + // Store className if set + dataPriv.set( this, "__className__", className ); + } + + // If the element has a class name or if we're passed `false`, + // then remove the whole classname (if there was one, the above saved it). + // Otherwise bring back whatever was previously saved (if anything), + // falling back to the empty string if nothing was stored. + if ( this.setAttribute ) { + this.setAttribute( "class", + className || value === false ? + "" : + dataPriv.get( this, "__className__" ) || "" + ); + } + } + } ); + }, + + hasClass: function( selector ) { + var className, elem, + i = 0; + + className = " " + selector + " "; + while ( ( elem = this[ i++ ] ) ) { + if ( elem.nodeType === 1 && + ( " " + stripAndCollapse( getClass( elem ) ) + " " ).indexOf( className ) > -1 ) { + return true; + } + } + + return false; + } +} ); + + + + +var rreturn = /\r/g; + +jQuery.fn.extend( { + val: function( value ) { + var hooks, ret, valueIsFunction, + elem = this[ 0 ]; + + if ( !arguments.length ) { + if ( elem ) { + hooks = jQuery.valHooks[ elem.type ] || + jQuery.valHooks[ elem.nodeName.toLowerCase() ]; + + if ( hooks && + "get" in hooks && + ( ret = hooks.get( elem, "value" ) ) !== undefined + ) { + return ret; + } + + ret = elem.value; + + // Handle most common string cases + if ( typeof ret === "string" ) { + return ret.replace( rreturn, "" ); + } + + // Handle cases where value is null/undef or number + return ret == null ? "" : ret; + } + + return; + } + + valueIsFunction = isFunction( value ); + + return this.each( function( i ) { + var val; + + if ( this.nodeType !== 1 ) { + return; + } + + if ( valueIsFunction ) { + val = value.call( this, i, jQuery( this ).val() ); + } else { + val = value; + } + + // Treat null/undefined as ""; convert numbers to string + if ( val == null ) { + val = ""; + + } else if ( typeof val === "number" ) { + val += ""; + + } else if ( Array.isArray( val ) ) { + val = jQuery.map( val, function( value ) { + return value == null ? "" : value + ""; + } ); + } + + hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; + + // If set returns undefined, fall back to normal setting + if ( !hooks || !( "set" in hooks ) || hooks.set( this, val, "value" ) === undefined ) { + this.value = val; + } + } ); + } +} ); + +jQuery.extend( { + valHooks: { + option: { + get: function( elem ) { + + var val = jQuery.find.attr( elem, "value" ); + return val != null ? + val : + + // Support: IE <=10 - 11 only + // option.text throws exceptions (#14686, #14858) + // Strip and collapse whitespace + // https://html.spec.whatwg.org/#strip-and-collapse-whitespace + stripAndCollapse( jQuery.text( elem ) ); + } + }, + select: { + get: function( elem ) { + var value, option, i, + options = elem.options, + index = elem.selectedIndex, + one = elem.type === "select-one", + values = one ? null : [], + max = one ? index + 1 : options.length; + + if ( index < 0 ) { + i = max; + + } else { + i = one ? index : 0; + } + + // Loop through all the selected options + for ( ; i < max; i++ ) { + option = options[ i ]; + + // Support: IE <=9 only + // IE8-9 doesn't update selected after form reset (#2551) + if ( ( option.selected || i === index ) && + + // Don't return options that are disabled or in a disabled optgroup + !option.disabled && + ( !option.parentNode.disabled || + !nodeName( option.parentNode, "optgroup" ) ) ) { + + // Get the specific value for the option + value = jQuery( option ).val(); + + // We don't need an array for one selects + if ( one ) { + return value; + } + + // Multi-Selects return an array + values.push( value ); + } + } + + return values; + }, + + set: function( elem, value ) { + var optionSet, option, + options = elem.options, + values = jQuery.makeArray( value ), + i = options.length; + + while ( i-- ) { + option = options[ i ]; + + /* eslint-disable no-cond-assign */ + + if ( option.selected = + jQuery.inArray( jQuery.valHooks.option.get( option ), values ) > -1 + ) { + optionSet = true; + } + + /* eslint-enable no-cond-assign */ + } + + // Force browsers to behave consistently when non-matching value is set + if ( !optionSet ) { + elem.selectedIndex = -1; + } + return values; + } + } + } +} ); + +// Radios and checkboxes getter/setter +jQuery.each( [ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = { + set: function( elem, value ) { + if ( Array.isArray( value ) ) { + return ( elem.checked = jQuery.inArray( jQuery( elem ).val(), value ) > -1 ); + } + } + }; + if ( !support.checkOn ) { + jQuery.valHooks[ this ].get = function( elem ) { + return elem.getAttribute( "value" ) === null ? "on" : elem.value; + }; + } +} ); + + + + +// Return jQuery for attributes-only inclusion + + +support.focusin = "onfocusin" in window; + + +var rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, + stopPropagationCallback = function( e ) { + e.stopPropagation(); + }; + +jQuery.extend( jQuery.event, { + + trigger: function( event, data, elem, onlyHandlers ) { + + var i, cur, tmp, bubbleType, ontype, handle, special, lastElement, + eventPath = [ elem || document ], + type = hasOwn.call( event, "type" ) ? event.type : event, + namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split( "." ) : []; + + cur = lastElement = tmp = elem = elem || document; + + // Don't do events on text and comment nodes + if ( elem.nodeType === 3 || elem.nodeType === 8 ) { + return; + } + + // focus/blur morphs to focusin/out; ensure we're not firing them right now + if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { + return; + } + + if ( type.indexOf( "." ) > -1 ) { + + // Namespaced trigger; create a regexp to match event type in handle() + namespaces = type.split( "." ); + type = namespaces.shift(); + namespaces.sort(); + } + ontype = type.indexOf( ":" ) < 0 && "on" + type; + + // Caller can pass in a jQuery.Event object, Object, or just an event type string + event = event[ jQuery.expando ] ? + event : + new jQuery.Event( type, typeof event === "object" && event ); + + // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) + event.isTrigger = onlyHandlers ? 2 : 3; + event.namespace = namespaces.join( "." ); + event.rnamespace = event.namespace ? + new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ) : + null; + + // Clean up the event in case it is being reused + event.result = undefined; + if ( !event.target ) { + event.target = elem; + } + + // Clone any incoming data and prepend the event, creating the handler arg list + data = data == null ? + [ event ] : + jQuery.makeArray( data, [ event ] ); + + // Allow special events to draw outside the lines + special = jQuery.event.special[ type ] || {}; + if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { + return; + } + + // Determine event propagation path in advance, per W3C events spec (#9951) + // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) + if ( !onlyHandlers && !special.noBubble && !isWindow( elem ) ) { + + bubbleType = special.delegateType || type; + if ( !rfocusMorph.test( bubbleType + type ) ) { + cur = cur.parentNode; + } + for ( ; cur; cur = cur.parentNode ) { + eventPath.push( cur ); + tmp = cur; + } + + // Only add window if we got to document (e.g., not plain obj or detached DOM) + if ( tmp === ( elem.ownerDocument || document ) ) { + eventPath.push( tmp.defaultView || tmp.parentWindow || window ); + } + } + + // Fire handlers on the event path + i = 0; + while ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) { + lastElement = cur; + event.type = i > 1 ? + bubbleType : + special.bindType || type; + + // jQuery handler + handle = ( dataPriv.get( cur, "events" ) || {} )[ event.type ] && + dataPriv.get( cur, "handle" ); + if ( handle ) { + handle.apply( cur, data ); + } + + // Native handler + handle = ontype && cur[ ontype ]; + if ( handle && handle.apply && acceptData( cur ) ) { + event.result = handle.apply( cur, data ); + if ( event.result === false ) { + event.preventDefault(); + } + } + } + event.type = type; + + // If nobody prevented the default action, do it now + if ( !onlyHandlers && !event.isDefaultPrevented() ) { + + if ( ( !special._default || + special._default.apply( eventPath.pop(), data ) === false ) && + acceptData( elem ) ) { + + // Call a native DOM method on the target with the same name as the event. + // Don't do default actions on window, that's where global variables be (#6170) + if ( ontype && isFunction( elem[ type ] ) && !isWindow( elem ) ) { + + // Don't re-trigger an onFOO event when we call its FOO() method + tmp = elem[ ontype ]; + + if ( tmp ) { + elem[ ontype ] = null; + } + + // Prevent re-triggering of the same event, since we already bubbled it above + jQuery.event.triggered = type; + + if ( event.isPropagationStopped() ) { + lastElement.addEventListener( type, stopPropagationCallback ); + } + + elem[ type ](); + + if ( event.isPropagationStopped() ) { + lastElement.removeEventListener( type, stopPropagationCallback ); + } + + jQuery.event.triggered = undefined; + + if ( tmp ) { + elem[ ontype ] = tmp; + } + } + } + } + + return event.result; + }, + + // Piggyback on a donor event to simulate a different one + // Used only for `focus(in | out)` events + simulate: function( type, elem, event ) { + var e = jQuery.extend( + new jQuery.Event(), + event, + { + type: type, + isSimulated: true + } + ); + + jQuery.event.trigger( e, null, elem ); + } + +} ); + +jQuery.fn.extend( { + + trigger: function( type, data ) { + return this.each( function() { + jQuery.event.trigger( type, data, this ); + } ); + }, + triggerHandler: function( type, data ) { + var elem = this[ 0 ]; + if ( elem ) { + return jQuery.event.trigger( type, data, elem, true ); + } + } +} ); + + +// Support: Firefox <=44 +// Firefox doesn't have focus(in | out) events +// Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787 +// +// Support: Chrome <=48 - 49, Safari <=9.0 - 9.1 +// focus(in | out) events fire after focus & blur events, +// which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order +// Related ticket - https://bugs.chromium.org/p/chromium/issues/detail?id=449857 +if ( !support.focusin ) { + jQuery.each( { focus: "focusin", blur: "focusout" }, function( orig, fix ) { + + // Attach a single capturing handler on the document while someone wants focusin/focusout + var handler = function( event ) { + jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ) ); + }; + + jQuery.event.special[ fix ] = { + setup: function() { + var doc = this.ownerDocument || this, + attaches = dataPriv.access( doc, fix ); + + if ( !attaches ) { + doc.addEventListener( orig, handler, true ); + } + dataPriv.access( doc, fix, ( attaches || 0 ) + 1 ); + }, + teardown: function() { + var doc = this.ownerDocument || this, + attaches = dataPriv.access( doc, fix ) - 1; + + if ( !attaches ) { + doc.removeEventListener( orig, handler, true ); + dataPriv.remove( doc, fix ); + + } else { + dataPriv.access( doc, fix, attaches ); + } + } + }; + } ); +} +var location = window.location; + +var nonce = Date.now(); + +var rquery = ( /\?/ ); + + + +// Cross-browser xml parsing +jQuery.parseXML = function( data ) { + var xml; + if ( !data || typeof data !== "string" ) { + return null; + } + + // Support: IE 9 - 11 only + // IE throws on parseFromString with invalid input. + try { + xml = ( new window.DOMParser() ).parseFromString( data, "text/xml" ); + } catch ( e ) { + xml = undefined; + } + + if ( !xml || xml.getElementsByTagName( "parsererror" ).length ) { + jQuery.error( "Invalid XML: " + data ); + } + return xml; +}; + + +var + rbracket = /\[\]$/, + rCRLF = /\r?\n/g, + rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i, + rsubmittable = /^(?:input|select|textarea|keygen)/i; + +function buildParams( prefix, obj, traditional, add ) { + var name; + + if ( Array.isArray( obj ) ) { + + // Serialize array item. + jQuery.each( obj, function( i, v ) { + if ( traditional || rbracket.test( prefix ) ) { + + // Treat each array item as a scalar. + add( prefix, v ); + + } else { + + // Item is non-scalar (array or object), encode its numeric index. + buildParams( + prefix + "[" + ( typeof v === "object" && v != null ? i : "" ) + "]", + v, + traditional, + add + ); + } + } ); + + } else if ( !traditional && toType( obj ) === "object" ) { + + // Serialize object item. + for ( name in obj ) { + buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add ); + } + + } else { + + // Serialize scalar item. + add( prefix, obj ); + } +} + +// Serialize an array of form elements or a set of +// key/values into a query string +jQuery.param = function( a, traditional ) { + var prefix, + s = [], + add = function( key, valueOrFunction ) { + + // If value is a function, invoke it and use its return value + var value = isFunction( valueOrFunction ) ? + valueOrFunction() : + valueOrFunction; + + s[ s.length ] = encodeURIComponent( key ) + "=" + + encodeURIComponent( value == null ? "" : value ); + }; + + if ( a == null ) { + return ""; + } + + // If an array was passed in, assume that it is an array of form elements. + if ( Array.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) { + + // Serialize the form elements + jQuery.each( a, function() { + add( this.name, this.value ); + } ); + + } else { + + // If traditional, encode the "old" way (the way 1.3.2 or older + // did it), otherwise encode params recursively. + for ( prefix in a ) { + buildParams( prefix, a[ prefix ], traditional, add ); + } + } + + // Return the resulting serialization + return s.join( "&" ); +}; + +jQuery.fn.extend( { + serialize: function() { + return jQuery.param( this.serializeArray() ); + }, + serializeArray: function() { + return this.map( function() { + + // Can add propHook for "elements" to filter or add form elements + var elements = jQuery.prop( this, "elements" ); + return elements ? jQuery.makeArray( elements ) : this; + } ) + .filter( function() { + var type = this.type; + + // Use .is( ":disabled" ) so that fieldset[disabled] works + return this.name && !jQuery( this ).is( ":disabled" ) && + rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) && + ( this.checked || !rcheckableType.test( type ) ); + } ) + .map( function( i, elem ) { + var val = jQuery( this ).val(); + + if ( val == null ) { + return null; + } + + if ( Array.isArray( val ) ) { + return jQuery.map( val, function( val ) { + return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + } ); + } + + return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + } ).get(); + } +} ); + + +var + r20 = /%20/g, + rhash = /#.*$/, + rantiCache = /([?&])_=[^&]*/, + rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg, + + // #7653, #8125, #8152: local protocol detection + rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/, + rnoContent = /^(?:GET|HEAD)$/, + rprotocol = /^\/\//, + + /* Prefilters + * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example) + * 2) These are called: + * - BEFORE asking for a transport + * - AFTER param serialization (s.data is a string if s.processData is true) + * 3) key is the dataType + * 4) the catchall symbol "*" can be used + * 5) execution will start with transport dataType and THEN continue down to "*" if needed + */ + prefilters = {}, + + /* Transports bindings + * 1) key is the dataType + * 2) the catchall symbol "*" can be used + * 3) selection will start with transport dataType and THEN go to "*" if needed + */ + transports = {}, + + // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression + allTypes = "*/".concat( "*" ), + + // Anchor tag for parsing the document origin + originAnchor = document.createElement( "a" ); + originAnchor.href = location.href; + +// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport +function addToPrefiltersOrTransports( structure ) { + + // dataTypeExpression is optional and defaults to "*" + return function( dataTypeExpression, func ) { + + if ( typeof dataTypeExpression !== "string" ) { + func = dataTypeExpression; + dataTypeExpression = "*"; + } + + var dataType, + i = 0, + dataTypes = dataTypeExpression.toLowerCase().match( rnothtmlwhite ) || []; + + if ( isFunction( func ) ) { + + // For each dataType in the dataTypeExpression + while ( ( dataType = dataTypes[ i++ ] ) ) { + + // Prepend if requested + if ( dataType[ 0 ] === "+" ) { + dataType = dataType.slice( 1 ) || "*"; + ( structure[ dataType ] = structure[ dataType ] || [] ).unshift( func ); + + // Otherwise append + } else { + ( structure[ dataType ] = structure[ dataType ] || [] ).push( func ); + } + } + } + }; +} + +// Base inspection function for prefilters and transports +function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) { + + var inspected = {}, + seekingTransport = ( structure === transports ); + + function inspect( dataType ) { + var selected; + inspected[ dataType ] = true; + jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) { + var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR ); + if ( typeof dataTypeOrTransport === "string" && + !seekingTransport && !inspected[ dataTypeOrTransport ] ) { + + options.dataTypes.unshift( dataTypeOrTransport ); + inspect( dataTypeOrTransport ); + return false; + } else if ( seekingTransport ) { + return !( selected = dataTypeOrTransport ); + } + } ); + return selected; + } + + return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" ); +} + +// A special extend for ajax options +// that takes "flat" options (not to be deep extended) +// Fixes #9887 +function ajaxExtend( target, src ) { + var key, deep, + flatOptions = jQuery.ajaxSettings.flatOptions || {}; + + for ( key in src ) { + if ( src[ key ] !== undefined ) { + ( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ]; + } + } + if ( deep ) { + jQuery.extend( true, target, deep ); + } + + return target; +} + +/* Handles responses to an ajax request: + * - finds the right dataType (mediates between content-type and expected dataType) + * - returns the corresponding response + */ +function ajaxHandleResponses( s, jqXHR, responses ) { + + var ct, type, finalDataType, firstDataType, + contents = s.contents, + dataTypes = s.dataTypes; + + // Remove auto dataType and get content-type in the process + while ( dataTypes[ 0 ] === "*" ) { + dataTypes.shift(); + if ( ct === undefined ) { + ct = s.mimeType || jqXHR.getResponseHeader( "Content-Type" ); + } + } + + // Check if we're dealing with a known content-type + if ( ct ) { + for ( type in contents ) { + if ( contents[ type ] && contents[ type ].test( ct ) ) { + dataTypes.unshift( type ); + break; + } + } + } + + // Check to see if we have a response for the expected dataType + if ( dataTypes[ 0 ] in responses ) { + finalDataType = dataTypes[ 0 ]; + } else { + + // Try convertible dataTypes + for ( type in responses ) { + if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[ 0 ] ] ) { + finalDataType = type; + break; + } + if ( !firstDataType ) { + firstDataType = type; + } + } + + // Or just use first one + finalDataType = finalDataType || firstDataType; + } + + // If we found a dataType + // We add the dataType to the list if needed + // and return the corresponding response + if ( finalDataType ) { + if ( finalDataType !== dataTypes[ 0 ] ) { + dataTypes.unshift( finalDataType ); + } + return responses[ finalDataType ]; + } +} + +/* Chain conversions given the request and the original response + * Also sets the responseXXX fields on the jqXHR instance + */ +function ajaxConvert( s, response, jqXHR, isSuccess ) { + var conv2, current, conv, tmp, prev, + converters = {}, + + // Work with a copy of dataTypes in case we need to modify it for conversion + dataTypes = s.dataTypes.slice(); + + // Create converters map with lowercased keys + if ( dataTypes[ 1 ] ) { + for ( conv in s.converters ) { + converters[ conv.toLowerCase() ] = s.converters[ conv ]; + } + } + + current = dataTypes.shift(); + + // Convert to each sequential dataType + while ( current ) { + + if ( s.responseFields[ current ] ) { + jqXHR[ s.responseFields[ current ] ] = response; + } + + // Apply the dataFilter if provided + if ( !prev && isSuccess && s.dataFilter ) { + response = s.dataFilter( response, s.dataType ); + } + + prev = current; + current = dataTypes.shift(); + + if ( current ) { + + // There's only work to do if current dataType is non-auto + if ( current === "*" ) { + + current = prev; + + // Convert response if prev dataType is non-auto and differs from current + } else if ( prev !== "*" && prev !== current ) { + + // Seek a direct converter + conv = converters[ prev + " " + current ] || converters[ "* " + current ]; + + // If none found, seek a pair + if ( !conv ) { + for ( conv2 in converters ) { + + // If conv2 outputs current + tmp = conv2.split( " " ); + if ( tmp[ 1 ] === current ) { + + // If prev can be converted to accepted input + conv = converters[ prev + " " + tmp[ 0 ] ] || + converters[ "* " + tmp[ 0 ] ]; + if ( conv ) { + + // Condense equivalence converters + if ( conv === true ) { + conv = converters[ conv2 ]; + + // Otherwise, insert the intermediate dataType + } else if ( converters[ conv2 ] !== true ) { + current = tmp[ 0 ]; + dataTypes.unshift( tmp[ 1 ] ); + } + break; + } + } + } + } + + // Apply converter (if not an equivalence) + if ( conv !== true ) { + + // Unless errors are allowed to bubble, catch and return them + if ( conv && s.throws ) { + response = conv( response ); + } else { + try { + response = conv( response ); + } catch ( e ) { + return { + state: "parsererror", + error: conv ? e : "No conversion from " + prev + " to " + current + }; + } + } + } + } + } + } + + return { state: "success", data: response }; +} + +jQuery.extend( { + + // Counter for holding the number of active queries + active: 0, + + // Last-Modified header cache for next request + lastModified: {}, + etag: {}, + + ajaxSettings: { + url: location.href, + type: "GET", + isLocal: rlocalProtocol.test( location.protocol ), + global: true, + processData: true, + async: true, + contentType: "application/x-www-form-urlencoded; charset=UTF-8", + + /* + timeout: 0, + data: null, + dataType: null, + username: null, + password: null, + cache: null, + throws: false, + traditional: false, + headers: {}, + */ + + accepts: { + "*": allTypes, + text: "text/plain", + html: "text/html", + xml: "application/xml, text/xml", + json: "application/json, text/javascript" + }, + + contents: { + xml: /\bxml\b/, + html: /\bhtml/, + json: /\bjson\b/ + }, + + responseFields: { + xml: "responseXML", + text: "responseText", + json: "responseJSON" + }, + + // Data converters + // Keys separate source (or catchall "*") and destination types with a single space + converters: { + + // Convert anything to text + "* text": String, + + // Text to html (true = no transformation) + "text html": true, + + // Evaluate text as a json expression + "text json": JSON.parse, + + // Parse text as xml + "text xml": jQuery.parseXML + }, + + // For options that shouldn't be deep extended: + // you can add your own custom options here if + // and when you create one that shouldn't be + // deep extended (see ajaxExtend) + flatOptions: { + url: true, + context: true + } + }, + + // Creates a full fledged settings object into target + // with both ajaxSettings and settings fields. + // If target is omitted, writes into ajaxSettings. + ajaxSetup: function( target, settings ) { + return settings ? + + // Building a settings object + ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) : + + // Extending ajaxSettings + ajaxExtend( jQuery.ajaxSettings, target ); + }, + + ajaxPrefilter: addToPrefiltersOrTransports( prefilters ), + ajaxTransport: addToPrefiltersOrTransports( transports ), + + // Main method + ajax: function( url, options ) { + + // If url is an object, simulate pre-1.5 signature + if ( typeof url === "object" ) { + options = url; + url = undefined; + } + + // Force options to be an object + options = options || {}; + + var transport, + + // URL without anti-cache param + cacheURL, + + // Response headers + responseHeadersString, + responseHeaders, + + // timeout handle + timeoutTimer, + + // Url cleanup var + urlAnchor, + + // Request state (becomes false upon send and true upon completion) + completed, + + // To know if global events are to be dispatched + fireGlobals, + + // Loop variable + i, + + // uncached part of the url + uncached, + + // Create the final options object + s = jQuery.ajaxSetup( {}, options ), + + // Callbacks context + callbackContext = s.context || s, + + // Context for global events is callbackContext if it is a DOM node or jQuery collection + globalEventContext = s.context && + ( callbackContext.nodeType || callbackContext.jquery ) ? + jQuery( callbackContext ) : + jQuery.event, + + // Deferreds + deferred = jQuery.Deferred(), + completeDeferred = jQuery.Callbacks( "once memory" ), + + // Status-dependent callbacks + statusCode = s.statusCode || {}, + + // Headers (they are sent all at once) + requestHeaders = {}, + requestHeadersNames = {}, + + // Default abort message + strAbort = "canceled", + + // Fake xhr + jqXHR = { + readyState: 0, + + // Builds headers hashtable if needed + getResponseHeader: function( key ) { + var match; + if ( completed ) { + if ( !responseHeaders ) { + responseHeaders = {}; + while ( ( match = rheaders.exec( responseHeadersString ) ) ) { + responseHeaders[ match[ 1 ].toLowerCase() + " " ] = + ( responseHeaders[ match[ 1 ].toLowerCase() + " " ] || [] ) + .concat( match[ 2 ] ); + } + } + match = responseHeaders[ key.toLowerCase() + " " ]; + } + return match == null ? null : match.join( ", " ); + }, + + // Raw string + getAllResponseHeaders: function() { + return completed ? responseHeadersString : null; + }, + + // Caches the header + setRequestHeader: function( name, value ) { + if ( completed == null ) { + name = requestHeadersNames[ name.toLowerCase() ] = + requestHeadersNames[ name.toLowerCase() ] || name; + requestHeaders[ name ] = value; + } + return this; + }, + + // Overrides response content-type header + overrideMimeType: function( type ) { + if ( completed == null ) { + s.mimeType = type; + } + return this; + }, + + // Status-dependent callbacks + statusCode: function( map ) { + var code; + if ( map ) { + if ( completed ) { + + // Execute the appropriate callbacks + jqXHR.always( map[ jqXHR.status ] ); + } else { + + // Lazy-add the new callbacks in a way that preserves old ones + for ( code in map ) { + statusCode[ code ] = [ statusCode[ code ], map[ code ] ]; + } + } + } + return this; + }, + + // Cancel the request + abort: function( statusText ) { + var finalText = statusText || strAbort; + if ( transport ) { + transport.abort( finalText ); + } + done( 0, finalText ); + return this; + } + }; + + // Attach deferreds + deferred.promise( jqXHR ); + + // Add protocol if not provided (prefilters might expect it) + // Handle falsy url in the settings object (#10093: consistency with old signature) + // We also use the url parameter if available + s.url = ( ( url || s.url || location.href ) + "" ) + .replace( rprotocol, location.protocol + "//" ); + + // Alias method option to type as per ticket #12004 + s.type = options.method || options.type || s.method || s.type; + + // Extract dataTypes list + s.dataTypes = ( s.dataType || "*" ).toLowerCase().match( rnothtmlwhite ) || [ "" ]; + + // A cross-domain request is in order when the origin doesn't match the current origin. + if ( s.crossDomain == null ) { + urlAnchor = document.createElement( "a" ); + + // Support: IE <=8 - 11, Edge 12 - 15 + // IE throws exception on accessing the href property if url is malformed, + // e.g. http://example.com:80x/ + try { + urlAnchor.href = s.url; + + // Support: IE <=8 - 11 only + // Anchor's host property isn't correctly set when s.url is relative + urlAnchor.href = urlAnchor.href; + s.crossDomain = originAnchor.protocol + "//" + originAnchor.host !== + urlAnchor.protocol + "//" + urlAnchor.host; + } catch ( e ) { + + // If there is an error parsing the URL, assume it is crossDomain, + // it can be rejected by the transport if it is invalid + s.crossDomain = true; + } + } + + // Convert data if not already a string + if ( s.data && s.processData && typeof s.data !== "string" ) { + s.data = jQuery.param( s.data, s.traditional ); + } + + // Apply prefilters + inspectPrefiltersOrTransports( prefilters, s, options, jqXHR ); + + // If request was aborted inside a prefilter, stop there + if ( completed ) { + return jqXHR; + } + + // We can fire global events as of now if asked to + // Don't fire events if jQuery.event is undefined in an AMD-usage scenario (#15118) + fireGlobals = jQuery.event && s.global; + + // Watch for a new set of requests + if ( fireGlobals && jQuery.active++ === 0 ) { + jQuery.event.trigger( "ajaxStart" ); + } + + // Uppercase the type + s.type = s.type.toUpperCase(); + + // Determine if request has content + s.hasContent = !rnoContent.test( s.type ); + + // Save the URL in case we're toying with the If-Modified-Since + // and/or If-None-Match header later on + // Remove hash to simplify url manipulation + cacheURL = s.url.replace( rhash, "" ); + + // More options handling for requests with no content + if ( !s.hasContent ) { + + // Remember the hash so we can put it back + uncached = s.url.slice( cacheURL.length ); + + // If data is available and should be processed, append data to url + if ( s.data && ( s.processData || typeof s.data === "string" ) ) { + cacheURL += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data; + + // #9682: remove data so that it's not used in an eventual retry + delete s.data; + } + + // Add or update anti-cache param if needed + if ( s.cache === false ) { + cacheURL = cacheURL.replace( rantiCache, "$1" ); + uncached = ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ( nonce++ ) + uncached; + } + + // Put hash and anti-cache on the URL that will be requested (gh-1732) + s.url = cacheURL + uncached; + + // Change '%20' to '+' if this is encoded form body content (gh-2658) + } else if ( s.data && s.processData && + ( s.contentType || "" ).indexOf( "application/x-www-form-urlencoded" ) === 0 ) { + s.data = s.data.replace( r20, "+" ); + } + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + if ( jQuery.lastModified[ cacheURL ] ) { + jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] ); + } + if ( jQuery.etag[ cacheURL ] ) { + jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] ); + } + } + + // Set the correct header, if data is being sent + if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) { + jqXHR.setRequestHeader( "Content-Type", s.contentType ); + } + + // Set the Accepts header for the server, depending on the dataType + jqXHR.setRequestHeader( + "Accept", + s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[ 0 ] ] ? + s.accepts[ s.dataTypes[ 0 ] ] + + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) : + s.accepts[ "*" ] + ); + + // Check for headers option + for ( i in s.headers ) { + jqXHR.setRequestHeader( i, s.headers[ i ] ); + } + + // Allow custom headers/mimetypes and early abort + if ( s.beforeSend && + ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || completed ) ) { + + // Abort if not done already and return + return jqXHR.abort(); + } + + // Aborting is no longer a cancellation + strAbort = "abort"; + + // Install callbacks on deferreds + completeDeferred.add( s.complete ); + jqXHR.done( s.success ); + jqXHR.fail( s.error ); + + // Get transport + transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR ); + + // If no transport, we auto-abort + if ( !transport ) { + done( -1, "No Transport" ); + } else { + jqXHR.readyState = 1; + + // Send global event + if ( fireGlobals ) { + globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] ); + } + + // If request was aborted inside ajaxSend, stop there + if ( completed ) { + return jqXHR; + } + + // Timeout + if ( s.async && s.timeout > 0 ) { + timeoutTimer = window.setTimeout( function() { + jqXHR.abort( "timeout" ); + }, s.timeout ); + } + + try { + completed = false; + transport.send( requestHeaders, done ); + } catch ( e ) { + + // Rethrow post-completion exceptions + if ( completed ) { + throw e; + } + + // Propagate others as results + done( -1, e ); + } + } + + // Callback for when everything is done + function done( status, nativeStatusText, responses, headers ) { + var isSuccess, success, error, response, modified, + statusText = nativeStatusText; + + // Ignore repeat invocations + if ( completed ) { + return; + } + + completed = true; + + // Clear timeout if it exists + if ( timeoutTimer ) { + window.clearTimeout( timeoutTimer ); + } + + // Dereference transport for early garbage collection + // (no matter how long the jqXHR object will be used) + transport = undefined; + + // Cache response headers + responseHeadersString = headers || ""; + + // Set readyState + jqXHR.readyState = status > 0 ? 4 : 0; + + // Determine if successful + isSuccess = status >= 200 && status < 300 || status === 304; + + // Get response data + if ( responses ) { + response = ajaxHandleResponses( s, jqXHR, responses ); + } + + // Convert no matter what (that way responseXXX fields are always set) + response = ajaxConvert( s, response, jqXHR, isSuccess ); + + // If successful, handle type chaining + if ( isSuccess ) { + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + modified = jqXHR.getResponseHeader( "Last-Modified" ); + if ( modified ) { + jQuery.lastModified[ cacheURL ] = modified; + } + modified = jqXHR.getResponseHeader( "etag" ); + if ( modified ) { + jQuery.etag[ cacheURL ] = modified; + } + } + + // if no content + if ( status === 204 || s.type === "HEAD" ) { + statusText = "nocontent"; + + // if not modified + } else if ( status === 304 ) { + statusText = "notmodified"; + + // If we have data, let's convert it + } else { + statusText = response.state; + success = response.data; + error = response.error; + isSuccess = !error; + } + } else { + + // Extract error from statusText and normalize for non-aborts + error = statusText; + if ( status || !statusText ) { + statusText = "error"; + if ( status < 0 ) { + status = 0; + } + } + } + + // Set data for the fake xhr object + jqXHR.status = status; + jqXHR.statusText = ( nativeStatusText || statusText ) + ""; + + // Success/Error + if ( isSuccess ) { + deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] ); + } else { + deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] ); + } + + // Status-dependent callbacks + jqXHR.statusCode( statusCode ); + statusCode = undefined; + + if ( fireGlobals ) { + globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError", + [ jqXHR, s, isSuccess ? success : error ] ); + } + + // Complete + completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] ); + + if ( fireGlobals ) { + globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] ); + + // Handle the global AJAX counter + if ( !( --jQuery.active ) ) { + jQuery.event.trigger( "ajaxStop" ); + } + } + } + + return jqXHR; + }, + + getJSON: function( url, data, callback ) { + return jQuery.get( url, data, callback, "json" ); + }, + + getScript: function( url, callback ) { + return jQuery.get( url, undefined, callback, "script" ); + } +} ); + +jQuery.each( [ "get", "post" ], function( i, method ) { + jQuery[ method ] = function( url, data, callback, type ) { + + // Shift arguments if data argument was omitted + if ( isFunction( data ) ) { + type = type || callback; + callback = data; + data = undefined; + } + + // The url can be an options object (which then must have .url) + return jQuery.ajax( jQuery.extend( { + url: url, + type: method, + dataType: type, + data: data, + success: callback + }, jQuery.isPlainObject( url ) && url ) ); + }; +} ); + + +jQuery._evalUrl = function( url, options ) { + return jQuery.ajax( { + url: url, + + // Make this explicit, since user can override this through ajaxSetup (#11264) + type: "GET", + dataType: "script", + cache: true, + async: false, + global: false, + + // Only evaluate the response if it is successful (gh-4126) + // dataFilter is not invoked for failure responses, so using it instead + // of the default converter is kludgy but it works. + converters: { + "text script": function() {} + }, + dataFilter: function( response ) { + jQuery.globalEval( response, options ); + } + } ); +}; + + +jQuery.fn.extend( { + wrapAll: function( html ) { + var wrap; + + if ( this[ 0 ] ) { + if ( isFunction( html ) ) { + html = html.call( this[ 0 ] ); + } + + // The elements to wrap the target around + wrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true ); + + if ( this[ 0 ].parentNode ) { + wrap.insertBefore( this[ 0 ] ); + } + + wrap.map( function() { + var elem = this; + + while ( elem.firstElementChild ) { + elem = elem.firstElementChild; + } + + return elem; + } ).append( this ); + } + + return this; + }, + + wrapInner: function( html ) { + if ( isFunction( html ) ) { + return this.each( function( i ) { + jQuery( this ).wrapInner( html.call( this, i ) ); + } ); + } + + return this.each( function() { + var self = jQuery( this ), + contents = self.contents(); + + if ( contents.length ) { + contents.wrapAll( html ); + + } else { + self.append( html ); + } + } ); + }, + + wrap: function( html ) { + var htmlIsFunction = isFunction( html ); + + return this.each( function( i ) { + jQuery( this ).wrapAll( htmlIsFunction ? html.call( this, i ) : html ); + } ); + }, + + unwrap: function( selector ) { + this.parent( selector ).not( "body" ).each( function() { + jQuery( this ).replaceWith( this.childNodes ); + } ); + return this; + } +} ); + + +jQuery.expr.pseudos.hidden = function( elem ) { + return !jQuery.expr.pseudos.visible( elem ); +}; +jQuery.expr.pseudos.visible = function( elem ) { + return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length ); +}; + + + + +jQuery.ajaxSettings.xhr = function() { + try { + return new window.XMLHttpRequest(); + } catch ( e ) {} +}; + +var xhrSuccessStatus = { + + // File protocol always yields status code 0, assume 200 + 0: 200, + + // Support: IE <=9 only + // #1450: sometimes IE returns 1223 when it should be 204 + 1223: 204 + }, + xhrSupported = jQuery.ajaxSettings.xhr(); + +support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported ); +support.ajax = xhrSupported = !!xhrSupported; + +jQuery.ajaxTransport( function( options ) { + var callback, errorCallback; + + // Cross domain only allowed if supported through XMLHttpRequest + if ( support.cors || xhrSupported && !options.crossDomain ) { + return { + send: function( headers, complete ) { + var i, + xhr = options.xhr(); + + xhr.open( + options.type, + options.url, + options.async, + options.username, + options.password + ); + + // Apply custom fields if provided + if ( options.xhrFields ) { + for ( i in options.xhrFields ) { + xhr[ i ] = options.xhrFields[ i ]; + } + } + + // Override mime type if needed + if ( options.mimeType && xhr.overrideMimeType ) { + xhr.overrideMimeType( options.mimeType ); + } + + // X-Requested-With header + // For cross-domain requests, seeing as conditions for a preflight are + // akin to a jigsaw puzzle, we simply never set it to be sure. + // (it can always be set on a per-request basis or even using ajaxSetup) + // For same-domain requests, won't change header if already provided. + if ( !options.crossDomain && !headers[ "X-Requested-With" ] ) { + headers[ "X-Requested-With" ] = "XMLHttpRequest"; + } + + // Set headers + for ( i in headers ) { + xhr.setRequestHeader( i, headers[ i ] ); + } + + // Callback + callback = function( type ) { + return function() { + if ( callback ) { + callback = errorCallback = xhr.onload = + xhr.onerror = xhr.onabort = xhr.ontimeout = + xhr.onreadystatechange = null; + + if ( type === "abort" ) { + xhr.abort(); + } else if ( type === "error" ) { + + // Support: IE <=9 only + // On a manual native abort, IE9 throws + // errors on any property access that is not readyState + if ( typeof xhr.status !== "number" ) { + complete( 0, "error" ); + } else { + complete( + + // File: protocol always yields status 0; see #8605, #14207 + xhr.status, + xhr.statusText + ); + } + } else { + complete( + xhrSuccessStatus[ xhr.status ] || xhr.status, + xhr.statusText, + + // Support: IE <=9 only + // IE9 has no XHR2 but throws on binary (trac-11426) + // For XHR2 non-text, let the caller handle it (gh-2498) + ( xhr.responseType || "text" ) !== "text" || + typeof xhr.responseText !== "string" ? + { binary: xhr.response } : + { text: xhr.responseText }, + xhr.getAllResponseHeaders() + ); + } + } + }; + }; + + // Listen to events + xhr.onload = callback(); + errorCallback = xhr.onerror = xhr.ontimeout = callback( "error" ); + + // Support: IE 9 only + // Use onreadystatechange to replace onabort + // to handle uncaught aborts + if ( xhr.onabort !== undefined ) { + xhr.onabort = errorCallback; + } else { + xhr.onreadystatechange = function() { + + // Check readyState before timeout as it changes + if ( xhr.readyState === 4 ) { + + // Allow onerror to be called first, + // but that will not handle a native abort + // Also, save errorCallback to a variable + // as xhr.onerror cannot be accessed + window.setTimeout( function() { + if ( callback ) { + errorCallback(); + } + } ); + } + }; + } + + // Create the abort callback + callback = callback( "abort" ); + + try { + + // Do send the request (this may raise an exception) + xhr.send( options.hasContent && options.data || null ); + } catch ( e ) { + + // #14683: Only rethrow if this hasn't been notified as an error yet + if ( callback ) { + throw e; + } + } + }, + + abort: function() { + if ( callback ) { + callback(); + } + } + }; + } +} ); + + + + +// Prevent auto-execution of scripts when no explicit dataType was provided (See gh-2432) +jQuery.ajaxPrefilter( function( s ) { + if ( s.crossDomain ) { + s.contents.script = false; + } +} ); + +// Install script dataType +jQuery.ajaxSetup( { + accepts: { + script: "text/javascript, application/javascript, " + + "application/ecmascript, application/x-ecmascript" + }, + contents: { + script: /\b(?:java|ecma)script\b/ + }, + converters: { + "text script": function( text ) { + jQuery.globalEval( text ); + return text; + } + } +} ); + +// Handle cache's special case and crossDomain +jQuery.ajaxPrefilter( "script", function( s ) { + if ( s.cache === undefined ) { + s.cache = false; + } + if ( s.crossDomain ) { + s.type = "GET"; + } +} ); + +// Bind script tag hack transport +jQuery.ajaxTransport( "script", function( s ) { + + // This transport only deals with cross domain or forced-by-attrs requests + if ( s.crossDomain || s.scriptAttrs ) { + var script, callback; + return { + send: function( _, complete ) { + script = jQuery( " + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +
+

API documentation

+

This section contains auto-generated API documentation for AppTools.

+ +
+ + +
+
+
+
+
+ +

Previous topic

+

File I/O

+

Next topic

+

apptools

+

This Page

+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/api/apptools.html b/api/apptools.html new file mode 100644 index 000000000..1bf597fd6 --- /dev/null +++ b/api/apptools.html @@ -0,0 +1,367 @@ + + + + + + + apptools package — Apptools Documentation + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +
+

apptools package

+
+

Subpackages

+
+ +
+
+
+

Module contents

+
+
+ + +
+
+
+
+
+ +

Table of Contents

+ + +

Previous topic

+

apptools

+

Next topic

+

apptools.io package

+

This Page

+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/api/apptools.io.h5.html b/api/apptools.io.h5.html new file mode 100644 index 000000000..4a6f98d18 --- /dev/null +++ b/api/apptools.io.h5.html @@ -0,0 +1,705 @@ + + + + + + + apptools.io.h5 package — Apptools Documentation + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+ +
+ + +
+
+ +
+
+ +
+

apptools.io.h5 package

+
+

Submodules

+
+
+

apptools.io.h5.dict_node module

+
+
+apptools.io.h5.dict_node.ARRAY_PROXY_KEY = '__array__'
+

The key name which identifies array objects in the JSON dict.

+
+ +
+
+class apptools.io.h5.dict_node.H5DictNode(h5_group, auto_flush=True)[source]
+

Bases: object

+

Dictionary-like node interface.

+

Data for the dict is stored as a JSON file in a PyTables FileNode. This +allows easy storage of Python objects, such as dictionaries and lists of +different data types.

+

Note that this is implemented using a group-node assuming that arrays are +valid inputs and will be stored as H5 array nodes.

+
+
Parameters
+
    +
  • h5_group (H5Group instance) – Group node which will be used as a dictionary store.

  • +
  • auto_flush (bool) – If True, write data to disk whenever the dict data is altered. +Otherwise, call flush() explicitly to write data to disk.

  • +
+
+
+
+
+classmethod add_to_h5file(h5, node_path, data=None, **kwargs)[source]
+

Add dict node to an H5 file at the specified path.

+
+
Parameters
+
    +
  • h5 (H5File) – The H5 file where the dictionary data will be stored.

  • +
  • node_path (str) – Path to node where data is stored (e.g. ‘/path/to/my_dict’)

  • +
  • data (dict) – Data for initialization, if desired.

  • +
+
+
+
+ +
+
+property data
+
+ +
+
+flush()[source]
+

Write buffered data to disk.

+
+ +
+
+classmethod is_dict_node(pytables_node)[source]
+

Return True if PyTables node looks like an H5DictNode.

+

NOTE: That this returns False if the node is an H5DictNode instance, +since the input node should be a normal PyTables Group node.

+
+ +
+
+keys()[source]
+
+ +
+ +
+
+

apptools.io.h5.file module

+
+
+class apptools.io.h5.file.H5Attrs(node_attrs)[source]
+

Bases: collections.abc.MutableMapping

+

An attributes dictionary for an h5 node.

+

This intercepts __setitem__ so that python sequences can be converted to +numpy arrays. This helps preserve the readability of our HDF5 files by +other (non-python) programs.

+
+
+get(k[, d]) → D[k] if k in D, else d. d defaults to None.[source]
+
+ +
+
+items() → a set-like object providing a view on D's items[source]
+
+ +
+
+keys() → a set-like object providing a view on D's keys[source]
+
+ +
+
+values() → an object providing a view on D's values[source]
+
+ +
+ +
+
+class apptools.io.h5.file.H5File(filename, mode='r+', delete_existing=False, auto_groups=True, auto_open=True, h5filters=None)[source]
+

Bases: collections.abc.Mapping

+

File object for HDF5 files.

+

This class wraps PyTables to provide a cleaner, but only implements an +interface for accessing arrays.

+
+
Parameters
+
    +
  • filename (str or a tables.File instance) – Filename for an HDF5 file, or a PyTables File object.

  • +
  • mode (str) –

    Mode to open the file:

    +
    +

    ’r’ : Read-only +‘w’ : Write; create new file (an existing file would be deleted). +‘a’ : Read and write to file; create if not existing +‘r+’: Read and write to file; must already exist

    +
    +

  • +
  • delete_existing (bool) – If True, an existing node will be deleted when a create_* method is +called. Otherwise, a ValueError will be raise.

  • +
  • auto_groups (bool) – If True, create_array will automatically create parent groups.

  • +
  • auto_open (bool) – If True, open the file automatically on initialization. Otherwise, +you can call H5File.open() explicitly after initialization.

  • +
  • chunked (bool) – If True, the default behavior of create_array will be a chunked +array (see PyTables create_carray).

  • +
+
+
+
+
+close()[source]
+
+ +
+
+create_array(node_path, array_or_shape, dtype=None, chunked=False, extendable=False, **kwargs)[source]
+

Create node to store an array.

+
+
Parameters
+
    +
  • node_path (str) – PyTable node path; e.g. ‘/path/to/node’.

  • +
  • array_or_shape (array or shape tuple) – Array or shape tuple for an array. If given a shape tuple, the +dtype parameter must also specified.

  • +
  • dtype (str or numpy.dtype) – Data type of array. Only necessary if array_or_shape is a shape.

  • +
  • chunked (bool) – Controls whether the array is chunked.

  • +
  • extendable ({None | bool}) – Controls whether the array is extendable.

  • +
  • kwargs (key/value pairs) – Keyword args passed to PyTables File.create_(c|e)array.

  • +
+
+
+
+ +
+
+create_dict(node_path, data=None, **kwargs)[source]
+

Create dict node at the specified path.

+
+
Parameters
+
    +
  • node_path (str) – Path to node where data is stored (e.g. ‘/path/to/my_dict’)

  • +
  • data (dict) – Data for initialization, if desired.

  • +
+
+
+
+ +
+
+create_group(group_path, **kwargs)[source]
+

Create group.

+
+
Parameters
+
    +
  • group_path (str) – PyTable group path; e.g. ‘/path/to/group’.

  • +
  • kwargs (key/value pairs) – Keyword args passed to PyTables File.create_group.

  • +
+
+
+
+ +
+
+create_table(node_path, description, **kwargs)[source]
+

Create table node at the specified path.

+
+
Parameters
+
    +
  • node_path (str) – Path to node where data is stored (e.g. ‘/path/to/my_dict’)

  • +
  • description (dict or numpy dtype object) – The description of the columns in the table. This is either a dict +of column name -> dtype items or a numpy record array dtype. For +more information, see the documentation for Table in pytables.

  • +
+
+
+
+ +
+
+exists_error = "'{}' exists in '{}'; set `delete_existing` attribute to True to overwrite existing calculations."
+
+ +
+
+property is_open
+
+ +
+
+iteritems(path='/')[source]
+

Iterate over node paths and nodes of the h5 file.

+
+ +
+
+classmethod join_path(*args)[source]
+

Join parts of an h5 path.

+

For example, the 3 argmuments ‘path’, ‘to’, ‘node’ will return +‘/path/to/node’.

+
+
Parameters
+

args (str) – Parts of path to be joined.

+
+
+
+ +
+
+open()[source]
+
+ +
+
+remove_group(group_path, **kwargs)[source]
+

Remove group

+
+
Parameters
+

group_path (str) – PyTable group path; e.g. ‘/path/to/group’.

+
+
+
+ +
+
+remove_node(node_path)[source]
+

Remove node

+
+
Parameters
+

node_path (str) – PyTable node path; e.g. ‘/path/to/node’.

+
+
+
+ +
+
+property root
+
+ +
+
+classmethod split_path(node_path)[source]
+

Split node path returning the base path and node name.

+

For example: ‘/path/to/node’ will return ‘/path/to’ and ‘node’

+
+
Parameters
+

node_path (str) – PyTable node path; e.g. ‘/path/to/node’.

+
+
+
+ +
+ +
+
+class apptools.io.h5.file.H5Group(pytables_group)[source]
+

Bases: collections.abc.Mapping

+

A group node in an H5File.

+

This is a thin wrapper around PyTables’ Group object to expose attributes +and maintain the dict interface of H5File.

+
+
+property children_names
+
+ +
+
+create_array(node_subpath, array_or_shape, dtype=None, chunked=False, extendable=False, **kwargs)[source]
+

** H5Group wrapper for H5File.create_array: ** +Note that the first argument is a nodepath relative to the group, rather than +an absolute path. Below is the original docstring:

+

Create node to store an array.

+
+
Parameters
+
    +
  • node_path (str) – PyTable node path; e.g. ‘/path/to/node’.

  • +
  • array_or_shape (array or shape tuple) – Array or shape tuple for an array. If given a shape tuple, the +dtype parameter must also specified.

  • +
  • dtype (str or numpy.dtype) – Data type of array. Only necessary if array_or_shape is a shape.

  • +
  • chunked (bool) – Controls whether the array is chunked.

  • +
  • extendable ({None | bool}) – Controls whether the array is extendable.

  • +
  • kwargs (key/value pairs) – Keyword args passed to PyTables File.create_(c|e)array.

  • +
+
+
+
+ +
+
+create_dict(node_subpath, data=None, **kwargs)[source]
+

** H5Group wrapper for H5File.create_dict: ** +Note that the first argument is a nodepath relative to the group, rather than +an absolute path. Below is the original docstring:

+

Create dict node at the specified path.

+
+
Parameters
+
    +
  • node_path (str) – Path to node where data is stored (e.g. ‘/path/to/my_dict’)

  • +
  • data (dict) – Data for initialization, if desired.

  • +
+
+
+
+ +
+
+create_group(group_subpath, delete_existing=False, **kwargs)[source]
+

** H5Group wrapper for H5File.create_group: ** +Note that the first argument is a nodepath relative to the group, rather than +an absolute path. Below is the original docstring:

+

Create group.

+
+
Parameters
+
    +
  • group_path (str) – PyTable group path; e.g. ‘/path/to/group’.

  • +
  • kwargs (key/value pairs) – Keyword args passed to PyTables File.create_group.

  • +
+
+
+
+ +
+
+create_table(node_subpath, description, *args, **kwargs)[source]
+

** H5Group wrapper for H5File.create_table: ** +Note that the first argument is a nodepath relative to the group, rather than +an absolute path. Below is the original docstring:

+

Create table node at the specified path.

+
+
Parameters
+
    +
  • node_path (str) – Path to node where data is stored (e.g. ‘/path/to/my_dict’)

  • +
  • description (dict or numpy dtype object) – The description of the columns in the table. This is either a dict +of column name -> dtype items or a numpy record array dtype. For +more information, see the documentation for Table in pytables.

  • +
+
+
+
+ +
+
+property filename
+
+ +
+
+iter_groups()[source]
+

Iterate over H5Group nodes that are children of this group.

+
+ +
+
+property name
+
+ +
+
+property pathname
+
+ +
+
+remove_group(group_subpath, **kwargs)[source]
+

** H5Group wrapper for H5File.remove_group: ** +Note that the first argument is a nodepath relative to the group, rather than +an absolute path. Below is the original docstring:

+

Remove group

+
+
Parameters
+

group_path (str) – PyTable group path; e.g. ‘/path/to/group’.

+
+
+
+ +
+
+remove_node(node_subpath, **kwargs)[source]
+

** H5Group wrapper for H5File.remove_node: ** +Note that the first argument is a nodepath relative to the group, rather than +an absolute path. Below is the original docstring:

+

Remove node

+
+
Parameters
+

node_path (str) – PyTable node path; e.g. ‘/path/to/node’.

+
+
+
+ +
+
+property root
+
+ +
+
+property subgroup_names
+
+ +
+ +
+
+apptools.io.h5.file.get_atom(dtype)[source]
+

Return a PyTables Atom for the given dtype or dtype string.

+
+ +
+
+apptools.io.h5.file.h5_group_wrapper(original)[source]
+
+ +
+
+apptools.io.h5.file.iterator_length(iterator)[source]
+
+ +
+
+

apptools.io.h5.table_node module

+
+
+class apptools.io.h5.table_node.H5TableNode(node)[source]
+

Bases: object

+

A wrapper for PyTables Table nodes.

+
+
Parameters
+

node (tables.Table instance) – An H5 node which is a pytables.Table or H5TableNode instance

+
+
+
+
+classmethod add_to_h5file(h5, node_path, description, **kwargs)[source]
+

Add table node to an H5 file at the specified path.

+
+
Parameters
+
    +
  • h5 (H5File) – The H5 file where the table node will be stored.

  • +
  • node_path (str) – Path to node where data is stored (e.g. ‘/path/to/my_table’)

  • +
  • description (list of tuples or numpy dtype object) – The description of the columns in the table. This is either a list +of (column name, dtype, [, shape or itemsize]) tuples or a numpy +record array dtype. For more information, see the documentation for +Table in PyTables.

  • +
  • **kwargs (dict) – Additional keyword arguments to pass to pytables.File.create_table

  • +
+
+
+
+ +
+
+append(data)[source]
+

Add some data to the table.

+
+
Parameters
+

data (dict) – A dictionary of column name -> values items

+
+
+
+ +
+
+classmethod is_table_node(pytables_node)[source]
+

Return True if pytables_node is a pytables.Table or a H5TableNode.

+
+ +
+
+property ix
+

Return an object which provides access to row data.

+
+ +
+
+keys()[source]
+
+ +
+
+to_dataframe()[source]
+

Return table data as a pandas DataFrame.

+

XXX: This does not work if the table contains a multidimensional column

+

This method requires pandas to have been installed in the environment.

+
+ +
+ +
+
+

apptools.io.h5.utils module

+
+
+apptools.io.h5.utils.open_h5file(filename, mode='r+', **kwargs)[source]
+

Context manager for reading an HDF5 file as an H5File object.

+
+
Parameters
+
    +
  • filename (str) – HDF5 file name.

  • +
  • mode (str) –

    Mode to open the file:

    +

    ’r’ : Read-only +‘w’ : Write; create new file (an existing file would be deleted). +‘a’ : Read and write to file; create if not existing +‘r+’: Read and write to file; must already exist

    +

  • +
  • H5File for additional keyword arguments. (See) –

  • +
+
+
+
+ +
+
+

Module contents

+
+
+ + +
+
+
+ +
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/api/apptools.io.html b/api/apptools.io.html new file mode 100644 index 000000000..fa1514e92 --- /dev/null +++ b/api/apptools.io.html @@ -0,0 +1,256 @@ + + + + + + + apptools.io package — Apptools Documentation + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +
+

apptools.io package

+ +
+

Submodules

+
+
+

apptools.io.api module

+
+
+

apptools.io.file module

+

A representation of files and folders in a file system.

+
+
+class apptools.io.file.File(path, **traits)[source]
+

Bases: traits.has_traits.HasPrivateTraits

+

A representation of files and folders in a file system.

+
+
+copy(destination)[source]
+

Copies this file/folder.

+
+ +
+
+create_file(contents='')[source]
+

Creates a file at this path.

+
+ +
+
+create_folder()[source]
+

Creates a folder at this path.

+

All intermediate folders MUST already exist.

+
+ +
+
+create_folders()[source]
+

Creates a folder at this path.

+

This will attempt to create any missing intermediate folders.

+
+ +
+
+create_package()[source]
+

Creates a package at this path.

+

All intermediate folders/packages MUST already exist.

+
+ +
+
+delete()[source]
+

Deletes this file/folder.

+

Does nothing if the file/folder does not exist.

+
+ +
+
+make_writeable()[source]
+

Attempt to make the file/folder writeable.

+
+ +
+
+move(destination)[source]
+

Moves this file/folder.

+
+ +
+ +
+
+

Module contents

+

Provides an abstraction for files and folders in a file system. +Part of the AppTools project of the Enthought Tool Suite.

+
+
+ + +
+
+
+
+
+ +

Table of Contents

+ + +

Previous topic

+

apptools package

+

Next topic

+

apptools.io.h5 package

+

This Page

+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/api/apptools.logger.agent.html b/api/apptools.logger.agent.html new file mode 100644 index 000000000..109f12f4f --- /dev/null +++ b/api/apptools.logger.agent.html @@ -0,0 +1,286 @@ + + + + + + + apptools.logger.agent package — Apptools Documentation + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+ +
+ + +
+
+ +
+
+ +
+

apptools.logger.agent package

+
+

Submodules

+
+
+

apptools.logger.agent.attachments module

+

Attach relevant project files.

+

FIXME: there are no public project plugins for Envisage 3, yet. In any case, +this stuff should not be hard-coded, but extensible via extension points. The +code remains here because we can reuse the zip utility code in that extensible +rewrite.

+
+
+class apptools.logger.agent.attachments.Attachments(message, **traits)[source]
+

Bases: traits.has_traits.HasTraits

+
+
+package_any_relevant_files()[source]
+
+ +
+
+package_single_project()[source]
+
+ +
+
+package_workspace()[source]
+
+ +
+ +
+
+

apptools.logger.agent.quality_agent_mailer module

+
+
+apptools.logger.agent.quality_agent_mailer.create_email_message(fromaddr, toaddrs, ccaddrs, subject, priority, include_project=False, stack_trace='', comments='')[source]
+
+ +
+
+

apptools.logger.agent.quality_agent_view module

+
+
+class apptools.logger.agent.quality_agent_view.QualityAgentView(*args, **kwargs)[source]
+

Bases: pyface.base_toolkit.Unimplemented

+
+
+cc_address = <traits.trait_types.Str object>
+
+ +
+
+comments = <traits.trait_types.Str object>
+
+ +
+
+from_address = <traits.trait_types.Str object>
+
+ +
+
+help_id = 'enlib|HID_Quality_Agent_Dlg'
+
+ +
+
+include_userdata
+

alias of traits.trait_types.Any

+
+ +
+
+msg = <traits.trait_types.Str object>
+
+ +
+
+priority = <traits.trait_types.Str object>
+
+ +
+
+service = <traits.trait_types.Any object>
+
+ +
+
+size = <traits.trait_types.Tuple object>
+
+ +
+
+smtp_server = <traits.trait_types.Str object>
+
+ +
+
+subject = <traits.trait_types.Str object>
+
+ +
+
+title = <traits.trait_types.Str object>
+
+ +
+
+to_address = <traits.trait_types.Str object>
+
+ +
+ +
+
+

Module contents

+

lib.apptools.logger.agent

+
+
+ + +
+
+
+ +
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/api/apptools.logger.html b/api/apptools.logger.html new file mode 100644 index 000000000..454905560 --- /dev/null +++ b/api/apptools.logger.html @@ -0,0 +1,323 @@ + + + + + + + apptools.logger package — Apptools Documentation + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +
+

apptools.logger package

+ +
+

Submodules

+
+
+

apptools.logger.api module

+
+
+

apptools.logger.custom_excepthook module

+
+
+apptools.logger.custom_excepthook.custom_excepthook(type, value, traceback)[source]
+

Pass on the exception to the logging system.

+
+ +
+
+

apptools.logger.log_point module

+

Prints a stack trace every time it is called but does not halt execution +of the application.

+

Copied from Uche Ogbuji’s blog

+
+
+apptools.logger.log_point.log_point(msg='\n')[source]
+
+ +
+
+

apptools.logger.log_queue_handler module

+
+
+class apptools.logger.log_queue_handler.LogQueueHandler(size=1000)[source]
+

Bases: logging.Handler

+

Buffers up the log messages so that we can display them later. +This is important on startup when log messages are generated before +the ui has started. By putting them in this queue we can display +them once the ui is ready.

+
+
+emit(record)[source]
+

Actually this is more like an enqueue than an emit().

+
+ +
+
+get()[source]
+
+ +
+
+has_new_records()[source]
+
+ +
+
+reset()[source]
+
+ +
+ +
+
+

apptools.logger.logger module

+

Convenience functions for creating logging handlers etc.

+
+
+class apptools.logger.logger.LogFileHandler(path, maxBytes=1000000, backupCount=3, level=None, formatter=None)[source]
+

Bases: logging.handlers.RotatingFileHandler

+

The default log file handler.

+
+ +
+
+apptools.logger.logger.add_log_queue_handler(logger, level=None, formatter=None)[source]
+

Adds a queueing log handler to a logger.

+
+ +
+
+

apptools.logger.ring_buffer module

+

Copied from Python Cookbook.

+
+
+class apptools.logger.ring_buffer.RingBuffer(size_max)[source]
+

Bases: object

+
+
+append(x)[source]
+

append an element at the end of the buffer

+
+ +
+
+get()[source]
+

return a list of elements from the oldest to the newest

+
+ +
+ +
+
+class apptools.logger.ring_buffer.RingBufferFull(n)[source]
+

Bases: object

+
+
+append(x)[source]
+
+ +
+
+get()[source]
+
+ +
+ +
+
+

Module contents

+

Convenience functions for creating logging handlers. +Part of the EnthoughtBase project.

+
+
+ + +
+
+
+ +
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/api/apptools.logger.plugin.html b/api/apptools.logger.plugin.html new file mode 100644 index 000000000..0f53db074 --- /dev/null +++ b/api/apptools.logger.plugin.html @@ -0,0 +1,305 @@ + + + + + + + apptools.logger.plugin package — Apptools Documentation + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+ +
+ + +
+
+ +
+
+ +
+

apptools.logger.plugin package

+ +
+

Submodules

+
+
+

apptools.logger.plugin.logger_plugin module

+

Logger plugin.

+
+
+class apptools.logger.plugin.logger_plugin.LoggerPlugin(*args, **kwargs)[source]
+

Bases: envisage.api.Plugin

+

Logger plugin.

+
+
+MAIL_FILES = 'apptools.logger.plugin.mail_files'
+
+ +
+
+PREFERENCES = 'envisage.preferences'
+
+ +
+
+PREFERENCES_PAGES = 'envisage.ui.workbench.preferences_pages'
+
+ +
+
+VIEWS = 'envisage.ui.workbench.views'
+
+ +
+
+id = 'apptools.logger'
+
+ +
+
+mail_files
+
+ +
+
+name = 'Logger plugin'
+
+ +
+
+preferences = <traits.trait_types.List object>
+
+ +
+
+preferences_pages = <traits.trait_types.List object>
+
+ +
+
+start()[source]
+

Starts the plugin.

+
+ +
+
+stop()[source]
+

Stops the plugin.

+
+ +
+
+views = <traits.trait_types.List object>
+
+ +
+ +
+
+

apptools.logger.plugin.logger_preferences module

+
+
+class apptools.logger.plugin.logger_preferences.LoggerPreferences(**traits)[source]
+

Bases: apptools.preferences.preferences_helper.PreferencesHelper

+

The persistent service exposing the Logger plugin’s API.

+
+ +
+
+

apptools.logger.plugin.logger_service module

+
+
+class apptools.logger.plugin.logger_service.LoggerService[source]
+

Bases: traits.has_traits.HasTraits

+

The persistent service exposing the Logger plugin’s API.

+
+
+create_email_message(fromaddr, toaddrs, ccaddrs, subject, priority, include_userdata=False, stack_trace='', comments='', include_environment=True)[source]
+

Format a bug report email from the log files.

+
+ +
+
+save_preferences()[source]
+

Save the preferences.

+
+ +
+
+send_bug_report(smtp_server, fromaddr, toaddrs, ccaddrs, message)[source]
+

Send a bug report email.

+
+ +
+
+whole_log_text()[source]
+

Return all of the logged data as formatted text.

+
+ +
+ +
+
+

Module contents

+
+
+ + +
+
+
+ +
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/api/apptools.logger.plugin.view.html b/api/apptools.logger.plugin.view.html new file mode 100644 index 000000000..6cc16d720 --- /dev/null +++ b/api/apptools.logger.plugin.view.html @@ -0,0 +1,303 @@ + + + + + + + apptools.logger.plugin.view package — Apptools Documentation + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ + + + +
+
+ +
+
+ +
+

apptools.logger.plugin.view package

+
+

Submodules

+
+
+

apptools.logger.plugin.view.logger_preferences_page module

+
+
+class apptools.logger.plugin.view.logger_preferences_page.LoggerPreferencesPage(**traits)[source]
+

Bases: apptools.preferences.ui.preferences_page.PreferencesPage

+

A preference page for the logger plugin.

+
+ +
+
+

apptools.logger.plugin.view.logger_view module

+
+
+class apptools.logger.plugin.view.logger_view.LogRecordAdapter[source]
+

Bases: traitsui.tabular_adapter.TabularAdapter

+

A TabularEditor adapter for logging.LogRecord objects.

+
+
+column_widths = [80, 100, 120, -1]
+
+ +
+
+get_width(object, trait, column)[source]
+

Returns the width to use for a specified column.

+

If the value is <= 0, the column will have a default width, which is +the same as specifying a width of 0.1.

+

If the value is > 1.0, it is converted to an integer and the result is +the width of the column in pixels. This is referred to as a +fixed width column.

+

If the value is a float such that 0.0 < value <= 1.0, it is treated as +the unnormalized fraction of the available space that is to be +assigned to the column. What this means requires a little explanation.

+

To arrive at the size in pixels of the column at any given time, the +editor adds together all of the unnormalized fraction values +returned for all columns in the table to arrive at a total value. Each +unnormalized fraction is then divided by the total to create a +normalized fraction. Each column is then assigned an amount of space +in pixels equal to the maximum of 30 or its normalized fraction +multiplied by the available space. The available space is defined +as the actual width of the table minus the width of all fixed width +columns. Note that this calculation is performed each time the table is +resized in the user interface, thus allowing columns of this type to +increase or decrease their width dynamically, while leaving fixed +width columns unchanged.

+
+ +
+ +
+
+class apptools.logger.plugin.view.logger_view.LoggerView(*args, **kwargs)[source]
+

Bases: pyface.workbench.traits_ui_view.TraitsUIView

+

The Workbench View showing the list of log items.

+
+
+activated = <traits.trait_types.Instance object>
+
+ +
+
+activated_text = <traits.traits.ForwardProperty object>
+
+ +
+
+code_editor = <traitsui.editors.code_editor.ToolkitEditorFactory object>
+
+ +
+
+copy_button = <traits.trait_types.Button object>
+
+ +
+
+formatted_records = <traits.traits.ForwardProperty object>
+
+ +
+
+id = <traits.trait_types.Str object>
+
+ +
+
+log_records = <traits.trait_types.List object>
+
+ +
+
+log_records_editor = <traitsui.editors.tabular_editor.TabularEditor object>
+
+ +
+
+name = <traits.trait_types.Str object>
+
+ +
+
+reset_button = <traits.trait_types.Button object>
+
+ +
+
+service = <traits.trait_types.Instance object>
+
+ +
+
+show_button = <traits.trait_types.Button object>
+
+ +
+
+trait_view = ( Group( Item( 'log_records' object = 'object', style = 'simple', show_label = False ), Group( Item( 'reset_button' object = 'object', style = 'simple', show_label = False ), Item( 'trait_modified' object = 'object', style = 'simple' ), Item( 'show_button' object = 'object', style = 'simple', show_label = False ), Item( 'copy_button' object = 'object', style = 'simple', show_label = False ), orientation = 'horizontal', show_labels = False, object = 'object', style = 'simple' ), show_labels = False, object = 'object', style = 'simple' ) )
+
+ +
+
+update(force=False)[source]
+

Update ‘log_records’ if our handler has new records or ‘force’ is +set.

+
+ +
+ +
+
+

Module contents

+
+
+ + +
+
+
+ +
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/api/apptools.naming.html b/api/apptools.naming.html new file mode 100644 index 000000000..8879ceee8 --- /dev/null +++ b/api/apptools.naming.html @@ -0,0 +1,840 @@ + + + + + + + apptools.naming package — Apptools Documentation + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +
+

apptools.naming package

+ +
+

Submodules

+
+
+

apptools.naming.address module

+

The address of a commuications endpoint.

+
+
+class apptools.naming.address.Address[source]
+

Bases: traits.has_traits.HasTraits

+

The address of a communications end-point.

+

It contains a type that describes the communication mechanism, and the +actual address content.

+
+ +
+
+

apptools.naming.api module

+
+
+

apptools.naming.binding module

+

The representation of a name-to-object binding in a context.

+
+
+class apptools.naming.binding.Binding[source]
+

Bases: traits.has_traits.HasTraits

+

The representation of a name-to-object binding in a context.

+
+ +
+
+

apptools.naming.context module

+

The base class for all naming contexts.

+
+
+class apptools.naming.context.Context[source]
+

Bases: traits.has_traits.HasTraits

+

The base class for all naming contexts.

+
+
+INITIAL_CONTEXT_FACTORY = 'apptools.naming.factory.initial'
+
+ +
+
+OBJECT_FACTORIES = 'apptools.naming.factory.object'
+
+ +
+
+STATE_FACTORIES = 'apptools.naming.factory.state'
+
+ +
+
+bind(name, obj, make_contexts=False)[source]
+

Binds a name to an object.

+

If ‘make_contexts’ is True then any missing intermediate contexts are +created automatically.

+
+ +
+
+create_subcontext(name)[source]
+

Creates a sub-context.

+
+ +
+
+destroy_subcontext(name)[source]
+

Destroys a sub-context.

+
+ +
+
+get_unique_name(prefix)[source]
+

Returns a name that is unique within the context.

+

The name returned will start with the specified prefix.

+
+ +
+
+is_context(name)[source]
+

Returns True if the name is bound to a context.

+
+ +
+
+list_bindings(name='')[source]
+

Lists the bindings in a context.

+
+ +
+
+list_names(name='')[source]
+

Lists the names bound in a context.

+
+ +
+
+lookup(name)[source]
+

Resolves a name relative to this context.

+
+ +
+
+lookup_binding(name)[source]
+

Looks up the binding for a name relative to this context.

+
+ +
+
+lookup_context(name)[source]
+

Resolves a name relative to this context.

+

The name MUST resolve to a context.

+
+ +
+
+rebind(name, obj, make_contexts=False)[source]
+

Binds an object to a name that may already be bound.

+

If ‘make_contexts’ is True then any missing intermediate contexts are +created automatically.

+

The object may be a different object but may also be the same object +that is already bound to the specified name. The name may or may not be +already used. Think of this as a safer version of ‘bind’ since this +one will never raise an exception regarding a name being used.

+
+ +
+
+rename(old_name, new_name)[source]
+

Binds a new name to an object.

+
+ +
+
+search(obj)[source]
+

Returns a list of namespace names that are bound to obj.

+
+ +
+
+unbind(name)[source]
+

Unbinds a name.

+
+ +
+ +
+
+

apptools.naming.dir_context module

+

The base class for all directory contexts.

+
+
+class apptools.naming.dir_context.DirContext[source]
+

Bases: apptools.naming.context.Context

+

The base class for all directory contexts.

+
+
+find_bindings(visitor)[source]
+

Find bindings with attributes matching criteria in visitor.

+

Visitor is a function that is passed the bindings for each level of the +heirarchy and the attribute dictionary for those bindings. The visitor +examines the bindings and dictionary and returns the bindings it is +interested in.

+
+ +
+
+get_attributes(name)[source]
+

Returns the attributes associated with a named object.

+
+ +
+
+set_attributes(name, attributes)[source]
+

Sets the attributes associated with a named object.

+
+ +
+ +
+
+

apptools.naming.dynamic_context module

+

Provider of a framework that dynamically determines the contents of a +context at the time of interaction with the contents rather than at the +time a class is written.

+

This capability is particularly useful when the object acting as a context +is part of a plug-in application – such as Envisage. In general, this +capability allows the context to be:

+
    +
  • Extendable by contributions from somewhere other than the original +code writer

  • +
  • Dynamic in that the elements it is composed of can change each time +someone interacts with the contents of the context.

  • +
+

It should be noted that this capability is explicitly different from +contexts that look at another container to determine their contents, such +as a file system context!

+

Users of this framework contribute items to a dynamic context by adding +traits to the dynamic context instance. (This addition can happen +statically through the use of a Traits Category.) The trait value is the +context item’s value and the trait definition’s metadata determines how the +item is treated within the context. The support metadata is:

+
+
context_name: A non-empty string

Represents the name of the item within this context. This must be +present for the trait to show up as a context item though the value +may change over time as the item gets bound to different names.

+
+
context_order: A float value

Indicates the position for the item within this context. All +dynamically contributed context items are sorted by ascending order +of this value using the standard list sort function.

+
+
is_context: A boolean value

True if the item is itself a context.

+
+
+
+
+class apptools.naming.dynamic_context.DynamicContext[source]
+

Bases: apptools.naming.context.Context

+

A framework that dynamically determines the contents of a context at +the time of interaction with the contents rather than at the time a +context class is written.

+

It should be noted that this capability is explicitly different from +contexts that look at another container to determine their contents, +such as a file system context!

+
+ +
+
+

apptools.naming.exception module

+

Naming exceptions.

+
+
+exception apptools.naming.exception.InvalidNameError[source]
+

Bases: apptools.naming.exception.NamingError

+

Invalid name.

+

This exception is thrown when the name passed to a naming operation does +not conform to the syntax of the naming system (or is empty etc).

+
+ +
+
+exception apptools.naming.exception.NameAlreadyBoundError[source]
+

Bases: apptools.naming.exception.NamingError

+

Name already bound.

+

This exception is thrown when an attempt is made to bind a name that is +already bound in the current context.

+
+ +
+
+exception apptools.naming.exception.NameNotFoundError[source]
+

Bases: apptools.naming.exception.NamingError

+

Name not found.

+

This exception is thrown when a component of a name cannot be resolved +because it is not bound in the current context.

+
+ +
+
+exception apptools.naming.exception.NamingError[source]
+

Bases: Exception

+

Base class for all naming exceptions.

+
+ +
+
+exception apptools.naming.exception.NotContextError[source]
+

Bases: apptools.naming.exception.NamingError

+

Not a context.

+

This exception is thrown when a naming operation has reached a point where +a context is required to continue the operation, but the resolved object +is not a context.

+
+ +
+
+exception apptools.naming.exception.OperationNotSupportedError[source]
+

Bases: apptools.naming.exception.NamingError

+

The context does support the requested operation.

+
+ +
+
+

apptools.naming.initial_context module

+

The starting point for performing naming operations.

+
+
+apptools.naming.initial_context.InitialContext(environment)[source]
+

Creates an initial context for beginning name resolution.

+
+ +
+
+

apptools.naming.initial_context_factory module

+

The base class for all initial context factories.

+
+
+class apptools.naming.initial_context_factory.InitialContextFactory[source]
+

Bases: traits.has_traits.HasTraits

+

The base class for all initial context factories.

+
+
+get_initial_context(environment)[source]
+

Creates an initial context for beginning name resolution.

+
+ +
+ +
+
+

apptools.naming.naming_event module

+

The event fired by the tree model when it changes.

+
+
+class apptools.naming.naming_event.NamingEvent[source]
+

Bases: traits.has_traits.HasTraits

+

Information about tree model changes.

+
+ +
+
+

apptools.naming.naming_manager module

+

The naming manager.

+
+
+class apptools.naming.naming_manager.NamingManager[source]
+

Bases: traits.has_traits.HasTraits

+

The naming manager.

+
+
+get_object_instance(info, name, context)[source]
+

Creates an object using the specified state information.

+

The naming manager asks the context for its list of OBJECT factories +and calls them one by one until it gets a non-None result, indicating +that the factory recognised the information and created an object.

+

If none of the factories recognize the state information (or if the +context has no factories) then the state information itself is +returned.

+
+ +
+
+get_state_to_bind(obj, name, context)[source]
+

Returns the state of an object for binding.

+

The naming manager asks the context for its list of STATE factories +and then calls them one by one until it gets a non-None result +indicating that the factory recognised the object and created state +information for it.

+

If none of the factories recognize the object (or if the context +has no factories) then the object itself is returned.

+
+ +
+ +
+
+

apptools.naming.object_factory module

+

The base class for all object factories.

+
+
+class apptools.naming.object_factory.ObjectFactory[source]
+

Bases: traits.has_traits.HasTraits

+

The base class for all object factories.

+

An object factory accepts some information about how to create an object +(such as a reference) and returns an instance of that object.

+
+
+get_object_instance(state, name, context)[source]
+

Creates an object using the specified state information.

+

Returns None if the factory cannot create the object (ie. it does not +recognise the state passed to it).

+
+ +
+ +
+
+

apptools.naming.object_serializer module

+

The base class for all object serializers.

+
+
+class apptools.naming.object_serializer.ObjectSerializer[source]
+

Bases: traits.has_traits.HasTraits

+

The base class for all object serializers.

+
+
+can_load(path)[source]
+

Returns True if the serializer can load a file.

+
+ +
+
+can_save(obj)[source]
+

Returns True if the serializer can save an object.

+
+ +
+
+load(path)[source]
+

Loads an object from a file.

+
+ +
+
+save(path, obj)[source]
+

Saves an object to a file.

+
+ +
+ +
+
+

apptools.naming.py_context module

+

A naming context for a Python namespace.

+
+
+class apptools.naming.py_context.PyContext(**traits)[source]
+

Bases: apptools.naming.context.Context, apptools.naming.referenceable.Referenceable

+

A naming context for a Python namespace.

+
+ +
+
+

apptools.naming.py_object_factory module

+

Object factory for Python namespace contexts.

+
+
+class apptools.naming.py_object_factory.PyObjectFactory[source]
+

Bases: apptools.naming.object_factory.ObjectFactory

+

Object factory for Python namespace contexts.

+
+
+get_object_instance(state, name, context)[source]
+

Creates an object using the specified state information.

+
+ +
+ +
+
+

apptools.naming.pyfs_context module

+

A Python File System context.

+
+
+class apptools.naming.pyfs_context.PyFSContext(**traits)[source]
+

Bases: apptools.naming.dir_context.DirContext, apptools.naming.referenceable.Referenceable

+

A Python File System context.

+

This context represents a directory on a local file system.

+
+
+ATTRIBUTES_FILE = '__attributes__'
+
+ +
+
+FILTERS = 'apptools.naming.pyfs.filters'
+
+ +
+
+OBJECT_SERIALIZERS = 'apptools.naming.pyfs.object.serializers'
+
+ +
+
+get_unique_name(name)[source]
+

Returns a name that is unique within the context.

+

The name returned will start with the specified prefix.

+
+ +
+
+refresh()[source]
+

Refresh the context to reflect changes in the file system.

+
+ +
+ +
+
+

apptools.naming.pyfs_context_factory module

+

Object factory for Python File System contexts.

+
+
+class apptools.naming.pyfs_context_factory.PyFSContextFactory[source]
+

Bases: apptools.naming.object_factory.ObjectFactory

+

Object factory for Python File System contexts.

+
+
+get_object_instance(state, name, context)[source]
+

Creates an object using the specified state information.

+
+ +
+ +
+
+

apptools.naming.pyfs_initial_context_factory module

+

The initial context factory for Python file system contexts.

+
+
+class apptools.naming.pyfs_initial_context_factory.PyFSInitialContextFactory[source]
+

Bases: apptools.naming.initial_context_factory.InitialContextFactory

+

The initial context factory for Python file system contexts.

+
+
+get_initial_context(environment)[source]
+

Creates an initial context for beginning name resolution.

+
+ +
+ +
+
+

apptools.naming.pyfs_object_factory module

+

Object factory for Python File System contexts.

+
+
+class apptools.naming.pyfs_object_factory.PyFSObjectFactory[source]
+

Bases: apptools.naming.object_factory.ObjectFactory

+

Object factory for Python File System contexts.

+
+
+get_object_instance(state, name, context)[source]
+

Creates an object using the specified state information.

+
+ +
+ +
+
+

apptools.naming.pyfs_state_factory module

+

State factory for Python File System contexts.

+
+
+class apptools.naming.pyfs_state_factory.PyFSStateFactory[source]
+

Bases: apptools.naming.state_factory.StateFactory

+

State factory for Python File System contexts.

+
+
+get_state_to_bind(obj, name, context)[source]
+

Returns the state of an object for binding.

+
+ +
+ +
+
+

apptools.naming.reference module

+

A reference to an object that lives outside of the naming system.

+
+
+class apptools.naming.reference.Reference[source]
+

Bases: traits.has_traits.HasPrivateTraits

+

A reference to an object that lives outside of the naming system.

+

References provide a way to store the address(s) of objects that live +outside of the naming system. A reference consists of a list of +addresses that represent a communications endpoint for the object being +referenced.

+

A reference also contains information to assist in the creation of an +instance of the object to which it refers. It contains the name of +the class that will be created and the class name and location of a +factory that will be used to do the actual instance creation.

+
+ +
+
+

apptools.naming.referenceable module

+

Base class for classes that can produce a reference to themselves.

+
+
+class apptools.naming.referenceable.Referenceable[source]
+

Bases: traits.has_traits.HasPrivateTraits

+

Base class for classes that can produce a reference to themselves.

+
+ +
+
+

apptools.naming.referenceable_state_factory module

+

State factory for referenceable objects.

+
+
+class apptools.naming.referenceable_state_factory.ReferenceableStateFactory[source]
+

Bases: apptools.naming.state_factory.StateFactory

+

State factory for referenceable objects.

+
+
+get_state_to_bind(obj, name, context)[source]
+

Returns the state of an object for binding.

+
+ +
+ +
+
+

apptools.naming.state_factory module

+

The base class for all state factories.

+
+
+class apptools.naming.state_factory.StateFactory[source]
+

Bases: traits.has_traits.HasPrivateTraits

+

The base class for all state factories.

+

A state factory accepts an object and returns some data representing the +object that is suitable for storing in a particular context.

+
+
+get_state_to_bind(obj, name, context)[source]
+

Returns the state of an object for binding.

+

Returns None if the factory cannot create the state (ie. it does not +recognise the object passed to it).

+
+ +
+ +
+
+

apptools.naming.unique_name module

+

A re-usable method for calculating a unique name given a list of existing +names.

+
+
+apptools.naming.unique_name.make_unique_name(base, existing=[], format='%s_%s')[source]
+

Return a name, unique within a context, based on the specified name.

+

base: the desired base name of the generated unique name. +existing: a sequence of the existing names to avoid returning. +format: a formatting specification for how the name is made unique.

+
+ +
+
+

Module contents

+

Manages naming contexts. Supports non-string data types and scoped +preferences. Part of the AppTools project of the Enthought Tool Suite.

+
+
+ + +
+
+
+ +
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/api/apptools.naming.trait_defs.html b/api/apptools.naming.trait_defs.html new file mode 100644 index 000000000..6938f18d2 --- /dev/null +++ b/api/apptools.naming.trait_defs.html @@ -0,0 +1,271 @@ + + + + + + + apptools.naming.trait_defs package — Apptools Documentation + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+ +
+ + +
+
+ +
+
+ +
+

apptools.naming.trait_defs package

+
+

Submodules

+
+
+

apptools.naming.trait_defs.api module

+
+
+

apptools.naming.trait_defs.naming_traits module

+
+
+class apptools.naming.trait_defs.naming_traits.NamingTraitHandler(aClass, or_none, module)[source]
+

Bases: traits.trait_handler.TraitHandler

+
+
+find_class()[source]
+
+ +
+
+get_editor(trait)[source]
+

Returns a trait editor that allows the user to modify the trait +trait. +This method only needs to be specified if traits defined using this +trait handler require a non-default trait editor in trait user +interfaces. The default implementation of this method returns a trait +editor that allows the user to type an arbitrary string as the value.

+

For more information on trait user interfaces, refer to the Traits UI +User Guide.

+
+
Parameters
+

trait (Trait) – The trait to be edited.

+
+
+
+ +
+
+info()[source]
+

Must return a string describing the type of value accepted by the +trait handler.

+

The string should be a phrase describing the type defined by the +TraitHandler subclass, rather than a complete sentence. For example, +use the phrase, “a square sprocket” instead of the sentence, “The value +must be a square sprocket.” The value returned by info() is combined +with other information whenever an error occurs and therefore makes +more sense to the user if the result is a phrase. The info() method is +similar in purpose and use to the info attribute of a validator +function.

+

Note that the result can include information specific to the particular +trait handler instance. If the info() method is not overridden, the +default method returns the value of the ‘info_text’ attribute.

+
+ +
+
+post_setattr(object, name, value)[source]
+
+ +
+
+resolve_class(object, name, value)[source]
+
+ +
+
+validate(object, name, value)[source]
+

Verifies whether a new value assigned to a trait attribute is +valid.

+

This method must be implemented by subclasses of TraitHandler. It is +called whenever a new value is assigned to a trait attribute defined +using this trait handler.

+

If the value received by validate() is not valid for the trait +attribute, the method must called the predefined error() method to +raise a TraitError exception

+
+
Parameters
+
    +
  • object (HasTraits instance) – The object whose attribute is being assigned.

  • +
  • name (str) – The name of the attribute being assigned.

  • +
  • value (any) – The proposed new value for the attribute.

  • +
+
+
Returns
+

If the new value is valid, this method must return either the +original value passed to it, or an alternate value to be assigned +in place of the original value. Whatever value this method returns +is the actual value assigned to object.name.

+
+
Return type
+

any

+
+
+
+ +
+
+validate_failed(object, name, value)[source]
+
+ +
+ +
+
+

Module contents

+
+
+ + +
+
+
+ +
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/api/apptools.persistence.html b/api/apptools.persistence.html new file mode 100644 index 000000000..8640a7d0b --- /dev/null +++ b/api/apptools.persistence.html @@ -0,0 +1,794 @@ + + + + + + + apptools.persistence package — Apptools Documentation + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +
+

apptools.persistence package

+
+

Submodules

+
+
+

apptools.persistence.file_path module

+

Simple class to support file path objects that work well in the +context of persistent storage with the state_pickler.

+
+
+class apptools.persistence.file_path.FilePath(value='')[source]
+

Bases: object

+

This class stores two paths to the file. A relative path and +an absolute one. The absolute path is used by the end user. When +this object is pickled the state_pickler sets the relative path +relative to the file that is being generated. When unpickled, the +stored relative path is used to set the absolute path correctly +based on the path of the saved file.

+
+
+get()[source]
+

Get the path.

+
+ +
+
+set(value)[source]
+

Sets the value of the path.

+
+ +
+
+set_absolute(base_f_name)[source]
+

Sets the absolute file name for the current relative file +name with respect to the given base_f_name.

+
+ +
+
+set_relative(base_f_name)[source]
+

Sets the path relative to base_f_name. Note that +base_f_name and self.rel_pth should be valid file names +correct on the current os. The set name is a file name that +has a POSIX path.

+
+ +
+ +
+
+

apptools.persistence.project_loader module

+
+
+apptools.persistence.project_loader.load_project(pickle_filename, updater_path, application_version, protocol, max_pass=-1)[source]
+

Reads a project from a pickle file and if necessary will update it to +the latest version of the application.

+
+ +
+
+apptools.persistence.project_loader.upgrade_project(pickle_filename, updater_path, project_version, application_version, protocol, max_pass=-1)[source]
+

Repeatedly read and write the project to disk updating it one version +at a time.

+

Example the p5.project is at version 0 +The application is at version 3

+

p5.project — Update1 —> p5.project.v1 +p5.project.v1 — Update2 —> p5.project.v2 +p5.project.v2 — Update3 —> p5.project.v3 +p5.project.v3 —> loaded into app

+

The user then has the option to save the updated project as p5.project

+
+ +
+
+

apptools.persistence.state_pickler module

+

This module provides code that allows one to pickle the state of a +Python object to a dictionary.

+

The motivation for this is simple. The standard Python +pickler/unpickler is best used to pickle simple objects and does not +work too well for complex code. Specifically, there are two major +problems (1) the pickle file format is not easy to edit with a text +editor and (2) when a pickle is unpickled, it creates all the +necessary objects and sets the state of these objects.

+

Issue (2) might not appear to be a problem. However, often, the +determination of the entire ‘state’ of an application requires the +knowledge of the state of many objects that are not really in the +users concern. The user would ideally like to pickle just what he +thinks is relevant. Now, given that the user is not going to save the +entire state of the application, the use of pickle is insufficient +since the state is no longer completely known (or worth knowing). The +default Unpickler recreates the objects and the typical +implementation of __setstate__ is usually to simply update the +object’s __dict__ attribute. This is inadequate because the pickled +information is taken out of the real context when it was saved.

+

The StatePickler basically pickles the ‘state’ of an object into a +large dictionary. This pickled data may be easily unpickled and +modified on the interpreter or edited with a text editor +(pprint.saferepr is a friend). The second problem is also +eliminated. When this state is unpickled using StateUnpickler, what +you get is a special dictionary (a State instance). This allows one +to navigate the state just like the original object. Its up to the +user to create any new objects and set their states using this +information. This allows for a lot of flexibility while allowing one +to save and set the state of (almost) any Python object.

+

The StateSetter class helps set the state of a known instance. When +setting the state of an instance it checks to see if there is a +__set_pure_state__ method that in turn calls StateSetter.set +appropriately.

+

Additionally, there is support for versioning. The class’ version is +obtain from the __version__ class attribute. This version along +with the versions of the bases of a class is embedded into the +metadata of the state and stored. By using version_registry.py a +user may register a handler for a particular class and module. When +the state of an object is set using StateSetter.set_state, then +these handlers are called in reverse order of their MRO. This gives +the handler an opportunity to upgrade the state depending on its +version. Builtin classes are not scanned for versions. If a class +has no version, then by default it is assumed to be -1.

+

Example:

+
>>> class A:
+...    def __init__(self):
+...        self.a = 'a'
+...
+>>> a = A()
+>>> a.a = 100
+>>> import state_pickler
+>>> s = state_pickler.dumps(a)               # Dump the state of `a`.
+>>> state = state_pickler.loads_state(s)     # Get the state back.
+>>> b = state_pickler.create_instance(state) # Create the object.
+>>> state_pickler.set_state(b, state)        # Set the object's state.
+>>> assert b.a == 100
+
+
+
+

Features

+
+
    +
  • The output is a plain old dictionary so is easy to parse, edit etc.

  • +
  • Handles references to avoid duplication.

  • +
  • Gzips Numeric arrays when dumping them.

  • +
  • Support for versioning.

  • +
+
+
+
+

Caveats

+
+
    +
  • Does not pickle a whole bunch of stuff including code objects and +functions.

  • +
  • The output is a pure dictionary and does not contain instances. So +using this as it is in __setstate__ will not work. Instead +define a __set_pure_state__ and use the StateSetter class or +the set_state function provided by this module.

  • +
+
+

Notes

+

Browsing the code from XMarshaL and pickle.py proved useful for +ideas. None of the code is taken from there though.

+
+
+class apptools.persistence.state_pickler.State(**kw)[source]
+

Bases: dict

+

Used to encapsulate the state of an instance in a very +convenient form. The ‘__metadata__’ attribute/key is a dictionary +that has class specific details like the class name, module name +etc.

+
+ +
+
+class apptools.persistence.state_pickler.StateDict(**kw)[source]
+

Bases: dict

+

Used to encapsulate a dictionary stored in a State instance. +The has_instance attribute specifies if the dict has an instance +embedded in it.

+
+ +
+
+class apptools.persistence.state_pickler.StateList(seq=None)[source]
+

Bases: list

+

Used to encapsulate a list stored in a State instance. The +has_instance attribute specifies if the list has an instance +embedded in it.

+
+ +
+
+class apptools.persistence.state_pickler.StatePickler[source]
+

Bases: object

+

Pickles the state of an object into a dictionary. The +dictionary is itself either saved as a pickled file (dump) or +pickled string (dumps). Alternatively, the dump_state method +will return the dictionary that is pickled.

+

The format of the state dict is quite strightfoward. Basic types +(bool, int, long, float, complex, None, string) are +represented as they are. Everything else is stored as a +dictionary containing metadata information on the object’s type +etc. and also the actual object in the ‘data’ key. For example:

+
>>> p = StatePickler()
+>>> p.dump_state(1)
+1
+>>> l = [1,2.0, None, [1,2,3]]
+>>> p.dump_state(l)
+{'data': [1, 2.0, None, {'data': [1, 2, 3], 'type': 'list', 'id': 1}],
+ 'id': 0,
+ 'type': 'list'}
+
+
+

Classes are also represented similarly. The state in this case is +obtained from the __getstate__ method or from the __dict__. +Here is an example:

+
>>> class A:
+...     __version__ = 1  # State version
+...     def __init__(self):
+...         self.attribute = 1
+...
+>>> a = A()
+>>> p = StatePickler()
+>>> p.dump_state(a)
+{'class_name': 'A',
+ 'data': {'data': {'attribute': 1}, 'type': 'dict', 'id': 2},
+ 'id': 0,
+ 'initargs': {'data': (), 'type': 'tuple', 'id': 1},
+ 'module': '__main__',
+ 'type': 'instance',
+ 'version': [(('A', '__main__'), 1)]}
+
+
+

When pickling data, references are taken care of. Numeric arrays +can be pickled and are stored as a gzipped base64 encoded string.

+
+
+dump(value, file)[source]
+

Pickles the state of the object (value) into the passed +file.

+
+ +
+
+dump_state(value)[source]
+

Returns a dictionary or a basic type representing the +complete state of the object (value).

+

This value is pickled by the dump and dumps methods.

+
+ +
+
+dumps(value)[source]
+

Pickles the state of the object (value) and returns a +string.

+
+ +
+ +
+
+exception apptools.persistence.state_pickler.StatePicklerError[source]
+

Bases: Exception

+
+ +
+
+class apptools.persistence.state_pickler.StateSetter[source]
+

Bases: object

+

This is a convenience class that helps a user set the +attributes of an object given its saved state. For instances it +checks to see if a __set_pure_state__ method exists and calls +that when it sets the state.

+
+
+set(obj, state, ignore=None, first=None, last=None)[source]
+

Sets the state of the object.

+

This is to be used as a means to simplify loading the state of +an object from its __setstate__ method using the dictionary +describing its state. Note that before the state is set, the +registered handlers for the particular class are called in +order to upgrade the version of the state to the latest +version.

+
+
Parameters
+
    +
  • obj (-) – The object whose state is to be set. If this is None +(default) then the object is created.

  • +
  • state (-) – The dictionary representing the state of the object.

  • +
  • ignore (-) – The list of attributes specified in this list are ignored +and the state of these attributes are not set (this excludes +the ones specified in first and last). If one specifies +a ‘*’ then all attributes are ignored except the ones +specified in first and last.

  • +
  • first (-) – The list of attributes specified in this list are set first (in +order), before any other attributes are set.

  • +
  • last (-) – The list of attributes specified in this list are set last (in +order), after all other attributes are set.

  • +
+
+
+
+ +
+ +
+
+exception apptools.persistence.state_pickler.StateSetterError[source]
+

Bases: Exception

+
+ +
+
+class apptools.persistence.state_pickler.StateTuple[source]
+

Bases: tuple

+

Used to encapsulate a tuple stored in a State instance. The +has_instance attribute specifies if the tuple has an instance +embedded in it.

+
+ +
+
+class apptools.persistence.state_pickler.StateUnpickler[source]
+

Bases: object

+

Unpickles the state of an object saved using StatePickler.

+

Please note that unlike the standard Unpickler, no instances of +any user class are created. The data for the state is obtained +from the file or string, reference objects are setup to refer to +the same state value and this state is returned in the form +usually in the form of a dictionary. For example:

+
>>> class A:
+...     def __init__(self):
+...         self.attribute = 1
+...
+>>> a = A()
+>>> p = StatePickler()
+>>> s = p.dumps(a)
+>>> up = StateUnpickler()
+>>> state = up.loads_state(s)
+>>> state.__class__.__name__
+'State'
+>>> state.attribute
+1
+>>> state.__metadata__
+{'class_name': 'A',
+ 'has_instance': True,
+ 'id': 0,
+ 'initargs': (),
+ 'module': '__main__',
+ 'type': 'instance',
+ 'version': [(('A', '__main__'), -1)]}
+
+
+

Note that the state is actually a State instance and is +navigable just like the original object. The details of the +instance are stored in the __metadata__ attribute. This is +highly convenient since it is possible for someone to view and +modify the state very easily.

+
+
+load_state(file)[source]
+

Returns the state of an object loaded from the pickled data +in the given file.

+
+ +
+
+loads_state(string)[source]
+

Returns the state of an object loaded from the pickled data +in the given string.

+
+ +
+ +
+
+exception apptools.persistence.state_pickler.StateUnpicklerError[source]
+

Bases: Exception

+
+ +
+
+apptools.persistence.state_pickler.create_instance(state)[source]
+

Create an instance from the state if possible.

+
+ +
+
+apptools.persistence.state_pickler.dump(value, file)[source]
+

Pickles the state of the object (value) into the passed file +(or file name).

+
+ +
+
+apptools.persistence.state_pickler.dumps(value)[source]
+

Pickles the state of the object (value) and returns a string.

+
+ +
+
+apptools.persistence.state_pickler.get_state(obj)[source]
+

Returns the state of the object (usually as a dictionary). The +returned state may be used directy to set the state of the object +via set_state.

+
+ +
+
+apptools.persistence.state_pickler.gunzip_string(data)[source]
+

Given a gzipped string (data) this unzips the string and +returns it.

+
+ +
+
+apptools.persistence.state_pickler.gzip_string(data)[source]
+

Given a string (data) this gzips the string and returns it.

+
+ +
+
+apptools.persistence.state_pickler.load_state(file)[source]
+

Returns the state of an object loaded from the pickled data in +the given file (or file name).

+
+ +
+
+apptools.persistence.state_pickler.loads_state(string)[source]
+

Returns the state of an object loaded from the pickled data +in the given string.

+
+ +
+
+apptools.persistence.state_pickler.set_state(obj, state, ignore=None, first=None, last=None)[source]
+

Sets the state of the object.

+

This is to be used as a means to simplify loading the state of +an object from its __setstate__ method using the dictionary +describing its state. Note that before the state is set, the +registered handlers for the particular class are called in +order to upgrade the version of the state to the latest +version.

+
+
Parameters
+
    +
  • obj (-) – The object whose state is to be set. If this is None +(default) then the object is created.

  • +
  • state (-) – The dictionary representing the state of the object.

  • +
  • ignore (-) – The list of attributes specified in this list are ignored +and the state of these attributes are not set (this excludes +the ones specified in first and last). If one specifies +a ‘*’ then all attributes are ignored except the ones +specified in first and last.

  • +
  • first (-) – The list of attributes specified in this list are set first (in +order), before any other attributes are set.

  • +
  • last (-) – The list of attributes specified in this list are set last (in +order), after all other attributes are set.

  • +
+
+
+
+ +
+
+apptools.persistence.state_pickler.update_state(state)[source]
+

Given the state of an object, this updates the state to the +latest version using the handlers given in the version registry. +The state is modified in-place.

+
+ +
+
+
+

apptools.persistence.updater module

+
+
+class apptools.persistence.updater.Updater[source]
+

Bases: object

+

An abstract class to provide functionality common to the updaters.

+
+
+get_latest(module, name)[source]
+

The refactorings dictionary contains mappings between old and new +module names. Since we only bump the version number one increment +there is only one possible answer.

+
+ +
+
+strip(string)[source]
+
+ +
+ +
+
+

apptools.persistence.version_registry module

+

A version registry that manages handlers for different state +versions.

+
+
+class apptools.persistence.version_registry.HandlerRegistry[source]
+

Bases: object

+

A simple version conversion handler registry. Classes register +handlers in order to convert the state version to the latest +version. When an object’s state is about to be set, the update +method of the registy is called. This in turn calls any handlers +registered for the class/module and this handler is then called +with the state and the version of the state. The state is +modified in-place by the handlers.

+
+
+register(class_name, module, handler)[source]
+

Register handler that handles versioning for class having +class name (class_name) and module name (module). The +handler function will be passed the state and its version to fix.

+
+ +
+
+unregister(class_name, module)[source]
+

Unregisters any handlers for a class and module.

+
+ +
+
+update(state)[source]
+

Updates the given state using the handlers. Note that the +state is modified in-place.

+
+ +
+ +
+
+apptools.persistence.version_registry.get_version(obj)[source]
+

Walks the class hierarchy and obtains the versions of the +various classes and returns a list of tuples of the form +((class_name, module), version) in reverse order of the MRO.

+
+ +
+
+

apptools.persistence.versioned_unpickler module

+
+
+class apptools.persistence.versioned_unpickler.NewUnpickler(file, *, fix_imports=True, encoding='ASCII', errors='strict')[source]
+

Bases: pickle._Unpickler

+

An unpickler that implements a two-stage pickling process to make it +possible to unpickle complicated Python object hierarchies where the +unserialized state of an object depends on the state of other objects in +the same pickle.

+
+
+initialize(max_pass)[source]
+
+ +
+
+load(max_pass=-1)[source]
+

Read a pickled object representation from the open file.

+

Return the reconstituted object hierarchy specified in the file.

+
+ +
+
+classmethod load_build(obj)[source]
+
+ +
+ +
+
+class apptools.persistence.versioned_unpickler.VersionedUnpickler(file, updater=None)[source]
+

Bases: apptools.persistence.versioned_unpickler.NewUnpickler

+

This class reads in a pickled file created at revision version ‘n’ +and then applies the transforms specified in the updater class to +generate a new set of objects which are at revision version ‘n+1’.

+

I decided to keep the loading of the updater out of this generic class +because we will want updaters to be generated for each plugin’s type +of project.

+

This ensures that the VersionedUnpickler can remain ignorant about the +actual version numbers - all it needs to do is upgrade one release.

+
+
+add_updater(module, name, klass)[source]
+

If there is an updater defined for this class we will add it to the +class as the __setstate__ method.

+
+ +
+
+backup_setstate(module, klass)[source]
+

If the class has a user defined __setstate__ we back it up.

+
+ +
+
+find_class(module, name)[source]
+

Overridden method from Unpickler.

+

NB __setstate__ is not called until later.

+
+ +
+
+import_name(module, name)[source]
+

If the class is needed for the latest version of the application then +it should presumably exist.

+

If the class no longer exists then we should perhaps return +a proxy of the class.

+

If the persisted file is at v1 say and the application is at v3 then +objects that are required for v1 and v2 do not have to exist they only +need to be placeholders for the state during an upgrade.

+
+ +
+ +
+
+

Module contents

+

Supports flexible pickling and unpickling of the state of a Python object +to a dictionary. Part of the AppTools project of the Enthought Tool Suite.

+
+
+ + +
+
+
+ +
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/api/apptools.preferences.html b/api/apptools.preferences.html new file mode 100644 index 000000000..e7f3cde28 --- /dev/null +++ b/api/apptools.preferences.html @@ -0,0 +1,653 @@ + + + + + + + apptools.preferences package — Apptools Documentation + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +
+

apptools.preferences package

+ +
+

Submodules

+
+
+

apptools.preferences.api module

+
+
+

apptools.preferences.i_preferences module

+

The interface for a node in a preferences hierarchy.

+
+
+class apptools.preferences.i_preferences.IPreferences[source]
+

Bases: traits.has_traits.Interface

+

The interface for a node in a preferences hierarchy.

+
+
+clear(path='')[source]
+

Remove all preference from the node at the specified path.

+

If the path is the empty string (the default) then remove the +preferences in this node.

+

This does not affect any of the node’s children.

+

e.g. To clear the preferences out of a node directly:

+
preferences.clear()
+
+
+

Or to clear the preferences of a node at a given path:

+
preferences.clear('acme.ui')
+
+
+
+ +
+
+flush()[source]
+

Force any changes in the node to the backing store.

+

This includes any changes to the node’s descendants.

+
+ +
+
+get(path, default=None, inherit=False)[source]
+

Get the value of the preference at the specified path.

+

If no value exists for the path (or any part of the path does not +exist) then return the default value.

+

Preference values are always returned as strings.

+

e.g:

+
preferences.set('acme.ui.bgcolor', 'blue')
+preferences.get('acme.ui.bgcolor') -> 'blue'
+
+preferences.set('acme.ui.width', 100)
+preferences.get('acme.ui.width') -> '100'
+
+preferences.set('acme.ui.visible', True)
+preferences.get('acme.ui.visible') -> 'True'
+
+
+

If ‘inherit’ is True then we allow ‘inherited’ preference values.

+

e.g. If we are looking up:

+
'acme.ui.widget.bgcolor'
+
+
+

and it does not exist then we will also try:

+
'acme.ui.bgcolor'
+'acme.bgcolor'
+'bgcolor'
+
+
+

Raise a ‘ValueError’ exception if the path is the empty string.

+
+ +
+
+keys(path='')[source]
+

Return the preference keys of the node at the specified path.

+

If the path is the empty string (the default) then return the +preference keys of this node.

+

e.g:

+
keys = preferences.keys('acme.ui')
+
+
+
+ +
+
+node(path='')[source]
+

Return the node at the specified path.

+

If the path is the empty string (the default) then return this node.

+

Any missing nodes are created automatically.

+

e.g:

+
node = preferences.node('acme.ui')
+bgcolor = node.get('bgcolor')
+
+
+
+ +
+
+node_exists(path='')[source]
+

Return True if the node at the specified path exists

+

If the path is the empty string (the default) then return True.

+

e.g:

+
exists = preferences.exists('acme.ui')
+
+
+
+ +
+
+node_names(path='')[source]
+

Return the names of the children of the node at the specified path.

+

If the path is the empty string (the default) then return the names of +the children of this node.

+

e.g:

+
names = preferences.node_names('acme.ui')
+
+
+
+ +
+
+remove(path)[source]
+

Remove the preference at the specified path.

+

Does nothing if no value exists for the path (or any part of the path +does not exist.

+

Raise a ‘ValueError’ exception if the path is the empty string.

+

e.g.:

+
preferences.remove('acme.ui.bgcolor')
+
+
+
+ +
+
+set(path, value)[source]
+

Set the value of the preference at the specified path.

+

Any missing nodes are created automatically.

+

Primitive Python types can be set, but preferences are always +stored and returned as strings.

+

e.g:

+
preferences.set('acme.ui.bgcolor', 'blue')
+preferences.get('acme.ui.bgcolor') -> 'blue'
+
+preferences.set('acme.ui.width', 100)
+preferences.get('acme.ui.width') -> '100'
+
+preferences.set('acme.ui.visible', True)
+preferences.get('acme.ui.visible') -> 'True'
+
+
+

Raise a ‘ValueError’ exception if the path is the empty string.

+
+ +
+ +
+
+

apptools.preferences.package_globals module

+

Package-scope globals.

+

The default preferences node is currently used by ‘PreferencesHelper’ and +‘PreferencesBinding’ instances if no specific preferences node is set. This +makes it easy for them to access the root node of an application-wide +preferences hierarchy.

+
+
+apptools.preferences.package_globals.get_default_preferences()[source]
+

Get the default preferences node.

+
+ +
+
+apptools.preferences.package_globals.set_default_preferences(default_preferences)[source]
+

Set the default preferences node.

+
+ +
+
+

apptools.preferences.preference_binding module

+

A binding between a trait on an object and a preference value.

+
+
+class apptools.preferences.preference_binding.PreferenceBinding(**traits)[source]
+

Bases: traits.has_traits.HasTraits

+

A binding between a trait on an object and a preference value.

+
+ +
+
+apptools.preferences.preference_binding.bind_preference(obj, trait_name, preference_path, preferences=None)[source]
+

Create a new preference binding.

+
+ +
+
+

apptools.preferences.preferences module

+

The default implementation of a node in a preferences hierarchy.

+
+
+class apptools.preferences.preferences.Preferences(**traits)[source]
+

Bases: traits.has_traits.HasTraits

+

The default implementation of a node in a preferences hierarchy.

+
+
+add_preferences_listener(listener, path='')[source]
+

Add a listener for changes to a node’s preferences.

+
+ +
+
+clear(path='')[source]
+

Remove all preferences from the node at the specified path.

+
+ +
+
+dump(indent='')[source]
+

Dump the preferences hierarchy to stdout.

+
+ +
+
+flush()[source]
+

Force any changes in the node to the backing store.

+

This includes any changes to the node’s descendants.

+
+ +
+
+get(path, default=None, inherit=False)[source]
+

Get the value of the preference at the specified path.

+
+ +
+
+keys(path='')[source]
+

Return the preference keys of the node at the specified path.

+
+ +
+
+load(file_or_filename=None)[source]
+

Load preferences from a file.

+

This is a merge operation i.e. the contents of the file are added to +the node.

+

This implementation uses ‘ConfigObj’ files.

+
+ +
+
+node(path='')[source]
+

Return the node at the specified path.

+
+ +
+
+node_exists(path='')[source]
+

Return True if the node at the specified path exists.

+
+ +
+
+node_names(path='')[source]
+

Return the names of the children of the node at the specified path.

+
+ +
+
+remove(path)[source]
+

Remove the preference at the specified path.

+
+ +
+
+remove_preferences_listener(listener, path='')[source]
+

Remove a listener for changes to a node’s preferences.

+
+ +
+
+save(file_or_filename=None)[source]
+

Save the node’s preferences to a file.

+

This implementation uses ‘ConfigObj’ files.

+
+ +
+
+set(path, value)[source]
+

Set the value of the preference at the specified path.

+
+ +
+ +
+
+

apptools.preferences.preferences_helper module

+

An object that can be initialized from a preferences node.

+
+
+class apptools.preferences.preferences_helper.PreferencesHelper(**traits)[source]
+

Bases: traits.has_traits.HasTraits

+

A base class for objects that can be initialized from a preferences +node.

+

Additional traits defined on subclasses will be listened to. Changes +are then synchronized with the preferences. Note that mutations on nested +containers e.g. List(List(Str)) cannot be synchronized and should be +avoided.

+
+ +
+
+

apptools.preferences.scoped_preferences module

+

A preferences node that adds the notion of preferences scopes.

+
+
+class apptools.preferences.scoped_preferences.ScopedPreferences(**traits)[source]
+

Bases: apptools.preferences.preferences.Preferences

+

A preferences node that adds the notion of preferences scopes.

+

Scopes provide a way to access preferences in a precedence order, usually +depending on where they came from, for example from the command-line, +or set by the user in a preferences file, or the defaults (set by the +developer).

+

By default, this class provides two scopes - ‘application’ which is +persistent and ‘default’ which is not.

+

Path names passed to ‘ScopedPreferences’ nodes can be either:

+
a) a preference path as used in a standard 'Preferences' node, e.g::
+
+'acme.widget.bgcolor'.
+
+In this case the operation either takes place in the primary scope
+(for operations such as 'set' etc), or on all scopes in precedence
+order (for operations such as 'get' etc).
+
+or
+
+b) a preference path that refers to a specific scope e.g::
+
+'default/acme.widget.bgcolor'
+
+In this case the operation takes place *only* in the specified scope.
+
+
+

There is one drawback to this scheme. If you want to access a scope node +itself via the ‘clear’, ‘keys’, ‘node’, ‘node_exists’ or ‘node_names’ +methods then you have to append a trailing ‘/’ to the path. Without that, +the node would try to perform the operation in the primary scope.

+

e.g. To get the names of the children of the ‘application’ scope, use:

+
scoped.node_names('application/')
+
+
+

If you did this:

+
scoped.node_names('application')
+
+
+

Then the node would get the primary scope and try to find its child node +called ‘application’.

+

Of course you can just get the scope via:

+
application_scope = scoped.get_scope('application')
+
+
+

and then call whatever methods you like on it - which is definitely more +intentional and is highly recommended:

+
application_scope.node_names()
+
+
+
+
+add_preferences_listener(listener, path='')[source]
+

Add a listener for changes to a node’s preferences.

+
+ +
+
+clear(path='')[source]
+

Remove all preference from the node at the specified path.

+
+ +
+
+dump(indent='')[source]
+

Dump the preferences hierarchy to stdout.

+
+ +
+
+get(path, default=None, inherit=False)[source]
+

Get the value of the preference at the specified path.

+
+ +
+
+get_scope(scope_name)[source]
+

Return the scope with the specified name.

+

Return None if no such scope exists.

+
+ +
+
+keys(path='')[source]
+

Return the preference keys of the node at the specified path.

+
+ +
+
+load(file_or_filename=None)[source]
+

Load preferences from a file.

+

This loads the preferences into the primary scope.

+

fixme: I’m not sure it is worth providing an implentation here. I +think it would be better to encourage people to explicitly reference +a particular scope.

+
+ +
+
+node(path='')[source]
+

Return the node at the specified path.

+
+ +
+
+node_exists(path='')[source]
+

Return True if the node at the specified path exists.

+
+ +
+
+node_names(path='')[source]
+

Return the names of the children of the node at the specified path.

+
+ +
+
+remove(path)[source]
+

Remove the preference at the specified path.

+
+ +
+
+remove_preferences_listener(listener, path='')[source]
+

Remove a listener for changes to a node’s preferences.

+
+ +
+
+save(file_or_filename=None)[source]
+

Save the node’s preferences to a file.

+

This asks each scope in turn to save its preferences.

+

If a file or filename is specified then it is only passed to the +primary scope.

+
+ +
+
+set(path, value)[source]
+

Set the value of the preference at the specified path.

+
+ +
+ +
+
+

Module contents

+

Manages application preferences. +Part of the AppTools project of the Enthought Tool Suite

+
+
+ + +
+
+
+ +
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/api/apptools.preferences.ui.html b/api/apptools.preferences.ui.html new file mode 100644 index 000000000..fa72bce2b --- /dev/null +++ b/api/apptools.preferences.ui.html @@ -0,0 +1,405 @@ + + + + + + + apptools.preferences.ui package — Apptools Documentation + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ + + + +
+
+ +
+
+ +
+

apptools.preferences.ui package

+
+

Submodules

+
+
+

apptools.preferences.ui.api module

+
+
+

apptools.preferences.ui.i_preferences_page module

+

The interface for pages in a preferences dialog.

+
+
+class apptools.preferences.ui.i_preferences_page.IPreferencesPage[source]
+

Bases: traits.has_traits.Interface

+

The interface for pages in a preferences dialog.

+
+
+apply()[source]
+

Apply the page’s preferences.

+
+ +
+ +
+
+

apptools.preferences.ui.preferences_manager module

+

The preferences manager.

+
+
+class apptools.preferences.ui.preferences_manager.PreferencesHelpWindow[source]
+

Bases: traits.has_traits.HasTraits

+

Container class to present a view with string info.

+
+
+traits_view()[source]
+

Default view to show for this class.

+
+ +
+ +
+
+class apptools.preferences.ui.preferences_manager.PreferencesManager[source]
+

Bases: traits.has_traits.HasTraits

+

The preferences manager.

+
+
+apply()[source]
+

Apply all changes made in the manager.

+
+ +
+
+traits_view()[source]
+

Default traits view for this class.

+
+ +
+ +
+
+class apptools.preferences.ui.preferences_manager.PreferencesManagerHandler[source]
+

Bases: traitsui.handler.Handler

+

The traits UI handler for the preferences manager.

+
+
+apply(info)[source]
+

Handle the Apply button being clicked.

+
+ +
+
+close(info, is_ok)[source]
+

Close a dialog-based user interface.

+
+ +
+
+init(info)[source]
+

Initialize the controls of a user interface.

+
+ +
+
+preferences_help(info)[source]
+

Custom preferences help panel. The Traits help doesn’t work.

+
+ +
+ +
+
+

apptools.preferences.ui.preferences_node module

+

Abstract base class for a node in a preferences dialog.

+
+
+class apptools.preferences.ui.preferences_node.PreferencesNode[source]
+

Bases: apptools.preferences.ui.tree_item.TreeItem

+

Abstract base class for a node in a preferences dialog.

+

A preferences node has a name and an image which are used to represent the +node in a preferences dialog (usually in the form of a tree).

+
+
+create_page(parent)[source]
+

Creates the preference page for this node.

+
+ +
+
+dump(indent='')[source]
+

Pretty-print the node to stdout.

+
+ +
+
+lookup(name)[source]
+

Returns the child of this node with the specified Id.

+

Returns None if no such child exists.

+
+ +
+ +
+
+

apptools.preferences.ui.preferences_page module

+

A page in a preferences dialog.

+
+
+class apptools.preferences.ui.preferences_page.PreferencesPage(**traits)[source]
+

Bases: apptools.preferences.preferences_helper.PreferencesHelper

+

A page in a preferences dialog.

+
+
+apply()[source]
+

Apply the page’s preferences.

+
+ +
+ +
+
+

apptools.preferences.ui.tree_item module

+

A generic base-class for items in a tree data structure.

+

An example:-

+

root = TreeItem(data=’Root’)

+

fruit = TreeItem(data=’Fruit’) +fruit.append(TreeItem(data=’Apple’, allows_children=False)) +fruit.append(TreeItem(data=’Orange’, allows_children=False)) +fruit.append(TreeItem(data=’Pear’, allows_children=False)) +root.append(fruit)

+

veg = TreeItem(data=’Veg’) +veg.append(TreeItem(data=’Carrot’, allows_children=False)) +veg.append(TreeItem(data=’Cauliflower’, allows_children=False)) +veg.append(TreeItem(data=’Sprout’, allows_children=False)) +root.append(veg)

+
+
+class apptools.preferences.ui.tree_item.TreeItem[source]
+

Bases: traits.has_traits.HasTraits

+

A generic base-class for items in a tree data structure.

+
+
+append(child)[source]
+

Appends a child to this item.

+

This removes the child from its current parent (if it has one).

+
+ +
+
+insert(index, child)[source]
+

Inserts a child into this item at the specified index.

+

This removes the child from its current parent (if it has one).

+
+ +
+
+insert_after(after, child)[source]
+

Inserts a child into this item after the specified item.

+

This removes the child from its current parent (if it has one).

+
+ +
+
+insert_before(before, child)[source]
+

Inserts a child into this item before the specified item.

+

This removes the child from its current parent (if it has one).

+
+ +
+
+remove(child)[source]
+

Removes a child from this item.

+
+ +
+ +
+
+

apptools.preferences.ui.widget_editor module

+

An instance editor that allows total control over widget creation.

+
+
+class apptools.preferences.ui.widget_editor.WidgetEditor(*args, **traits)[source]
+

Bases: traitsui.editor_factory.EditorFactory

+

A factory widget editors.

+
+
+custom_editor(ui, object, name, description, parent)
+

Create a simple editor.

+
+ +
+
+readonly_editor(ui, object, name, description, parent)
+

Create a simple editor.

+
+ +
+
+simple_editor(ui, object, name, description, parent)[source]
+

Create a simple editor.

+
+ +
+
+text_editor(ui, object, name, description, parent)
+

Create a simple editor.

+
+ +
+ +
+
+

Module contents

+
+
+ + +
+
+
+ +
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/api/apptools.scripting.html b/api/apptools.scripting.html new file mode 100644 index 000000000..8ec515512 --- /dev/null +++ b/api/apptools.scripting.html @@ -0,0 +1,414 @@ + + + + + + + apptools.scripting package — Apptools Documentation + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +
+

apptools.scripting package

+
+

Submodules

+
+
+

apptools.scripting.api module

+

Public API for the scripting package.

+
+
+

apptools.scripting.package_globals module

+

Globals for the scripting package.

+
+
+apptools.scripting.package_globals.get_recorder()[source]
+

Return the global recorder. Does not create a new one if none +exists.

+
+ +
+
+apptools.scripting.package_globals.set_recorder(rec)[source]
+

Set the global recorder instance.

+
+ +
+
+

apptools.scripting.recordable module

+

Decorator to mark functions and methods as recordable.

+
+
+apptools.scripting.recordable.recordable(func)[source]
+

A decorator that wraps a function into one that is recordable.

+

This will record the function only if the global recorder has been +set via a set_recorder function call.

+
+ +
+
+

apptools.scripting.recorder module

+

Code to support recording to a readable and executable Python script.

+
+
FIXME:
    +
  • Support for dictionaries?

  • +
+
+
+
+
+class apptools.scripting.recorder.Recorder[source]
+

Bases: traits.has_traits.HasTraits

+
+
+clear()[source]
+

Clears all previous recorded state and unregisters all +registered objects.

+
+ +
+
+get_code()[source]
+

Returns the recorded lines as a string of printable code.

+
+ +
+
+get_object_path(object)[source]
+

Returns the path in the object hierarchy of a registered +object. Useful for debugging.

+
+ +
+
+get_script_id(object)[source]
+

Returns the script_id of a registered object. Useful when +you want to manually add a record statement.

+
+ +
+
+is_registered(object)[source]
+

Returns True if the given object is registered with the +recorder.

+
+ +
+
+record(code)[source]
+

Record a string to be stored to the output file.

+
+
Parameters
+

code (str) – A string of text.

+
+
+
+ +
+
+record_function(func, args, kw)[source]
+

Record a function call given the function and its +arguments.

+
+ +
+
+register(object, parent=None, trait_name_on_parent='', ignore=None, known=False, script_id=None)[source]
+

Register an object with the recorder. This sets up the +object for recording.

+

By default all traits (except those starting and ending with +‘_’) are recorded. For attributes that are themselves +recordable, one may mark traits with a ‘record’ metadata as +follows:

+
    +
  • If metadata record=False is set, the nested object will not be +recorded.

  • +
  • If record=True, then that object is also recorded if it is +not None.

  • +
+

If the object is a list or dict that is marked with +record=True, the list is itself not listened to for changes +but all its contents are registered.

+

If the object has a trait named recorder then this recorder +instance will be set to it if possible.

+
+
Parameters
+
    +
  • object (Instance(HasTraits)) – The object to register in the registry.

  • +
  • parent (Instance(HasTraits)) – An optional parent object in which object is contained

  • +
  • trait_name_on_parent (str) – An optional trait name of the object in the parent.

  • +
  • ignore (list(str)) – An optional list of trait names on the object to be +ignored.

  • +
  • known (bool) – Optional specification if the object id is known on the +interpreter. This is needed if you are manually injecting +code to define/create an object.

  • +
  • script_id (str) – Optionally specify a script_id to use for this object. It +is not guaranteed that this ID will be used since it may +already be in use.

  • +
+
+
+
+ +
+
+save(file)[source]
+

Save the recorded lines to the given file. It does not close +the file.

+
+ +
+
+ui_save()[source]
+

Save recording to file, pop up a UI dialog to find out where +and close the file when done.

+
+ +
+
+unregister(object)[source]
+

Unregister the given object from the recorder. This inverts +the logic of the register(…) method.

+
+ +
+
+write_script_id_in_namespace(script_id)[source]
+

If a script_id is not known in the current script’s namespace, +this sets it using the path of the object or actually +instantiating it. If this is not possible (since the script_id +matches no existing object), nothing is recorded but the +framework is notified that the particular script_id is available +in the namespace. This is useful when you want to inject code +in the namespace to create a particular object.

+
+ +
+ +
+
+exception apptools.scripting.recorder.RecorderError[source]
+

Bases: Exception

+
+ +
+
+

apptools.scripting.recorder_with_ui module

+

A Recorder subclass that presents a simple user interface.

+
+
+class apptools.scripting.recorder_with_ui.CloseHandler[source]
+

Bases: traitsui.handler.Handler

+

This class cleans up after the UI for the recorder is closed.

+
+
+close(info, is_ok)[source]
+

This method is invoked when the user closes the UI.

+
+ +
+ +
+
+class apptools.scripting.recorder_with_ui.RecorderWithUI[source]
+

Bases: apptools.scripting.recorder.Recorder

+

This class represents a Recorder but with a simple user interface.

+
+
+on_ui_close()[source]
+

Called from the CloseHandler when the UI is closed. This +method basically stops the recording.

+
+ +
+ +
+
+

apptools.scripting.util module

+

Simple utility functions provided by the scripting API.

+
+
+apptools.scripting.util.start_recording(object, ui=True, **kw)[source]
+

Convenience function to start recording. Returns the recorder.

+
+
Parameters
+
    +
  • object (object to record.) –

  • +
  • ui (bool specifying if a UI is to be shown or not) –

  • +
  • kw (Keyword arguments to pass to the register function of the) –

  • +
  • recorder.

  • +
+
+
+
+ +
+
+apptools.scripting.util.stop_recording(object, save=True)[source]
+

Stop recording the object. If save is True, this will pop up +a UI to ask where to save the script.

+
+ +
+
+

Module contents

+

Automatic script recording framework, part of the AppTools project +of the Enthought Tool Suite.

+
+
+ + +
+
+
+ +
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/api/apptools.selection.html b/api/apptools.selection.html new file mode 100644 index 000000000..a8950c469 --- /dev/null +++ b/api/apptools.selection.html @@ -0,0 +1,482 @@ + + + + + + + apptools.selection package — Apptools Documentation + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +
+

apptools.selection package

+
+

Submodules

+
+
+

apptools.selection.api module

+
+
+

apptools.selection.errors module

+
+
+exception apptools.selection.errors.IDConflictError(provider_id)[source]
+

Bases: Exception

+

Raised when a provider is added and its ID is already registered.

+
+ +
+
+exception apptools.selection.errors.ListenerNotConnectedError(provider_id, listener)[source]
+

Bases: Exception

+

Raised when a listener that was never connected is disconnected.

+
+ +
+
+exception apptools.selection.errors.ProviderNotRegisteredError(provider_id)[source]
+

Bases: Exception

+

Raised when a provider is requested by ID and not found.

+
+ +
+
+

apptools.selection.i_selection module

+
+
+class apptools.selection.i_selection.IListSelection[source]
+

Bases: apptools.selection.i_selection.ISelection

+

Selection for ordered sequences of items.

+
+
+indices = List
+

Indices of the selected objects in the selection provider.

+
+ +
+
+items = List
+

Selected objects.

+
+ +
+ +
+
+class apptools.selection.i_selection.ISelection[source]
+

Bases: traits.has_traits.Interface

+

Collection of selected items.

+
+
+is_empty()[source]
+

Is the selection empty?

+
+ +
+
+provider_id = Str
+

ID of the selection provider that created this selection object.

+
+ +
+ +
+
+

apptools.selection.i_selection_provider module

+
+
+class apptools.selection.i_selection_provider.ISelectionProvider[source]
+

Bases: traits.has_traits.Interface

+

Source of selections.

+
+
+get_selection()[source]
+

Return the current selection.

+
+
Returns
+

+
selection – ISelection

Object representing the current selection.

+
+
+

+
+
+
+ +
+
+provider_id = Str()
+

Unique ID identifying the provider.

+
+ +
+
+selection = Event
+

Event triggered when the selection changes. +The content of the event is an ISelection instance.

+
+ +
+
+set_selection(items, ignore_missing=False)[source]
+

Set the current selection to the given items.

+

If ignore_missing is True, items that are not available in the +selection provider are silently ignored. If it is False (default), +an ValueError should be raised.

+
+
Parameters
+
    +
  • -- list (items) – List of items to be selected.

  • +
  • -- bool (ignore_missing) – If False (default), the provider raises an exception if any +of the items in items is not available to be selected. +Otherwise, missing elements are silently ignored, and the rest +is selected.

  • +
+
+
+
+ +
+ +
+
+

apptools.selection.list_selection module

+
+
+class apptools.selection.list_selection.ListSelection[source]
+

Bases: traits.has_traits.HasTraits

+

Selection for ordered sequences of items.

+

This is the default implementation of the IListSelection +interface.

+
+
+classmethod from_available_items(provider_id, selected, all_items)[source]
+

Create a list selection given a list of all available items.

+

Fills in the required information (in particular, the indices) based +on a list of selected items and a list of all available items.

+
+

Note

+
    +
  • The list of available items must not contain any duplicate items.

  • +
  • It is expected that selected is populated by items in +all_items.

  • +
+
+
+ +
+
+indices = List
+

Indices of the selected objects in the selection provider.

+
+ +
+
+is_empty()[source]
+

Is the selection empty?

+
+ +
+
+items = List
+

Selected objects.

+
+ +
+
+provider_id = Str
+

ID of the selection provider that created this selection object.

+
+ +
+ +
+
+

apptools.selection.selection_service module

+
+
+class apptools.selection.selection_service.SelectionService[source]
+

Bases: traits.has_traits.HasTraits

+

The selection service connects selection providers and listeners.

+

The selection service is a register of selection providers, i.e., objects +that publish their current selection.

+

Selections can be requested actively, by explicitly requesting the current +selection in a provider (get_selection(id)()), or passively by +connecting selection listeners.

+
+
+add_selection_provider(provider)[source]
+

Add a selection provider.

+

The provider is identified by its ID. If a provider with the same +ID has been already registered, an IDConflictError +is raised.

+
+
Parameters
+

-- ISelectionProvider (provider) – The selection provider added to the internal registry.

+
+
+
+ +
+
+connect_selection_listener(provider_id, func)[source]
+

Connect a listener to selection events from a specific provider.

+

The signature if the listener callback is func(i_selection). +The listener is called:

+
    +
  1. When a provider with the given ID is registered, with its initial +selection as argument, or

  2. +
  3. whenever the provider fires a selection event.

  4. +
+

It is perfectly valid to connect a listener before a provider with the +given ID is registered. The listener will remain connected even if +the provider is repeatedly connected and disconnected.

+
+
Parameters
+
    +
  • -- str (provider_id) – The selection provider ID.

  • +
  • -- callable (func) – A callable object that is notified when the selection changes.

  • +
+
+
+
+ +
+
+disconnect_selection_listener(provider_id, func)[source]
+

Disconnect a listener from a specific provider.

+
+
Parameters
+
    +
  • -- str (provider_id) – The selection provider ID.

  • +
  • -- callable (func) – A callable object that is notified when the selection changes.

  • +
+
+
+
+ +
+
+get_selection(provider_id)[source]
+

Return the current selection of the provider with the given ID.

+

If a provider with that ID has not been registered, a +ProviderNotRegisteredError is raised.

+
+
Parameters
+

-- str (provider_id) – The selection provider ID.

+
+
Returns
+

+
selection – ISelection

The current selection of the provider.

+
+
+

+
+
+
+ +
+
+has_selection_provider(provider_id)[source]
+

Has a provider with the given ID been registered?

+
+ +
+
+remove_selection_provider(provider)[source]
+

Remove a selection provider.

+

If the provider has not been registered, a +ProviderNotRegisteredError is raised.

+
+
Parameters
+

-- ISelectionProvider (provider) – The selection provider added to the internal registry.

+
+
+
+ +
+
+set_selection(provider_id, items, ignore_missing=False)[source]
+

Set the current selection in a provider to the given items.

+

If a provider with the given ID has not been registered, a +ProviderNotRegisteredError is raised.

+

If ignore_missing is True, items that are not available in the +selection provider are silently ignored. If it is False (default), +a ValueError should be raised.

+
+
Parameters
+
    +
  • -- str (provider_id) – The selection provider ID.

  • +
  • -- list (items) – List of items to be selected.

  • +
  • -- bool (ignore_missing) – If False (default), the provider raises an exception if any +of the items in items is not available to be selected. +Otherwise, missing elements are silently ignored, and the rest +is selected.

  • +
+
+
+
+ +
+ +
+
+

Module contents

+
+
+ + +
+
+
+ +
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/api/apptools.type_registry.html b/api/apptools.type_registry.html new file mode 100644 index 000000000..276d40e0b --- /dev/null +++ b/api/apptools.type_registry.html @@ -0,0 +1,344 @@ + + + + + + + apptools.type_registry package — Apptools Documentation + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +
+

apptools.type_registry package

+
+

Submodules

+
+
+

apptools.type_registry.api module

+
+
+

apptools.type_registry.type_registry module

+
+
+class apptools.type_registry.type_registry.LazyRegistry[source]
+

Bases: apptools.type_registry.type_registry.TypeRegistry

+

A type registry that will lazily import the registered objects.

+

Register ‘__module__:__name__’ strings for the lazily imported objects. +These will only be imported when the matching type is looked up. The module +name must be a fully-qualified absolute name with all of the parent +packages specified.

+
+
+lookup_by_type(typ)[source]
+

Look up the registered object for a type.

+
+ +
+ +
+
+class apptools.type_registry.type_registry.TypeRegistry[source]
+

Bases: object

+

Register objects for types.

+

Each type maintains a stack of registered objects that can be pushed and +popped.

+
+
+lookup(instance)[source]
+

Look up the registered object for the given instance.

+
+
Parameters
+

instance (object) – An instance of a possibly registered type.

+
+
Returns
+

obj – The registered object for the type of the instance, one of the +type’s superclasses, or else one of the ABCs the type implements.

+
+
Return type
+

object

+
+
Raises
+

KeyError if the instance's type has not been registered.

+
+
+
+ +
+
+lookup_all(instance)[source]
+

Look up all the registered objects for the given instance.

+
+
Parameters
+

instance (object) – An instance of a possibly registered type.

+
+
Returns
+

objs – The list of registered objects for the instance. If the given +instance is not registered, its superclasses are searched. If none +of the superclasses are registered, search the possible ABCs.

+
+
Return type
+

list of objects

+
+
Raises
+

KeyError if the instance's type has not been registered.

+
+
+
+ +
+
+lookup_all_by_type(typ)[source]
+

Look up all the registered objects for a type.

+
+
Parameters
+

typ (type) –

+
+
Returns
+

objs – The list of registered objects for the type. If the given type is +not registered, its superclasses are searched. If none of the +superclasses are registered, search the possible ABCs.

+
+
Return type
+

list of objects

+
+
Raises
+

KeyError if the type has not been registered.

+
+
+
+ +
+
+lookup_by_type(typ)[source]
+

Look up the registered object for a type.

+
+
Parameters
+

typ (type) –

+
+
Returns
+

obj – The registered object for the type, one of its superclasses, or +else one of the ABCs it implements.

+
+
Return type
+

object

+
+
Raises
+

KeyError if the type has not been registered.

+
+
+
+ +
+
+pop(typ)[source]
+

Pop a registered object for the given type.

+
+
Parameters
+

typ (type or '__module__:__name__' string for a type) –

+
+
Returns
+

obj – The last registered object for the type.

+
+
Return type
+

object

+
+
Raises
+

KeyError if the type is not registered.

+
+
+
+ +
+
+push(typ, obj)[source]
+

Push an object onto the stack for the given type.

+
+
Parameters
+
    +
  • typ (type or '__module__:__name__' string for a type) –

  • +
  • obj (object) – The object to register.

  • +
+
+
+
+ +
+
+push_abc(typ, obj)[source]
+

Push an object onto the stack for the given ABC.

+
+
Parameters
+
    +
  • typ (abc.ABCMeta) –

  • +
  • obj (object) –

  • +
+
+
+
+ +
+ +
+
+apptools.type_registry.type_registry.get_mro(obj_class)[source]
+

Get a reasonable method resolution order of a class and its +superclasses for both old-style and new-style classes.

+
+ +
+
+

Module contents

+
+
+ + +
+
+
+
+ +
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/api/apptools.undo.action.html b/api/apptools.undo.action.html new file mode 100644 index 000000000..375395026 --- /dev/null +++ b/api/apptools.undo.action.html @@ -0,0 +1,241 @@ + + + + + + + apptools.undo.action package — Apptools Documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ + + + +
+
+ +
+
+ +
+

apptools.undo.action package

+
+

Submodules

+
+
+

apptools.undo.action.abstract_command_stack_action module

+
+
+class apptools.undo.action.abstract_command_stack_action.AbstractCommandStackAction(**traits)[source]
+

Bases: pyface.action.action.Action

+

The abstract base class for all actions that operate on a command +stack.

+
+
+destroy()[source]
+

Called when the action is no longer required.

+

By default this method does nothing, but this would be a great place to +unhook trait listeners etc.

+
+ +
+ +
+
+

apptools.undo.action.api module

+
+
+

apptools.undo.action.command_action module

+
+
+class apptools.undo.action.command_action.CommandAction[source]
+

Bases: pyface.action.action.Action

+

The CommandAction class is an Action class that wraps undo/redo +commands. It is only useful for commands that do not take any arguments or +return any result.

+
+
+perform(event)[source]
+

This is reimplemented to push a new command instance onto the +command stack.

+
+ +
+ +
+
+

apptools.undo.action.redo_action module

+
+
+class apptools.undo.action.redo_action.RedoAction(**traits)[source]
+

Bases: apptools.undo.action.abstract_command_stack_action.AbstractCommandStackAction

+

An action that redos the last command undone of the active command +stack.

+
+
+perform(event)[source]
+

Perform the action.

+
+ +
+ +
+
+

apptools.undo.action.undo_action module

+
+
+class apptools.undo.action.undo_action.UndoAction(**traits)[source]
+

Bases: apptools.undo.action.abstract_command_stack_action.AbstractCommandStackAction

+

An action that undos the last command of the active command stack.

+
+
+perform(event)[source]
+

Perform the action.

+
+ +
+ +
+
+

Module contents

+
+
+ + +
+
+
+ +
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/api/apptools.undo.html b/api/apptools.undo.html new file mode 100644 index 000000000..e78c602c1 --- /dev/null +++ b/api/apptools.undo.html @@ -0,0 +1,468 @@ + + + + + + + apptools.undo package — Apptools Documentation + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +
+

apptools.undo package

+ +
+

Submodules

+
+
+

apptools.undo.abstract_command module

+
+
+class apptools.undo.abstract_command.AbstractCommand[source]
+

Bases: traits.has_traits.HasTraits

+

The AbstractCommand class is an abstract base class that implements the +ICommand interface.

+
+
+do()[source]
+

This is called by the command stack to do the command and to return +any value. The command must save any state necessary for the ‘redo()’ +and ‘undo()’ methods to work. The class’s __init__() must also ensure +that deep copies of any arguments are made if appropriate. It is +guaranteed that this will only ever be called once and that it will be +called before any call to ‘redo()’ or ‘undo()’.

+
+ +
+
+merge(other)[source]
+

This is called by the command stack to try and merge another +command with this one. True is returned if the commands were merged. +‘other’ is the command that is about to be executed. If the commands +are merged then ‘other’ will discarded and not placed on the command +stack. A subsequent undo or redo of this modified command must have +the same effect as the two original commands.

+
+ +
+
+redo()[source]
+

This is called by the command stack to redo the command. Any +returned value will replace the value that the command stack references +from the original call to ‘do()’ or previous call to ‘redo()’.

+
+ +
+
+undo()[source]
+

This is called by the command stack to undo the command.

+
+ +
+ +
+
+

apptools.undo.api module

+
+
+

apptools.undo.command_stack module

+
+
+class apptools.undo.command_stack.CommandStack[source]
+

Bases: traits.has_traits.HasTraits

+

The CommandStack class is the default implementation of the +ICommandStack interface.

+
+
+begin_macro(name)[source]
+

This begins a macro by creating an empty command with the given +‘name’. All subsequent calls to ‘push()’ create commands that will be +children of the empty command until the next call to ‘end_macro()’. +Macros may be nested. The stack is disabled (ie. nothing can be undone +or redone) while a macro is being created (ie. while there is an +outstanding ‘end_macro()’ call).

+
+ +
+
+clear()[source]
+

This clears the stack, without undoing or redoing any commands, and +leaves the stack in a clean state. It is typically used when all +changes to the data have been abandoned.

+
+ +
+
+end_macro()[source]
+

This ends a macro.

+
+ +
+
+push(command)[source]
+

This executes a command and saves it on the command stack so that +it can be subsequently undone and redone. ‘command’ is an instance +that implements the ICommand interface. Its ‘do()’ method is called +to execute the command. If any value is returned by ‘do()’ then it is +returned by ‘push()’.

+
+ +
+
+redo(sequence_nr=0)[source]
+

If ‘sequence_nr’ is 0 then the last command that was undone is +redone and any result returned. Otherwise commands are redone up to +and including the given ‘sequence_nr’ and any result of the last of +these is returned.

+
+ +
+
+undo(sequence_nr=0)[source]
+

If ‘sequence_nr’ is 0 then the last command is undone. Otherwise +commands are undone up to and including the given ‘sequence_nr’.

+
+ +
+ +
+
+

apptools.undo.i_command module

+
+
+class apptools.undo.i_command.ICommand[source]
+

Bases: traits.has_traits.Interface

+

The command interface. The state of the data can be changed by passing +an instance that implements this interface to the ‘push()’ method of a +command stack along with any arguments.

+
+
+do()[source]
+

This is called by the command stack to do the command and to return +any value. The command must save any state necessary for the ‘redo()’ +and ‘undo()’ methods to work. The class’s __init__() must also ensure +that deep copies of any arguments are made if appropriate. It is +guaranteed that this will only ever be called once and that it will be +called before any call to ‘redo()’ or ‘undo()’.

+
+ +
+
+merge(other)[source]
+

This is called by the command stack to try and merge another +command with this one. True is returned if the commands were merged. +‘other’ is the command that is about to be executed. If the commands +are merged then ‘other’ will discarded and not placed on the command +stack. A subsequent undo or redo of this modified command must have +the same effect as the two original commands.

+
+ +
+
+redo()[source]
+

This is called by the command stack to redo the command. Any +returned value will replace the value that the command stack references +from the original call to ‘do()’ or previous call to ‘redo()’.

+
+ +
+
+undo()[source]
+

This is called by the command stack to undo the command.

+
+ +
+ +
+
+

apptools.undo.i_command_stack module

+
+
+class apptools.undo.i_command_stack.ICommandStack[source]
+

Bases: traits.has_traits.Interface

+

The command stack interface. A command stack is responsible for +managing the changes to a data model and recording those changes so that +they can be undone or redone.

+
+
+begin_macro(name)[source]
+

This begins a macro by creating an empty command with the given +‘name’. The commands passed to all subsequent calls to ‘push()’ will +be contained in the macro until the next call to ‘end_macro()’. Macros +may be nested. The stack is disabled (ie. nothing can be undone or +redone) while a macro is being created (ie. while there is an +outstanding ‘end_macro()’ call).

+
+ +
+
+clear()[source]
+

This clears the stack, without undoing or redoing any commands, and +leaves the stack in a clean state. It is typically used when all +changes to the data have been abandoned.

+
+ +
+
+end_macro()[source]
+

This ends a macro.

+
+ +
+
+push(command)[source]
+

This executes a command and saves it on the command stack so that +it can be subsequently undone and redone. ‘command’ is an instance +that implements the ICommand interface. Its ‘do()’ method is called +to execute the command. If any value is returned by ‘do()’ then it is +returned by ‘push()’. The command stack will keep a reference to the +result so that it can recognise it as an argument to a subsequent +command (which allows a script to properly save a result needed later).

+
+ +
+
+redo(sequence_nr=0)[source]
+

If ‘sequence_nr’ is 0 then the last command that was undone is +redone and any result returned. Otherwise commands are redone up to +and including the given ‘sequence_nr’ and any result of the last of +these is returned.

+
+ +
+
+undo(sequence_nr=0)[source]
+

If ‘sequence_nr’ is 0 then the last command is undone. Otherwise +commands are undone up to and including the given ‘sequence_nr’.

+
+ +
+ +
+
+

apptools.undo.i_undo_manager module

+
+
+class apptools.undo.i_undo_manager.IUndoManager[source]
+

Bases: traits.has_traits.Interface

+

The undo manager interface. An undo manager is responsible for one or +more command stacks. Typically an application would have a single undo +manager.

+
+
+redo()[source]
+

Redo the last undone command of the active command stack.

+
+ +
+
+undo()[source]
+

Undo the last command of the active command stack.

+
+ +
+ +
+
+

apptools.undo.undo_manager module

+
+
+class apptools.undo.undo_manager.UndoManager[source]
+

Bases: traits.has_traits.HasTraits

+

The UndoManager class is the default implementation of the +IUndoManager interface.

+
+
+redo()[source]
+

Redo the last undone command of the active command stack.

+
+ +
+
+undo()[source]
+

Undo the last command of the active command stack.

+
+ +
+ +
+
+

Module contents

+

Supports undoing and scripting application commands. +Part of the AppTools project of the Enthought Tool Suite.

+
+
+ + +
+
+
+ +
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/api/modules.html b/api/modules.html new file mode 100644 index 000000000..2cef3b579 --- /dev/null +++ b/api/modules.html @@ -0,0 +1,280 @@ + + + + + + + apptools — Apptools Documentation + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +
+

apptools

+
+ +
+
+ + +
+
+
+
+
+ +

Previous topic

+

API documentation

+

Next topic

+

apptools package

+

This Page

+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/genindex.html b/genindex.html new file mode 100644 index 000000000..abcc35441 --- /dev/null +++ b/genindex.html @@ -0,0 +1,1536 @@ + + + + + + + + Index — Apptools Documentation + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ + +

Index

+ +
+ A + | B + | C + | D + | E + | F + | G + | H + | I + | J + | K + | L + | M + | N + | O + | P + | Q + | R + | S + | T + | U + | V + | W + +
+

A

+ + + +
+ +

B

+ + + +
+ +

C

+ + + +
+ +

D

+ + + +
+ +

E

+ + + +
+ +

F

+ + + +
+ +

G

+ + + +
+ +

H

+ + + +
+ +

I

+ + + +
+ +

J

+ + +
+ +

K

+ + +
+ +

L

+ + + +
+ +

M

+ + + +
+ +

N

+ + + +
+ +

O

+ + + +
+ +

P

+ + + +
+ +

Q

+ + +
+ +

R

+ + + +
+ +

S

+ + + +
+ +

T

+ + + +
+ +

U

+ + + +
+ +

V

+ + + +
+ +

W

+ + + +
+ + + +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 000000000..53e7b1311 --- /dev/null +++ b/index.html @@ -0,0 +1,179 @@ + + + + + + + AppTools Documentation — Apptools Documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + + +
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/io/introduction.html b/io/introduction.html new file mode 100644 index 000000000..1f4d10d3d --- /dev/null +++ b/io/introduction.html @@ -0,0 +1,169 @@ + + + + + + + File I/O — Apptools Documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +
+

File I/O

+

The apptools.io package provides a traited File object provides +properties and methods for common file path manipulation operations. Much of +this functionality was implemented before Python 3 pathlib standard library +became available to provide similar support. For new code we encourage users +to investigate if pathlib can satisfy their use cases before they turn to +the apptools.io File object

+
+

HDF5 File Support

+

The apptools.io.h5 sub-package provides a wrapper around PyTables +with a dictionary-style mapping.

+
+
+ + +
+
+
+
+
+ +

Table of Contents

+ + +

Previous topic

+

Naming

+

Next topic

+

API documentation

+

This Page

+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/naming/Introduction.html b/naming/Introduction.html new file mode 100644 index 000000000..6c5f08423 --- /dev/null +++ b/naming/Introduction.html @@ -0,0 +1,154 @@ + + + + + + + Naming — Apptools Documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +
+

Naming

+

apptools.naming package is a Python implementation of the Naming portion of the Java +Naming and Directory Interface, +including specific implementations for a heirarchy of Python objects. You can +also find the Java JNDI tutorial here.

+
+ + +
+
+
+
+
+ +

Previous topic

+

The selection service

+

Next topic

+

File I/O

+

This Page

+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/objects.inv b/objects.inv new file mode 100644 index 0000000000000000000000000000000000000000..784d3243de5585acd54760ce4354ddbbff8cf933 GIT binary patch literal 4807 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 + + + + + + Preferences — Apptools Documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +
+

Preferences

+

The preferences package provides a simple API for managing application +preferences. The classes in the package are implemented using a layered +approach where the lowest layer provides access to the raw preferences +mechanism and each layer on top providing more convenient ways to get and set +preference values.

+
+

The Basic Preferences Mechanism

+

Lets start by taking a look at the lowest layer which consists of the +IPreferences interface and its default implementation in the Preferences +class. This layer implements the basic preferences system which is a +hierarchical arrangement of preferences ‘nodes’ (where each node is simply an +object that implements the IPreferences interface). Nodes in the hierarchy can +contain preference settings and/or child nodes. This layer also provides a +default way to read and write preferences from the filesystem using the +excellent ConfigObj package.

+

This all sounds a bit complicated but, believe me, it isn’t! To prove it +(hopefully) lets look at an example. Say I have the following preferences in +a file ‘example.ini’:

+
[acme.ui]
+bgcolor = blue
+width = 50
+ratio = 1.0
+visible = True
+
+[acme.ui.splash_screen]
+image = splash
+fgcolor = red
+
+
+

I can create a preferences hierarchy from this file by:

+
>>> from apptools.preferences.api import Preferences
+>>> preferences = Preferences(filename='example.ini')
+>>> preferences.dump()
+
+ Node() {}
+   Node(acme) {}
+     Node(ui) {'bgcolor': 'blue', 'width': '50', 'ratio': '1.0', 'visible': 'True'}
+       Node(splash_screen) {'image': 'splash', 'fgcolor': 'red'}
+
+
+

The ‘dump’ method (useful for debugging etc) simply ‘pretty prints’ a +preferences hierarchy. The dictionary next to each node contains the node’s +actual preferences. In this case, the root node (the node with no name) is the +preferences object that we created. This node now has one child node ‘acme’, +which contains no preferences. The ‘acme’ node has one child, ‘ui’, which +contains some preferences (e.g. ‘bgcolor’) and also a child node +‘splash_screen’ which also contains preferences (e.g. ‘image’).

+

To look up a preference we use:

+
>>> preferences.get('acme.ui.bgcolor')
+'blue'
+
+
+

If no such preferences exists then, by default, None is returned:

+
>>> preferences.get('acme.ui.bogus') is None
+True
+
+
+

You can also specify an explicit default value:

+
>>> preferences.get('acme.ui.bogus', 'fred')
+'fred'
+
+
+

To set a preference we use:

+
>>> preferences.set('acme.ui.bgcolor', 'red')
+>>> preferences.get('acme.ui.bgcolor')
+'red'
+
+
+

And to make sure the preferences are saved back to disk:

+
>>> preferences.flush()
+
+
+

To add a new preference value we simply set it:

+
>>> preferences.set('acme.ui.fgcolor', 'black')
+>>> preferences.get('acme.ui.fgcolor')
+'black'
+
+
+

Any missing nodes in a call to ‘set’ are created automatically, hence:

+
>>> preferences.set('acme.ui.button.fgcolor', 'white')
+>>> preferences.get('acme.ui.button.fgcolor')
+'white'
+
+
+

Preferences can also be ‘inherited’. e.g. Notice that the ‘splash_screen’ +node does not contain a ‘bgcolor’ preference, and hence:

+
>>> preferences.get('acme.ui.splash_screen.bgcolor') is None
+True
+
+
+

But if we allow the ‘inheritance’ of preference values then:

+
>>> preferences.get('acme.ui.splash_screen.bgcolor', inherit=True)
+'red'
+
+
+

By using ‘inheritance’ here the preferences system will try the following +preferences:

+
'acme.ui.splash_screen.bgcolor'
+'acme.ui.bgcolor'
+'acme.bgcolor'
+'bgcolor'
+
+
+
+

Strings, Glorious Strings

+

At this point it is worth mentioning that preferences are always stored and +returned as strings. This is because of the limitations of the traditional +‘.ini’ file format i.e. they don’t contain any type information! Now before you +start panicking, this doesn’t mean that all of your preferences have to be +strings! Currently the preferences system allows, strings(!), booleans, ints, +longs, floats and complex numbers. When you store a non-string value it gets +converted to a string for you, but you always get a string back:

+
>>> preferences.get('acme.ui.width')
+'50'
+>>> preferences.set('acme.ui.width', 100)
+>>> preferences.get('acme.ui.width')
+'100'
+
+>>> preferences.get('acme.ui.visible')
+'True'
+>>> preferences.set('acme.ui.visible', False)
+>>> preferences.get('acme.ui.visible')
+'False'
+
+
+

This is obviously not terribly convenient, and so the following section +discusses how we associate type information with our preferences to make +getting and setting them more natural.

+
+
+
+

Preferences and Types

+

As mentioned previously, we would like to be able to get and set non-string +preferences in a more convenient way. This is where the PreferencesHelper +class comes in.

+

Let’s take another look at ‘example.ini’:

+
[acme.ui]
+bgcolor = blue
+width = 50
+ratio = 1.0
+visible = True
+
+[acme.ui.splash_screen]
+image = splash
+fgcolor = red
+
+
+

Say, I am interested in the preferences in the ‘acme.ui’ section. I can use a +preferences helper as follows:

+
from apptools.preferences.api import PreferencesHelper
+
+class SplashScreenPreferences(PreferencesHelper):
+    """ A preferences helper for the splash screen. """
+
+    preferences_path = 'acme.ui'
+
+    bgcolor = Str
+    width   = Int
+    ratio   = Float
+    visible = Bool
+
+>>> preferences = Preferences(filename='example.ini')
+>>> helper = SplashScreenPreferences(preferences=preferences)
+>>> helper.bgcolor
+'blue'
+>>> helper.width
+50
+>>> helper.ratio
+1.0
+>>> helper.visible
+True
+
+
+

And, obviously, I can set the value of the preferences via the helper too:

+
>>> helper.ratio = 0.5
+
+
+

And if you want to prove to yourself it really did set the preference:

+
>>> preferences.get('acme.ui.ratio')
+'0.5'
+
+
+

Using a preferences helper you also get notified via the usual trait +mechanism when the preferences are changed (either via the helper or via the +preferences node directly:

+
def listener(obj, trait_name, old, new):
+    print(trait_name, old, new)
+
+>>> helper.on_trait_change(listener)
+>>> helper.ratio = 0.75
+ratio 0.5 0.75
+>>> preferences.set('acme.ui.ratio', 0.33)
+ratio 0.75 0.33
+
+
+
+
+

Scoped Preferences

+

In many applications the idea of preferences scopes is useful. In a scoped +system, an actual preference value can be stored in any scope and when a call +is made to the ‘get’ method the scopes are searched in order of precedence.

+

The default implementation (in the ScopedPreferences class) provides two +scopes by default:

+
    +
  1. The application scope

  2. +
+

This scope stores itself in the ‘ETSConfig.application_home’ directory. This +scope is generally used when setting any user preferences.

+
    +
  1. The default scope

  2. +
+

This scope is transient (i.e. it does not store itself anywhere). This scope +is generally used to load any predefined default values into the preferences +system.

+

If you are happy with the default arrangement, then using the scoped +preferences is just like using the plain old non-scoped version:

+
>>> from apptools.preferences.api import ScopedPreferences
+>>> preferences = ScopedPreferences(filename='example.ini')
+>>> preferences.load('example.ini')
+>>> preferences.dump()
+
+ Node() {}
+   Node(application) {}
+     Node(acme) {}
+       Node(ui) {'bgcolor': 'blue', 'width': '50', 'ratio': '1.0', 'visible': 'True'}
+         Node(splash_screen) {'image': 'splash', 'fgcolor': 'red'}
+   Node(default) {}
+
+
+

Here you can see that the root node now has a child node representing each +scope.

+

When we are getting and setting preferences using scopes we generally want the +following behaviour:

+

a) When we get a preference we want to look it up in each scope in order. The +first scope that contains a value ‘wins’.

+

b) When we set a preference, we want to set it in the first scope. By default +this means that when we set a preference it will be set in the application +scope. This is exactly what we want as the application scope is the scope that +is persistent.

+

So usually, we just use the scoped preferences as before:

+
>>> preferences.get('acme.ui.bgcolor')
+'blue'
+>>> preferences.set('acme.ui.bgcolor', 'red')
+>>> preferences.dump()
+
+ Node() {}
+   Node(application) {}
+     Node(acme) {}
+       Node(ui) {'bgcolor': 'red', 'width': '50', 'ratio': '1.0', 'visible': 'True'}
+         Node(splash_screen) {'image': 'splash', 'fgcolor': 'red'}
+   Node(default) {}
+
+
+

And, conveniently, preference helpers work just the same with scoped +preferences too:

+
>>> helper = SplashScreenPreferences(preferences=preferences)
+>>> helper.bgcolor
+'red'
+>>> helper.width
+50
+>>> helper.ratio
+1.0
+>>> helper.visible
+True
+
+
+
+

Accessing a particular scope

+

Should you care about getting or setting a preference in a particular scope +then you use the following syntax:

+
>>> preferences.set('default/acme.ui.bgcolor', 'red')
+>>> preferences.get('default/acme.ui.bgcolor')
+'red'
+>>> preferences.dump()
+
+ Node() {}
+   Node(application) {}
+     Node(acme) {}
+       Node(ui) {'bgcolor': 'red', 'width': '50', 'ratio': '1.0', 'visible': 'True'}
+         Node(splash_screen) {'image': 'splash', 'fgcolor': 'red'}
+   Node(default) {}
+     Node(acme) {}
+       Node(ui) {'bgcolor': 'red'}
+
+
+

You can also get hold of a scope via:

+
>>> default = preferences.get_scope('default')
+
+
+

And then perform any of the usual operations on it.

+
+
+
+

Further Reading

+

So that’s a quick tour around the basic useage of the preferences API. For more +information about what is provided take a look at the API documentation.

+

If you are using Envisage to build your applications then you might also be +interested in the Preferences in Envisage section.

+
+
+ + +
+
+
+
+ +
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/preferences/PreferencesInEnvisage.html b/preferences/PreferencesInEnvisage.html new file mode 100644 index 000000000..cba25dc9f --- /dev/null +++ b/preferences/PreferencesInEnvisage.html @@ -0,0 +1,189 @@ + + + + + + + Preferences in Envisage — Apptools Documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +
+

Preferences in Envisage

+

This section discusses how an Envisage application uses the preferences +mechanism. Envisage tries not to dictate too much, and so this describes the +default behaviour, but you are free to override it as desired.

+

Envisage uses the default implementation of the ScopedPreferences class which +is made available via the application’s ‘preferences’ trait:

+
>>> application = Application(id='myapplication')
+>>> application.preferences.set('acme.ui.bgcolor', 'yellow')
+>>> application.preferences.get('acme.ui.bgcolor')
+'yellow'
+
+
+

Hence, you use the Envisage preferences just like you would any other scoped +preferences.

+

It also registers itself as the default preferences node used by the +PreferencesHelper class. Hence you don’t need to provide a preferences node +explicitly to your helper:

+
>>> helper = SplashScreenPreferences()
+>>> helper.bgcolor
+'blue'
+>>> helper.width
+100
+>>> helper.ratio
+1.0
+>>> helper.visible
+True
+
+
+

The only extra thing that Envisage does for you is to provide an extension +point that allows you to contribute any number of ‘.ini’ files that are +loaded into the default scope when the application is started.

+

e.g. To contribute a preference file for my plugin I might use:

+
class MyPlugin(Plugin):
+    ...
+
+    @contributes_to('envisage.preferences')
+    def get_preferences(self, application):
+        return ['pkgfile://mypackage:preferences.ini']
+
+
+
+ + +
+
+
+
+
+ +

Previous topic

+

Preferences

+

Next topic

+

Automatic script recording

+

This Page

+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/py-modindex.html b/py-modindex.html new file mode 100644 index 000000000..7b3cbde79 --- /dev/null +++ b/py-modindex.html @@ -0,0 +1,692 @@ + + + + + + + Python Module Index — Apptools Documentation + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ + +

Python Module Index

+ +
+ a +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
+ a
+ apptools +
    + apptools.io +
    + apptools.io.api +
    + apptools.io.file +
    + apptools.io.h5 +
    + apptools.io.h5.dict_node +
    + apptools.io.h5.file +
    + apptools.io.h5.table_node +
    + apptools.io.h5.utils +
    + apptools.logger +
    + apptools.logger.agent +
    + apptools.logger.agent.attachments +
    + apptools.logger.agent.quality_agent_mailer +
    + apptools.logger.agent.quality_agent_view +
    + apptools.logger.api +
    + apptools.logger.custom_excepthook +
    + apptools.logger.log_point +
    + apptools.logger.log_queue_handler +
    + apptools.logger.logger +
    + apptools.logger.plugin +
    + apptools.logger.plugin.logger_plugin +
    + apptools.logger.plugin.logger_preferences +
    + apptools.logger.plugin.logger_service +
    + apptools.logger.plugin.view +
    + apptools.logger.plugin.view.logger_preferences_page +
    + apptools.logger.plugin.view.logger_view +
    + apptools.logger.ring_buffer +
    + apptools.naming +
    + apptools.naming.address +
    + apptools.naming.api +
    + apptools.naming.binding +
    + apptools.naming.context +
    + apptools.naming.dir_context +
    + apptools.naming.dynamic_context +
    + apptools.naming.exception +
    + apptools.naming.initial_context +
    + apptools.naming.initial_context_factory +
    + apptools.naming.naming_event +
    + apptools.naming.naming_manager +
    + apptools.naming.object_factory +
    + apptools.naming.object_serializer +
    + apptools.naming.py_context +
    + apptools.naming.py_object_factory +
    + apptools.naming.pyfs_context +
    + apptools.naming.pyfs_context_factory +
    + apptools.naming.pyfs_initial_context_factory +
    + apptools.naming.pyfs_object_factory +
    + apptools.naming.pyfs_state_factory +
    + apptools.naming.reference +
    + apptools.naming.referenceable +
    + apptools.naming.referenceable_state_factory +
    + apptools.naming.state_factory +
    + apptools.naming.trait_defs +
    + apptools.naming.trait_defs.api +
    + apptools.naming.trait_defs.naming_traits +
    + apptools.naming.unique_name +
    + apptools.persistence +
    + apptools.persistence.file_path +
    + apptools.persistence.project_loader +
    + apptools.persistence.state_pickler +
    + apptools.persistence.updater +
    + apptools.persistence.version_registry +
    + apptools.persistence.versioned_unpickler +
    + apptools.preferences +
    + apptools.preferences.api +
    + apptools.preferences.i_preferences +
    + apptools.preferences.package_globals +
    + apptools.preferences.preference_binding +
    + apptools.preferences.preferences +
    + apptools.preferences.preferences_helper +
    + apptools.preferences.scoped_preferences +
    + apptools.preferences.ui +
    + apptools.preferences.ui.api +
    + apptools.preferences.ui.i_preferences_page +
    + apptools.preferences.ui.preferences_manager +
    + apptools.preferences.ui.preferences_node +
    + apptools.preferences.ui.preferences_page +
    + apptools.preferences.ui.tree_item +
    + apptools.preferences.ui.widget_editor +
    + apptools.scripting +
    + apptools.scripting.api +
    + apptools.scripting.package_globals +
    + apptools.scripting.recordable +
    + apptools.scripting.recorder +
    + apptools.scripting.recorder_with_ui +
    + apptools.scripting.util +
    + apptools.selection +
    + apptools.selection.api +
    + apptools.selection.errors +
    + apptools.selection.i_selection +
    + apptools.selection.i_selection_provider +
    + apptools.selection.list_selection +
    + apptools.selection.selection_service +
    + apptools.type_registry +
    + apptools.type_registry.api +
    + apptools.type_registry.type_registry +
    + apptools.undo +
    + apptools.undo.abstract_command +
    + apptools.undo.action +
    + apptools.undo.action.abstract_command_stack_action +
    + apptools.undo.action.api +
    + apptools.undo.action.command_action +
    + apptools.undo.action.redo_action +
    + apptools.undo.action.undo_action +
    + apptools.undo.api +
    + apptools.undo.command_stack +
    + apptools.undo.i_command +
    + apptools.undo.i_command_stack +
    + apptools.undo.i_undo_manager +
    + apptools.undo.undo_manager +
+ + +
+
+
+
+
+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/scripting/introduction.html b/scripting/introduction.html new file mode 100644 index 000000000..add6b06ce --- /dev/null +++ b/scripting/introduction.html @@ -0,0 +1,336 @@ + + + + + + + Automatic script recording — Apptools Documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +
+

Automatic script recording

+

This package provides a very handy and powerful Python script recording +facility. This can be used to:

+
+
    +
  • record all actions performed on a traits based UI into a human +readable, Python script that should be able to recreate your UI +actions.

  • +
  • easily learn the scripting API of an application.

  • +
+
+

This package is not just a toy framework and is powerful enough to +provide full script recording to the Mayavi application. Mayavi is a +powerful 3D visualization tool that is part of ETS.

+
+

The scripting API

+

The scripting API primarily allows you to record UI actions for objects +that have Traits. Technically the framework listens to all trait +changes so will work outside a UI. We do not document the full API +here, the best place to look for that is the +apptools.scripting.recorder module which is reasonably well +documented. We provide a high level overview of the library.

+

The quickest way to get started is to look at a small example.

+
+

A tour by example

+

The following example is taken from the test suite. Consider a set of +simple objects organized in a hierarchy:

+
from traits.api import (HasTraits, Float, Instance,
+        Str, List, Bool, HasStrictTraits, Tuple, PrefixMap, Range,
+        Trait)
+from apptools.scripting.api import (Recorder, recordable,
+    set_recorder)
+
+class Property(HasStrictTraits):
+    color = Tuple(Range(0.0, 1.0), Range(0.0, 1.0), Range(0.0, 1.0))
+    opacity = Range(0.0, 1.0, 1.0)
+    representation = PrefixMap(
+        {"surface": 2, "wireframe": 1, "points": 0},
+        default_value="surface"
+    )
+
+class Toy(HasTraits):
+    color = Str
+    type = Str
+    # Note the use of the trait metadata to ignore this trait.
+    ignore = Bool(False, record=False)
+
+class Child(HasTraits):
+    name = Str('child')
+    age = Float(10.0)
+    # The recorder walks through sub-instances if they are marked
+    # with record=True
+    property = Instance(Property, (), record=True)
+    toy = Instance(Toy, record=True)
+    friends = List(Str)
+
+    # The decorator records the method.
+    @recordable
+    def grow(self, x):
+        """Increase age by x years."""
+        self.age += x
+
+class Parent(HasTraits):
+    children = List(Child, record=True)
+    recorder = Instance(Recorder, record=False)
+
+
+

Using these simple classes we first create a simple object hierarchy as +follows:

+
p = Parent()
+c = Child()
+t = Toy()
+c.toy = t
+p.children.append(c)
+
+
+

Given this hierarchy, we’d like to be able to record a script. To do +this we setup the recording infrastructure:

+
from mayavi.core.recorder import Recorder, set_recorder
+# Create a recorder.
+r = Recorder()
+# Set the global recorder so the decorator works.
+set_recorder(r)
+r.register(p)
+r.recording = True
+
+
+

The key method here is the r.register(p) call above. It looks at +the traits of p and finds all traits and nested objects that specify +a record=True in their trait metadata (all methods starting and +ending with _ are ignored). All sub-objects are in turn registered +with the recorder and so on. Callbacks are attached to traits changes +and these are wired up to produce readable and executable code. The +set_recorder(r) call is also very important and sets the global +recorder so the framework listens to any functions that are decorated +with the recordable decorator.

+

Now lets test this out like so:

+
# The following will be recorded.
+c.name = 'Shiva'
+c.property.representation = 'w'
+c.property.opacity = 0.4
+c.grow(1)
+
+
+

To see what’s been recorded do this:

+
print(r.script)
+
+
+

This prints:

+
child = parent.children[0]
+child.name = 'Shiva'
+child.property.representation = 'wireframe'
+child.property.opacity = 0.40000000000000002
+child.grow(1)
+
+
+

The recorder internally maintains a mapping between objects and unique +names for each object. It also stores the information about the +location of a particular object in the object hierarchy. For example, +the path to the Toy instance in the hierarchy above is +parent.children[0].toy. Since scripting with lists this way can be +tedious, the recorder first instantiates the child:

+
child = parent.children[0]
+
+
+

Subsequent lines use the child attribute. The recorder always tries +to instantiate the object referred to using its path information in this +manner.

+

To record a function or method call one must simply decorate the +function/method with the recordable decorator. Nested recordable +functions are not recorded and trait changes are also not recorded if +done inside a recordable function.

+
+

Note

+
    +
  1. It is very important to note that the global recorder must be set +via the set_recorder method. The recordable decorator +relies on this being set to work.

  2. +
  3. The recordable decorator will work with plain Python classes +and with functions too.

  4. +
+
+

To stop recording do this:

+
r.unregister(p)
+r.recording = False
+
+
+

The r.unregister(p) reverses the r.register(p) call and +unregisters all nested objects as well.

+
+
+

Advanced use cases

+

Here are a few advanced use cases.

+
+
    +
  • The API also provides a RecorderWithUI class that provides a +simple user interface that prints the recorded script and allows the +user to save the script.

  • +
  • Sometimes it is not enough to just record trait changes, one may want +to pass an arbitrary string or command when recording is occurring. +To allow for this, if one defines a recorder trait on the object, +it is set to the current recorder. One can then use this recorder to +do whatever one wants. This is very convenient.

  • +
  • To ignore specific traits one must specify either a record=False +metadata to the trait definition or specify a list of strings to the +register method in the ignore keyword argument.

  • +
  • If you want to use a specific name for an object on the script you +can pass the script_id parameter to the register function.

  • +
+
+

For more details on the recorder itself we suggest reading the module +source code. It is fairly well documented and with the above background +should be enough to get you going.

+
+
+
+ + +
+
+
+
+
+ +

Table of Contents

+ + +

Previous topic

+

Preferences in Envisage

+

Next topic

+

Undo Framework

+

This Page

+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/search.html b/search.html new file mode 100644 index 000000000..fb3726636 --- /dev/null +++ b/search.html @@ -0,0 +1,143 @@ + + + + + + + Search — Apptools Documentation + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +

Search

+
+ +

+ Please activate JavaScript to enable the search + functionality. +

+
+

+ From here you can search these documents. Enter your search + words into the box below and click "search". Note that the search + function will automatically search for all of the words. Pages + containing fewer words won't appear in the result list. +

+
+ + + +
+ +
+ +
+ +
+
+
+
+
+ +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/searchindex.js b/searchindex.js new file mode 100644 index 000000000..2f5fef6a4 --- /dev/null +++ b/searchindex.js @@ -0,0 +1 @@ +Search.setIndex({docnames:["api","api/apptools","api/apptools.io","api/apptools.io.h5","api/apptools.logger","api/apptools.logger.agent","api/apptools.logger.plugin","api/apptools.logger.plugin.view","api/apptools.naming","api/apptools.naming.trait_defs","api/apptools.persistence","api/apptools.preferences","api/apptools.preferences.ui","api/apptools.scripting","api/apptools.selection","api/apptools.type_registry","api/apptools.undo","api/apptools.undo.action","api/modules","index","io/introduction","naming/Introduction","preferences/Preferences","preferences/PreferencesInEnvisage","scripting/introduction","selection/selection","undo/Introduction"],envversion:{"sphinx.domains.c":1,"sphinx.domains.changeset":1,"sphinx.domains.citation":1,"sphinx.domains.cpp":1,"sphinx.domains.javascript":1,"sphinx.domains.math":2,"sphinx.domains.python":1,"sphinx.domains.rst":1,"sphinx.domains.std":1,"sphinx.ext.viewcode":1,sphinx:56},filenames:["api.rst","api/apptools.rst","api/apptools.io.rst","api/apptools.io.h5.rst","api/apptools.logger.rst","api/apptools.logger.agent.rst","api/apptools.logger.plugin.rst","api/apptools.logger.plugin.view.rst","api/apptools.naming.rst","api/apptools.naming.trait_defs.rst","api/apptools.persistence.rst","api/apptools.preferences.rst","api/apptools.preferences.ui.rst","api/apptools.scripting.rst","api/apptools.selection.rst","api/apptools.type_registry.rst","api/apptools.undo.rst","api/apptools.undo.action.rst","api/modules.rst","index.rst","io/introduction.rst","naming/Introduction.rst","preferences/Preferences.rst","preferences/PreferencesInEnvisage.rst","scripting/introduction.rst","selection/selection.rst","undo/Introduction.rst"],objects:{"":{apptools:[1,0,0,"-"]},"apptools.io":{api:[2,0,0,"-"],file:[2,0,0,"-"],h5:[3,0,0,"-"]},"apptools.io.file":{File:[2,1,1,""]},"apptools.io.file.File":{"delete":[2,2,1,""],copy:[2,2,1,""],create_file:[2,2,1,""],create_folder:[2,2,1,""],create_folders:[2,2,1,""],create_package:[2,2,1,""],make_writeable:[2,2,1,""],move:[2,2,1,""]},"apptools.io.h5":{dict_node:[3,0,0,"-"],file:[3,0,0,"-"],table_node:[3,0,0,"-"],utils:[3,0,0,"-"]},"apptools.io.h5.dict_node":{ARRAY_PROXY_KEY:[3,3,1,""],H5DictNode:[3,1,1,""]},"apptools.io.h5.dict_node.H5DictNode":{add_to_h5file:[3,2,1,""],data:[3,2,1,""],flush:[3,2,1,""],is_dict_node:[3,2,1,""],keys:[3,2,1,""]},"apptools.io.h5.file":{H5Attrs:[3,1,1,""],H5File:[3,1,1,""],H5Group:[3,1,1,""],get_atom:[3,5,1,""],h5_group_wrapper:[3,5,1,""],iterator_length:[3,5,1,""]},"apptools.io.h5.file.H5Attrs":{get:[3,2,1,""],items:[3,2,1,""],keys:[3,2,1,""],values:[3,2,1,""]},"apptools.io.h5.file.H5File":{close:[3,2,1,""],create_array:[3,2,1,""],create_dict:[3,2,1,""],create_group:[3,2,1,""],create_table:[3,2,1,""],exists_error:[3,4,1,""],is_open:[3,2,1,""],iteritems:[3,2,1,""],join_path:[3,2,1,""],open:[3,2,1,""],remove_group:[3,2,1,""],remove_node:[3,2,1,""],root:[3,2,1,""],split_path:[3,2,1,""]},"apptools.io.h5.file.H5Group":{children_names:[3,2,1,""],create_array:[3,2,1,""],create_dict:[3,2,1,""],create_group:[3,2,1,""],create_table:[3,2,1,""],filename:[3,2,1,""],iter_groups:[3,2,1,""],name:[3,2,1,""],pathname:[3,2,1,""],remove_group:[3,2,1,""],remove_node:[3,2,1,""],root:[3,2,1,""],subgroup_names:[3,2,1,""]},"apptools.io.h5.table_node":{H5TableNode:[3,1,1,""]},"apptools.io.h5.table_node.H5TableNode":{add_to_h5file:[3,2,1,""],append:[3,2,1,""],is_table_node:[3,2,1,""],ix:[3,2,1,""],keys:[3,2,1,""],to_dataframe:[3,2,1,""]},"apptools.io.h5.utils":{open_h5file:[3,5,1,""]},"apptools.logger":{agent:[5,0,0,"-"],api:[4,0,0,"-"],custom_excepthook:[4,0,0,"-"],log_point:[4,0,0,"-"],log_queue_handler:[4,0,0,"-"],logger:[4,0,0,"-"],plugin:[6,0,0,"-"],ring_buffer:[4,0,0,"-"]},"apptools.logger.agent":{attachments:[5,0,0,"-"],quality_agent_mailer:[5,0,0,"-"],quality_agent_view:[5,0,0,"-"]},"apptools.logger.agent.attachments":{Attachments:[5,1,1,""]},"apptools.logger.agent.attachments.Attachments":{package_any_relevant_files:[5,2,1,""],package_single_project:[5,2,1,""],package_workspace:[5,2,1,""]},"apptools.logger.agent.quality_agent_mailer":{create_email_message:[5,5,1,""]},"apptools.logger.agent.quality_agent_view":{QualityAgentView:[5,1,1,""]},"apptools.logger.agent.quality_agent_view.QualityAgentView":{cc_address:[5,4,1,""],comments:[5,4,1,""],from_address:[5,4,1,""],help_id:[5,4,1,""],include_userdata:[5,4,1,""],msg:[5,4,1,""],priority:[5,4,1,""],service:[5,4,1,""],size:[5,4,1,""],smtp_server:[5,4,1,""],subject:[5,4,1,""],title:[5,4,1,""],to_address:[5,4,1,""]},"apptools.logger.custom_excepthook":{custom_excepthook:[4,5,1,""]},"apptools.logger.log_point":{log_point:[4,5,1,""]},"apptools.logger.log_queue_handler":{LogQueueHandler:[4,1,1,""]},"apptools.logger.log_queue_handler.LogQueueHandler":{emit:[4,2,1,""],get:[4,2,1,""],has_new_records:[4,2,1,""],reset:[4,2,1,""]},"apptools.logger.logger":{LogFileHandler:[4,1,1,""],add_log_queue_handler:[4,5,1,""]},"apptools.logger.plugin":{logger_plugin:[6,0,0,"-"],logger_preferences:[6,0,0,"-"],logger_service:[6,0,0,"-"],view:[7,0,0,"-"]},"apptools.logger.plugin.logger_plugin":{LoggerPlugin:[6,1,1,""]},"apptools.logger.plugin.logger_plugin.LoggerPlugin":{MAIL_FILES:[6,4,1,""],PREFERENCES:[6,4,1,""],PREFERENCES_PAGES:[6,4,1,""],VIEWS:[6,4,1,""],id:[6,4,1,""],mail_files:[6,4,1,""],name:[6,4,1,""],preferences:[6,4,1,""],preferences_pages:[6,4,1,""],start:[6,2,1,""],stop:[6,2,1,""],views:[6,4,1,""]},"apptools.logger.plugin.logger_preferences":{LoggerPreferences:[6,1,1,""]},"apptools.logger.plugin.logger_service":{LoggerService:[6,1,1,""]},"apptools.logger.plugin.logger_service.LoggerService":{create_email_message:[6,2,1,""],save_preferences:[6,2,1,""],send_bug_report:[6,2,1,""],whole_log_text:[6,2,1,""]},"apptools.logger.plugin.view":{logger_preferences_page:[7,0,0,"-"],logger_view:[7,0,0,"-"]},"apptools.logger.plugin.view.logger_preferences_page":{LoggerPreferencesPage:[7,1,1,""]},"apptools.logger.plugin.view.logger_view":{LogRecordAdapter:[7,1,1,""],LoggerView:[7,1,1,""]},"apptools.logger.plugin.view.logger_view.LogRecordAdapter":{column_widths:[7,4,1,""],get_width:[7,2,1,""]},"apptools.logger.plugin.view.logger_view.LoggerView":{activated:[7,4,1,""],activated_text:[7,4,1,""],code_editor:[7,4,1,""],copy_button:[7,4,1,""],formatted_records:[7,4,1,""],id:[7,4,1,""],log_records:[7,4,1,""],log_records_editor:[7,4,1,""],name:[7,4,1,""],reset_button:[7,4,1,""],service:[7,4,1,""],show_button:[7,4,1,""],trait_view:[7,4,1,""],update:[7,2,1,""]},"apptools.logger.ring_buffer":{RingBuffer:[4,1,1,""],RingBufferFull:[4,1,1,""]},"apptools.logger.ring_buffer.RingBuffer":{append:[4,2,1,""],get:[4,2,1,""]},"apptools.logger.ring_buffer.RingBufferFull":{append:[4,2,1,""],get:[4,2,1,""]},"apptools.naming":{address:[8,0,0,"-"],api:[8,0,0,"-"],binding:[8,0,0,"-"],context:[8,0,0,"-"],dir_context:[8,0,0,"-"],dynamic_context:[8,0,0,"-"],exception:[8,0,0,"-"],initial_context:[8,0,0,"-"],initial_context_factory:[8,0,0,"-"],naming_event:[8,0,0,"-"],naming_manager:[8,0,0,"-"],object_factory:[8,0,0,"-"],object_serializer:[8,0,0,"-"],py_context:[8,0,0,"-"],py_object_factory:[8,0,0,"-"],pyfs_context:[8,0,0,"-"],pyfs_context_factory:[8,0,0,"-"],pyfs_initial_context_factory:[8,0,0,"-"],pyfs_object_factory:[8,0,0,"-"],pyfs_state_factory:[8,0,0,"-"],reference:[8,0,0,"-"],referenceable:[8,0,0,"-"],referenceable_state_factory:[8,0,0,"-"],state_factory:[8,0,0,"-"],trait_defs:[9,0,0,"-"],unique_name:[8,0,0,"-"]},"apptools.naming.address":{Address:[8,1,1,""]},"apptools.naming.binding":{Binding:[8,1,1,""]},"apptools.naming.context":{Context:[8,1,1,""]},"apptools.naming.context.Context":{INITIAL_CONTEXT_FACTORY:[8,4,1,""],OBJECT_FACTORIES:[8,4,1,""],STATE_FACTORIES:[8,4,1,""],bind:[8,2,1,""],create_subcontext:[8,2,1,""],destroy_subcontext:[8,2,1,""],get_unique_name:[8,2,1,""],is_context:[8,2,1,""],list_bindings:[8,2,1,""],list_names:[8,2,1,""],lookup:[8,2,1,""],lookup_binding:[8,2,1,""],lookup_context:[8,2,1,""],rebind:[8,2,1,""],rename:[8,2,1,""],search:[8,2,1,""],unbind:[8,2,1,""]},"apptools.naming.dir_context":{DirContext:[8,1,1,""]},"apptools.naming.dir_context.DirContext":{find_bindings:[8,2,1,""],get_attributes:[8,2,1,""],set_attributes:[8,2,1,""]},"apptools.naming.dynamic_context":{DynamicContext:[8,1,1,""]},"apptools.naming.exception":{InvalidNameError:[8,6,1,""],NameAlreadyBoundError:[8,6,1,""],NameNotFoundError:[8,6,1,""],NamingError:[8,6,1,""],NotContextError:[8,6,1,""],OperationNotSupportedError:[8,6,1,""]},"apptools.naming.initial_context":{InitialContext:[8,5,1,""]},"apptools.naming.initial_context_factory":{InitialContextFactory:[8,1,1,""]},"apptools.naming.initial_context_factory.InitialContextFactory":{get_initial_context:[8,2,1,""]},"apptools.naming.naming_event":{NamingEvent:[8,1,1,""]},"apptools.naming.naming_manager":{NamingManager:[8,1,1,""]},"apptools.naming.naming_manager.NamingManager":{get_object_instance:[8,2,1,""],get_state_to_bind:[8,2,1,""]},"apptools.naming.object_factory":{ObjectFactory:[8,1,1,""]},"apptools.naming.object_factory.ObjectFactory":{get_object_instance:[8,2,1,""]},"apptools.naming.object_serializer":{ObjectSerializer:[8,1,1,""]},"apptools.naming.object_serializer.ObjectSerializer":{can_load:[8,2,1,""],can_save:[8,2,1,""],load:[8,2,1,""],save:[8,2,1,""]},"apptools.naming.py_context":{PyContext:[8,1,1,""]},"apptools.naming.py_object_factory":{PyObjectFactory:[8,1,1,""]},"apptools.naming.py_object_factory.PyObjectFactory":{get_object_instance:[8,2,1,""]},"apptools.naming.pyfs_context":{PyFSContext:[8,1,1,""]},"apptools.naming.pyfs_context.PyFSContext":{ATTRIBUTES_FILE:[8,4,1,""],FILTERS:[8,4,1,""],OBJECT_SERIALIZERS:[8,4,1,""],get_unique_name:[8,2,1,""],refresh:[8,2,1,""]},"apptools.naming.pyfs_context_factory":{PyFSContextFactory:[8,1,1,""]},"apptools.naming.pyfs_context_factory.PyFSContextFactory":{get_object_instance:[8,2,1,""]},"apptools.naming.pyfs_initial_context_factory":{PyFSInitialContextFactory:[8,1,1,""]},"apptools.naming.pyfs_initial_context_factory.PyFSInitialContextFactory":{get_initial_context:[8,2,1,""]},"apptools.naming.pyfs_object_factory":{PyFSObjectFactory:[8,1,1,""]},"apptools.naming.pyfs_object_factory.PyFSObjectFactory":{get_object_instance:[8,2,1,""]},"apptools.naming.pyfs_state_factory":{PyFSStateFactory:[8,1,1,""]},"apptools.naming.pyfs_state_factory.PyFSStateFactory":{get_state_to_bind:[8,2,1,""]},"apptools.naming.reference":{Reference:[8,1,1,""]},"apptools.naming.referenceable":{Referenceable:[8,1,1,""]},"apptools.naming.referenceable_state_factory":{ReferenceableStateFactory:[8,1,1,""]},"apptools.naming.referenceable_state_factory.ReferenceableStateFactory":{get_state_to_bind:[8,2,1,""]},"apptools.naming.state_factory":{StateFactory:[8,1,1,""]},"apptools.naming.state_factory.StateFactory":{get_state_to_bind:[8,2,1,""]},"apptools.naming.trait_defs":{api:[9,0,0,"-"],naming_traits:[9,0,0,"-"]},"apptools.naming.trait_defs.naming_traits":{NamingTraitHandler:[9,1,1,""]},"apptools.naming.trait_defs.naming_traits.NamingTraitHandler":{find_class:[9,2,1,""],get_editor:[9,2,1,""],info:[9,2,1,""],post_setattr:[9,2,1,""],resolve_class:[9,2,1,""],validate:[9,2,1,""],validate_failed:[9,2,1,""]},"apptools.naming.unique_name":{make_unique_name:[8,5,1,""]},"apptools.persistence":{file_path:[10,0,0,"-"],project_loader:[10,0,0,"-"],state_pickler:[10,0,0,"-"],updater:[10,0,0,"-"],version_registry:[10,0,0,"-"],versioned_unpickler:[10,0,0,"-"]},"apptools.persistence.file_path":{FilePath:[10,1,1,""]},"apptools.persistence.file_path.FilePath":{get:[10,2,1,""],set:[10,2,1,""],set_absolute:[10,2,1,""],set_relative:[10,2,1,""]},"apptools.persistence.project_loader":{load_project:[10,5,1,""],upgrade_project:[10,5,1,""]},"apptools.persistence.state_pickler":{State:[10,1,1,""],StateDict:[10,1,1,""],StateList:[10,1,1,""],StatePickler:[10,1,1,""],StatePicklerError:[10,6,1,""],StateSetter:[10,1,1,""],StateSetterError:[10,6,1,""],StateTuple:[10,1,1,""],StateUnpickler:[10,1,1,""],StateUnpicklerError:[10,6,1,""],create_instance:[10,5,1,""],dump:[10,5,1,""],dumps:[10,5,1,""],get_state:[10,5,1,""],gunzip_string:[10,5,1,""],gzip_string:[10,5,1,""],load_state:[10,5,1,""],loads_state:[10,5,1,""],set_state:[10,5,1,""],update_state:[10,5,1,""]},"apptools.persistence.state_pickler.StatePickler":{dump:[10,2,1,""],dump_state:[10,2,1,""],dumps:[10,2,1,""]},"apptools.persistence.state_pickler.StateSetter":{set:[10,2,1,""]},"apptools.persistence.state_pickler.StateUnpickler":{load_state:[10,2,1,""],loads_state:[10,2,1,""]},"apptools.persistence.updater":{Updater:[10,1,1,""]},"apptools.persistence.updater.Updater":{get_latest:[10,2,1,""],strip:[10,2,1,""]},"apptools.persistence.version_registry":{HandlerRegistry:[10,1,1,""],get_version:[10,5,1,""]},"apptools.persistence.version_registry.HandlerRegistry":{register:[10,2,1,""],unregister:[10,2,1,""],update:[10,2,1,""]},"apptools.persistence.versioned_unpickler":{NewUnpickler:[10,1,1,""],VersionedUnpickler:[10,1,1,""]},"apptools.persistence.versioned_unpickler.NewUnpickler":{initialize:[10,2,1,""],load:[10,2,1,""],load_build:[10,2,1,""]},"apptools.persistence.versioned_unpickler.VersionedUnpickler":{add_updater:[10,2,1,""],backup_setstate:[10,2,1,""],find_class:[10,2,1,""],import_name:[10,2,1,""]},"apptools.preferences":{api:[11,0,0,"-"],i_preferences:[11,0,0,"-"],package_globals:[11,0,0,"-"],preference_binding:[11,0,0,"-"],preferences:[11,0,0,"-"],preferences_helper:[11,0,0,"-"],scoped_preferences:[11,0,0,"-"],ui:[12,0,0,"-"]},"apptools.preferences.i_preferences":{IPreferences:[11,1,1,""]},"apptools.preferences.i_preferences.IPreferences":{clear:[11,2,1,""],flush:[11,2,1,""],get:[11,2,1,""],keys:[11,2,1,""],node:[11,2,1,""],node_exists:[11,2,1,""],node_names:[11,2,1,""],remove:[11,2,1,""],set:[11,2,1,""]},"apptools.preferences.package_globals":{get_default_preferences:[11,5,1,""],set_default_preferences:[11,5,1,""]},"apptools.preferences.preference_binding":{PreferenceBinding:[11,1,1,""],bind_preference:[11,5,1,""]},"apptools.preferences.preferences":{Preferences:[11,1,1,""]},"apptools.preferences.preferences.Preferences":{add_preferences_listener:[11,2,1,""],clear:[11,2,1,""],dump:[11,2,1,""],flush:[11,2,1,""],get:[11,2,1,""],keys:[11,2,1,""],load:[11,2,1,""],node:[11,2,1,""],node_exists:[11,2,1,""],node_names:[11,2,1,""],remove:[11,2,1,""],remove_preferences_listener:[11,2,1,""],save:[11,2,1,""],set:[11,2,1,""]},"apptools.preferences.preferences_helper":{PreferencesHelper:[11,1,1,""]},"apptools.preferences.scoped_preferences":{ScopedPreferences:[11,1,1,""]},"apptools.preferences.scoped_preferences.ScopedPreferences":{add_preferences_listener:[11,2,1,""],clear:[11,2,1,""],dump:[11,2,1,""],get:[11,2,1,""],get_scope:[11,2,1,""],keys:[11,2,1,""],load:[11,2,1,""],node:[11,2,1,""],node_exists:[11,2,1,""],node_names:[11,2,1,""],remove:[11,2,1,""],remove_preferences_listener:[11,2,1,""],save:[11,2,1,""],set:[11,2,1,""]},"apptools.preferences.ui":{api:[12,0,0,"-"],i_preferences_page:[12,0,0,"-"],preferences_manager:[12,0,0,"-"],preferences_node:[12,0,0,"-"],preferences_page:[12,0,0,"-"],tree_item:[12,0,0,"-"],widget_editor:[12,0,0,"-"]},"apptools.preferences.ui.i_preferences_page":{IPreferencesPage:[12,1,1,""]},"apptools.preferences.ui.i_preferences_page.IPreferencesPage":{apply:[12,2,1,""]},"apptools.preferences.ui.preferences_manager":{PreferencesHelpWindow:[12,1,1,""],PreferencesManager:[12,1,1,""],PreferencesManagerHandler:[12,1,1,""]},"apptools.preferences.ui.preferences_manager.PreferencesHelpWindow":{traits_view:[12,2,1,""]},"apptools.preferences.ui.preferences_manager.PreferencesManager":{apply:[12,2,1,""],traits_view:[12,2,1,""]},"apptools.preferences.ui.preferences_manager.PreferencesManagerHandler":{apply:[12,2,1,""],close:[12,2,1,""],init:[12,2,1,""],preferences_help:[12,2,1,""]},"apptools.preferences.ui.preferences_node":{PreferencesNode:[12,1,1,""]},"apptools.preferences.ui.preferences_node.PreferencesNode":{create_page:[12,2,1,""],dump:[12,2,1,""],lookup:[12,2,1,""]},"apptools.preferences.ui.preferences_page":{PreferencesPage:[12,1,1,""]},"apptools.preferences.ui.preferences_page.PreferencesPage":{apply:[12,2,1,""]},"apptools.preferences.ui.tree_item":{TreeItem:[12,1,1,""]},"apptools.preferences.ui.tree_item.TreeItem":{append:[12,2,1,""],insert:[12,2,1,""],insert_after:[12,2,1,""],insert_before:[12,2,1,""],remove:[12,2,1,""]},"apptools.preferences.ui.widget_editor":{WidgetEditor:[12,1,1,""]},"apptools.preferences.ui.widget_editor.WidgetEditor":{custom_editor:[12,2,1,""],readonly_editor:[12,2,1,""],simple_editor:[12,2,1,""],text_editor:[12,2,1,""]},"apptools.scripting":{api:[13,0,0,"-"],package_globals:[13,0,0,"-"],recordable:[13,0,0,"-"],recorder:[13,0,0,"-"],recorder_with_ui:[13,0,0,"-"],util:[13,0,0,"-"]},"apptools.scripting.package_globals":{get_recorder:[13,5,1,""],set_recorder:[13,5,1,""]},"apptools.scripting.recordable":{recordable:[13,5,1,""]},"apptools.scripting.recorder":{Recorder:[13,1,1,""],RecorderError:[13,6,1,""]},"apptools.scripting.recorder.Recorder":{clear:[13,2,1,""],get_code:[13,2,1,""],get_object_path:[13,2,1,""],get_script_id:[13,2,1,""],is_registered:[13,2,1,""],record:[13,2,1,""],record_function:[13,2,1,""],register:[13,2,1,""],save:[13,2,1,""],ui_save:[13,2,1,""],unregister:[13,2,1,""],write_script_id_in_namespace:[13,2,1,""]},"apptools.scripting.recorder_with_ui":{CloseHandler:[13,1,1,""],RecorderWithUI:[13,1,1,""]},"apptools.scripting.recorder_with_ui.CloseHandler":{close:[13,2,1,""]},"apptools.scripting.recorder_with_ui.RecorderWithUI":{on_ui_close:[13,2,1,""]},"apptools.scripting.util":{start_recording:[13,5,1,""],stop_recording:[13,5,1,""]},"apptools.selection":{api:[14,0,0,"-"],errors:[14,0,0,"-"],i_selection:[14,0,0,"-"],i_selection_provider:[14,0,0,"-"],list_selection:[14,0,0,"-"],selection_service:[14,0,0,"-"]},"apptools.selection.errors":{IDConflictError:[14,6,1,""],ListenerNotConnectedError:[14,6,1,""],ProviderNotRegisteredError:[14,6,1,""]},"apptools.selection.i_selection":{IListSelection:[14,1,1,""],ISelection:[14,1,1,""]},"apptools.selection.i_selection.IListSelection":{indices:[14,4,1,""],items:[14,4,1,""]},"apptools.selection.i_selection.ISelection":{is_empty:[14,2,1,""],provider_id:[14,4,1,""]},"apptools.selection.i_selection_provider":{ISelectionProvider:[14,1,1,""]},"apptools.selection.i_selection_provider.ISelectionProvider":{get_selection:[14,2,1,""],provider_id:[14,4,1,""],selection:[14,4,1,""],set_selection:[14,2,1,""]},"apptools.selection.list_selection":{ListSelection:[14,1,1,""]},"apptools.selection.list_selection.ListSelection":{from_available_items:[14,2,1,""],indices:[14,4,1,""],is_empty:[14,2,1,""],items:[14,4,1,""],provider_id:[14,4,1,""]},"apptools.selection.selection_service":{SelectionService:[14,1,1,""]},"apptools.selection.selection_service.SelectionService":{add_selection_provider:[14,2,1,""],connect_selection_listener:[14,2,1,""],disconnect_selection_listener:[14,2,1,""],get_selection:[14,2,1,""],has_selection_provider:[14,2,1,""],remove_selection_provider:[14,2,1,""],set_selection:[14,2,1,""]},"apptools.type_registry":{api:[15,0,0,"-"],type_registry:[15,0,0,"-"]},"apptools.type_registry.type_registry":{LazyRegistry:[15,1,1,""],TypeRegistry:[15,1,1,""],get_mro:[15,5,1,""]},"apptools.type_registry.type_registry.LazyRegistry":{lookup_by_type:[15,2,1,""]},"apptools.type_registry.type_registry.TypeRegistry":{lookup:[15,2,1,""],lookup_all:[15,2,1,""],lookup_all_by_type:[15,2,1,""],lookup_by_type:[15,2,1,""],pop:[15,2,1,""],push:[15,2,1,""],push_abc:[15,2,1,""]},"apptools.undo":{abstract_command:[16,0,0,"-"],action:[17,0,0,"-"],api:[16,0,0,"-"],command_stack:[16,0,0,"-"],i_command:[16,0,0,"-"],i_command_stack:[16,0,0,"-"],i_undo_manager:[16,0,0,"-"],undo_manager:[16,0,0,"-"]},"apptools.undo.abstract_command":{AbstractCommand:[16,1,1,""]},"apptools.undo.abstract_command.AbstractCommand":{"do":[16,2,1,""],merge:[16,2,1,""],redo:[16,2,1,""],undo:[16,2,1,""]},"apptools.undo.action":{abstract_command_stack_action:[17,0,0,"-"],api:[17,0,0,"-"],command_action:[17,0,0,"-"],redo_action:[17,0,0,"-"],undo_action:[17,0,0,"-"]},"apptools.undo.action.abstract_command_stack_action":{AbstractCommandStackAction:[17,1,1,""]},"apptools.undo.action.abstract_command_stack_action.AbstractCommandStackAction":{destroy:[17,2,1,""]},"apptools.undo.action.command_action":{CommandAction:[17,1,1,""]},"apptools.undo.action.command_action.CommandAction":{perform:[17,2,1,""]},"apptools.undo.action.redo_action":{RedoAction:[17,1,1,""]},"apptools.undo.action.redo_action.RedoAction":{perform:[17,2,1,""]},"apptools.undo.action.undo_action":{UndoAction:[17,1,1,""]},"apptools.undo.action.undo_action.UndoAction":{perform:[17,2,1,""]},"apptools.undo.command_stack":{CommandStack:[16,1,1,""]},"apptools.undo.command_stack.CommandStack":{begin_macro:[16,2,1,""],clear:[16,2,1,""],end_macro:[16,2,1,""],push:[16,2,1,""],redo:[16,2,1,""],undo:[16,2,1,""]},"apptools.undo.i_command":{ICommand:[16,1,1,""]},"apptools.undo.i_command.ICommand":{"do":[16,2,1,""],merge:[16,2,1,""],redo:[16,2,1,""],undo:[16,2,1,""]},"apptools.undo.i_command_stack":{ICommandStack:[16,1,1,""]},"apptools.undo.i_command_stack.ICommandStack":{begin_macro:[16,2,1,""],clear:[16,2,1,""],end_macro:[16,2,1,""],push:[16,2,1,""],redo:[16,2,1,""],undo:[16,2,1,""]},"apptools.undo.i_undo_manager":{IUndoManager:[16,1,1,""]},"apptools.undo.i_undo_manager.IUndoManager":{redo:[16,2,1,""],undo:[16,2,1,""]},"apptools.undo.undo_manager":{UndoManager:[16,1,1,""]},"apptools.undo.undo_manager.UndoManager":{redo:[16,2,1,""],undo:[16,2,1,""]},apptools:{io:[2,0,0,"-"],logger:[4,0,0,"-"],naming:[8,0,0,"-"],persistence:[10,0,0,"-"],preferences:[11,0,0,"-"],scripting:[13,0,0,"-"],selection:[14,0,0,"-"],type_registry:[15,0,0,"-"],undo:[16,0,0,"-"]}},objnames:{"0":["py","module","Python module"],"1":["py","class","Python class"],"2":["py","method","Python method"],"3":["py","data","Python data"],"4":["py","attribute","Python attribute"],"5":["py","function","Python function"],"6":["py","exception","Python exception"]},objtypes:{"0":"py:module","1":"py:class","2":"py:method","3":"py:data","4":"py:attribute","5":"py:function","6":"py:exception"},terms:{"abstract":[2,10,12,16,17,26],"boolean":[8,22,26],"case":[5,10,11,20,22,25],"class":[2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,22,23,24,26],"default":[3,4,7,9,10,11,12,13,14,16,17,22,23,26],"final":25,"float":[7,8,10,22,24],"function":[4,8,9,10,13,20,24,25],"import":[4,10,15,22,24,25],"int":[10,22],"long":[10,22,26],"new":[3,7,8,9,10,11,13,15,17,20,22,25],"public":[5,13,25],"return":[3,4,6,7,8,9,10,11,12,13,14,15,16,17,22,23,25,26],"static":8,"transient":22,"true":[3,6,8,10,11,13,14,16,22,23,24,25,26],"try":[11,16,22,26],"while":[7,10,16,25,26],And:22,But:22,ETS:24,For:[3,9,10,13,20,22,24,25],Has:14,Its:[10,16,26],Not:8,One:24,That:3,The:[3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,19,20,23,26],Then:11,There:11,These:15,Used:10,Useful:13,Using:[22,24,26],__array__:3,__attributes__:8,__class__:10,__dict__:10,__getstate__:10,__init__:[10,16,26],__main__:10,__metadata__:10,__module__:15,__name__:[10,15],__set_pure_state__:10,__setitem__:3,__setstate__:10,__version__:10,_unpickl:10,abandon:[16,26],abc:[3,15],abcmeta:15,abl:[22,24],about:[8,10,16,22,24,25],abov:24,absolut:[3,10,15],abstract_command:[1,18],abstract_command_stack_act:[1,16],abstractcommand:16,abstractcommandstackact:17,accept:[8,9],access:[3,11],aclass:9,acm:[11,22,23],act:[8,25],action:[1,16,24,25,26],activ:[7,14,16,17,26],activated_text:7,active_stack:26,active_stack_clean:26,actual:[4,7,8,9,10,13,22],adapt:7,add:[3,4,7,10,11,13,14,22,26],add_log_queue_handl:4,add_preferences_listen:11,add_selection_provid:[14,25],add_to_h5fil:3,add_updat:10,added:[11,14],adding:8,addit:[3,8,11],addition:10,address:[1,18],affect:11,after:[3,10,12,13],age:24,agent:[1,4],alia:5,all:[2,6,7,8,10,11,12,13,14,15,16,17,22,24,26],all_item:14,alloc:26,allow:[3,7,8,9,10,11,12,16,22,23,24,26],allows_children:12,almost:10,along:[10,16],alreadi:[2,3,8,13,14],also:[3,8,10,11,13,16,21,22,23,24,25,26],alter:3,altern:[9,10,26],alwai:[11,22,24],amount:7,ani:[2,5,7,8,9,10,11,14,16,17,22,23,24,25,26],anoth:[8,16,22],answer:10,anymor:25,anywher:22,api:[1,6,18,19,22],app:10,appear:[10,26],append:[3,4,11,12,24],appl:12,appli:[10,12],applic:[4,8,10,11,16,22,23,24,25,26],application_hom:22,application_scop:11,application_vers:10,approach:22,appropri:[10,16,26],apptool:[0,20,21,22,24],arbitrari:[9,24],arg:[3,5,6,7,12,13,26],argmument:3,argument:[3,13,14,16,17,24,25,26],around:[3,20,22,26],arrai:[3,10],arrang:22,array_or_shap:3,array_proxy_kei:3,arriv:7,ascend:8,ascii:10,ask:[8,11,13],assert:10,assign:[7,9],assist:8,associ:[8,22],assum:[3,10],atom:3,attach:[1,4,24],attempt:[2,8],attribut:[3,8,9,10,13,24],attributes_fil:8,auto:0,auto_flush:3,auto_group:3,auto_open:3,automat:[3,8,11,13,19,22,26],avail:[7,13,14,20,23,25,26],avoid:[8,10,11],back:[10,11,22],background:24,backup_setst:10,backupcount:4,base64:10,base:[2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,24,25,26],base_f_nam:10,base_toolkit:5,basic:[10,13,19],becam:20,becaus:[5,8,10,22,25],becom:[25,26],been:[3,13,14,15,16,24,25,26],befor:[4,10,12,14,16,20,22,25,26],begin:[8,16,26],begin_macro:[16,26],behavior:3,behaviour:[22,23],being:[8,9,10,12,16,24,26],believ:22,below:3,best:[10,24],better:11,between:[10,11,24,25],bgcolor:[11,22,23],bind:[1,11,18],bind_prefer:11,bit:22,black:22,blog:4,blue:[11,22,23],bogu:22,bool:[3,10,13,14,22,24],both:[15,26],bound:8,brief:26,broker:25,brows:10,buffer:[3,4],bug:6,build:22,builtin:10,bump:10,bunch:10,button:[7,12,22,25],calcul:[3,7,8],call:[3,4,8,9,10,11,13,14,16,17,22,24,25,26],callabl:[14,25,26],callback:[14,24,25],came:11,can:[3,4,5,8,9,10,11,14,15,16,20,21,22,24,25,26],can_load:8,can_sav:8,cannot:[8,11],capabl:8,care:[10,22,26],carrot:12,categori:8,cauliflow:12,caveat:1,cc_address:5,ccaddr:[5,6],central:25,chang:[8,11,12,13,14,16,22,24,25,26],charact:26,check:10,child:[11,12,22,24],children:[3,11,16,24],children_nam:3,chunk:3,class_nam:10,classmethod:[3,10,14],clean:[13,16,26],cleaner:3,clear:[11,13,16,26],click:12,close:[3,12,13,25],closehandl:13,code:[5,8,10,13,20,24],code_editor:7,collaps:26,collect:[3,14,25],color:24,column:[3,7],column_width:7,combin:9,come:22,command:[11,16,17,24,26],command_act:[1,16],command_stack:[1,18,26],commandact:17,commandstack:16,comment:[5,6],common:[10,20,25,26],commuic:8,commun:[8,25],complet:[9,10,26],complex:[10,22,25],complic:[10,22],compon:[8,25,26],compos:8,concept:19,concern:10,configobj:[11,22],configur:26,conform:8,confus:26,connect:[14,25],connect_selection_listen:[14,25],consid:24,consist:[8,22],consumpt:25,contain:[0,3,8,10,11,12,13,14,16,22,25,26],content:[18,25],context:[1,3,10,18],context_nam:8,context_ord:8,continu:8,contribut:[8,23],contributes_to:23,control:[3,12,25],conveni:[4,10,13,22,24,26],convers:10,convert:[3,7,10,22],cookbook:4,copi:[2,4,16,26],copy_button:7,core:24,correct:10,correctli:10,correspond:25,could:25,couldn:26,cours:11,creat:[2,3,4,7,8,10,11,12,13,14,16,22,24,26],create_:3,create_arrai:3,create_carrai:3,create_dict:3,create_email_messag:[5,6],create_fil:2,create_fold:2,create_group:3,create_inst:10,create_packag:2,create_pag:12,create_subcontext:8,create_t:3,creation:[8,12],criteria:8,current:[8,10,11,12,13,14,22,24,25,26],custom:12,custom_editor:12,custom_excepthook:[1,18],data:[3,6,8,10,12,16,26],datafram:3,debug:[13,22],decid:10,decor:[13,24],decreas:7,deep:[16,26],def:[10,22,23,24],default_prefer:11,default_valu:24,defin:[7,9,10,11,13,24,25,26],definit:[8,11,24],delet:[2,3],delete_exist:3,demonstr:26,depend:[10,11,25],descend:11,describ:[8,9,10,23],descript:[3,12],desir:[3,8,23],destin:2,destroi:[8,17,25],destroy_subcontext:8,detail:[10,24],determin:[8,10],develop:[11,25,26],dialog:[12,13,25],dict:[3,10,13],dict_nod:[1,2],dictat:23,dictionari:[3,8,10,13,20,22],did:[11,22],differ:[3,8,10],dir_context:[1,18],dircontext:8,direct:25,directi:10,directli:[11,22,25],directori:[8,21,22],disabl:[16,26],disappear:25,discard:16,disconnect:[14,25],disconnect_selection_listen:[14,25],discuss:[22,23],disk:[3,10,22,26],displai:[4,25],distinct:25,divid:7,docstr:3,document:[3,22,24,26],doe:[2,3,4,8,10,11,13,17,22,23],doesn:[12,22],don:[22,23],done:[13,24,25,26],drawback:11,dtype:3,dump:[10,11,12,22],dump_stat:10,duplic:[10,14],dure:10,dynam:[7,8],dynamic_context:[1,18],dynamiccontext:8,each:[7,8,10,11,15,22,24,26],easi:[3,10,11],easili:[10,24],edit:[9,10,26],editor:[7,9,10,12,25,26],editor_factori:12,editorfactori:12,effect:[16,26],either:[3,9,10,11,22,24],element:[4,8,14,25,26],elimin:10,els:[3,10,15],email:6,embed:10,emit:4,empti:[8,11,14,16,26],encapsul:10,encod:10,encourag:[11,20],end:[4,8,10,13,16,24,26],end_macro:[16,26],endo:26,endpoint:8,enlib:5,enough:24,enqueu:4,ensur:[10,16,26],enthought:[2,8,10,11,13,16,26],enthoughtbas:4,entir:10,entri:26,environ:[3,8],envisag:[5,6,8,19,22,25],equal:7,error:[1,9,10,18],especi:25,etc:[4,8,10,11,17,22],etsconfig:22,even:14,event:[8,14,17,25,26],ever:[16,26],everi:4,everyth:10,exactli:22,examin:8,exampl:[3,9,10,11,12,22,25,26],excel:22,except:[1,4,9,10,11,13,14,18],exclud:10,execut:[4,13,16,24,26],exist:[2,3,8,10,11,12,13,22],exists_error:3,expect:14,explan:7,explicit:22,explicitli:[3,8,11,14,23,26],expos:[3,6,25],extend:[3,8],extens:[5,23,25],extern:25,extra:23,facil:24,factori:[8,12,26],fairli:24,fals:[3,5,6,7,8,11,12,13,14,22,24],featur:[1,26],few:24,fgcolor:22,file:[1,4,5,6,8,10,11,13,18,19,22,23],file_or_filenam:11,file_path:[1,18],filenam:[3,11,22],filenod:3,filepath:10,filesystem:22,fill:14,filter:8,find:[8,11,13,21,24],find_bind:8,find_class:[9,10],fire:[8,14,25,26],first:[3,10,22,24,25],fix:[7,10],fix_import:10,fixm:[5,11,13],flexibl:10,flush:[3,11,22],folder:2,follow:[13,22,24,26],forc:[7,11],form:[10,12],format:[6,8,10,22],formatt:4,formatted_record:7,forwardproperti:7,found:[8,14],fraction:7,framework:[8,13,19,24],fred:22,free:23,friend:[10,24],from:[4,6,8,10,11,12,13,14,16,22,24],from_address:5,from_available_item:14,fromaddr:[5,6],fruit:12,full:24,fulli:15,func:[13,14],further:19,gener:[0,4,8,10,12,22,26],get:[3,4,8,10,11,15,22,23,24,25],get_atom:3,get_attribut:8,get_cod:13,get_default_prefer:11,get_editor:9,get_initial_context:8,get_latest:10,get_mro:15,get_object_inst:8,get_object_path:13,get_prefer:23,get_record:13,get_scop:[11,22],get_script_id:13,get_select:[14,25],get_stat:10,get_state_to_bind:8,get_unique_nam:8,get_vers:10,get_width:7,give:[10,26],given:[3,7,8,10,11,13,14,15,16,24,25,26],global:[11,13,24],going:[10,24],great:17,group:[3,7],group_path:3,group_subpath:3,grow:24,guarante:[13,16,26],gui:[25,26],guid:9,gunzip_str:10,gzip:10,gzip_str:10,h5_group:3,h5_group_wrapp:3,h5attr:3,h5dictnod:3,h5file:3,h5filter:3,h5group:3,h5tablenod:3,halt:4,handi:24,handl:[10,12,25,26],handler:[4,7,9,10,12,13],handlerregistri:10,happen:8,happi:22,hard:5,has:[4,7,8,10,12,13,14,15,22,25,26],has_inst:10,has_new_record:4,has_selection_provid:14,has_trait:[2,5,6,8,11,12,13,14,16],hasprivatetrait:[2,8],hasstricttrait:24,hastrait:[5,6,8,9,11,12,13,14,16,24],have:[3,7,10,11,16,22,24,25,26],hdf5:[3,19],heirarchi:[8,21],help:[3,10,12],help_id:5,helper:[22,23],henc:[22,23],here:[5,10,11,21,22,24],hid_quality_agent_dlg:5,hierarch:22,hierarchi:[10,11,13,22,24],high:24,highli:[10,11],hold:22,hopefulli:22,horizont:7,how:[8,22,23,26],howev:[10,26],html:26,human:24,i_command:[1,18],i_command_stack:[1,18],i_prefer:[1,18],i_preferences_pag:[1,11],i_select:[1,18],i_selection_provid:[1,18],i_undo_manag:[1,18],icommand:16,icommandstack:[16,26],idconflicterror:14,idea:[10,22],ideal:[10,25],identifi:[3,14,26],ignor:[10,13,14,24,26],ignore_miss:[14,25],ilistselect:14,imag:[12,22],immedi:26,implement:[3,9,10,11,14,15,16,20,21,22,23,25,26],implent:11,import_nam:10,inadequ:10,inappropri:26,includ:[9,10,11,16,21,26],include_environ:6,include_project:5,include_userdata:[5,6],increas:[7,24],increment:[10,26],indent:[11,12],independ:26,index:[12,26],indic:[8,14,25,26],info:[8,9,12,13],info_text:9,inform:[3,8,9,10,14,22,24,25],infrastructur:24,inherit:[11,22],ini:[22,23],init:12,initarg:10,initi:[3,8,10,11,12,14,25],initial_context:[1,18],initial_context_factori:[1,18],initialcontext:8,initialcontextfactori:8,inject:13,input:3,insert:12,insert_aft:12,insert_befor:12,insid:24,instal:3,instanc:[3,7,8,9,10,11,12,13,14,15,16,17,24,25,26],instanti:[13,24],instead:[9,10,25],insuffici:10,integ:[7,26],intend:26,intent:11,interact:8,intercept:3,interest:[8,22],interfac:[3,7,9,11,12,13,14,16,21,22,24,25,26],intermedi:[2,8],intern:[14,24,25],interpret:[10,13],invalid:8,invalidnameerror:8,invert:13,investig:20,invok:[13,26],iprefer:[11,22],ipreferencespag:12,irrespect:26,is_context:8,is_dict_nod:3,is_empti:14,is_ok:[12,13],is_open:3,is_regist:13,is_table_nod:3,iselect:[14,25],iselectionprovid:[14,25],isn:22,issu:10,item:[3,7,8,12,14,25,26],items:3,iter:3,iter_group:3,iterator_length:3,iteritem:3,its:[7,8,10,11,12,13,14,15,22,24,25,26],itself:[8,10,11,13,22,23,24,26],iundomanag:[16,26],java:21,jndi:21,join:3,join_path:3,json:3,just:[10,11,22,23,24],keep:[10,16,25],kei:[3,10,11,24],keyboard:26,keyerror:15,keyword:[3,13,24,25],klass:10,know:10,knowledg:10,known:[10,13],kwarg:[3,5,6,7],larg:10,last:[10,15,16,17,26],later:[4,10,16],latest:10,layer:22,lazi:26,lazili:15,lazyregistri:15,learn:24,leav:[7,16,26],let:[22,24],level:[4,8,24],lib:5,librari:[20,24],like:[3,4,10,11,22,23,24],limit:22,line:[11,13,24],link:26,list:[3,4,6,7,8,10,11,13,14,15,24,25],list_bind:8,list_nam:8,list_select:[1,18],listen:[11,13,14,17,19,22,24],listenernotconnectederror:14,listselect:[14,25],littl:7,live:8,load:[8,10,11,22,23,26],load_build:10,load_project:10,load_stat:10,loads_stat:10,local:8,locat:[8,24],log:[4,6,7],log_point:[1,18],log_queue_handl:[1,18],log_record:7,log_records_editor:7,logfilehandl:4,logger:[1,18],logger_plugin:[1,4],logger_prefer:[1,4],logger_preferences_pag:[1,4,6],logger_servic:[1,4],logger_view:[1,4,6],loggerplugin:6,loggerprefer:6,loggerpreferencespag:7,loggerservic:6,loggerview:7,logic:13,logqueuehandl:4,logrecord:7,logrecordadapt:7,longer:[10,17,25],look:[3,8,11,15,22,24],lookup:[8,12,15],lookup_al:15,lookup_all_by_typ:15,lookup_bind:8,lookup_by_typ:15,lookup_context:8,lot:10,lowest:22,macro:[16,26],made:[8,12,16,22,23,26],mai:[8,10,13,16,24,26],mail_fil:6,main:25,maintain:[3,15,24,26],major:[10,26],make:[2,9,10,11,22,25],make_context:8,make_unique_nam:8,make_writ:2,manag:[3,8,10,11,12,16,22,25,26],mani:[10,22,26],manipul:[20,26],manner:24,manual:13,map:[3,10,20,24],mark:[13,24,26],match:[8,13,15],max_pass:10,maxbyt:4,maximum:7,mayavi:24,mean:[7,10,22],mechan:[8,19,23],mention:22,menu:26,merg:[11,16,26],messag:[4,5,6],metadata:[8,10,13,24],method:[3,8,9,10,11,13,15,16,17,20,22,24,25,26],might:[10,22,23,25],minu:7,miss:[2,8,11,14,22],mode:3,model:[8,16],modifi:[9,10,16,26],modul:[18,24],more:[3,4,9,11,16,22,24,25,26],motiv:10,move:[2,26],mro:10,msg:[4,5],much:[20,23],multidimension:3,multipl:[25,26],multipli:7,must:[2,3,8,9,14,15,16,24,25,26],mutablemap:3,mutat:11,my_dict:3,my_tabl:3,myapplic:23,mypackag:23,myplugin:23,name:[1,3,6,7,10,11,12,13,15,16,18,19,22,24,26],namealreadybounderror:8,namenotfounderror:8,namespac:[8,13],naming_ev:[1,18],naming_manag:[1,18],naming_trait:[1,8],namingerror:8,namingev:8,namingmanag:8,namingtraithandl:9,natur:22,navig:10,necessari:[3,10,16,26],need:[9,10,13,16,23,25,26],nest:[11,13,16,24,26],never:[8,14],new_nam:8,newest:4,newunpickl:10,next:[16,22,26],node:[3,11,12,22,23],node_attr:3,node_exist:11,node_nam:11,node_path:3,node_subpath:3,nodepath:3,non:[3,8,9,22],none:[3,4,8,10,11,12,13,15,22,26],normal:[3,7],notcontexterror:8,note:[3,7,8,9,10,11,24],noth:[2,11,13,16,17,26],notic:22,notif:25,notifi:[13,14,22,25],notion:11,now:[10,22,24],number:[10,22,23,26],numer:10,numpi:3,obj:[8,10,11,15,22],obj_class:15,object:[3,4,5,6,7,8,9,10,11,12,13,14,15,19,20,21,22,24,26],object_factori:[1,18],object_seri:[1,18],objectfactori:8,objectseri:8,obtain:10,obvious:22,occur:[9,24],often:10,ogbuji:4,old:[10,15,22],old_nam:8,oldest:4,on_trait_chang:22,on_ui_clos:13,onc:[4,16,26],one:[8,10,11,12,13,15,16,22,24,25,26],ones:10,onli:[3,9,10,11,13,15,16,17,23,25,26],onto:[15,17,26],opac:24,open:[3,10,25],open_h5fil:3,oper:[8,11,17,20,22,26],operationnotsupportederror:8,opportun:10,option:[10,13,25,26],or_non:9,orang:12,order:[8,10,11,14,15,22],organ:[24,25],organis:26,orient:7,origin:[3,8,9,10,16,26],other:[3,8,9,10,16,23,25,26],otherwis:[3,14,16,26],our:[3,7,22],out:[10,11,13,24],output:[10,13],outsid:[8,24],outstand:[16,26],over:[3,8,12],overrid:23,overridden:[9,10],overview:[19,24],overwrit:3,packag:[0,18,20,21,22,24,25],package_any_relevant_fil:5,package_glob:[1,18],package_single_project:5,package_workspac:5,page:[7,12,19],pair:3,panda:3,panel:12,panick:22,paramet:[3,9,10,13,14,15,24],parent:[3,12,13,15,24],pars:10,part:[2,3,4,8,10,11,13,16,24,25],particular:[8,9,10,11,13,14,24,26],particularli:[8,26],pass:[3,4,8,9,10,11,13,16,24,26],passiv:14,path:[2,3,4,8,10,11,13,20,24],pathlib:20,pathnam:3,pattern:26,pear:12,peopl:11,perfectli:14,perform:[7,8,11,17,22,24],perhap:10,persist:[1,6,11,18,22],phrase:9,pickl:10,pickle_filenam:10,pickler:10,pixel:7,pkgfile:23,place:[9,10,11,16,17,24,26],placehold:10,plain:[10,22,24],pleas:10,plug:8,plugin:[1,4,5,10,23,25,26],point:[5,8,22,23,24,26],pop:[13,15],popul:14,portion:21,posit:[8,26],posix:10,possibl:[10,13,15,25,26],post_setattr:9,potenti:26,power:24,pprint:10,preced:[11,22],predefin:[9,22],prefer:[1,6,7,8,18,19],preference_bind:[1,18],preference_path:11,preferencebind:11,preferences_help:[1,6,12,18],preferences_manag:[1,11],preferences_nod:[1,11],preferences_pag:[1,6,7,11],preferences_path:22,preferencesbind:11,preferenceshelp:[6,11,12,22,23],preferenceshelpwindow:12,preferencesmanag:12,preferencesmanagerhandl:12,preferencesnod:12,preferencespag:[7,12],prefix:8,prefixmap:24,present:[8,12,13],preserv:3,presum:10,pretti:[12,22],previou:[13,16,26],previous:22,primari:11,primarili:24,primit:11,print:[4,12,22,24],printabl:13,prioriti:[5,6],problem:10,process:10,produc:[8,24],program:3,project:[2,4,5,8,10,11,13,16],project_load:[1,18],project_vers:10,properli:16,properti:[3,20,24],propos:9,protocol:10,prove:[10,22],provid:[2,3,8,10,11,13,14,19,20,22,23,24,26],provider_id:[14,25],providernotregisterederror:[14,25],proxi:10,publish:[14,25],pure:10,purpos:9,push:[15,16,17,26],push_abc:15,put:4,py_context:[1,18],py_object_factori:[1,18],pycontext:8,pyf:8,pyfac:[5,7,17,26],pyfs_context:[1,18],pyfs_context_factori:[1,18],pyfs_initial_context_factori:[1,18],pyfs_object_factori:[1,18],pyfs_state_factori:[1,18],pyfscontext:8,pyfscontextfactori:8,pyfsinitialcontextfactori:8,pyfsobjectfactori:8,pyfsstatefactori:8,pyobjectfactori:8,pytabl:[3,20],pytables_group:3,pytables_nod:3,python:[3,4,8,10,11,13,20,21,24],qualifi:15,quality_agent_mail:[1,4],quality_agent_view:[1,4],qualityagentview:5,queue:4,quick:22,quickest:24,quit:[10,25],rais:[3,8,9,11,14,15,25],rang:24,rather:[3,8,9],ratio:[22,23],raw:22,reach:8,react:25,read:[3,10,19,24],readabl:[3,13,24],readi:4,readonly_editor:12,real:10,realli:[10,22],reason:[15,24],rebind:8,rec:13,receiv:[9,25],recogn:8,recognis:[8,16],recommend:11,reconstitut:10,record:[1,3,4,7,16,18,19],record_funct:13,recorder_with_ui:[1,18],recordererror:13,recorderwithui:[13,24],recreat:[10,24],red:22,redo:[16,17,26],redo_act:[1,16],redo_nam:26,redoabl:26,redoact:17,redon:[16,26],refactor:10,refer:[1,7,9,10,11,16,18,24,26],referenc:[1,18],referenceable_state_factori:[1,18],referenceablestatefactori:8,reflect:[8,26],refresh:8,regard:[8,25],regist:[10,13,14,15,23,24,25],registi:10,registri:[10,13,14,15],reimplement:17,rel:[3,8,10],rel_pth:10,releas:10,relev:[5,10],reli:24,remain:[5,10,14],remov:[3,11,12,14,26],remove_group:3,remove_nod:3,remove_preferences_listen:11,remove_selection_provid:[14,25],renam:8,repeat:26,repeatedli:[10,14],replac:16,report:6,repres:[8,10,12,13,14,22,25],represent:[2,8,10,24],request:[8,14,25],requir:[3,7,8,9,10,14,17,26],reset:4,reset_button:7,resiz:7,resolut:[8,15],resolv:8,resolve_class:9,respect:10,respons:[16,26],rest:14,restor:26,result:[7,8,9,16,17,26],reus:5,revers:[10,24],revert:26,revis:10,rewrit:5,ring_buff:[1,18],ringbuff:4,ringbufferful:4,root:[3,11,12,22],rotatingfilehandl:4,row:3,safer:8,saferepr:10,sai:[10,22],same:[7,8,10,14,16,22,25,26],satisfi:20,save:[6,8,10,11,13,16,22,24,26],save_prefer:6,scan:10,scheme:11,scope:[8,11,19,23],scope_nam:11,scoped_prefer:[1,18],scopedprefer:[11,22,23],screen:22,script:[1,16,18,19],script_id:[13,24],search:[8,15,19,22],second:10,section:[0,22,23,26],see:[3,10,22,24],select:[1,18,19],selection_servic:[1,18],selectionservic:[14,19],self:[10,23,24],send:[6,25],send_bug_report:6,sens:9,sentenc:9,seq:10,sequenc:[3,8,14,26],sequence_nr:[16,26],serial:8,servic:[5,6,7,14,19],set:[3,7,8,10,11,13,14,22,23,24,26],set_absolut:10,set_attribut:8,set_default_prefer:11,set_record:[13,24],set_rel:10,set_select:[14,25],set_stat:10,setup:[10,24],shape:3,shiva:24,shortcut:26,should:[3,5,8,9,10,11,14,22,24,25,26],show:[7,8,12,25],show_button:7,show_label:7,shown:13,signatur:14,silent:14,similar:[9,20,26],similarli:10,simpl:[7,10,12,13,22,24],simple_editor:12,simpli:[10,22,24],simplifi:10,sinc:[3,8,10,13,24,26],singl:[16,26],size:[4,5,7],size_max:4,small:24,smtp_server:[5,6],some:[3,8,22,26],someon:[8,10],sometim:24,somewher:8,soon:25,sort:[8,26],sound:22,sourc:[2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,24],space:7,special:10,specif:[8,9,10,11,13,14,21,24,25],specifi:[3,7,8,9,10,11,12,13,15,22,24,25],splash:22,splash_screen:22,splashscreenprefer:[22,23],split:3,split_path:3,sprocket:9,sprout:12,squar:9,stack:[4,15,16,17,26],stack_trac:[5,6],stack_upd:26,stage:10,standard:[8,10,11,20,26],start:[4,6,8,13,22,23,24],start_record:13,startup:4,state:[8,10,13,16,26],state_factori:[1,18],state_pickl:[1,18],statedict:10,statefactori:8,statelist:10,statement:13,statepickl:10,statepicklererror:10,statesett:10,statesettererror:10,statetupl:10,stateunpickl:10,stateunpicklererror:10,statu:26,stdout:[11,12],stop:[6,13,24],stop_record:13,storag:[3,10],store:[3,8,10,11,13,22,24],str:[3,5,7,9,11,13,14,22,24,26],strict:10,strightfoward:10,string:[3,8,9,10,11,12,13,15,24],strip:10,structur:12,stuff:[5,10],style:[7,15,20],sub:[8,20,24,26],subclass:[9,11,13],subgroup_nam:3,subject:[5,6],submodul:[1,18],subpackag:18,subsequ:[16,24,26],suffici:26,suggest:24,suit:[2,8,10,11,13,16,24,26],suitabl:8,superclass:15,support:[8,10,13,16,19,25,26],sure:[11,22,25],surfac:24,synchron:[11,25],synchronis:26,syntax:[8,22],system:[2,4,8,22],tabl:[3,7],table_nod:[1,2],tabular_adapt:7,tabular_editor:7,tabularadapt:7,tabulareditor:7,take:[11,17,22,25,26],taken:[10,24],technic:24,tediou:24,tell:26,terribl:22,test:24,text:[6,10,13,26],text_editor:12,than:[3,4,8,9],thei:[10,11,16,20,22,24,25,26],them:[4,8,10,11,22,26],themselv:[8,13],therefor:9,thi:[0,2,3,4,5,7,8,9,10,11,12,13,14,16,17,20,22,23,24,25,26],thin:3,thing:23,think:[8,10,11],those:[8,13,16,26],though:[8,10],thought:26,through:[8,24,25],thrown:8,thu:7,time:[4,7,8,10,25,26],titl:5,to_address:5,to_datafram:3,toaddr:[5,6],togeth:7,toi:24,too:[10,22,23,24],tool:[2,8,10,11,13,16,24,26],toolbar:25,toolkiteditorfactori:7,top:22,total:[7,12],tour:22,trace:4,traceback:4,tradit:22,trail:11,trait:[2,5,6,7,8,9,11,12,13,14,16,17,20,22,23,24,26],trait_def:[1,8],trait_handl:9,trait_modifi:7,trait_nam:[11,22],trait_name_on_par:13,trait_typ:[5,6,7],trait_view:7,traiterror:9,traithandl:9,traits_ui_view:7,traits_view:12,traitsui:[7,12,13],traitsuiview:7,transform:10,treat:[7,8,26],tree:[8,12,25],tree_item:[1,11],treeitem:12,tri:[23,24],trigger:14,tupl:[3,5,10,24],turn:[10,11,20,24],tutori:21,two:[10,11,16,22,25,26],typ:15,type:[3,4,7,8,9,10,11,15,19,24,26],type_registri:[1,18],typeregistri:15,typic:[10,16,25,26],uch:4,ui_sav:13,unbind:8,unchang:7,undo:[1,18,19],undo_act:[1,16],undo_manag:[1,18,26],undo_nam:26,undoabl:26,undoact:17,undomanag:16,undon:[16,17,26],unhook:17,unimpl:5,uniqu:[8,14,24,25,26],unique_nam:[1,18],unless:25,unlik:10,unnorm:7,unpickl:10,unregist:[10,13,24],unseri:10,until:[8,10,16,26],unzip:10,updat:[1,7,18,26],update1:10,update2:10,update3:10,update_st:10,updater_path:10,upgrad:10,upgrade_project:10,usabl:8,usag:26,use:[7,8,9,10,11,13,20,22,23,25],useag:22,used:[3,8,10,11,12,13,16,22,23,24,25,26],useful:[8,10,13,17,22],user:[7,8,9,10,11,12,13,20,22,24,25,26],uses:[11,23],using:[3,8,9,10,13,22,24,25],usual:[10,11,12,22],util:[1,2,5,18],valid:[3,9,10,14],validate_fail:9,valu:[3,4,7,8,9,10,11,16,22,26],valueerror:[3,11,14],variou:[10,26],veg:12,veri:[10,24],verifi:9,version:[8,10,22],version_registri:[1,18],versioned_unpickl:[1,18],versionedunpickl:10,via:[5,10,11,13,22,23,24],view:[1,3,4,6,10,12,25],visibl:[11,22,23],visitor:8,visual:24,wai:[8,11,22,24],walk:[10,24],want:[10,11,13,22,24,25],well:[10,24],were:[16,26],what:[7,10,22,24,25],whatev:[9,11,24],when:[3,4,8,10,13,14,15,16,17,22,23,24,25,26],whenev:[3,9,14,25,26],where:[3,8,10,11,13,22,25],whether:[3,9],which:[3,7,8,10,11,12,13,16,22,23,24,25,26],white:22,whole:10,whole_log_text:6,whose:[9,10],wide:11,widget:[11,12],widget_editor:[1,11],widgeteditor:12,width:[7,11,22,23],win:22,window:25,wire:24,wirefram:24,wish:25,within:8,without:[11,16,26],word:26,work:[3,10,12,16,22,24,26],workbench:[6,7,26],worth:[10,11,22],would:[3,10,11,16,17,22,23,25],wrap:[3,13,17,26],wrapper:[3,20,26],write:[3,10,22],write_script_id_in_namespac:13,writeabl:2,writer:8,written:8,xmarshal:10,xxx:3,year:24,yellow:23,yet:5,you:[3,10,11,13,21,22,23,24],your:[22,23,24],yourself:22,zip:5},titles:["API documentation","apptools package","apptools.io package","apptools.io.h5 package","apptools.logger package","apptools.logger.agent package","apptools.logger.plugin package","apptools.logger.plugin.view package","apptools.naming package","apptools.naming.trait_defs package","apptools.persistence package","apptools.preferences package","apptools.preferences.ui package","apptools.scripting package","apptools.selection package","apptools.type_registry package","apptools.undo package","apptools.undo.action package","apptools","AppTools Documentation","File I/O","Naming","Preferences","Preferences in Envisage","Automatic script recording","The selection service","Undo Framework"],titleterms:{"case":24,The:[22,24,25],abstract_command:16,abstract_command_stack_act:17,abstractcommand:26,access:22,action:17,activ:25,address:8,advanc:24,agent:5,api:[0,2,4,8,9,11,12,13,14,15,16,17,24,26],apptool:[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19],attach:5,automat:24,basic:22,bind:8,caveat:10,command_act:17,command_stack:16,commandact:26,commandstack:26,concept:26,content:[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17],context:8,custom_excepthook:4,dict_nod:3,dir_context:8,document:[0,19],dynamic_context:8,envisag:23,error:14,exampl:24,except:8,featur:10,file:[2,3,20],file_path:10,framework:26,further:22,gloriou:22,hdf5:20,i_command:16,i_command_stack:16,i_prefer:11,i_preferences_pag:12,i_select:14,i_selection_provid:14,i_undo_manag:16,icommand:26,initial_context:8,initial_context_factori:8,list_select:14,listen:25,log_point:4,log_queue_handl:4,logger:[4,5,6,7],logger_plugin:6,logger_prefer:6,logger_preferences_pag:7,logger_servic:6,logger_view:7,mechan:22,modul:[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17],name:[8,9,21],naming_ev:8,naming_manag:8,naming_trait:9,object:25,object_factori:8,object_seri:8,overview:26,packag:[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17],package_glob:[11,13],particular:22,passiv:25,persist:10,plugin:[6,7],prefer:[11,12,22,23],preference_bind:11,preferences_help:11,preferences_manag:12,preferences_nod:12,preferences_pag:12,project_load:10,provid:25,py_context:8,py_object_factori:8,pyfs_context:8,pyfs_context_factori:8,pyfs_initial_context_factori:8,pyfs_object_factori:8,pyfs_state_factori:8,quality_agent_mail:5,quality_agent_view:5,queri:25,read:22,record:[13,24],recorder_with_ui:13,redo_act:17,redoact:26,refer:8,referenc:8,referenceable_state_factori:8,registr:25,ring_buff:4,scope:22,scoped_prefer:11,script:[13,24],select:[14,25],selection_servic:14,selectionservic:25,servic:25,set:25,state_factori:8,state_pickl:10,string:22,submodul:[2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17],subpackag:[1,2,4,6,8,11,16],support:20,table_nod:3,tour:24,trait_def:9,tree_item:12,type:22,type_registri:15,undo:[16,17,26],undo_act:17,undo_manag:16,undoact:26,undomanag:26,unique_nam:8,updat:10,use:24,util:[3,13],version_registri:10,versioned_unpickl:10,view:7,widget_editor:12}}) \ No newline at end of file diff --git a/selection/selection.html b/selection/selection.html new file mode 100644 index 000000000..9d1f7613a --- /dev/null +++ b/selection/selection.html @@ -0,0 +1,282 @@ + + + + + + + The selection service — Apptools Documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +
+

The selection service

+

It is quite common in GUI applications to have a UI element displaying a +collection of items that a user can select (“selection providers”), while +other parts of the application must react to changes in the selection +(“selection listeners”).

+

Ideally, the listeners would not have a direct dependency on the UI object. +This is especially important in extensible envisage applications, where +a plugin might need to react to a selection change, but we do not want to +expose the internal organization of the application to external developers.

+

This package defines a selection service that manages the communication +between providers and listener.

+
+

The SelectionService object

+

The SelectionService object is the central manager that handles +the communication between selection providers and listener.

+

Selection providers are components that wish to +publish information about their current selection for public consumption. +They register to a selection +service instance when they first have a selection available (e.g., when the +UI showing a list of selectable items is initialized), and un-register as soon +as the selection is not available anymore (e.g., the UI is destroyed when the +windows is closed).

+

Selection listeners can query the selection +service to get the current selection published by a provider, using the +provider unique ID.

+

The service acts as a broker between providers and listeners, making sure that +they are notified when the +selection +event is fired.

+
+
+

Selection providers

+

Any object can become a selection provider by implementing the +ISelectionProvider +interface, and registering to the selection service.

+

Selection providers must provide a unique ID +provider_id, +which is used by listeners to request its current selection.

+

Whenever its selection changes, providers fire a +selection +event. The content of the event is an instance implementing +ISelection that contains information about the selected items. +For example, a ListSelection object contains a list of selected +items, and their indices.

+

Selection providers can also be queried directly about their current selection +using the +get_selection +method, and can be requested to change their selection to a new one with the +set_selection +method.

+
+

Registration

+

Selection providers publish their selection by registering to the selection +service using the +add_selection_provider +method. When the selection is no longer available, selection providers +should un-register through +remove_selection_provider.

+

Typically, selection providers are UI objects showing a list or tree of items, +they register as soon as the UI component is initialized, and un-register +when the UI component disappears (e.g., because their window has been closed). +In more complex applications, the registration could be done by a controller +object instead.

+
+
+
+

Selection listeners

+

Selection listeners request information regarding the current selection +of a selection provider given their provider ID. The SelectionService +supports two distinct use cases:

+
+
    +
  1. Passively listening to selection changes: listener connect to a specific +provider and are notified when the provider’s selection changes.

  2. +
  3. Actively querying a provider for its current selection: the selection +service can be used to query a provider using its unique ID.

  4. +
+
+
+

Passive listening

+

Listeners connect to the selection events for a given provider using the +connect_selection_listener +method. They need to provide the unique ID of the provider, and a function +(or callable) that is called to send the event. This callback function takes +one argument, an implementation of the ISelection that represents +the selection.

+

It is possible for a listener to connect to a provider ID before it is +registered. As soon as the provider is registered, the listener will receive +a notification containing the provider’s initial selection.

+

To disconnect a listener use the methods +disconnect_selection_listener.

+
+
+

Active querying

+

In other instances, an element of the application only needs the current +selection at a specific time. For example, a toolbar button could open dialog +representing a user action based on what is currently selected in the active +editor.

+

The +get_selection +method calls the corresponding method on the provider with the given ID and +returns an ISelection instance.

+
+
+

Setting a selection

+

Finally, it is possible to request a provider to set its selection to a given +set of objects with +set_selection. +The main use case for this method is multiple views of the same list of +objects, which need to keep their selection synchronized.

+

If the items specified in the arguments are not available in the provider, +a ProviderNotRegisteredError is raised, +unless the optional keyword argument ignore_missing is set to True.

+
+
+
+ + +
+
+
+
+
+ +

Table of Contents

+ + +

Previous topic

+

Undo Framework

+

Next topic

+

Naming

+

This Page

+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file diff --git a/undo/Introduction.html b/undo/Introduction.html new file mode 100644 index 000000000..4351eaac1 --- /dev/null +++ b/undo/Introduction.html @@ -0,0 +1,386 @@ + + + + + + + Undo Framework — Apptools Documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ +
+
+
+ + + + + + +
+
+
+ + +
+
+ +
+
+ +
+

Undo Framework

+

The Undo Framework is a component of the Enthought Tool Suite that provides +developers with an API that implements the standard pattern for do/undo/redo +commands.

+

The framework is completely configurable. Alternate implementations of all +major components can be provided if necessary.

+
+

Framework Concepts

+

The following are the concepts supported by the framework.

+
    +
  • Command

    +

    A command is an application defined operation that can be done (i.e. +executed), undone (i.e. reverted) and redone (i.e. repeated).

    +

    A command operates on some data and maintains sufficient state to allow it to +revert or repeat a change to the data.

    +

    Commands may be merged so that potentially long sequences of similar +commands (e.g. to add a character to some text) can be collapsed into a +single command (e.g. to add a word to some text).

    +
  • +
  • Macro

    +

    A macro is a sequence of commands that is treated as a single command when +being undone or redone.

    +
  • +
  • Command Stack

    +

    A command is done by pushing it onto a command stack. The last command can +be undone and redone by calling appropriate command stack methods. It is +also possible to move the stack’s position to any point and the command stack +will ensure that commands are undone or redone as required.

    +

    A command stack maintains a clean state which is updated as commands are +done and undone. It may be explicitly set, for example when the data being +manipulated by the commands is saved to disk.

    +

    Canned PyFace actions are provided as wrappers around command stack methods +to implement common menu items.

    +
  • +
  • Undo Manager

    +

    An undo manager is responsible for one or more command stacks and maintains +a reference to the currently active stack. It provides convenience undo and +redo methods that operate on the currently active stack.

    +

    An undo manager ensures that each command execution is allocated a unique +sequence number, irrespective of which command stack it is pushed to. Using +this it is possible to synchronise multiple command stacks and restore them +to a particular point in time.

    +

    An undo manager will generate an event whenever the clean state of the active +stack changes. This can be used to maintain some sort of GUI status +indicator to tell the user that their data has been modified since it was +last saved.

    +
  • +
+

Typically an application will have one undo manager and one undo stack for +each data type that can be edited. However this is not a requirement: how the +command stack’s in particular are organised and linked (with the user +manager’s sequence number) can need careful thought so as not to confuse the +user - particularly in a plugin based application that may have many editors.

+

To support this typical usage the PyFace Workbench class has an +undo_manager trait and the PyFace Editor class has a command_stack +trait. Both are lazy loaded so can be completely ignored if they are not used.

+
+
+

API Overview

+

This section gives a brief overview of the various classes implemented in the +framework. The complete API documentation is available as endo generated +HTML.

+

The example application demonstrates all the major features of the framework.

+
+

UndoManager

+

The UndoManager class is the default implementation of the IUndoManager +interface.

+
+
active_stack

This trait is a reference to the currently active command stack and may be +None. Typically it is set when some sort of editor becomes active.

+
+
active_stack_clean

This boolean trait reflects the clean state of the currently active +command stack. It is intended to support a “document modified” indicator +in the GUI. It is maintained by the undo manager.

+
+
stack_updated

This event is fired when the index of a command stack is changed. A +reference to the stack is passed as an argument to the event and may not +be the currently active stack.

+
+
undo_name

This Str trait is the name of the command that can be undone, and will +be empty if there is no such command. It is maintained by the undo +manager.

+
+
redo_name

This Str trait is the name of the command that can be redone, and will +be empty if there is no such command. It is maintained by the undo +manager.

+
+
sequence_nr

This integer trait is the sequence number of the next command to be +executed. It is incremented immediately before a command’s do() +method is called. A particular sequence number identifies the state of +all command stacks handled by the undo manager and allows those stacks to +be set to the point they were at at a particular point in time. In other +words, the sequence number allows otherwise independent command stacks to +be synchronised.

+
+
undo()

This method calls the undo() method of the last command on the active +command stack.

+
+
redo()

This method calls the redo() method of the last undone command on the +active command stack.

+
+
+
+
+

CommandStack

+

The CommandStack class is the default implementation of the +ICommandStack interface.

+
+
clean

This boolean traits reflects the clean state of the command stack. Its +value changes as commands are executed, undone and redone. It may also be +explicitly set to mark the current stack position as being clean (when +data is saved to disk for example).

+
+
undo_name

This Str trait is the name of the command that can be undone, and will +be empty if there is no such command. It is maintained by the command +stack.

+
+
redo_name

This Str trait is the name of the command that can be redone, and will +be empty if there is no such command. It is maintained by the command +stack.

+
+
undo_manager

This trait is a reference to the undo manager that manages the command +stack.

+
+
push(command)

This method executes the given command by calling its do() method. +Any value returned by do() is returned by push(). If the command +couldn’t be merged with the previous one then it is saved on the command +stack.

+
+
undo(sequence_nr=0)

This method undoes the last command. If a sequence number is given then +all commands are undone up to an including the sequence number.

+
+
redo(sequence_nr=0)

This method redoes the last command and returns any result. If a sequence +number is given then all commands are redone up to an including the +sequence number and any result of the last of these is returned.

+
+
clear()

This method clears the command stack, without undoing or redoing any +commands, and leaves the stack in a clean state. It is typically used +when all changes to the data have been abandoned.

+
+
begin_macro(name)

This method begins a macro by creating an empty command with the given +name. The commands passed to all subsequent calls to push() will be +contained in the macro until the next call to end_macro(). Macros may +be nested. The command stack is disabled (ie. nothing can be undone or +redone) while a macro is being created (ie. while there is an outstanding +end_macro() call).

+
+
end_macro()

This method ends the current macro.

+
+
+
+
+

ICommand

+

The ICommand interface defines the interface that must be implemented by +any undoable/redoable command.

+
+
data

This optional trait is a reference to the data object that the command +operates on. It is not used by the framework itself.

+
+
name

This Str trait is the name of the command as it will appear in any GUI +element (e.g. in the text of an undo and redo menu entry). It may include +& to indicate a keyboard shortcut which will be automatically removed +whenever it is inappropriate.

+
+
__init__(*args)

If the command takes arguments then the command must ensure that deep +copies should be made if appropriate.

+
+
do()

This method is called by a command stack to execute the command and to +return any result. The command must save any state necessary for the +undo() and redo() methods to work. It is guaranteed that this +will only ever be called once and that it will be called before any call +to undo() or redo().

+
+
undo()

This method is called by a command stack to undo the command.

+
+
redo()

This method is called by a command stack to redo the command and to return +any result.

+
+
merge(other)

This method is called by the command stack to try and merge the other +command with this one. True should be returned if the commands were +merged. If the commands are merged then other will not be placed on +the command stack. A subsequent undo or redo of this modified command +must have the same effect as the two original commands.

+
+
+
+
+

AbstractCommand

+

AbstractCommand is an abstract base class that implements the ICommand +interface. It provides a default implementation of the merge() method.

+
+
+

CommandAction

+

The CommandAction class is a sub-class of the PyFace Action class that +is used to wrap commands.

+
+
command

This callable trait must be set to a factory that will return an object +that implements ICommand. It will be called when the action is invoked +and the object created pushed onto the command stack.

+
+
command_stack

This instance trait must be set to the command stack that commands invoked +by the action are pushed to.

+
+
data

This optional trait is a reference to the data object that will be passed +to the command factory when it is called.

+
+
+
+
+

UndoAction

+

The UndoAction class is a canned PyFace action that undoes the last +command of the active command stack.

+
+
+

RedoAction

+

The RedoAction class is a canned PyFace action that redoes the last +command undone of the active command stack.

+
+
+
+ + +
+
+
+
+
+ +

Table of Contents

+ + +

Previous topic

+

Automatic script recording

+

Next topic

+

The selection service

+

This Page

+ + + +
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + \ No newline at end of file