testman4trac.1.4.5/0000755000175000017500000000000011566010112014036 5ustar robertorobertotestman4trac.1.4.5/clean.sh0000755000175000017500000000040111565162323015465 0ustar robertorobertorm -rf bin cd tracgenericclass/trunk rm -rf build dist *.egg-info cd ../../tracgenericworkflow/trunk rm -rf build dist *.egg-info cd ../../sqlexecutor/trunk rm -rf build dist *.egg-info cd ../../testman4trac/trunk rm -rf build dist *.egg-info cd ../.. testman4trac.1.4.5/BUILD.txt0000755000175000017500000000322111565162323015452 0ustar robertoroberto To build the four plugins that make up the application: On Linux: 1) Make sure you have Trac installed and available in the Python lookup path. Genshi should also be available in the Python lookup path. To make these libraries available to Python you can either install them centrally, or reference them using the PYTHONLIB environment variable. 2) Unzip the source code distribution package 3) Open a shell, cd into the root directory coming from the source code distribution package 4) Build the source code using the provided buil.sh script: . ./build.sh The build shell script can copy the resulting egg files into your desired destination. If you wish to directly install the egg files under your Trac environment, you can do that by providing your project root directory to the build.sh script: . ./build.sh path/to/your/project 5) You will find the built egg files under the directory "bin". On Windows: 1), 2), 3) Same as for Linux 4) Build the source code using the provided buil.sh script: build.cmd The build shell script can copy the resulting egg files into your desired destination. If you wish to directly install the egg files under your Trac environment, you can do that by providing your project root directory to the build.sh script: build.cmd path\to\your\project 5) You will find the built egg files under the directory "bin". ============================================================================== Use the following command to clean up the build: On Linux: . ./build.sh On Windows: build.cmd testman4trac.1.4.5/clean.cmd0000755000175000017500000000071611565162323015627 0ustar robertorobertormdir /s /q bin cd tracgenericclass\trunk rmdir /s /q build rmdir /s /q dist rmdir /s /q TracGenericClass.egg-info cd ..\..\tracgenericworkflow/trunk rmdir /s /q build rmdir /s /q dist rmdir /s /q TracGenericWorkflow.egg-info cd ..\..\sqlexecutor/trunk rmdir /s /q build rmdir /s /q dist rmdir /s /q SQLExecutor.egg-info cd ..\..\testman4trac/trunk rmdir /s /q build rmdir /s /q dist rmdir /s /q TestManager.egg-info cd ..\.. testman4trac.1.4.5/tracgenericworkflow/0000755000175000017500000000000011565162323020132 5ustar robertorobertotestman4trac.1.4.5/tracgenericworkflow/trunk/0000755000175000017500000000000011566010105021264 5ustar robertorobertotestman4trac.1.4.5/tracgenericworkflow/trunk/tracgenericworkflow/0000755000175000017500000000000011565162323025356 5ustar robertorobertotestman4trac.1.4.5/tracgenericworkflow/trunk/tracgenericworkflow/__init__.py0000755000175000017500000000016011565162323027467 0ustar robertoroberto# -*- coding: utf-8 -*- # # Copyright (C) 2010 Roberto Longobardi # import api import model import operations testman4trac.1.4.5/tracgenericworkflow/trunk/tracgenericworkflow/LICENSE.txt0000755000175000017500000000244711565162323027213 0ustar robertorobertoCopyright (c) 2010, Roberto Longobardi, Marco Cipriani All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.testman4trac.1.4.5/tracgenericworkflow/trunk/tracgenericworkflow/templates/0000755000175000017500000000000011565162323027354 5ustar robertorobertotestman4trac.1.4.5/tracgenericworkflow/trunk/tracgenericworkflow/templates/empty.html0000755000175000017500000000041111565162323031377 0ustar robertoroberto testman4trac.1.4.5/tracgenericworkflow/trunk/tracgenericworkflow/htdocs/0000755000175000017500000000000011565162323026642 5ustar robertorobertotestman4trac.1.4.5/tracgenericworkflow/trunk/tracgenericworkflow/htdocs/images/0000755000175000017500000000000011565162323030107 5ustar robertorobertotestman4trac.1.4.5/tracgenericworkflow/trunk/tracgenericworkflow/htdocs/place.holder0000755000175000017500000000000011565162323031116 0ustar robertorobertotestman4trac.1.4.5/tracgenericworkflow/trunk/tracgenericworkflow/htdocs/js/0000755000175000017500000000000011565162323027256 5ustar robertorobertotestman4trac.1.4.5/tracgenericworkflow/trunk/tracgenericworkflow/htdocs/css/0000755000175000017500000000000011565162323027432 5ustar robertorobertotestman4trac.1.4.5/tracgenericworkflow/trunk/tracgenericworkflow/operations.py0000755000175000017500000001422611565162323030123 0ustar robertorobertofrom trac.core import * from trac.perm import PermissionSystem from trac.resource import Resource from trac.util.datefmt import utc from trac.util.translation import _, N_, gettext from trac.web.chrome import Chrome from genshi.builder import tag from genshi.filters.transform import Transformer from genshi import HTML from tracgenericclass.util import * from tracgenericworkflow.model import ResourceWorkflowState from tracgenericworkflow.api import IWorkflowOperationProvider, ResourceWorkflowSystem # Out-of-the-box operations class WorkflowStandardOperations(Component): """Adds a set of standard, out-of-the-box workflow operations.""" implements(IWorkflowOperationProvider) # IWorkflowOperationProvider methods def get_implemented_operations(self): self.log.debug(">>> WorkflowStandardOperations - get_implemented_operations") self.log.debug("<<< WorkflowStandardOperations - get_implemented_operations") yield 'set_owner' yield 'set_owner_to_self' yield 'std_notify' def get_operation_control(self, req, action, operation, res_wf_state, resource): self.log.debug(">>> WorkflowStandardOperations - get_operation_control: %s" % operation) id = 'action_%s_operation_%s' % (action, operation) # A custom field named "owner" is required in the ResourceWorkflowState # class for this operation to be available self.env.log.debug(res_wf_state.fields) if operation == 'set_owner' and self._has_field_named('owner', res_wf_state.fields): self.log.debug("Creating control for setting owner.") current_owner = res_wf_state['owner'] or '(none)' if not (Chrome(self.env).show_email_addresses or 'EMAIL_VIEW' in req.perm(resource)): format_user = obfuscate_email_address else: format_user = lambda address: address current_owner = format_user(current_owner) self.log.debug("Current owner is %s." % current_owner) selected_owner = req.args.get(id, req.authname) control = None hint = '' owners = None available_owners = self.config.get(resource.realm, 'available_owners') if available_owners is not None and not available_owners == '': owners = [x.strip() for x in available_owners.split(',')] elif self.config.getbool(resource.realm, 'restrict_owner'): target_permission = self.config.get(resource.realm, 'restrict_owner_to_permission') if target_permission is not None and not target_permission == '': perm = PermissionSystem(self.env) owners = perm.get_users_with_permission(target_permission) owners.sort() if owners == None: owner = req.args.get(id, req.authname) control = tag('Assign to ', tag.input(type='text', id=id, name=id, value=owner)) hint = "The owner will be changed from %s" % current_owner elif len(owners) == 1: owner = tag.input(type='hidden', id=id, name=id, value=owners[0]) formatted_owner = format_user(owners[0]) control = tag('Assign to ', tag(formatted_owner, owner)) if res_wf_state['owner'] != owners[0]: hint = "The owner will be changed from %s to %s" % (current_owner, formatted_owner) else: control = tag('Assign to ', tag.select( [tag.option(format_user(x), value=x, selected=(x == selected_owner or None)) for x in owners], id=id, name=id)) hint = "The owner will be changed from %s" % current_owner return control, hint elif operation == 'set_owner_to_self' and self._has_field_named('owner', res_wf_state.fields) and \ res_wf_state['owner'] != req.authname: current_owner = res_wf_state['owner'] or '(none)' if not (Chrome(self.env).show_email_addresses or 'EMAIL_VIEW' in req.perm(resource)): format_user = obfuscate_email_address else: format_user = lambda address: address current_owner = format_user(current_owner) control = tag('') hint = "The owner will be changed from %s to %s" % (current_owner, req.authname) self.log.debug("<<< WorkflowStandardOperations - get_operation_control - set_owner_to_self") return control, hint elif operation == 'std_notify': pass self.log.debug("<<< WorkflowStandardOperations - get_operation_control") return None, '' def perform_operation(self, req, action, operation, old_state, new_state, res_wf_state, resource): self.log.debug("---> Performing operation %s while transitioning from %s to %s." % (operation, old_state, new_state)) if operation == 'set_owner': if self.config.getbool(resource.realm, 'set_owners'): target_owners = self.config.getstring(resource.realm, 'restrict_to_permission') new_owner = target_owners.strip() else: new_owner = req.args.get('action_%s_operation_%s' % (action, operation), None) if new_owner is not None and len(new_owner.strip()) > 0: res_wf_state['owner'] = new_owner.strip() #res_wf_state.save_changes() else: self.log.debug("Unable to get the new owner!") elif operation == 'set_owner_to_self': res_wf_state['owner'] = req.authname.strip() def _has_field_named(self, field_name, fields): for f in fields: if 'name' in f and f['name'] == field_name: return True return False testman4trac.1.4.5/tracgenericworkflow/trunk/tracgenericworkflow/README.txt0000755000175000017500000000175711565162323027071 0ustar robertorobertoTrac Generic Workflow Engine - Part of the Test Manager plugin for Trac Copyright 2010 Roberto Longobardi Project web page on TracHacks: http://trac-hacks.org/wiki/TestManagerForTracPlugin Project web page on SourceForge.net: http://sourceforge.net/projects/testman4trac/ Project web page on Pypi: http://pypi.python.org/pypi/TestManager A Trac plugin to create Test Cases, organize them in catalogs, generate test plans and track their execution status and outcome. This module provides a framework to help creating workflows around any Trac Resource. ================================================================================================= Change History: (Refer to the tickets on trac-hacks for complete descriptions.) Release 1.0.2 (2010-11-30): o Added out of the box operation to workflow engine: set_owner and set_owner_to_self Release 1.0.0 (2010-10-01): o First release publicly available apart from the core Test Manager plugin testman4trac.1.4.5/tracgenericworkflow/trunk/tracgenericworkflow/api.py0000755000175000017500000004066611565162323026520 0ustar robertoroberto# -*- coding: utf-8 -*- # # Copyright (C) 2010 Roberto Longobardi # import re import time from datetime import datetime from trac.core import * from trac.resource import Resource, render_resource_link, get_resource_url from trac.util import get_reporter_id from trac.util.translation import _, N_, gettext from trac.web.api import IRequestHandler from trac.web.chrome import ITemplateProvider from genshi.builder import tag from tracgenericclass.util import * from tracgenericworkflow.model import ResourceWorkflowState class IWorkflowTransitionListener(Interface): """ Extension point interface for components that require notification when objects transition between states. """ def object_transition(res_wf_state, resource, action, old_state, new_state): """ Called when an object has transitioned to a new state. :param res_wf_state: the ResourceWorkflowState transitioned from old_state to new_state :param resource: the Resource object transitioned. :param action: the action been performed. """ class IWorkflowTransitionAuthorization(Interface): """ Extension point interface for components that wish to augment the state machine at runtime, by allowing or denying each transition based on the object and the current and new states. """ def is_authorized(res_wf_state, resource, action, old_state, new_state): """ Called before allowing the transition. Return True to allow for the transition, False to deny it. :param res_wf_state: the ResourceWorkflowState being transitioned from old_state to new_state :param resource: the Resource object being transitioned. :param action: the action being performed. """ class IWorkflowOperationProvider(Interface): """ Extension point interface for components willing to implement custom workflow operations. """ def get_implemented_operations(): """ Return custom actions provided by the component. :rtype: `basestring` generator """ def get_operation_control(req, action, operation, res_wf_state, resource): """ Asks the provider to provide UI control to let the User perform the specified operation on the given resource. This control(s) will be rendered inside a form and the values will be eventually available to this provvider in the perform_operation method, to actually perform the operation. :param req: the http request. :param action: the action being performed by the User. :param operation: the name of the operation to be rendered. :param res_wf_state: the ResourceWorkflowState transitioned from old_state to new_state :param resource: the Resource object transitioned. :return: a Genshi tag with the required control(s) and a string with the operation hint. """ def perform_operation(req, action, operation, old_state, new_state, res_wf_state, resource): """ Perform the specified operation on the given resource, which has transitioned from the given old to the given new state. :param req: the http request, which parameters contain the input fields (provided by means of 'get_operation_control') that the User has now valorized. :param res_wf_state: the ResourceWorkflowState transitioned from old_state to new_state :param resource: the Resource object transitioned. """ class ResourceWorkflowSystem(Component): """ Generic Resource workflow system for Trac. """ implements(IRequestHandler, ITemplateProvider) transition_listeners = ExtensionPoint(IWorkflowTransitionListener) transition_authorizations = ExtensionPoint(IWorkflowTransitionAuthorization) operation_providers = ExtensionPoint(IWorkflowOperationProvider) actions = {} _operation_providers_map = None def __init__(self, *args, **kwargs): """ Parses the configuration file to find all the workflows defined. To define a workflow state machine for a particular resource realm, add a "-resource_workflow" section in trac.ini and describe the state machine with the same syntax as the ConfigurableTicketWorkflow component. """ Component.__init__(self, *args, **kwargs) from trac.ticket.default_workflow import parse_workflow_config for section in self.config.sections(): if section.find('-resource_workflow') > 0: self.log.debug("ResourceWorkflowSystem - parsing config section %s" % section) realm = section.partition('-')[0] raw_actions = list(self.config.options(section)) self.actions[realm] = parse_workflow_config(raw_actions) self._operation_providers_map = None # Workflow state machine management def get_available_actions(self, req, realm, resource=None): """ Returns a list of (weight, action) tuples, for the specified realm, that are valid for this request and the current state. """ self.log.debug(">>> ResourceWorkflowSystem - get_available_actions") # Get the list of actions that can be performed user_perm = None if resource is not None: user_perms = req.perm(resource) rws = ResourceWorkflowState(self.env, resource.id, realm) if rws.exists: curr_state = rws['state'] else: curr_state = 'new' allowed_actions = [] if realm in self.actions: for action_name, action_info in self.actions[realm].items(): oldstates = action_info['oldstates'] if oldstates == ['*'] or curr_state in oldstates: # This action is valid in this state. # Check permissions if possible. if user_perms is not None: required_perms = action_info['permissions'] if not self._is_action_allowed(user_perms, required_perms): continue allowed_actions.append((action_info['default'], action_name)) self.log.debug("<<< ResourceWorkflowSystem - get_available_actions") return allowed_actions def _is_action_allowed(self, user_perms, required_perms): if not required_perms: return True for permission in required_perms: if permission in user_perms: return True return False def get_all_states(self, realm): """ Return a set with all the states described by the configuration for the specified realm. Returns an empty set if none. """ all_states = set() if realm in self.actions: for action_name, action_info in self.actions[realm].items(): all_states.update(action_info['oldstates']) all_states.add(action_info['newstate']) all_states.discard('*') return all_states def get_action_markup(self, req, realm, action, resource=None): self.log.debug('get_action_markup: action "%s"' % action) id = None if resource is not None: id = resource.id rws = ResourceWorkflowState(self.env, id, realm) this_action = self.actions[realm][action] status = this_action['newstate'] operations = this_action['operations'] controls = [] # default to nothing hints = [] for operation in operations: self.env.log.debug(">>>>>>>>>>>>>>> "+operation) provider = self.get_operation_provider(operation) self.env.log.debug (provider) if provider is not None: control, hint = provider.get_operation_control(req, action, operation, rws, resource) controls.append(control) hints.append(hint) if 'leave_status' not in operations: if status != '*': hints.append(_(" Next status will be '%(name)s'", name=status)) return (this_action['name'], tag(*controls), '. '.join(hints)) def get_workflow_markup(self, req, base_href, realm, resource): rws = ResourceWorkflowState(self.env, resource.id, realm) # action_controls is an ordered list of "renders" tuples, where # renders is a list of (action_key, label, widgets, hints) representing # the user interface for each action action_controls = [] sorted_actions = self.get_available_actions( req, realm, resource=resource) if len(sorted_actions) > 0: for action in sorted_actions: first_label = None hints = [] widgets = [] label, widget, hint = self.get_action_markup(req, realm, action[1], resource) if not first_label: first_label = label widgets.append(widget) hints.append(hint) action_controls.append((action[1], first_label, tag(widgets), hints)) form = tag.form(id='resource_workflow_form', name='resource_workflow_form', action=base_href+'/workflowtransition', method='get')( tag.input(name='id', type='hidden', value=resource.id), tag.input(name='res_realm', type='hidden', value=realm) ) form.append(tag.div()( tag.span()("Current state: %s" % rws['state']), tag.br(), tag.br() )) for i, ac in enumerate(action_controls): # The default action is the first in the action_controls list. if i==0: is_checked = 'true' else: is_checked = None form.append(tag.input(name='selected_action', type='radio', value=ac[0], checked=is_checked)( ac[1], tag.div()( ac[2], ac[3] ) )) form.append(tag.span()( tag.br(), tag.input(id='resource_workflow_form_submit_button', type='submit', value='Perform Action') )) else: form = tag('') return form # Workflow operations management def get_operation_provider(self, operation_name): """Return the component responsible for providing the specified custom workflow operation :param operation_name: the operation name :return: a `Component` implementing `IWorkflowOperationProvider` or `None` """ # build a dict of operation keys to IWorkflowOperationProvider # implementations if not self._operation_providers_map: map = {} for provider in self.operation_providers: for operation_name in provider.get_implemented_operations() or []: map[operation_name] = provider self._operation_providers_map = map if operation_name in self._operation_providers_map: return self._operation_providers_map.get(operation_name) else: return None def get_known_operations(self): """ Return a list of all the operation names of operation providers. """ operation_names = [] for provider in self.operation_providers: for operation_name in provider.get_implemented_operations() or []: operation_names.append(operation_name) return operation_names # IRequestHandler methods # Workflow transition implementation def match_request(self, req): return req.path_info.startswith('/workflowtransition') def process_request(self, req): """Handles requests to perform a state transition.""" author = get_reporter_id(req, 'author') if req.path_info.startswith('/workflowtransition'): # Check permission #req.perm.require('TEST_EXECUTE') selected_action = req.args.get('selected_action') id = req.args.get('id') res_realm = req.args.get('res_realm') res = Resource(res_realm, id) rws = ResourceWorkflowState(self.env, id, res_realm) if rws.exists: curr_state = rws['state'] else: curr_state = 'new' this_action = self.actions[res_realm][selected_action] new_state = this_action['newstate'] if new_state == '*': new_state = curr_state self.env.log.debug("Performing action %s. Transitioning the resource %s in realm %s from the state %s to the state %s" % (selected_action, id, res_realm, curr_state, new_state)) try: # Check external authorizations for external_auth in self.transition_authorizations: if not external_auth.is_authorized(rws, res, selected_action, curr_state, new_state): TracError("External authorization to the workflow transition denied.") # Perform operations operations = this_action['operations'] for operation in operations: provider = self.get_operation_provider(operation) if provider is not None: provider.perform_operation(req, selected_action, operation, curr_state, new_state, rws, res) else: self.env.log.debug("Unable to find operation provider for operation %s" % operation) # Transition the resource to the new state if rws.exists: if not new_state == curr_state: # Check that the resource is still in the state it # was when the User browsed it if rws['state'] == curr_state: rws['state'] = new_state try: rws.save_changes(author, "State changed") except: self.log.debug("Error saving the resource %s with id %s" % (realm, id)) else: TracError("Resource with id %s has already changed state in the meanwhile. Current state is %s." % (id, rws['state'])) else: rws['state'] = new_state rws.insert() # Call listeners for listener in self.transition_listeners: listener.object_transition(rws, res, selected_action, curr_state, new_state) except: self.env.log.debug(formatExceptionInfo()) raise # Redirect to the resource URL. href = get_resource_url(self.env, res, req.href) req.redirect(href) return 'empty.html', {}, None # ITemplateProvider methods def get_templates_dirs(self): """ Return the absolute path of the directory containing the provided Genshi templates. """ from pkg_resources import resource_filename return [resource_filename(__name__, 'templates')] def get_htdocs_dirs(self): """Return the absolute path of a directory containing additional static resources (such as images, style sheets, etc). """ from pkg_resources import resource_filename return [('tracgenericworkflow', resource_filename(__name__, 'htdocs'))] testman4trac.1.4.5/tracgenericworkflow/trunk/tracgenericworkflow/model.py0000755000175000017500000001062311565162323027035 0ustar robertoroberto# -*- coding: utf-8 -*- # # Copyright (C) 2010 Roberto Longobardi # import re import time from datetime import date, datetime from trac.core import * from trac.db import Table, Column, Index from trac.env import IEnvironmentSetupParticipant from trac.resource import Resource, ResourceNotFound from trac.util.translation import _, N_, gettext from tracgenericclass.model import IConcreteClassProvider, AbstractVariableFieldsObject, need_db_upgrade, upgrade_db from tracgenericclass.util import * class ResourceWorkflowState(AbstractVariableFieldsObject): """ This object represents the current workflow state of the associated resource. """ # Fields that must not be modified directly by the user protected_fields = ('id', 'res_realm', 'state') def __init__(self, env, id=None, res_realm=None, state='new', db=None): """ The resource workflow state is related to a resource, the 'id' and 'res_realm' arguments. The state can be any string. """ self.values = {} self.values['id'] = id self.values['res_realm'] = res_realm self.values['state'] = state key = self.build_key_object() AbstractVariableFieldsObject.__init__(self, env, 'resourceworkflowstate', key, db) def get_key_prop_names(self): return ['id', 'res_realm'] def create_instance(self, key): return ResourceWorkflowState(self.env, key['id'], key['res_realm']) class GenericWorkflowModelProvider(Component): """ This class provides the data model for the generic workflow plugin. The actual data model on the db is created starting from the SCHEMA declaration below. For each table, we specify whether to create also a '_custom' and a '_change' table. This class also provides the specification of the available fields for each class, being them standard fields and the custom fields specified in the trac.ini file. The custom field specification follows the same syntax as for Tickets. Currently, only 'text' type of custom fields are supported. """ implements(IConcreteClassProvider, IEnvironmentSetupParticipant) SCHEMA = { 'resourceworkflowstate': {'table': Table('resourceworkflowstate', key = ('id', 'res_realm'))[ Column('id'), Column('res_realm'), Column('state')], 'has_custom': True, 'has_change': True} } FIELDS = { 'resourceworkflowstate': [ {'name': 'id', 'type': 'text', 'label': N_('ID')}, {'name': 'res_realm', 'type': 'text', 'label': N_('Resource realm')}, {'name': 'state', 'type': 'text', 'label': N_('Workflow state')} ] } METADATA = { 'resourceworkflowstate': { 'label': "Workflow State", 'searchable': False, 'has_custom': True, 'has_change': True }, } # IConcreteClassProvider methods def get_realms(self): yield 'resourceworkflowstate' def get_data_models(self): return self.SCHEMA def get_fields(self): return self.FIELDS def get_metadata(self): return self.METADATA def create_instance(self, realm, key=None): obj = None if realm == 'resourceworkflowstate': if key is not None: obj = ResourceWorkflowState(self.env, key['id'], key['res_realm']) else: obj = ResourceWorkflowState(self.env) return obj def check_permission(self, req, realm, key_str=None, operation='set', name=None, value=None): pass # IEnvironmentSetupParticipant methods def environment_created(self): self.upgrade_environment(self.env.get_db_cnx()) def environment_needs_upgrade(self, db): return self._need_initialization(db) def upgrade_environment(self, db): # Create db if self._need_initialization(db): upgrade_db(self.env, self.SCHEMA, db) def _need_initialization(self, db): return need_db_upgrade(self.env, self.SCHEMA, db) testman4trac.1.4.5/tracgenericworkflow/trunk/setup.py0000755000175000017500000000226411565162323023016 0ustar robertorobertofrom setuptools import setup setup( name='TracGenericWorkflow', version='1.0.2', packages=['tracgenericworkflow'], package_data={'tracgenericworkflow' : ['*.txt', 'templates/*.html', 'htdocs/*.*', 'htdocs/js/*.js', 'htdocs/css/*.css', 'htdocs/images/*.*']}, author = 'Roberto Longobardi', author_email='seccanj@gmail.com', license='BSD. See the file LICENSE.txt contained in the package.', url='http://trac-hacks.org/wiki/TestManagerForTracPlugin', download_url='https://sourceforge.net/projects/testman4trac/files/', description='Test management plugin for Trac - Generic Workflow Engine component', long_description='A Trac plugin to create Test Cases, organize them in catalogs and track their execution status and outcome. This module provides a generic workflow engine working on any Trac Resource.', keywords='trac plugin test case management workflow engine resource project quality assurance statistics stats charts charting graph', entry_points = {'trac.plugins': ['tracgenericworkflow = tracgenericworkflow']}, dependency_links=['http://svn.edgewall.org/repos/genshi/trunk#egg=Genshi-dev'], install_requires=['Genshi >= 0.5'], ) testman4trac.1.4.5/LICENSE.txt0000755000175000017500000000242711565162323015704 0ustar robertorobertoCopyright (c) 2010, Roberto Longobardi All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.testman4trac.1.4.5/build_wininst.cmd0000755000175000017500000000045311565162323017415 0ustar robertorobertomkdir bin cd tracgenericclass\trunk python setup.py bdist_wininst cd ..\..\tracgenericworkflow\trunk python setup.py bdist_wininst cd ..\..\sqlexecutor\trunk python setup.py bdist_wininst cd ..\..\testman4trac\trunk python setup.py bdist_wininst cd ..\.. xcopy /y *.txt bin testman4trac.1.4.5/sqlexecutor/0000755000175000017500000000000011565162323016427 5ustar robertorobertotestman4trac.1.4.5/sqlexecutor/trunk/0000755000175000017500000000000011566010105017561 5ustar robertorobertotestman4trac.1.4.5/sqlexecutor/trunk/sqlexecutor/0000755000175000017500000000000011565162323022150 5ustar robertorobertotestman4trac.1.4.5/sqlexecutor/trunk/sqlexecutor/__init__.py0000755000175000017500000000012011565162323024255 0ustar robertoroberto# -*- coding: utf-8 -*- # # Copyright (C) 2010 Roberto Longobardi # import sql testman4trac.1.4.5/sqlexecutor/trunk/sqlexecutor/LICENSE.txt0000755000175000017500000000242711565162323024003 0ustar robertorobertoCopyright (c) 2010, Roberto Longobardi All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.testman4trac.1.4.5/sqlexecutor/trunk/sqlexecutor/templates/0000755000175000017500000000000011565162323024146 5ustar robertorobertotestman4trac.1.4.5/sqlexecutor/trunk/sqlexecutor/templates/empty.html0000755000175000017500000000041111565162323026171 0ustar robertoroberto testman4trac.1.4.5/sqlexecutor/trunk/sqlexecutor/templates/result.html0000755000175000017500000000014511565162323026355 0ustar robertoroberto

Result:


$result
testman4trac.1.4.5/sqlexecutor/trunk/sqlexecutor/htdocs/0000755000175000017500000000000011565162323023434 5ustar robertorobertotestman4trac.1.4.5/sqlexecutor/trunk/sqlexecutor/htdocs/images/0000755000175000017500000000000011565162323024701 5ustar robertorobertotestman4trac.1.4.5/sqlexecutor/trunk/sqlexecutor/htdocs/place.holder0000755000175000017500000000000011565162323025710 0ustar robertorobertotestman4trac.1.4.5/sqlexecutor/trunk/sqlexecutor/htdocs/js/0000755000175000017500000000000011565162323024050 5ustar robertorobertotestman4trac.1.4.5/sqlexecutor/trunk/sqlexecutor/htdocs/css/0000755000175000017500000000000011565162323024224 5ustar robertorobertotestman4trac.1.4.5/sqlexecutor/trunk/sqlexecutor/README.txt0000755000175000017500000000202411565162323023647 0ustar robertorobertoTrac Generic SQL Executor - Part of the Test Manager plugin for Trac Copyright 2010 Roberto Longobardi Project web page on TracHacks: http://trac-hacks.org/wiki/TestManagerForTracPlugin Project web page on SourceForge.net: http://sourceforge.net/projects/testman4trac/ Project web page on Pypi: http://pypi.python.org/pypi/TestManager A Trac plugin to create Test Cases, organize them in catalogs, generate test plans and track their execution status and outcome. This module provides a simple way of running any SQL in the context of the Trac environment. This is an extremely helpful debug tool when dealing with database persistence. Caution: This plugin is not intended for a production environment. ================================================================================================= Change History: (Refer to the tickets on trac-hacks for complete descriptions.) Release 1.0.0 (2010-10-01): o First release publicly available apart from the core Test Manager plugin testman4trac.1.4.5/sqlexecutor/trunk/sqlexecutor/sql.py0000755000175000017500000000600411565162323023324 0ustar robertoroberto# -*- coding: utf-8 -*- # # Copyright (C) 2010 Roberto Longobardi # import re import sys import time import traceback from datetime import datetime from trac.core import * from trac.perm import IPermissionRequestor from trac.util.text import CRLF from trac.util.translation import _, N_, gettext from trac.web.api import IRequestHandler from trac.web.chrome import ITemplateProvider from tracgenericclass.util import * class SqlExecutor(Component): """SQL Executor.""" implements(IPermissionRequestor, IRequestHandler, ITemplateProvider) # IPermissionRequestor methods def get_permission_actions(self): return ['SQL_RUN'] # IRequestHandler methods def match_request(self, req): return req.path_info.startswith('/sqlexec') and 'SQL_RUN' in req.perm def process_request(self, req): """ Executes a generic SQL. """ req.perm.require('SQL_RUN') sql = req.args.get('sql') self.env.log.debug(sql) strdata = """

Result:


""" try: db = self.env.get_db_cnx() cursor = db.cursor() cursor.execute(sql) for row in cursor: strdata += '' for i in row: strdata += '' db.commit() self.env.log.debug(strdata) except: strdata = formatExceptionInfo() db.rollback() self.env.log.debug("SqlExecutor - Exception: ") self.env.log.debug(strdata) strdata += """
' if isinstance(i, basestring): strdata += i.encode('utf-8') elif isinstance(i, long): strdata += from_any_timestamp(i).isoformat() + ' (' + str(i) + ')' else: strdata += str(i).encode('utf-8') strdata += '' strdata += '
""" req.send_header("Content-Length", len(strdata)) req.write(strdata) #return 'result.html', {'result': result}, None return # ITemplateProvider methods def get_templates_dirs(self): """ Return the absolute path of the directory containing the provided Genshi templates. """ from pkg_resources import resource_filename return [resource_filename(__name__, 'templates')] def get_htdocs_dirs(self): """Return the absolute path of a directory containing additional static resources (such as images, style sheets, etc). """ from pkg_resources import resource_filename return [('sqlexecutor', resource_filename(__name__, 'htdocs'))] testman4trac.1.4.5/sqlexecutor/trunk/setup.py0000755000175000017500000000204411565162323021307 0ustar robertorobertofrom setuptools import setup setup( name='SQLExecutor', version='1.0.3', packages=['sqlexecutor'], package_data={'sqlexecutor' : ['*.txt', 'templates/*.html', 'htdocs/*.*', 'htdocs/js/*.js', 'htdocs/css/*.css', 'htdocs/images/*.*']}, author = 'Roberto Longobardi', author_email='seccanj@gmail.com', license='BSD. See the file LICENSE.txt contained in the package.', url='http://trac-hacks.org/wiki/TestManagerForTracPlugin', download_url='https://sourceforge.net/projects/testman4trac/files/', description='Test management plugin for Trac - SQL Executor component', long_description='A Trac plugin to create Test Cases, organize them in catalogs and track their execution status and outcome. This module provides a generic SQL executor to help debugging your application.', keywords='trac plugin generic class framework persistence sql execution run test case management project quality assurance statistics stats charts charting graph', entry_points = {'trac.plugins': ['sqlexecutor = sqlexecutor']} ) testman4trac.1.4.5/build.sh0000755000175000017500000000121611565162323015507 0ustar robertorobertoproject_path=$1 mkdir bin cd tracgenericclass/trunk python setup.py bdist_egg cp dist/*.egg ../../bin cd ../../tracgenericworkflow/trunk python setup.py bdist_egg cp dist/*.egg ../../bin cd ../../sqlexecutor/trunk python setup.py bdist_egg cp dist/*.egg ../../bin cd ../../testman4trac/trunk #python setup.py extract_messages #python setup.py extract_messages_js #python ./setup.py update_catalog -l it #python ./setup.py update_catalog_js -l it #python ./setup.py compile_catalog -f -l it #python ./setup.py compile_catalog_js -f -l it python setup.py bdist_egg cp dist/*.egg ../../bin cd ../.. cp *.txt bin cp bin/*.egg $project_path/plugins testman4trac.1.4.5/testman4trac/0000755000175000017500000000000011565162323016462 5ustar robertorobertotestman4trac.1.4.5/testman4trac/trunk/0000755000175000017500000000000011566010105017614 5ustar robertorobertotestman4trac.1.4.5/testman4trac/trunk/messages-js.cfg0000755000175000017500000000035711565162323022537 0ustar robertoroberto# mapping file for extracting messages from javascript files into # /locale/messages-js.pot (see setup.cfg) [javascript: **.js] [extractors] javascript_script = trac.util.dist:extract_javascript_script [javascript_script: **.html] testman4trac.1.4.5/testman4trac/trunk/setup.cfg0000755000175000017500000000262111565162323021452 0ustar robertoroberto[extract_messages] add_comments = TRANSLATOR: copyright_holder = Roberto Longobardi msgid_bugs_address = seccanj@gmail.com output_file = testmanager/locale/messages.pot # Note: specify as 'keywords' the functions for which the messages # should be extracted. This should match the list of functions # that you've listed in the 'domain_functions()' call above. keywords = _ N_ tag_ # Other example: #keywords = _ ngettext:1,2 N_ tag_ width = 72 [init_catalog] input_file = testmanager/locale/messages.pot output_dir = testmanager/locale domain = testmanager [compile_catalog] directory = testmanager/locale domain = testmanager [update_catalog] input_file = testmanager/locale/messages.pot output_dir = testmanager/locale domain = testmanager [extract_messages_js] add_comments = TRANSLATOR: copyright_holder = Roberto Longobardi msgid_bugs_address = seccanj@gmail.com output_file = testmanager/locale/messages-js.pot keywords = _ N_ tag_ mapping_file = messages-js.cfg [init_catalog_js] domain = testmanager-js input_file = testmanager/locale/messages-js.pot output_dir = testmanager/locale [compile_catalog_js] domain = testmanager-js directory = testmanager/locale [update_catalog_js] domain = testmanager-js input_file = testmanager/locale/messages-js.pot output_dir = testmanager/locale [generate_messages_js] domain = testmanager-js input_dir = testmanager/locale output_dir = testmanager/htdocs/js testman4trac.1.4.5/testman4trac/trunk/testmanager/0000755000175000017500000000000011566010022022124 5ustar robertorobertotestman4trac.1.4.5/testman4trac/trunk/testmanager/web_ui.py0000755000175000017500000000412211565162323023765 0ustar robertoroberto# -*- coding: utf-8 -*- # # Copyright (C) 2010 Roberto Longobardi # import re import math from genshi.builder import tag from trac.core import * from trac.mimeview import Context from trac.resource import Resource from trac.search import ISearchSource from trac.util import to_unicode from trac.util.compat import sorted, set, any from trac.util.text import CRLF from trac.web.chrome import ITemplateProvider, INavigationContributor, \ add_stylesheet, add_script, add_ctxtnav from trac.wiki.formatter import Formatter from trac.wiki.model import WikiPage from testmanager.api import TestManagerSystem from testmanager.model import TestCatalog, TestCase, TestCaseInPlan, TestPlan, TestManagerModelProvider try: from testmanager.api import _, tag_, N_ except ImportError: from trac.util.translation import _, N_ tag_ = _ class TestManagerTemplateProvider(Component): """Provides templates and static resources for the TestManager plugin.""" implements(ITemplateProvider) # ITemplateProvider methods def get_templates_dirs(self): """ Return the absolute path of the directory containing the provided Genshi templates. """ from pkg_resources import resource_filename return [resource_filename(__name__, 'templates')] def get_htdocs_dirs(self): """Return the absolute path of a directory containing additional static resources (such as images, style sheets, etc). """ from pkg_resources import resource_filename return [('testmanager', resource_filename(__name__, 'htdocs'))] class TestManager(Component): """Implements the /testmanager handler and the Test Manager tab.""" implements(INavigationContributor) # INavigationContributor methods def get_active_navigation_item(self, req): if 'TEST_VIEW' in req.perm: return 'testmanager' def get_navigation_items(self, req): if 'TEST_VIEW' in req.perm: yield ('mainnav', 'testmanager', tag.a(_("Test Manager"), href=req.href.wiki()+'/TC', accesskey='M')) testman4trac.1.4.5/testman4trac/trunk/testmanager/__init__.py0000755000175000017500000000040711565162323024254 0ustar robertoroberto# -*- coding: utf-8 -*- # # Copyright (C) 2010 Roberto Longobardi, Marco Cipriani # """ See testmanager.api for detailed information. """ import api import macros import web_ui import wiki import model import util import stats import workflow import rpcsupport testman4trac.1.4.5/testman4trac/trunk/testmanager/wiki.py0000755000175000017500000007116111565470753023476 0ustar robertoroberto# -*- coding: utf-8 -*- # # Copyright (C) 2010 Roberto Longobardi # from trac.core import * from trac.web.chrome import add_stylesheet, add_script, ITemplateProvider from trac.wiki.api import IWikiSyntaxProvider from trac.resource import Resource from trac.mimeview.api import Context from trac.web.api import ITemplateStreamFilter from trac.wiki.api import WikiSystem, IWikiChangeListener from trac.wiki.model import WikiPage from trac.wiki.formatter import Formatter from genshi.builder import tag from genshi.filters.transform import Transformer from genshi import HTML from tracgenericclass.model import GenericClassModelProvider from testmanager.api import TestManagerSystem from testmanager.macros import TestCaseBreadcrumbMacro, TestCaseTreeMacro, TestPlanTreeMacro, TestPlanListMacro, TestCaseStatusMacro, TestCaseChangeStatusMacro, TestCaseStatusHistoryMacro from testmanager.model import TestCatalog, TestCase, TestCaseInPlan, TestPlan try: from testmanager.api import _, tag_, N_ except ImportError: from trac.util.translation import _, N_ tag_ = _ class WikiTestManagerInterface(Component): """Implement generic template provider.""" implements(ITemplateStreamFilter, IWikiChangeListener) _config_properties = {} def __init__(self, *args, **kwargs): """ Parses the configuration file for the section 'testmanager'. Available properties are: testplan.sortby = {modification_time|name} (default is name) """ Component.__init__(self, *args, **kwargs) for section in self.config.sections(): if section == 'testmanager': self.log.debug("WikiTestManagerInterface - parsing config section %s" % section) options = list(self.config.options(section)) self._parse_config_options(options) break def _parse_config_options(self, options): for option in options: name = option[0] value = option[1] self.env.log.debug(" %s = %s" % (name, value)) self._config_properties[name] = value # IWikiChangeListener methods def wiki_page_added(self, page): #page_on_db = WikiPage(self.env, page.name) pass def wiki_page_changed(self, page, version, t, comment, author, ipnr): pass def wiki_page_deleted(self, page): if page.name.find('_TC') >= 0: # Only Test Case deletion is supported. # Deleting a Test Catalog will not delete all of the inner # Test Cases. tc_id = page.name.rpartition('_TC')[2] tc = TestCase(self.env, tc_id) if tc.exists: tc.delete(del_wiki_page=False) else: self.env.log.debug("Test case not found") def wiki_page_version_deleted(self, page): pass # ITemplateStreamFilter methods def filter_stream(self, req, method, filename, stream, data): page_name = req.args.get('page', 'WikiStart') planid = req.args.get('planid', '-1') formatter = Formatter( self.env, Context.from_request(req, Resource('testmanager')) ) if page_name.startswith('TC'): req.perm.require('TEST_VIEW') if page_name.find('_TC') >= 0: if filename == 'wiki_view.html': if not planid or planid == '-1': return self._testcase_wiki_view(req, formatter, planid, page_name, stream) else: return self._testcase_in_plan_wiki_view(req, formatter, planid, page_name, stream) else: if filename == 'wiki_view.html': if not planid or planid == '-1': return self._catalog_wiki_view(req, formatter, page_name, stream) else: return self._testplan_wiki_view(req, formatter, page_name, planid, stream) return stream # Internal methods def _catalog_wiki_view(self, req, formatter, page_name, stream): path_name = req.path_info cat_name = path_name.rpartition('/')[2] cat_id = cat_name.rpartition('TT')[2] mode = req.args.get('mode', 'tree') fulldetails = req.args.get('fulldetails', 'False') tmmodelprovider = GenericClassModelProvider(self.env) test_catalog = TestCatalog(self.env, cat_id, page_name) tree_macro = TestCaseTreeMacro(self.env) if page_name == 'TC': # Root of all catalogs insert1 = tag.div()( tag.div(id='pasteMultipleTCsHereMessage', class_='messageBox', style='display: none;')(_("Select the catalog into which to paste the Test Cases and click on 'Paste the copied Test Cases here'. "), tag.a(href='javascript:void(0);', onclick='cancelTCsCopy()')(_("Cancel")) ), tag.br(), tag.div(id='pasteTCHereMessage', class_='messageBox', style='display: none;')(_("Select the catalog into which to paste the Test Case and click on 'Move the copied Test Case here'. "), tag.a(href='javascript:void(0);', onclick='cancelTCMove()')(_("Cancel")) ), tag.h1(_("Test Catalogs List")), tag.br(), tag.br() ) fieldLabel = _("New Catalog:") buttonLabel = _("Add a Catalog") else: insert1 = tag.div()( self._get_breadcrumb_markup(formatter, None, page_name, mode, fulldetails), tag.div(style='border: 1px, solid, gray; padding: 1px;')( tag.span()( tag.a(href=req.href.wiki(page_name, mode='tree'))( tag.img(src='../chrome/testmanager/images/tree.png', title="Tree View")) ), tag.span()( tag.a(href=req.href.wiki(page_name, mode='tree_table', fulldetails='True'))( tag.img(src='../chrome/testmanager/images/tree_table.png', title="Table View")) )), tag.br(), tag.div(id='pasteMultipleTCsHereMessage', class_='messageBox', style='display: none;')( _("Select the catalog (even this one) into which to paste the Test Cases and click on 'Paste the copied Test Cases here'. "), tag.a(href='javascript:void(0);', onclick='cancelTCsCopy()')(_("Cancel")) ), tag.br(), tag.div(id='pasteTCHereMessage', class_='messageBox', style='display: none;')( _("Select the catalog (even this one) into which to paste the Test Case and click on 'Move the copied Test Case here'. "), tag.a(href='javascript:void(0);', onclick='cancelTCMove()')(_("Cancel")) ), tag.br(), tag.h1(_("Test Catalog")) ) fieldLabel = _("New Sub-Catalog:") buttonLabel = _("Add a Sub-Catalog") insert2 = tag.div()( HTML(tree_macro.expand_macro(formatter, None, 'mode='+mode+',fulldetails='+fulldetails+',catalog_path='+page_name)), tag.div(class_='testCaseList')( tag.br(), tag.br() )) if not page_name == 'TC': # The root of all catalogs cannot contain itself test cases insert2.append(tag.div()( self._get_custom_fields_markup(test_catalog, tmmodelprovider.get_custom_fields_for_realm('testcatalog')), tag.br() )) insert2.append(tag.div(class_='field')( tag.br(), tag.br(), tag.br(), tag.label( fieldLabel, tag.span(id='catErrorMsgSpan', style='color: red;'), tag.br(), tag.input(id='catName', type='text', name='catName', size='50'), tag.input(type='button', value=buttonLabel, onclick='creaTestCatalog("'+cat_name+'")') ) )) if not page_name == 'TC': # The root of all catalogs cannot contain itself test cases, # cannot generate test plans and does not need a test plans list insert2.append(tag.div(class_='field')( tag.label( _("New Test Case:"), tag.span(id='errorMsgSpan', style='color: red;'), tag.br(), tag.input(id='tcName', type='text', name='tcName', size='50'), tag.input(type='button', value=_("Add a Test Case"), onclick='creaTestCase("'+cat_name+'")') ), tag.br(), tag.label( _("New Test Plan:"), tag.span(id='errorMsgSpan2', style='color: red;'), tag.br(), tag.input(id='planName', type='text', name='planName', size='50'), tag.input(type='button', value=_("Generate a new Test Plan"), onclick='creaTestPlan("'+cat_name+'")') ), tag.br(), )) insert2.append(tag.br()) insert2.append(tag.br()) insert2.append(tag.input( type='button', id='showSelectionBoxesButton', value=_("Select Multiple Test Cases"), onclick='showSelectionCheckboxes()') ) insert2.append(tag.input( type='button', id='copyMultipleTCsButton', value=_("Copy the Selected Test Cases"), onclick='copyMultipleTestCasesToClipboard()') ) if not page_name == 'TC': insert2.append(tag.input(type='button', id='pasteMultipleTCsHereButton', value=_("Paste the copied Test Cases here"), onclick='pasteMultipleTestCasesIntoCatalog("'+cat_name+'")') ) insert2.append(tag.input(type='button', id='pasteTCHereButton', value=_("Move the copied Test Case here"), onclick='pasteTestCaseIntoCatalog("'+cat_name+'")') ) insert2.append(HTML(self._get_import_dialog_markup(req.href(), cat_name))) insert2.append(tag.input(type='button', id='importTestCasesButton', value=_("Import Test Cases"), onclick='importTestCasesIntoCatalog("'+cat_name+'")') ) insert2.append(tag.div(class_='field')( self._get_testplan_list_markup(formatter, cat_name, mode, fulldetails) )) insert2.append(tag.div()(tag.br(), tag.br(), tag.br(), tag.br())) common_code = self._write_common_code(req) return stream | Transformer('//body').append(common_code) | Transformer('//div[contains(@class,"wikipage")]').after(insert2) | Transformer('//div[contains(@class,"wikipage")]').before(insert1) def _testplan_wiki_view(self, req, formatter, page_name, planid, stream): path_name = req.path_info cat_name = path_name.rpartition('/')[2] cat_id = cat_name.rpartition('TT')[2] mode = req.args.get('mode', 'tree') fulldetails = req.args.get('fulldetails', 'False') if 'testplan.sortby' in self._config_properties: sortby = self._config_properties['testplan.sortby'] else: sortby = 'name' tmmodelprovider = GenericClassModelProvider(self.env) test_plan = TestPlan(self.env, planid, cat_id, page_name) tree_macro = TestPlanTreeMacro(self.env) tp = TestPlan(self.env, planid) insert1 = tag.div()( tag.a(href=req.href.wiki(page_name))(_("Back to the Catalog")), tag.div(style='border: 1px, solid, gray; padding: 1px;')( tag.span()( tag.a(href=req.href.wiki(page_name, mode='tree', planid=planid))( tag.img(src='../chrome/testmanager/images/tree.png', title="Tree View")) ), tag.span()( tag.a(href=req.href.wiki(page_name, mode='tree_table', planid=planid))( tag.img(src='../chrome/testmanager/images/tree_table.png', title="Table View")) )), tag.br(), tag.h1(_("Test Plan: ")+tp['name']) ) insert2 = tag.div()( HTML(tree_macro.expand_macro(formatter, None, 'planid='+str(planid)+',catalog_path='+page_name+',mode='+mode+',fulldetails='+fulldetails+',sortby='+sortby)), tag.div(class_='testCaseList')( tag.br(), tag.br(), self._get_custom_fields_markup(test_plan, tmmodelprovider.get_custom_fields_for_realm('testplan')), tag.br(), tag.div(class_='field')( tag.br(), tag.br(), tag.br(), tag.br() ) )) insert2.append(tag.div()(tag.br(), tag.br(), tag.br(), tag.br())) common_code = self._write_common_code(req) return stream | Transformer('//body').append(common_code) | Transformer('//div[contains(@class,"wikipage")]').after(insert2) | Transformer('//div[contains(@class,"wikipage")]').before(insert1) def _testcase_wiki_view(self, req, formatter, planid, page_name, stream): tc_name = page_name cat_name = page_name.partition('_TC')[0] mode = req.args.get('mode', 'tree') fulldetails = req.args.get('fulldetails', 'False') is_edit = req.args.get('edit_custom', 'false') has_status = False plan_name = '' tc_id = tc_name.partition('_TC')[2] test_case = TestCase(self.env, tc_id, tc_name) tmmodelprovider = GenericClassModelProvider(self.env) insert1 = tag.div()( self._get_breadcrumb_markup(formatter, planid, page_name, mode, fulldetails), tag.br(), tag.div(id='copiedMultipleTCsMessage', class_='messageBox', style='display: none;')( _("The Test Cases have been copied. Now select the catalog into which to paste the Test Cases and click on 'Paste the copied Test Cases here'. "), tag.a(href='javascript:void(0);', onclick='cancelTCsCopy()')(_("Cancel")) ), tag.br(), tag.div(id='copiedTCMessage', class_='messageBox', style='display: none;')( _("The Test Case has been cut. Now select the catalog into which to move the Test Case and click on 'Move the copied Test Case here'. "), tag.a(href='javascript:void(0);', onclick='cancelTCMove()')(_("Cancel")) ), tag.br(), tag.span(style='font-size: large; font-weight: bold;')( tag.span()( _("Test Case") ) ) ) insert2 = tag.div(class_='field', style='marging-top: 60px;')( tag.br(), tag.br(), self._get_custom_fields_markup(test_case, tmmodelprovider.get_custom_fields_for_realm('testcase')), tag.br(), tag.input(type='button', value=_("Open a Ticket on this Test Case"), onclick='creaTicket("'+tc_name+'", "", "")'), HTML('  '), tag.input(type='button', value=_("Show Related Tickets"), onclick='showTickets("'+tc_name+'", "", "")'), HTML('  '), tag.input(type='button', id='moveTCButton', value=_("Move the Test Case into another catalog"), onclick='copyTestCaseToClipboard("'+tc_name+'")'), HTML('  '), tag.input(type='button', id='duplicateTCButton', value=_("Duplicate the Test Case"), onclick='duplicateTestCase("'+tc_name+'", "'+cat_name+'")'), tag.br(), tag.br() ) common_code = self._write_common_code(req) return stream | Transformer('//body').append(common_code) | Transformer('//div[contains(@class,"wikipage")]').after(insert2) | Transformer('//div[contains(@class,"wikipage")]').before(insert1) def _testcase_in_plan_wiki_view(self, req, formatter, planid, page_name, stream): tc_name = page_name cat_name = page_name.partition('_TC')[0] mode = req.args.get('mode', 'tree') fulldetails = req.args.get('fulldetails', 'False') has_status = True tp = TestPlan(self.env, planid) plan_name = tp['name'] tc_id = tc_name.partition('_TC')[2] # Note that assigning a default status here is functional. If the tcip actually exists, # the real status will override this value. tcip = TestCaseInPlan(self.env, tc_id, planid, page_name, TestManagerSystem(self.env).get_default_tc_status()) tmmodelprovider = GenericClassModelProvider(self.env) add_stylesheet(req, 'testmanager/css/menu.css') add_script(req, 'testmanager/js/menu.js') insert1 = tag.div()( self._get_breadcrumb_markup(formatter, planid, page_name, mode, fulldetails), tag.br(), tag.br(), tag.br(), tag.span(style='font-size: large; font-weight: bold;')( self._get_testcase_status_markup(formatter, has_status, page_name, planid), tag.span()( _("Test Case") ) ) ) insert2 = tag.div(class_='field', style='marging-top: 60px;')( tag.br(), tag.br(), self._get_custom_fields_markup(tcip, tmmodelprovider.get_custom_fields_for_realm('testcaseinplan'), ('page_name', 'status')), tag.br(), self._get_testcase_change_status_markup(formatter, has_status, page_name, planid), tag.br(), tag.br(), tag.input(type='button', value=_("Open a Ticket on this Test Case"), onclick='creaTicket("'+tc_name+'", "'+planid+'", "'+plan_name+'")'), HTML('  '), tag.input(type='button', value=_("Show Related Tickets"), onclick='showTickets("'+tc_name+'", "'+planid+'", "'+plan_name+'")'), HTML('  '), tag.br(), tag.br(), self._get_testcase_status_history_markup(formatter, has_status, page_name, planid), tag.br(), tag.br() ) common_code = self._write_common_code(req) return stream | Transformer('//body').append(common_code) | Transformer('//div[contains(@class,"wikipage")]').after(insert2) | Transformer('//div[contains(@class,"wikipage")]').before(insert1) def _get_breadcrumb_markup(self, formatter, planid, page_name, mode='tree', fulldetails='False'): if planid and not planid == '-1': # We are in the context of a test plan if not page_name.rpartition('_TC')[2] == '': # It's a test case in plan tp = TestPlan(self.env, planid) catpath = tp['page_name'] return tag.a(href=formatter.req.href.wiki(catpath, planid=planid, mode=mode, fulldetails=fulldetails))(_("Back to the Test Plan")) else: # It's a test plan return tag.a(href=formatter.req.href.wiki(page_name))(_("Back to the Catalog")) else: # It's a test catalog or test case description breadcrumb_macro = TestCaseBreadcrumbMacro(self.env) return HTML(breadcrumb_macro.expand_macro(formatter, None, 'page_name='+page_name+',mode='+mode+',fulldetails='+fulldetails)) def _get_testcase_status_markup(self, formatter, has_status, page_name, planid): if has_status: testcase_status_macro = TestCaseStatusMacro(self.env) return tag.span(style='float: left; padding-top: 4px; padding-right: 5px;')( HTML(testcase_status_macro.expand_macro(formatter, None, 'page_name='+page_name+',planid='+planid)) ) else: return tag.span()() def _get_testcase_change_status_markup(self, formatter, has_status, page_name, planid): if has_status: testcase_change_status_macro = TestCaseChangeStatusMacro(self.env) return HTML(testcase_change_status_macro.expand_macro(formatter, None, 'page_name='+page_name+',planid='+planid)) else: return tag.span()() def _get_testcase_status_history_markup(self, formatter, has_status, page_name, planid): if has_status: testcase_status_history_macro = TestCaseStatusHistoryMacro(self.env) return HTML(testcase_status_history_macro.expand_macro(formatter, None, 'page_name='+page_name+',planid='+planid)) else: return tag.span()() def _get_testplan_list_markup(self, formatter, cat_name, mode, fulldetails): testplan_list_macro = TestPlanListMacro(self.env) return HTML(testplan_list_macro.expand_macro(formatter, None, 'catalog_path='+cat_name+',mode='+mode+',fulldetails='+str(fulldetails))) def _get_custom_fields_markup(self, obj, fields, props=None): obj_key = obj.gey_key_string() obj_props = '' if props is not None: obj_props = obj.get_values_as_string(props) result = '' result += '' result += '' for f in fields: result += "" if f['type'] == 'text': result += '' result += '' result += '' # TODO Support other field types result += '' result += '
' result += '' if obj[f['name']] is not None: result += obj[f['name']] result += '' result += '' result += '' result += '' result += '
' return HTML(result) def _get_import_dialog_markup(self, base_location, cat_name): result = """ """ % (base_location, cat_name) return result; def _write_common_code(self, req): add_stylesheet(req, 'common/css/report.css') add_stylesheet(req, 'testmanager/css/blitzer/jquery-ui-1.8.13.custom.css') add_stylesheet(req, 'testmanager/css/testmanager.css') before_jquery = 'var baseLocation="'+req.href()+'";' + \ 'var jQuery_trac_old = $.noConflict(true);' after_jquery = 'var jQuery_testmanager = $.noConflict(true);' common_code = tag.div()( tag.script(before_jquery, type='text/javascript'), tag.script(src='../chrome/testmanager/js/jquery-1.5.1.min.js', type='text/javascript'), tag.script(src='../chrome/testmanager/js/jquery-ui-1.8.13.custom.min.js', type='text/javascript'), tag.script(after_jquery, type='text/javascript'), tag.script(src='../chrome/testmanager/js/cookies.js', type='text/javascript'), tag.script(src='../chrome/testmanager/js/testmanager.js', type='text/javascript'), ) if self.env.get_version() < 25: common_code.append(tag.script(src='../chrome/testmanager/js/compatibility.js', type='text/javascript')) try: if req.locale is not None: common_code.append(tag.script(src='../chrome/testmanager/js/%s.js' % req.locale, type='text/javascript')) except: # Trac 0.11 pass #common_code.append(tag.script(""" # (function($) { # $('') # .click(function() { # alert('Top: ' + $(this).offset().top + '\n' + # 'jQuery: ' + $.fn.jquery); # }) # .appendTo('body'); # })(jQuery_testmanager); #""", type='text/javascript')) return common_code testman4trac.1.4.5/testman4trac/trunk/testmanager/LICENSE.txt0000755000175000017500000000244711565162323023774 0ustar robertorobertoCopyright (c) 2010, Roberto Longobardi, Marco Cipriani All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.testman4trac.1.4.5/testman4trac/trunk/testmanager/rpcsupport.py0000755000175000017500000004014011565162323024734 0ustar robertoroberto# -*- coding: utf-8 -*- # # Copyright (C) 2010 Roberto Longobardi # import os from datetime import datetime from trac.core import * from trac.util import get_reporter_id from tracgenericclass.model import GenericClassModelProvider from tracgenericclass.util import formatExceptionInfo from testmanager.api import TestManagerSystem from testmanager.model import TestCatalog, TestCase, TestCaseInPlan, TestPlan try: # Check that tracrpc plugin is available. Otherwise, an ImportError exception will be raised. from tracrpc.api import IXMLRPCHandler __all__ = ['TestManagerRPC'] class TestManagerRPC(Component): implements(IXMLRPCHandler) def __init__(self): self.testmanagersys = TestManagerSystem(self.env) def xmlrpc_namespace(self): return 'testmanager' def xmlrpc_methods(self): yield ('TEST_MODIFY', ((str, str, str, str),), self.createTestCatalog) yield ('TEST_MODIFY', ((str, str, str, str),), self.createTestCase) yield ('TEST_PLAN_ADMIN', ((str, str, str),), self.createTestPlan) yield (None, ((bool, str, str),), self.deleteTestObject) yield (None, ((bool, str, str),(bool, str, str, dict)), self.modifyTestObject) yield (None, ((bool, str, str, str),), self.setTestCaseStatus) yield ('TEST_VIEW', ((list, str),(list, str, str)), self.listTestCases) yield ('TEST_VIEW', ((list, str),), self.getTestCatalog) yield ('TEST_VIEW', ((list, str),(list, str, str)), self.getTestCase) yield ('TEST_VIEW', ((list, str, str),), self.getTestPlan) yield ('TEST_VIEW', ((list, str),), self.listSubCatalogs) yield ('TEST_VIEW', ((list, str),), self.listTestPlans) def createTestCatalog(self, req, parent_catalog_id, title, description): """ Creates a new test catalog, in the parent catalog specified, with the specified title and description. To create a root catalog, specify '' as the parent catalog. Returns the generated object ID, or '-1' if an error occurs. """ result = '-1' try: id = self.testmanagersys.get_next_id('catalog') pagename = None if parent_catalog_id is not None and parent_catalog_id != '': # Check parent catalog really exists, and get its page_name tcat = TestCatalog(self.env, parent_catalog_id) if not tcat.exists: self.env.log.error("Input parent test catalog with ID %s not found." % parent_catalog_id) return result pagename = tcat['page_name'] + '_TT' + id else: pagename = 'TC_TT' + id author = get_reporter_id(req, 'author') new_tc = TestCatalog(self.env, id, pagename, title, description) new_tc.author = author new_tc.remote_addr = req.remote_addr # This also creates the Wiki page new_tc.insert() result = id except: self.env.log.error("Error adding test catalog with title '%s' in catalog with ID %s!" % (title, parent_catalog_id)) self.env.log.error(formatExceptionInfo()) return id def createTestCase(self, req, catalog_id, title, description): """ Creates a new test case, in the catalog specified, with the specified title and description. Returns the generated object ID, or '-1' if an error occurs. """ result = '-1' try: if catalog_id is None or catalog_id == '': self.env.log.error("Cannot create a test plan on the root catalog container.") return result # Check catalog really exists, and get its page_name tcat = TestCatalog(self.env, catalog_id) if not tcat.exists: self.env.log.error("Input test catalog with ID %s not found." % catalog_id) return result author = get_reporter_id(req, 'author') id = self.testmanagersys.get_next_id('testcase') pagename = tcat['page_name'] + '_TC' + id new_tc = TestCase(self.env, id, pagename, title, description) new_tc.author = author new_tc.remote_addr = req.remote_addr # This also creates the Wiki page new_tc.insert() result = id except: self.env.log.error("Error adding test case with title '%s' in catalog with ID %s!" % (title, catalog_id)) self.env.log.error(formatExceptionInfo()) return id def createTestPlan(self, req, catalog_id, name): """ Creates a new test plan, on the catalog specified, with the specified name. Returns the generated object ID, or '-1' if an error occurs. """ result = '-1' try: # Check catalog really exists, and get its page_name tcat = TestCatalog(self.env, catalog_id) if not tcat.exists: self.env.log.error("Input test catalog with ID %s not found." % catalog_id) return result author = get_reporter_id(req, 'author') id = self.testmanagersys.get_next_id('testplan') pagename = tcat['page_name'] new_tp = TestPlan(self.env, id, catalog_id, pagename, name, author) new_tp.insert() result = id except: self.env.log.error("Error adding test plan with name '%s' for catalog with ID %s!" % (name, catalog_id)) self.env.log.error(formatExceptionInfo()) return result def deleteTestObject(self, req, objtype, id): """ Deletes the test object of the specified type identified by the given id. Returns True if successful, False otherwise. """ try: # Check the object exists obj = None if objtype == 'testcatalog': req.perm.require('TEST_MODIFY') obj = TestCatalog(self.env, id) elif objtype == 'testcase': req.perm.require('TEST_MODIFY') obj = TestCase(self.env, id) elif objtype == 'testplan': req.perm.require('TEST_PLAN_ADMIN') obj = TestPlan(self.env, id) if not obj.exists: self.env.log.error("Input test object of type %s with ID %s not found." % (objtype, id)) return False obj.delete() except: self.env.log.error("Error deleting test object of type %s with ID %s." % (objtype, id)) self.env.log.error(formatExceptionInfo()) return False return True def modifyTestObject(self, req, objtype, id, attributes={}): """ Modifies the test object of the specified type identified by the given id. Returns True if successful, False otherwise. """ try: # Check the object exists obj = None if objtype == 'testcatalog': req.perm.require('TEST_MODIFY') obj = TestCatalog(self.env, id) elif objtype == 'testcase': req.perm.require('TEST_MODIFY') obj = TestCase(self.env, id) elif objtype == 'testplan': req.perm.require('TEST_PLAN_ADMIN') obj = TestPlan(self.env, id) if not obj.exists: self.env.log.error("Input test object of type %s with ID %s not found." % (objtype, id)) return False author = get_reporter_id(req, 'author') for k, v in attributes.iteritems(): if k == 'title': obj.title = v elif k == 'description': obj.description = v else: obj[k] = v obj.author = author obj.remote_addr = req.remote_addr obj.save_changes(author, "Changed through RPC.") except: self.env.log.error("Error modifying test object of type %s with ID %s." % (objtype, id)) self.env.log.error(formatExceptionInfo()) return False return True def setTestCaseStatus(self, req, testcase_id, plan_id, status): """ Sets the test case status. Returns True if successful, False otherwise. """ try: author = get_reporter_id(req, 'author') tcip = TestCaseInPlan(self.env, testcase_id, plan_id) if tcip.exists: tcip.set_status(status, author) tcip.save_changes(author, "Status changed") else: tc = TestCase(self.env, testcase_id) tcip['page_name'] = tc['page_name'] tcip.set_status(status, author) tcip.insert() except: self.env.log.error("Error setting the test case status with ID %s on plan %s to %s!" % (testcase_id, plan_id, status)) self.env.log.error(formatExceptionInfo()) return False return True def getTestCatalog(self, req, catalog_id): """ Returns the catalog properties. The result is in the form, all strings: (wiki_page_name, title, description) """ try: # Check catalog really exists tcat = TestCatalog(self.env, catalog_id) if not tcat.exists: self.env.log.error("Input test catalog with ID %s not found." % catalog_id) else: return (tcat['page_name'], tcat.title, tcat.description) except: self.env.log.error("Error getting the test catalog with ID %s!" % catalog_id) self.env.log.error(formatExceptionInfo()) def getTestCase(self, req, testcase_id, plan_id=''): """ Returns the test case properties. If plan_id is provided, also the status of the test case in the plan will be returned. Each result is in the form, all strings: If plan_id is NOT provided: (wiki_page_name, title, description) If plan_id is provided: (wiki_page_name, title, description, status) """ try: # Check test case really exists tc = TestCase(self.env, testcase_id) if not tc.exists: self.env.log.error("Input test case with ID %s not found." % testcase_id) else: if plan_id is None or plan_id == '': return (tc['page_name'], tc.title, tc.description) else: tcip = TestCaseInPlan(self.env, testcase_id, plan_id) return (tc['page_name'], tc.title, tc.description, tcip['status']) except: self.env.log.error("Error getting the test case with ID %s!" % testcase_id) self.env.log.error(formatExceptionInfo()) def getTestPlan(self, req, plan_id, catalog_id): """ Returns the test plan properties. The result is in the form, all strings: (wiki_page_name, name) """ try: # Check test plan really exists tp = TestPlan(self.env, plan_id, catalog_id) if not tp.exists: self.env.log.error("Input test plan with ID %s on catalog %s not found." % (plan_id, catalog_id)) else: return (tp['page_name'], tp['name']) except: self.env.log.error("Error getting the test plan with ID %s on catalog %s." % (plan_id, catalog_id)) self.env.log.error(formatExceptionInfo()) def listSubCatalogs(self, req, catalog_id): """ Returns a iterator over the direct sub-catalogs of the specified catalog. Each result is in the form, all strings: (test_catalog_id, wiki_page_name, title, description) """ try: # Check catalog really exists tcat = TestCatalog(self.env, catalog_id) if not tcat.exists: self.env.log.error("Input test catalog with ID %s not found." % catalog_id) else: for tc in tcat.list_subcatalogs(): yield (tc['id'], tc['page_name'], tc.title, tc.description) except: self.env.log.error("Error listing the test catalogs!") self.env.log.error(formatExceptionInfo()) def listTestPlans(self, req, catalog_id): """ Returns a iterator over the test plans associated to the specified catalog. Each result is in the form, all strings: (testplan_id, name) """ try: # Check catalog really exists tcat = TestCatalog(self.env, catalog_id) if not tcat.exists: self.env.log.error("Input test catalog with ID %s not found." % catalog_id) else: for tp in tcat.list_testplans(): yield (tp['id'], tp['name']) except: self.env.log.error("Error listing the test plans!") self.env.log.error(formatExceptionInfo()) def listTestCases(self, req, catalog_id, plan_id=''): """ Returns a iterator over the test cases directly in the specified catalog (no sub-catalogs). If plan_id is provided, also the status of the test case in the plan will be returned. Each result is in the form, all strings: If plan_id is NOT provided: (testcase_id, wiki_page_name, title, description) If plan_id is provided: (testcase_id, wiki_page_name, status) """ try: # Check catalog really exists tcat = TestCatalog(self.env, catalog_id) if not tcat.exists: self.env.log.error("Input test catalog with ID %s not found." % catalog_id) else: if plan_id is None or plan_id == '': for tc in tcat.list_testcases(): # Returned object is a TestCase yield (tc['id'], tc['page_name'], tc.title, tc.description) else: for tcip in tcat.list_testcases(plan_id): # Returned object is a TestCaseInPlan yield (tcip['id'], tcip['page_name'], tcip['status']) except: self.env.log.error("Error listing the test cases in the catalog with ID %s!" % catalog_id) self.env.log.error(formatExceptionInfo()) except ImportError: print "\n\nError importing Trac XML-RPC Plugin. No XML-RPC remote interface will be available." print "Download and install it from http://trac-hacks.org/wiki/XmlRpcPlugin if you require XML-RPC access to the Test Manager.\n\n" testman4trac.1.4.5/testman4trac/trunk/testmanager/templates/0000755000175000017500000000000011565441073024137 5ustar robertorobertotestman4trac.1.4.5/testman4trac/trunk/testmanager/templates/testmanagerstats.html0000755000175000017500000001530211565162323030420 0ustar robertoroberto

Test Management Statistics

Settings

URL to bookmark:

Export this data to Excel   (CSV format)
testman4trac.1.4.5/testman4trac/trunk/testmanager/templates/testimportresults.html0000744000175000017500000000275511565455566030707 0ustar robertoroberto

Test Case import results


Got to the enclosing Test Catalog.

Errors

Row number Test Title Error
${error[0]} ${error[1]} ${error[2]}


Test Cases imported successfully

Test Title
${imported_tc}




testman4trac.1.4.5/testman4trac/trunk/testmanager/templates/empty.html0000755000175000017500000000041111565162323026160 0ustar robertoroberto testman4trac.1.4.5/testman4trac/trunk/testmanager/templates/jsondata.html0000755000175000017500000000100111565162323026621 0ustar robertoroberto ${t['date']} ${t['new_tcs']} ${t['successful']} ${t['failed']} ${t['all_tcs']} ${t['all_successful']} ${t['all_untested']} ${t['all_failed']} testman4trac.1.4.5/testman4trac/trunk/testmanager/htdocs/0000755000175000017500000000000011565162323023423 5ustar robertorobertotestman4trac.1.4.5/testman4trac/trunk/testmanager/htdocs/images/0000755000175000017500000000000011565162323024670 5ustar robertorobertotestman4trac.1.4.5/testman4trac/trunk/testmanager/htdocs/images/tree_table.png0000755000175000017500000000141311565162323027506 0ustar robertoroberto‰PNG  IHDRVÎŽWsRGB®ÎébKGDÿÿÿ ½§“ pHYs  šœtIMEÚ ;µ9û‹IDAT8Ë­”ÍnE…¿ûSÝíùq<™Qðø' Â$^ DÞƒgà=Ø!”ç`ņµÙ°Hd)ì‰GØ3¶§{Æãî*c@,"ÈÝ]ªSu޼§‘½½Ÿüáûý¦‰ïDKÖ×{œœ\b¦t»9N}g烯ò<|ÿ®7Fgìï?#Ï ìî®sçNúÚON&­¢XÑ#¥X[ë2¬R_'˜ÍjF£—}ŸÍj $ÏrES%¦„€`¦¤´$ÞZmÑ4 *B“WóÓË9^• ¶·»ìÿ<"Ɔ¿ýúœí»!*EÁ‹£C>n ¢¿|ÁÆÆ‰DŒÂîv—ùü 7SÜîÓ4 fFʽ{£ªäyNž9Ãá&"BŒáæ)ET ·sÜ wWB¼:xBJ‰Ó³cÆí€»Ró‹×”§ÂuL\Wg,.r®ëHž;¡¿Š™âª‚yàóÏ>%ÆH–et;m©‚v‹á”àðè­ÍMê&á&TÕ%抋 ÁÓÓ1!Ë(«’ÉäÁݘN§ü>žà*Ìgs&SL–žæ¹¡*¸Š`î RŒ„,c<žÐïß~ÂóK½5ÊiÉ­Õr³kšf†«‚à¸/-63Üíoy ÁuäÍ!)%T}©HLA`<“€<Ë(«Š²,T•rVQU3D ®\]-H)Þ<-CUq…½^RÂÜév:´Ûí7*ºí6­Ö ­•ò<{ø—®…§Å"òôéÌ 3àààÜ(Š1QUG¤ª‰£Ñ1"Šˆ0èß&M¾÷Ý7?<ÿåË/꺮ÿK}„üÑ£oüóßôÖQ|_½ÆÉBõ]w3†IEND®B`‚testman4trac.1.4.5/testman4trac/trunk/testmanager/htdocs/images/gray.png0000755000175000017500000000107011565162323026341 0ustar robertoroberto‰PNG  IHDRóÿagAMAÖØÔOX2tEXtSoftwareAdobe ImageReadyqÉe<ÊIDATxÚÌRÍJQ¾M©0I¨iF¥#LÐÊð!D ¢½´mã2ÜDÏà ´$z[äBd"DP°tük‘£·s†¹â ¶îÀÇ=÷ÞïüÜï\Bþ¥R){»Ý¾ /³ÙlL)ÕÀíõz ¼û+v#ŸÏ ÃáP¢kl4½#¹KÑñx|*ÉHì÷û´T*Ñ\.G³Ù,•$‰v»]= p>’Éä.KÂ2YÊår" ÝB«¤X,’Édb*`µZI8&N§“T«Õ»@ pÇgÜÛ\.×9:µZÀ3Èt:5Ï*•ŠN6¸º#ƒçùStêõ:ñVŠÔl6õÕn·a à›%Øb$MÓÖ&à8Îô*ýŒí@¤O\=ÏRû кÎó¤Æ:) Oè€óN&‚¾‚Èϳ8Þëõ†a\÷n·{_UU"˲®šßï'¢(ŸÏ‡ÕÕH$r c~ƒ«>ëµ8ŒÅbqEQ¾Ö}¤N§£F£Ñ+à3 Í¡ÚY:~Å,°Õj©™Læ1 ^çÄ¡é#1uöŒQ¡ýœ2 (Yñ§q¿iTp,Œwl$Ah,íW€'pu( ¥IEND®B`‚testman4trac.1.4.5/testman4trac/trunk/testmanager/htdocs/images/tree.png0000755000175000017500000000117311565162323026342 0ustar robertoroberto‰PNG  IHDRVÎŽWsRGB®ÎébKGDÿÿÿ ½§“ pHYs  šœtIMEÚ  8¦þGûIDAT8Ë­”1OTAdz3Ë»39àŽË¢Fh Á’ÄÂ;; Mì-ü~c‚†/à ¢P;‚†Â’(4—#‘;9Ž÷îxï­Å©1†xeŠÍî&ó›Ý™ÿ \ÉÚÚ[[Y™ŸÊ²|$‡V«ËÜ\™f³ƒª£TŠØÙ©µÅÅé‡Qä_¹^?b{û3QäayyŽZ-<±f³}©P(ºóœÂ˜œ,Q­Ž“žˆã”zý`Êâ8NHÓ"‚0Ø#ðcAB€bqp36x¦9';íöYX˜ #Ërâ^'0æÇþÕ¨`$=LÕaf´Û];öúWÙÚ=äÑÍŒéñÒP©a¦˜ÙT.—™©VØùÐ`é²°P«ÑK‡WÒ{CÕaÎ Þ<ªJ{7¦(W*œÄ Þëè q‚šq|tÌ·8a÷Høøæ=V—( CAN§‚9€ffg™žníäWªúý³‘r¤ª˜sàDÉóœ<Ïy}ÿ:Y–“e9ª:‚DÎ &ê! êèôS6·>Ñh%ܽ5Ïìäß«Â@²Î9LÅ¡ê9h4ˆ¼ðîKD;VWJdY©mTV(XH’3Æ'ª@àùàô4ý­µêû¼÷¶¾þbS~eìÿ,¿¨¹ÆwŪ”…A¢IEND®B`‚testman4trac.1.4.5/testman4trac/trunk/testmanager/htdocs/images/trash.png0000755000175000017500000000104711565162323026524 0ustar robertoroberto‰PNG  IHDR(-SsRGB®ÎéÞPLTE·‡Ÿ……¨54ÌÌ̪;;¥®££¹cbÓC0¤Û¥¥âßߤ+)£Å@4™”¬Á||¸F?Á(ÞJ2éææÅ<0ãÄÄé××  ¤êÍ͸ªª®# Œ»H@¼ts¼ ³*$äJ/Ç:,Ḹðïï³A:¢¦¶ ÎB2³50íÞÞ¥””ÍJ;™ßÑÑăƒ½jj»··ëP2ÖJ1ìçç·-&µŽ³¥¥»?9¤ í××ìÐе::¤……ª””çÜÜ·NHº+"š ¶83g–¼þtRNS@æØfbKGDˆH pHYs  šœtIMEÚ  6¨óÕµIDATÓ5‚0 …ãÀ­EQ@ÜDKõ<×᩸ÇÿÿC&-¼k¯ï{I˜ ,j/7ô\/ÞÛ'ã¬ÝQˆõcbމã\·YÝ”ÙBÌae»¦iF{ãˆ-âb›ÄOú;Æ„I¹šš.!D3@á“VN¸7hHÝý˜»œs™TCâ[G×uŽUÅžò¹”ê%Ræ_d=uÆ]dNOXiŠUò £ ûjú¶.¬|²?‹÷gjì£'IzIEND®B`‚testman4trac.1.4.5/testman4trac/trunk/testmanager/htdocs/images/pencil.png0000755000175000017500000000100711565162323026651 0ustar robertoroberto‰PNG  IHDR‘h6 pHYs  šœ¹IDATxœ}Ðï/ðç®X‡8Ó-ÕÐhMÜf¼d˜y/ÿƒ6/¼ãÞ0¯½óÎÒf~Ì0ò›ÆÌlÌJ™¤Üu8eÉ•»®óBó£ÒóúóÝ÷yDEÈ:‚uÅsE½­0š]X¬]± *ŠûH~û, ³s–ºZ“Œ¡4""Áø äãÿf-sD}½Ãé 2Œ)ÊCñ˜ƒŠ\ÜgDŽ'˜Bý™ y PåZí‰ÝÞpEîè«Qááx´ "¤S{ÊÄu…R¹³iÃ0l’¾r·T “© ‰Ó1T™à ƒ­3óîicuM£Ó.,/¥- Iª±o €¦÷p[¨ùKÿy+¿?˜®!"ªoýÓ>Ÿa%^¤=Xñ^dô½ãîß;'¼œuÇ€×WðæMj›Ðž¢“ ,ãÅÄ‹0ÍÏ4÷ð€½]˹½ù€ª‚Lÿ¡ |ù‘“ŸxqðÏ4ƹ"¸÷©¼Ê—®@ʲìÊÑm«2Æß¹ÿfww·é'š¦¹W(Þ.--ÝÎ{+œ’?ãYà40œ¢¼ ´€mÀô¨ûI€1¼¢cëx.0ÄŸßßõ)iëYÓzIEND®B`‚testman4trac.1.4.5/testman4trac/trunk/testmanager/htdocs/images/red.png0000755000175000017500000000107711565162323026160 0ustar robertoroberto‰PNG  IHDRóÿagAMAÖØÔOX2tEXtSoftwareAdobe ImageReadyqÉe<ÑIDATxÚÌR=OA]î(& QŒ1XhAEEh,ˆ¥QB”Hclˆ­…ÿ€ž CŒ¿€$ BAáp>l¼ã¼Ýuvs—ÖNòr»³ï½›Y„þ]d³Yg¿ß¿Tå¢QJuX—G£Ñ;ûKk+•JÛªª>Ò91_‡q©“ɤ nzfDÒlRœÉPýðê§Ó”ÔëÜ8ÍT*µ:mboµZ×\ÜhPíèˆj‘È$ Gžž¸I»ÝN3  †Á’Çã9a ýæYFDU'9=—ãdƒËûa–áÅ¿ ‚`ÿ ‡Ò´Ù]r8³\FÐ\]ÅMÈtìæ‘ÉÁŠ2ßcënÁú h8¾³¯ !&³ ƒœ #í˜:Óà»Z­y)ççˆÂðßV°œã쌓kµÚ=ÓXËYñz½{’$½³.k• ýL$¨ìvs|ÆãT+ù R)ìƒÆ51FÀf,Köz½yi0ÈÑhô¸[f¬±Øñù|Çù|þ¶ÛívL!T& …;¿ß œ]c„64õšØz°ج–³/@ð0*DÑŒ7Íö¢qòe¼ša ›b? `pIzÅ)E¿IEND®B`‚testman4trac.1.4.5/testman4trac/trunk/testmanager/htdocs/images/plus.png0000755000175000017500000000052611565162323026367 0ustar robertoroberto‰PNG  IHDRóÿasRGB®ÎébKGDÿÿÿ ½§“ pHYs  d_‘tIMEÚ %꼧 ÖIDAT8ËÝRÁŠÅ œ”‡ÞüþÿoõØkbb’wŠØÖ…½-¬ ¢3“™}(Š}ß=¥„mÛ`fè½CUafPUôÞ!"´ÖÆ¿O)%Çñbp÷âî(¥Üú€ˆ–ݪ:Ô¸û­¿Eafïùˆ»ˆ€™¡ªk½÷¥‚ðCD†Š¥‚™ˆÆH9g”RÀÌ`æÁgfrw¸;Df†œ3®ëB­u)ÿðŒ,×ZÀÓÀ@Ì8ç}ž'Zk`æ=Úf#ó§’0n5M‹äáEÜßlð?8_·»×{¡;ýàIEND®B`‚testman4trac.1.4.5/testman4trac/trunk/testmanager/htdocs/images/yellow.png0000755000175000017500000000106111565162323026712 0ustar robertoroberto‰PNG  IHDRóÿagAMAÖØÔOX2tEXtSoftwareAdobe ImageReadyqÉe<ÃIDATxÚÌR;KAžÜÅ7Ѩˆ„Xhý Úh%!(XÙÈa#hca•ÊÂÒ.þ„à)ZÚˆHTŒ°P4H I.æQyy]ÆÙõN.ÉÅÚfg¾ovvfþ…B¡Þ\.·­ªêU½^¯ büëb±¸Ãrim‘Hd¢T*=`+—ËOŒÃ¸-jI’tÓ#gæïïÖOFˆ·kˆŸ7 90 || catName.length < 4) { document.getElementById('catErrorMsgSpan').innerHTML = _("Length between 4 and 90 characters."); } else { document.getElementById('catErrorMsgSpan').innerHTML = ''; var url = baseLocation+"/testcreate?type=catalog&path="+path+"&title="+catName; window.location = url; } } } function creaTestCase(catName){ var tcInput = document.getElementById('tcName'); var testCaseName = tcInput.value; if (testCaseName == null || testCaseName.length == 0) { document.getElementById('errorMsgSpan').innerHTML = _("You must specify a name. Length between 4 and 90 characters."); } else { var tcName = stripLessSpecialChars(testCaseName); if (tcName.length > 90 || tcName.length < 4) { document.getElementById('errorMsgSpan').innerHTML = _("Length between 4 and 90 characters."); } else { document.getElementById('errorMsgSpan').innerHTML = ''; var url = baseLocation+"/testcreate?type=testcase&path="+catName+"&title="+tcName; window.location = url; } } } function creaTestPlan(catName){ var planInput = document.getElementById('planName'); var testPlanName = planInput.value; if (testPlanName == null || testPlanName.length == 0) { document.getElementById('errorMsgSpan2').innerHTML = _("You must specify a name. Length between 4 and 90 characters."); } else { var tplanName = stripLessSpecialChars(testPlanName); if (tplanName.length > 90 || tplanName.length < 4) { document.getElementById('errorMsgSpan2').innerHTML = _("Length between 4 and 90 characters."); } else { document.getElementById('errorMsgSpan2').innerHTML = ''; var url = baseLocation+"/testcreate?type=testplan&path="+catName+"&title="+tplanName; window.location = url; } } } function duplicateTestCase(tcName, catName){ var url = baseLocation+'/testcreate?type=testcase&duplicate=true&tcId='+tcName+'&path='+catName; window.location = url; } function regenerateTestPlan(planId, path) { var url = baseLocation+"/testcreate?type=testplan&update=true&planid="+planId+"&path="+path; window.location = url; } function creaTicket(tcName, planId, planName){ var url = baseLocation+'/newticket?testcaseid='+tcName+'&planid='+planId+'&planname='+planName+'&description=Test%20Case:%20[wiki:'+tcName+'?planid='+planId+'],%20Test%20Plan:%20'+planName+'%20('+planId+')'; window.location = url; } function showTickets(tcName, planId, planName){ var url = baseLocation+'/query?description=~'+tcName+'?planId='+planId; window.location = url; } function duplicateTestCatalog(catName){ if (confirm(_("Are you sure you want to duplicate the test catalog and all its contained test cases?"))) { var url = baseLocation+'/testcreate?type=catalog&duplicate=true&path='+catName; window.location = url; } } function deleteTestPlan(url){ if (confirm(_("Are you sure you want to delete the test plan and the state of all its contained test cases?"))) { window.location = url; } } /******************************************************/ /** Move or copy test case into another catalog */ /******************************************************/ function checkMoveTCDisplays() { displayNode('copiedTCMessage', isPasteEnabled()); displayNode('pasteTCHereMessage', isPasteEnabled()); displayNode('pasteTCHereButton', isPasteEnabled(), 'inline'); displayNode('copiedMultipleTCsMessage', isMultiplePasteEnabled()); displayNode('pasteMultipleTCsHereMessage', isMultiplePasteEnabled()); displayNode('pasteMultipleTCsHereButton', isMultiplePasteEnabled(), 'inline'); } function isPasteEnabled() { if (getCookie('TestManager_TestCase')) { return true; } return false; } function isMultiplePasteEnabled() { if (getCookie('TestManager_MultipleTestCases')) { return true; } return false; } function showSelectionCheckboxes(id) { /* toggleAll(true); */ var nodes=document.getElementById("ticketContainer").getElementsByTagName('input'); for (var i=0;i'; document.getElementById(el.id+"_list").style.display = "none"; } } function expand(id) { el = document.getElementById(id); if (el.getAttribute("name") === "toggable") { el.firstChild['expanded'] = true; el.firstChild.innerHTML = ''; document.getElementById(el.id+"_list").style.display = ""; } } function toggle(id) { var el=document.getElementById(id); if (el.firstChild['expanded']) { collapse(id) } else { expand(id) } } function highlight(str) { clearSelection(); if (str && str !== "") { var res=[]; var tks=str.split(" "); for (var i=0;i!"%&=@#\[\]\-\\\\^\$\.\|\?\*\+\(\)\{\}]/g, ''); return result; } function stripLessSpecialChars(str) { result = str.replace(/[;#&\?]/g, ''); return result; } function displayNode(id, show, mode) { var msgNode = document.getElementById(id); if (msgNode) { msgNode.style.display = show ? (mode ? mode : "block") : "none"; } } function doAjaxCall(url) { if (window.XMLHttpRequest) { /* code for IE7+, Firefox, Chrome, Opera, Safari */ xmlhttp = new XMLHttpRequest(); } else { /* code for IE6, IE5 */ xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); } xmlhttp.open("GET", url, false); xmlhttp.send(""); responseText = xmlhttp.responseText; return responseText; } function editField(name) { displayNode('custom_field_value_'+name, false); displayNode('custom_field_'+name, true); displayNode('update_button_'+name, true); } function sendUpdate(realm, name) { var objKeyField = document.getElementById("obj_key_field"); var objKey = objKeyField.value; var objPropsField = document.getElementById("obj_props_field"); var objProps = objPropsField.value; var inputField = document.getElementById("custom_field_"+name); var value = inputField.value; var url = baseLocation+"/propertyupdate?realm="+realm+"&key="+objKey+"&props="+objProps+"&name="+name+"&value="+value; result = doAjaxCall(url); var readonlyField = document.getElementById("custom_field_value_"+name); readonlyField.innerHTML = value; displayNode('custom_field_value_'+name, true); displayNode('custom_field_'+name, false); displayNode('update_button_'+name, false); } function getLocale() { if ( navigator ) { if ( navigator.language ) { return navigator.language; } else if ( navigator.browserLanguage ) { return navigator.browserLanguage; } else if ( navigator.systemLanguage ) { return navigator.systemLanguage; } else if ( navigator.userLanguage ) { return navigator.userLanguage; } } } function include(filename) { var head = document.getElementsByTagName('head')[0]; script = document.createElement('script'); script.src = filename; script.type = 'text/javascript'; head.appendChild(script) } function loadMessageCatalog() { var lc = getLocale(); include('../chrome/testmanager/js/' + lc + '.js'); } /** * Adds the specified function, by name or by pointer, to the window.onload() queue. * * Usage: * * addLoadHandler(nameOfSomeFunctionToRunOnPageLoad); * * addLoadHandler(function() { * * }); */ function addLoadHandler(func) { var oldonload = window.onload; if (typeof window.onload != 'function') { window.onload = func; } else { window.onload = function() { if (oldonload) { oldonload(); } func(); } } } /** * Do some checks as soon as the page is loaded. */ addLoadHandler(function() { checkFilter(true); checkMoveTCDisplays(); /* loadMessageCatalog(); */ }); testman4trac.1.4.5/testman4trac/trunk/testmanager/htdocs/js/jquery-ui-1.8.13.custom.min.js0000744000175000017500000062746411565111456031175 0ustar robertoroberto/*! * jQuery UI 1.8.13 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. * http://jquery.org/license * * http://docs.jquery.com/UI */ (function(c,j){function k(a,b){var d=a.nodeName.toLowerCase();if("area"===d){b=a.parentNode;d=b.name;if(!a.href||!d||b.nodeName.toLowerCase()!=="map")return false;a=c("img[usemap=#"+d+"]")[0];return!!a&&l(a)}return(/input|select|textarea|button|object/.test(d)?!a.disabled:"a"==d?a.href||b:b)&&l(a)}function l(a){return!c(a).parents().andSelf().filter(function(){return c.curCSS(this,"visibility")==="hidden"||c.expr.filters.hidden(this)}).length}c.ui=c.ui||{};if(!c.ui.version){c.extend(c.ui,{version:"1.8.13", keyCode:{ALT:18,BACKSPACE:8,CAPS_LOCK:20,COMMA:188,COMMAND:91,COMMAND_LEFT:91,COMMAND_RIGHT:93,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,MENU:93,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38,WINDOWS:91}});c.fn.extend({_focus:c.fn.focus,focus:function(a,b){return typeof a==="number"?this.each(function(){var d=this;setTimeout(function(){c(d).focus(); b&&b.call(d)},a)}):this._focus.apply(this,arguments)},scrollParent:function(){var a;a=c.browser.msie&&/(static|relative)/.test(this.css("position"))||/absolute/.test(this.css("position"))?this.parents().filter(function(){return/(relative|absolute|fixed)/.test(c.curCSS(this,"position",1))&&/(auto|scroll)/.test(c.curCSS(this,"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0):this.parents().filter(function(){return/(auto|scroll)/.test(c.curCSS(this,"overflow",1)+c.curCSS(this, "overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0);return/fixed/.test(this.css("position"))||!a.length?c(document):a},zIndex:function(a){if(a!==j)return this.css("zIndex",a);if(this.length){a=c(this[0]);for(var b;a.length&&a[0]!==document;){b=a.css("position");if(b==="absolute"||b==="relative"||b==="fixed"){b=parseInt(a.css("zIndex"),10);if(!isNaN(b)&&b!==0)return b}a=a.parent()}}return 0},disableSelection:function(){return this.bind((c.support.selectstart?"selectstart":"mousedown")+".ui-disableSelection", function(a){a.preventDefault()})},enableSelection:function(){return this.unbind(".ui-disableSelection")}});c.each(["Width","Height"],function(a,b){function d(f,g,m,n){c.each(e,function(){g-=parseFloat(c.curCSS(f,"padding"+this,true))||0;if(m)g-=parseFloat(c.curCSS(f,"border"+this+"Width",true))||0;if(n)g-=parseFloat(c.curCSS(f,"margin"+this,true))||0});return g}var e=b==="Width"?["Left","Right"]:["Top","Bottom"],h=b.toLowerCase(),i={innerWidth:c.fn.innerWidth,innerHeight:c.fn.innerHeight,outerWidth:c.fn.outerWidth, outerHeight:c.fn.outerHeight};c.fn["inner"+b]=function(f){if(f===j)return i["inner"+b].call(this);return this.each(function(){c(this).css(h,d(this,f)+"px")})};c.fn["outer"+b]=function(f,g){if(typeof f!=="number")return i["outer"+b].call(this,f);return this.each(function(){c(this).css(h,d(this,f,true,g)+"px")})}});c.extend(c.expr[":"],{data:function(a,b,d){return!!c.data(a,d[3])},focusable:function(a){return k(a,!isNaN(c.attr(a,"tabindex")))},tabbable:function(a){var b=c.attr(a,"tabindex"),d=isNaN(b); return(d||b>=0)&&k(a,!d)}});c(function(){var a=document.body,b=a.appendChild(b=document.createElement("div"));c.extend(b.style,{minHeight:"100px",height:"auto",padding:0,borderWidth:0});c.support.minHeight=b.offsetHeight===100;c.support.selectstart="onselectstart"in b;a.removeChild(b).style.display="none"});c.extend(c.ui,{plugin:{add:function(a,b,d){a=c.ui[a].prototype;for(var e in d){a.plugins[e]=a.plugins[e]||[];a.plugins[e].push([b,d[e]])}},call:function(a,b,d){if((b=a.plugins[b])&&a.element[0].parentNode)for(var e= 0;e0)return true;a[b]=1;d=a[b]>0;a[b]=0;return d},isOverAxis:function(a,b,d){return a>b&&a=9)&&!a.button)return this._mouseUp(a);if(this._mouseStarted){this._mouseDrag(a);return a.preventDefault()}if(this._mouseDistanceMet(a)&&this._mouseDelayMet(a))(this._mouseStarted=this._mouseStart(this._mouseDownEvent,a)!==false)?this._mouseDrag(a):this._mouseUp(a);return!this._mouseStarted},_mouseUp:function(a){b(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate);if(this._mouseStarted){this._mouseStarted= false;a.target==this._mouseDownEvent.target&&b.data(a.target,this.widgetName+".preventClickEvent",true);this._mouseStop(a)}return false},_mouseDistanceMet:function(a){return Math.max(Math.abs(this._mouseDownEvent.pageX-a.pageX),Math.abs(this._mouseDownEvent.pageY-a.pageY))>=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return true}})})(jQuery); ;/* * jQuery UI Position 1.8.13 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. * http://jquery.org/license * * http://docs.jquery.com/UI/Position */ (function(c){c.ui=c.ui||{};var n=/left|center|right/,o=/top|center|bottom/,t=c.fn.position,u=c.fn.offset;c.fn.position=function(b){if(!b||!b.of)return t.apply(this,arguments);b=c.extend({},b);var a=c(b.of),d=a[0],g=(b.collision||"flip").split(" "),e=b.offset?b.offset.split(" "):[0,0],h,k,j;if(d.nodeType===9){h=a.width();k=a.height();j={top:0,left:0}}else if(d.setTimeout){h=a.width();k=a.height();j={top:a.scrollTop(),left:a.scrollLeft()}}else if(d.preventDefault){b.at="left top";h=k=0;j={top:b.of.pageY, left:b.of.pageX}}else{h=a.outerWidth();k=a.outerHeight();j=a.offset()}c.each(["my","at"],function(){var f=(b[this]||"").split(" ");if(f.length===1)f=n.test(f[0])?f.concat(["center"]):o.test(f[0])?["center"].concat(f):["center","center"];f[0]=n.test(f[0])?f[0]:"center";f[1]=o.test(f[1])?f[1]:"center";b[this]=f});if(g.length===1)g[1]=g[0];e[0]=parseInt(e[0],10)||0;if(e.length===1)e[1]=e[0];e[1]=parseInt(e[1],10)||0;if(b.at[0]==="right")j.left+=h;else if(b.at[0]==="center")j.left+=h/2;if(b.at[1]==="bottom")j.top+= k;else if(b.at[1]==="center")j.top+=k/2;j.left+=e[0];j.top+=e[1];return this.each(function(){var f=c(this),l=f.outerWidth(),m=f.outerHeight(),p=parseInt(c.curCSS(this,"marginLeft",true))||0,q=parseInt(c.curCSS(this,"marginTop",true))||0,v=l+p+(parseInt(c.curCSS(this,"marginRight",true))||0),w=m+q+(parseInt(c.curCSS(this,"marginBottom",true))||0),i=c.extend({},j),r;if(b.my[0]==="right")i.left-=l;else if(b.my[0]==="center")i.left-=l/2;if(b.my[1]==="bottom")i.top-=m;else if(b.my[1]==="center")i.top-= m/2;i.left=Math.round(i.left);i.top=Math.round(i.top);r={left:i.left-p,top:i.top-q};c.each(["left","top"],function(s,x){c.ui.position[g[s]]&&c.ui.position[g[s]][x](i,{targetWidth:h,targetHeight:k,elemWidth:l,elemHeight:m,collisionPosition:r,collisionWidth:v,collisionHeight:w,offset:e,my:b.my,at:b.at})});c.fn.bgiframe&&f.bgiframe();f.offset(c.extend(i,{using:b.using}))})};c.ui.position={fit:{left:function(b,a){var d=c(window);d=a.collisionPosition.left+a.collisionWidth-d.width()-d.scrollLeft();b.left= d>0?b.left-d:Math.max(b.left-a.collisionPosition.left,b.left)},top:function(b,a){var d=c(window);d=a.collisionPosition.top+a.collisionHeight-d.height()-d.scrollTop();b.top=d>0?b.top-d:Math.max(b.top-a.collisionPosition.top,b.top)}},flip:{left:function(b,a){if(a.at[0]!=="center"){var d=c(window);d=a.collisionPosition.left+a.collisionWidth-d.width()-d.scrollLeft();var g=a.my[0]==="left"?-a.elemWidth:a.my[0]==="right"?a.elemWidth:0,e=a.at[0]==="left"?a.targetWidth:-a.targetWidth,h=-2*a.offset[0];b.left+= a.collisionPosition.left<0?g+e+h:d>0?g+e+h:0}},top:function(b,a){if(a.at[1]!=="center"){var d=c(window);d=a.collisionPosition.top+a.collisionHeight-d.height()-d.scrollTop();var g=a.my[1]==="top"?-a.elemHeight:a.my[1]==="bottom"?a.elemHeight:0,e=a.at[1]==="top"?a.targetHeight:-a.targetHeight,h=-2*a.offset[1];b.top+=a.collisionPosition.top<0?g+e+h:d>0?g+e+h:0}}}};if(!c.offset.setOffset){c.offset.setOffset=function(b,a){if(/static/.test(c.curCSS(b,"position")))b.style.position="relative";var d=c(b), g=d.offset(),e=parseInt(c.curCSS(b,"top",true),10)||0,h=parseInt(c.curCSS(b,"left",true),10)||0;g={top:a.top-g.top+e,left:a.left-g.left+h};"using"in a?a.using.call(b,g):d.css(g)};c.fn.offset=function(b){var a=this[0];if(!a||!a.ownerDocument)return null;if(b)return this.each(function(){c.offset.setOffset(this,b)});return u.call(this)}}})(jQuery); ;/* * jQuery UI Draggable 1.8.13 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. * http://jquery.org/license * * http://docs.jquery.com/UI/Draggables * * Depends: * jquery.ui.core.js * jquery.ui.mouse.js * jquery.ui.widget.js */ (function(d){d.widget("ui.draggable",d.ui.mouse,{widgetEventPrefix:"drag",options:{addClasses:true,appendTo:"parent",axis:false,connectToSortable:false,containment:false,cursor:"auto",cursorAt:false,grid:false,handle:false,helper:"original",iframeFix:false,opacity:false,refreshPositions:false,revert:false,revertDuration:500,scope:"default",scroll:true,scrollSensitivity:20,scrollSpeed:20,snap:false,snapMode:"both",snapTolerance:20,stack:false,zIndex:false},_create:function(){if(this.options.helper== "original"&&!/^(?:r|a|f)/.test(this.element.css("position")))this.element[0].style.position="relative";this.options.addClasses&&this.element.addClass("ui-draggable");this.options.disabled&&this.element.addClass("ui-draggable-disabled");this._mouseInit()},destroy:function(){if(this.element.data("draggable")){this.element.removeData("draggable").unbind(".draggable").removeClass("ui-draggable ui-draggable-dragging ui-draggable-disabled");this._mouseDestroy();return this}},_mouseCapture:function(a){var b= this.options;if(this.helper||b.disabled||d(a.target).is(".ui-resizable-handle"))return false;this.handle=this._getHandle(a);if(!this.handle)return false;d(b.iframeFix===true?"iframe":b.iframeFix).each(function(){d('
').css({width:this.offsetWidth+"px",height:this.offsetHeight+"px",position:"absolute",opacity:"0.001",zIndex:1E3}).css(d(this).offset()).appendTo("body")});return true},_mouseStart:function(a){var b=this.options;this.helper= this._createHelper(a);this._cacheHelperProportions();if(d.ui.ddmanager)d.ui.ddmanager.current=this;this._cacheMargins();this.cssPosition=this.helper.css("position");this.scrollParent=this.helper.scrollParent();this.offset=this.positionAbs=this.element.offset();this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left};d.extend(this.offset,{click:{left:a.pageX-this.offset.left,top:a.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()}); this.originalPosition=this.position=this._generatePosition(a);this.originalPageX=a.pageX;this.originalPageY=a.pageY;b.cursorAt&&this._adjustOffsetFromHelper(b.cursorAt);b.containment&&this._setContainment();if(this._trigger("start",a)===false){this._clear();return false}this._cacheHelperProportions();d.ui.ddmanager&&!b.dropBehaviour&&d.ui.ddmanager.prepareOffsets(this,a);this.helper.addClass("ui-draggable-dragging");this._mouseDrag(a,true);return true},_mouseDrag:function(a,b){this.position=this._generatePosition(a); this.positionAbs=this._convertPositionTo("absolute");if(!b){b=this._uiHash();if(this._trigger("drag",a,b)===false){this._mouseUp({});return false}this.position=b.position}if(!this.options.axis||this.options.axis!="y")this.helper[0].style.left=this.position.left+"px";if(!this.options.axis||this.options.axis!="x")this.helper[0].style.top=this.position.top+"px";d.ui.ddmanager&&d.ui.ddmanager.drag(this,a);return false},_mouseStop:function(a){var b=false;if(d.ui.ddmanager&&!this.options.dropBehaviour)b= d.ui.ddmanager.drop(this,a);if(this.dropped){b=this.dropped;this.dropped=false}if((!this.element[0]||!this.element[0].parentNode)&&this.options.helper=="original")return false;if(this.options.revert=="invalid"&&!b||this.options.revert=="valid"&&b||this.options.revert===true||d.isFunction(this.options.revert)&&this.options.revert.call(this.element,b)){var c=this;d(this.helper).animate(this.originalPosition,parseInt(this.options.revertDuration,10),function(){c._trigger("stop",a)!==false&&c._clear()})}else this._trigger("stop", a)!==false&&this._clear();return false},_mouseUp:function(a){this.options.iframeFix===true&&d("div.ui-draggable-iframeFix").each(function(){this.parentNode.removeChild(this)});return d.ui.mouse.prototype._mouseUp.call(this,a)},cancel:function(){this.helper.is(".ui-draggable-dragging")?this._mouseUp({}):this._clear();return this},_getHandle:function(a){var b=!this.options.handle||!d(this.options.handle,this.element).length?true:false;d(this.options.handle,this.element).find("*").andSelf().each(function(){if(this== a.target)b=true});return b},_createHelper:function(a){var b=this.options;a=d.isFunction(b.helper)?d(b.helper.apply(this.element[0],[a])):b.helper=="clone"?this.element.clone().removeAttr("id"):this.element;a.parents("body").length||a.appendTo(b.appendTo=="parent"?this.element[0].parentNode:b.appendTo);a[0]!=this.element[0]&&!/(fixed|absolute)/.test(a.css("position"))&&a.css("position","absolute");return a},_adjustOffsetFromHelper:function(a){if(typeof a=="string")a=a.split(" ");if(d.isArray(a))a= {left:+a[0],top:+a[1]||0};if("left"in a)this.offset.click.left=a.left+this.margins.left;if("right"in a)this.offset.click.left=this.helperProportions.width-a.right+this.margins.left;if("top"in a)this.offset.click.top=a.top+this.margins.top;if("bottom"in a)this.offset.click.top=this.helperProportions.height-a.bottom+this.margins.top},_getParentOffset:function(){this.offsetParent=this.helper.offsetParent();var a=this.offsetParent.offset();if(this.cssPosition=="absolute"&&this.scrollParent[0]!=document&& d.ui.contains(this.scrollParent[0],this.offsetParent[0])){a.left+=this.scrollParent.scrollLeft();a.top+=this.scrollParent.scrollTop()}if(this.offsetParent[0]==document.body||this.offsetParent[0].tagName&&this.offsetParent[0].tagName.toLowerCase()=="html"&&d.browser.msie)a={top:0,left:0};return{top:a.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:a.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if(this.cssPosition=="relative"){var a= this.element.position();return{top:a.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:a.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}else return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.element.css("marginLeft"),10)||0,top:parseInt(this.element.css("marginTop"),10)||0,right:parseInt(this.element.css("marginRight"),10)||0,bottom:parseInt(this.element.css("marginBottom"),10)||0}},_cacheHelperProportions:function(){this.helperProportions= {width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var a=this.options;if(a.containment=="parent")a.containment=this.helper[0].parentNode;if(a.containment=="document"||a.containment=="window")this.containment=[(a.containment=="document"?0:d(window).scrollLeft())-this.offset.relative.left-this.offset.parent.left,(a.containment=="document"?0:d(window).scrollTop())-this.offset.relative.top-this.offset.parent.top,(a.containment=="document"?0:d(window).scrollLeft())+ d(a.containment=="document"?document:window).width()-this.helperProportions.width-this.margins.left,(a.containment=="document"?0:d(window).scrollTop())+(d(a.containment=="document"?document:window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top];if(!/^(document|window|parent)$/.test(a.containment)&&a.containment.constructor!=Array){a=d(a.containment);var b=a[0];if(b){a.offset();var c=d(b).css("overflow")!="hidden";this.containment=[(parseInt(d(b).css("borderLeftWidth"), 10)||0)+(parseInt(d(b).css("paddingLeft"),10)||0),(parseInt(d(b).css("borderTopWidth"),10)||0)+(parseInt(d(b).css("paddingTop"),10)||0),(c?Math.max(b.scrollWidth,b.offsetWidth):b.offsetWidth)-(parseInt(d(b).css("borderLeftWidth"),10)||0)-(parseInt(d(b).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left-this.margins.right,(c?Math.max(b.scrollHeight,b.offsetHeight):b.offsetHeight)-(parseInt(d(b).css("borderTopWidth"),10)||0)-(parseInt(d(b).css("paddingBottom"),10)||0)-this.helperProportions.height- this.margins.top-this.margins.bottom];this.relative_container=a}}else if(a.containment.constructor==Array)this.containment=a.containment},_convertPositionTo:function(a,b){if(!b)b=this.position;a=a=="absolute"?1:-1;var c=this.cssPosition=="absolute"&&!(this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,f=/(html|body)/i.test(c[0].tagName);return{top:b.top+this.offset.relative.top*a+this.offset.parent.top*a-(d.browser.safari&& d.browser.version<526&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollTop():f?0:c.scrollTop())*a),left:b.left+this.offset.relative.left*a+this.offset.parent.left*a-(d.browser.safari&&d.browser.version<526&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():f?0:c.scrollLeft())*a)}},_generatePosition:function(a){var b=this.options,c=this.cssPosition=="absolute"&&!(this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0], this.offsetParent[0]))?this.offsetParent:this.scrollParent,f=/(html|body)/i.test(c[0].tagName),e=a.pageX,h=a.pageY;if(this.originalPosition){var g;if(this.containment){if(this.relative_container){g=this.relative_container.offset();g=[this.containment[0]+g.left,this.containment[1]+g.top,this.containment[2]+g.left,this.containment[3]+g.top]}else g=this.containment;if(a.pageX-this.offset.click.leftg[2])e=g[2]+this.offset.click.left;if(a.pageY-this.offset.click.top>g[3])h=g[3]+this.offset.click.top}if(b.grid){h=this.originalPageY+Math.round((h-this.originalPageY)/b.grid[1])*b.grid[1];h=g?!(h-this.offset.click.topg[3])?h:!(h-this.offset.click.topg[2])?e:!(e-this.offset.click.left< g[0])?e-b.grid[0]:e+b.grid[0]:e}}return{top:h-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+(d.browser.safari&&d.browser.version<526&&this.cssPosition=="fixed"?0:this.cssPosition=="fixed"?-this.scrollParent.scrollTop():f?0:c.scrollTop()),left:e-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+(d.browser.safari&&d.browser.version<526&&this.cssPosition=="fixed"?0:this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():f?0:c.scrollLeft())}},_clear:function(){this.helper.removeClass("ui-draggable-dragging"); this.helper[0]!=this.element[0]&&!this.cancelHelperRemoval&&this.helper.remove();this.helper=null;this.cancelHelperRemoval=false},_trigger:function(a,b,c){c=c||this._uiHash();d.ui.plugin.call(this,a,[b,c]);if(a=="drag")this.positionAbs=this._convertPositionTo("absolute");return d.Widget.prototype._trigger.call(this,a,b,c)},plugins:{},_uiHash:function(){return{helper:this.helper,position:this.position,originalPosition:this.originalPosition,offset:this.positionAbs}}});d.extend(d.ui.draggable,{version:"1.8.13"}); d.ui.plugin.add("draggable","connectToSortable",{start:function(a,b){var c=d(this).data("draggable"),f=c.options,e=d.extend({},b,{item:c.element});c.sortables=[];d(f.connectToSortable).each(function(){var h=d.data(this,"sortable");if(h&&!h.options.disabled){c.sortables.push({instance:h,shouldRevert:h.options.revert});h.refreshPositions();h._trigger("activate",a,e)}})},stop:function(a,b){var c=d(this).data("draggable"),f=d.extend({},b,{item:c.element});d.each(c.sortables,function(){if(this.instance.isOver){this.instance.isOver= 0;c.cancelHelperRemoval=true;this.instance.cancelHelperRemoval=false;if(this.shouldRevert)this.instance.options.revert=true;this.instance._mouseStop(a);this.instance.options.helper=this.instance.options._helper;c.options.helper=="original"&&this.instance.currentItem.css({top:"auto",left:"auto"})}else{this.instance.cancelHelperRemoval=false;this.instance._trigger("deactivate",a,f)}})},drag:function(a,b){var c=d(this).data("draggable"),f=this;d.each(c.sortables,function(){this.instance.positionAbs= c.positionAbs;this.instance.helperProportions=c.helperProportions;this.instance.offset.click=c.offset.click;if(this.instance._intersectsWith(this.instance.containerCache)){if(!this.instance.isOver){this.instance.isOver=1;this.instance.currentItem=d(f).clone().removeAttr("id").appendTo(this.instance.element).data("sortable-item",true);this.instance.options._helper=this.instance.options.helper;this.instance.options.helper=function(){return b.helper[0]};a.target=this.instance.currentItem[0];this.instance._mouseCapture(a, true);this.instance._mouseStart(a,true,true);this.instance.offset.click.top=c.offset.click.top;this.instance.offset.click.left=c.offset.click.left;this.instance.offset.parent.left-=c.offset.parent.left-this.instance.offset.parent.left;this.instance.offset.parent.top-=c.offset.parent.top-this.instance.offset.parent.top;c._trigger("toSortable",a);c.dropped=this.instance.element;c.currentItem=c.element;this.instance.fromOutside=c}this.instance.currentItem&&this.instance._mouseDrag(a)}else if(this.instance.isOver){this.instance.isOver= 0;this.instance.cancelHelperRemoval=true;this.instance.options.revert=false;this.instance._trigger("out",a,this.instance._uiHash(this.instance));this.instance._mouseStop(a,true);this.instance.options.helper=this.instance.options._helper;this.instance.currentItem.remove();this.instance.placeholder&&this.instance.placeholder.remove();c._trigger("fromSortable",a);c.dropped=false}})}});d.ui.plugin.add("draggable","cursor",{start:function(){var a=d("body"),b=d(this).data("draggable").options;if(a.css("cursor"))b._cursor= a.css("cursor");a.css("cursor",b.cursor)},stop:function(){var a=d(this).data("draggable").options;a._cursor&&d("body").css("cursor",a._cursor)}});d.ui.plugin.add("draggable","opacity",{start:function(a,b){a=d(b.helper);b=d(this).data("draggable").options;if(a.css("opacity"))b._opacity=a.css("opacity");a.css("opacity",b.opacity)},stop:function(a,b){a=d(this).data("draggable").options;a._opacity&&d(b.helper).css("opacity",a._opacity)}});d.ui.plugin.add("draggable","scroll",{start:function(){var a=d(this).data("draggable"); if(a.scrollParent[0]!=document&&a.scrollParent[0].tagName!="HTML")a.overflowOffset=a.scrollParent.offset()},drag:function(a){var b=d(this).data("draggable"),c=b.options,f=false;if(b.scrollParent[0]!=document&&b.scrollParent[0].tagName!="HTML"){if(!c.axis||c.axis!="x")if(b.overflowOffset.top+b.scrollParent[0].offsetHeight-a.pageY=0;i--){var j=c.snapElements[i].left,l=j+c.snapElements[i].width,k=c.snapElements[i].top,m=k+c.snapElements[i].height;if(j-e=j&&f<=l||h>=j&&h<=l||fl)&&(e>= i&&e<=k||g>=i&&g<=k||ek);default:return false}};d.ui.ddmanager={current:null,droppables:{"default":[]},prepareOffsets:function(a,b){var c=d.ui.ddmanager.droppables[a.options.scope]||[],e=b?b.type:null,g=(a.currentItem||a.element).find(":data(droppable)").andSelf(),f=0;a:for(;f').css({position:this.element.css("position"),width:this.element.outerWidth(),height:this.element.outerHeight(), top:this.element.css("top"),left:this.element.css("left")}));this.element=this.element.parent().data("resizable",this.element.data("resizable"));this.elementIsWrapper=true;this.element.css({marginLeft:this.originalElement.css("marginLeft"),marginTop:this.originalElement.css("marginTop"),marginRight:this.originalElement.css("marginRight"),marginBottom:this.originalElement.css("marginBottom")});this.originalElement.css({marginLeft:0,marginTop:0,marginRight:0,marginBottom:0});this.originalResizeStyle= this.originalElement.css("resize");this.originalElement.css("resize","none");this._proportionallyResizeElements.push(this.originalElement.css({position:"static",zoom:1,display:"block"}));this.originalElement.css({margin:this.originalElement.css("margin")});this._proportionallyResize()}this.handles=a.handles||(!e(".ui-resizable-handle",this.element).length?"e,s,se":{n:".ui-resizable-n",e:".ui-resizable-e",s:".ui-resizable-s",w:".ui-resizable-w",se:".ui-resizable-se",sw:".ui-resizable-sw",ne:".ui-resizable-ne", nw:".ui-resizable-nw"});if(this.handles.constructor==String){if(this.handles=="all")this.handles="n,e,s,w,se,sw,ne,nw";var c=this.handles.split(",");this.handles={};for(var d=0;d');/sw|se|ne|nw/.test(f)&&g.css({zIndex:++a.zIndex});"se"==f&&g.addClass("ui-icon ui-icon-gripsmall-diagonal-se");this.handles[f]=".ui-resizable-"+f;this.element.append(g)}}this._renderAxis=function(h){h=h||this.element;for(var i in this.handles){if(this.handles[i].constructor== String)this.handles[i]=e(this.handles[i],this.element).show();if(this.elementIsWrapper&&this.originalElement[0].nodeName.match(/textarea|input|select|button/i)){var j=e(this.handles[i],this.element),k=0;k=/sw|ne|nw|se|n|s/.test(i)?j.outerHeight():j.outerWidth();j=["padding",/ne|nw|n/.test(i)?"Top":/se|sw|s/.test(i)?"Bottom":/^e$/.test(i)?"Right":"Left"].join("");h.css(j,k);this._proportionallyResize()}e(this.handles[i])}};this._renderAxis(this.element);this._handles=e(".ui-resizable-handle",this.element).disableSelection(); this._handles.mouseover(function(){if(!b.resizing){if(this.className)var h=this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i);b.axis=h&&h[1]?h[1]:"se"}});if(a.autoHide){this._handles.hide();e(this.element).addClass("ui-resizable-autohide").hover(function(){if(!a.disabled){e(this).removeClass("ui-resizable-autohide");b._handles.show()}},function(){if(!a.disabled)if(!b.resizing){e(this).addClass("ui-resizable-autohide");b._handles.hide()}})}this._mouseInit()},destroy:function(){this._mouseDestroy(); var b=function(c){e(c).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing").removeData("resizable").unbind(".resizable").find(".ui-resizable-handle").remove()};if(this.elementIsWrapper){b(this.element);var a=this.element;a.after(this.originalElement.css({position:a.css("position"),width:a.outerWidth(),height:a.outerHeight(),top:a.css("top"),left:a.css("left")})).remove()}this.originalElement.css("resize",this.originalResizeStyle);b(this.originalElement);return this},_mouseCapture:function(b){var a= false;for(var c in this.handles)if(e(this.handles[c])[0]==b.target)a=true;return!this.options.disabled&&a},_mouseStart:function(b){var a=this.options,c=this.element.position(),d=this.element;this.resizing=true;this.documentScroll={top:e(document).scrollTop(),left:e(document).scrollLeft()};if(d.is(".ui-draggable")||/absolute/.test(d.css("position")))d.css({position:"absolute",top:c.top,left:c.left});e.browser.opera&&/relative/.test(d.css("position"))&&d.css({position:"relative",top:"auto",left:"auto"}); this._renderProxy();c=m(this.helper.css("left"));var f=m(this.helper.css("top"));if(a.containment){c+=e(a.containment).scrollLeft()||0;f+=e(a.containment).scrollTop()||0}this.offset=this.helper.offset();this.position={left:c,top:f};this.size=this._helper?{width:d.outerWidth(),height:d.outerHeight()}:{width:d.width(),height:d.height()};this.originalSize=this._helper?{width:d.outerWidth(),height:d.outerHeight()}:{width:d.width(),height:d.height()};this.originalPosition={left:c,top:f};this.sizeDiff= {width:d.outerWidth()-d.width(),height:d.outerHeight()-d.height()};this.originalMousePosition={left:b.pageX,top:b.pageY};this.aspectRatio=typeof a.aspectRatio=="number"?a.aspectRatio:this.originalSize.width/this.originalSize.height||1;a=e(".ui-resizable-"+this.axis).css("cursor");e("body").css("cursor",a=="auto"?this.axis+"-resize":a);d.addClass("ui-resizable-resizing");this._propagate("start",b);return true},_mouseDrag:function(b){var a=this.helper,c=this.originalMousePosition,d=this._change[this.axis]; if(!d)return false;c=d.apply(this,[b,b.pageX-c.left||0,b.pageY-c.top||0]);if(this._aspectRatio||b.shiftKey)c=this._updateRatio(c,b);c=this._respectSize(c,b);this._propagate("resize",b);a.css({top:this.position.top+"px",left:this.position.left+"px",width:this.size.width+"px",height:this.size.height+"px"});!this._helper&&this._proportionallyResizeElements.length&&this._proportionallyResize();this._updateCache(c);this._trigger("resize",b,this.ui());return false},_mouseStop:function(b){this.resizing= false;var a=this.options,c=this;if(this._helper){var d=this._proportionallyResizeElements,f=d.length&&/textarea/i.test(d[0].nodeName);d=f&&e.ui.hasScroll(d[0],"left")?0:c.sizeDiff.height;f=f?0:c.sizeDiff.width;f={width:c.helper.width()-f,height:c.helper.height()-d};d=parseInt(c.element.css("left"),10)+(c.position.left-c.originalPosition.left)||null;var g=parseInt(c.element.css("top"),10)+(c.position.top-c.originalPosition.top)||null;a.animate||this.element.css(e.extend(f,{top:g,left:d}));c.helper.height(c.size.height); c.helper.width(c.size.width);this._helper&&!a.animate&&this._proportionallyResize()}e("body").css("cursor","auto");this.element.removeClass("ui-resizable-resizing");this._propagate("stop",b);this._helper&&this.helper.remove();return false},_updateCache:function(b){this.offset=this.helper.offset();if(l(b.left))this.position.left=b.left;if(l(b.top))this.position.top=b.top;if(l(b.height))this.size.height=b.height;if(l(b.width))this.size.width=b.width},_updateRatio:function(b){var a=this.position,c=this.size, d=this.axis;if(b.height)b.width=c.height*this.aspectRatio;else if(b.width)b.height=c.width/this.aspectRatio;if(d=="sw"){b.left=a.left+(c.width-b.width);b.top=null}if(d=="nw"){b.top=a.top+(c.height-b.height);b.left=a.left+(c.width-b.width)}return b},_respectSize:function(b){var a=this.options,c=this.axis,d=l(b.width)&&a.maxWidth&&a.maxWidthb.width,h=l(b.height)&&a.minHeight&&a.minHeight>b.height;if(g)b.width= a.minWidth;if(h)b.height=a.minHeight;if(d)b.width=a.maxWidth;if(f)b.height=a.maxHeight;var i=this.originalPosition.left+this.originalSize.width,j=this.position.top+this.size.height,k=/sw|nw|w/.test(c);c=/nw|ne|n/.test(c);if(g&&k)b.left=i-a.minWidth;if(d&&k)b.left=i-a.maxWidth;if(h&&c)b.top=j-a.minHeight;if(f&&c)b.top=j-a.maxHeight;if((a=!b.width&&!b.height)&&!b.left&&b.top)b.top=null;else if(a&&!b.top&&b.left)b.left=null;return b},_proportionallyResize:function(){if(this._proportionallyResizeElements.length)for(var b= this.helper||this.element,a=0;a');var a=e.browser.msie&&e.browser.version<7,c=a?1:0;a=a?2:-1;this.helper.addClass(this._helper).css({width:this.element.outerWidth()+a,height:this.element.outerHeight()+a,position:"absolute",left:this.elementOffset.left- c+"px",top:this.elementOffset.top-c+"px",zIndex:++b.zIndex});this.helper.appendTo("body").disableSelection()}else this.helper=this.element},_change:{e:function(b,a){return{width:this.originalSize.width+a}},w:function(b,a){return{left:this.originalPosition.left+a,width:this.originalSize.width-a}},n:function(b,a,c){return{top:this.originalPosition.top+c,height:this.originalSize.height-c}},s:function(b,a,c){return{height:this.originalSize.height+c}},se:function(b,a,c){return e.extend(this._change.s.apply(this, arguments),this._change.e.apply(this,[b,a,c]))},sw:function(b,a,c){return e.extend(this._change.s.apply(this,arguments),this._change.w.apply(this,[b,a,c]))},ne:function(b,a,c){return e.extend(this._change.n.apply(this,arguments),this._change.e.apply(this,[b,a,c]))},nw:function(b,a,c){return e.extend(this._change.n.apply(this,arguments),this._change.w.apply(this,[b,a,c]))}},_propagate:function(b,a){e.ui.plugin.call(this,b,[a,this.ui()]);b!="resize"&&this._trigger(b,a,this.ui())},plugins:{},ui:function(){return{originalElement:this.originalElement, element:this.element,helper:this.helper,position:this.position,size:this.size,originalSize:this.originalSize,originalPosition:this.originalPosition}}});e.extend(e.ui.resizable,{version:"1.8.13"});e.ui.plugin.add("resizable","alsoResize",{start:function(){var b=e(this).data("resizable").options,a=function(c){e(c).each(function(){var d=e(this);d.data("resizable-alsoresize",{width:parseInt(d.width(),10),height:parseInt(d.height(),10),left:parseInt(d.css("left"),10),top:parseInt(d.css("top"),10),position:d.css("position")})})}; if(typeof b.alsoResize=="object"&&!b.alsoResize.parentNode)if(b.alsoResize.length){b.alsoResize=b.alsoResize[0];a(b.alsoResize)}else e.each(b.alsoResize,function(c){a(c)});else a(b.alsoResize)},resize:function(b,a){var c=e(this).data("resizable");b=c.options;var d=c.originalSize,f=c.originalPosition,g={height:c.size.height-d.height||0,width:c.size.width-d.width||0,top:c.position.top-f.top||0,left:c.position.left-f.left||0},h=function(i,j){e(i).each(function(){var k=e(this),q=e(this).data("resizable-alsoresize"), p={},r=j&&j.length?j:k.parents(a.originalElement[0]).length?["width","height"]:["width","height","top","left"];e.each(r,function(n,o){if((n=(q[o]||0)+(g[o]||0))&&n>=0)p[o]=n||null});if(e.browser.opera&&/relative/.test(k.css("position"))){c._revertToRelativePosition=true;k.css({position:"absolute",top:"auto",left:"auto"})}k.css(p)})};typeof b.alsoResize=="object"&&!b.alsoResize.nodeType?e.each(b.alsoResize,function(i,j){h(i,j)}):h(b.alsoResize)},stop:function(){var b=e(this).data("resizable"),a=b.options, c=function(d){e(d).each(function(){var f=e(this);f.css({position:f.data("resizable-alsoresize").position})})};if(b._revertToRelativePosition){b._revertToRelativePosition=false;typeof a.alsoResize=="object"&&!a.alsoResize.nodeType?e.each(a.alsoResize,function(d){c(d)}):c(a.alsoResize)}e(this).removeData("resizable-alsoresize")}});e.ui.plugin.add("resizable","animate",{stop:function(b){var a=e(this).data("resizable"),c=a.options,d=a._proportionallyResizeElements,f=d.length&&/textarea/i.test(d[0].nodeName), g=f&&e.ui.hasScroll(d[0],"left")?0:a.sizeDiff.height;f={width:a.size.width-(f?0:a.sizeDiff.width),height:a.size.height-g};g=parseInt(a.element.css("left"),10)+(a.position.left-a.originalPosition.left)||null;var h=parseInt(a.element.css("top"),10)+(a.position.top-a.originalPosition.top)||null;a.element.animate(e.extend(f,h&&g?{top:h,left:g}:{}),{duration:c.animateDuration,easing:c.animateEasing,step:function(){var i={width:parseInt(a.element.css("width"),10),height:parseInt(a.element.css("height"), 10),top:parseInt(a.element.css("top"),10),left:parseInt(a.element.css("left"),10)};d&&d.length&&e(d[0]).css({width:i.width,height:i.height});a._updateCache(i);a._propagate("resize",b)}})}});e.ui.plugin.add("resizable","containment",{start:function(){var b=e(this).data("resizable"),a=b.element,c=b.options.containment;if(a=c instanceof e?c.get(0):/parent/.test(c)?a.parent().get(0):c){b.containerElement=e(a);if(/document/.test(c)||c==document){b.containerOffset={left:0,top:0};b.containerPosition={left:0, top:0};b.parentData={element:e(document),left:0,top:0,width:e(document).width(),height:e(document).height()||document.body.parentNode.scrollHeight}}else{var d=e(a),f=[];e(["Top","Right","Left","Bottom"]).each(function(i,j){f[i]=m(d.css("padding"+j))});b.containerOffset=d.offset();b.containerPosition=d.position();b.containerSize={height:d.innerHeight()-f[3],width:d.innerWidth()-f[1]};c=b.containerOffset;var g=b.containerSize.height,h=b.containerSize.width;h=e.ui.hasScroll(a,"left")?a.scrollWidth:h; g=e.ui.hasScroll(a)?a.scrollHeight:g;b.parentData={element:a,left:c.left,top:c.top,width:h,height:g}}}},resize:function(b){var a=e(this).data("resizable"),c=a.options,d=a.containerOffset,f=a.position;b=a._aspectRatio||b.shiftKey;var g={top:0,left:0},h=a.containerElement;if(h[0]!=document&&/static/.test(h.css("position")))g=d;if(f.left<(a._helper?d.left:0)){a.size.width+=a._helper?a.position.left-d.left:a.position.left-g.left;if(b)a.size.height=a.size.width/c.aspectRatio;a.position.left=c.helper?d.left: 0}if(f.top<(a._helper?d.top:0)){a.size.height+=a._helper?a.position.top-d.top:a.position.top;if(b)a.size.width=a.size.height*c.aspectRatio;a.position.top=a._helper?d.top:0}a.offset.left=a.parentData.left+a.position.left;a.offset.top=a.parentData.top+a.position.top;c=Math.abs((a._helper?a.offset.left-g.left:a.offset.left-g.left)+a.sizeDiff.width);d=Math.abs((a._helper?a.offset.top-g.top:a.offset.top-d.top)+a.sizeDiff.height);f=a.containerElement.get(0)==a.element.parent().get(0);g=/relative|absolute/.test(a.containerElement.css("position")); if(f&&g)c-=a.parentData.left;if(c+a.size.width>=a.parentData.width){a.size.width=a.parentData.width-c;if(b)a.size.height=a.size.width/a.aspectRatio}if(d+a.size.height>=a.parentData.height){a.size.height=a.parentData.height-d;if(b)a.size.width=a.size.height*a.aspectRatio}},stop:function(){var b=e(this).data("resizable"),a=b.options,c=b.containerOffset,d=b.containerPosition,f=b.containerElement,g=e(b.helper),h=g.offset(),i=g.outerWidth()-b.sizeDiff.width;g=g.outerHeight()-b.sizeDiff.height;b._helper&& !a.animate&&/relative/.test(f.css("position"))&&e(this).css({left:h.left-d.left-c.left,width:i,height:g});b._helper&&!a.animate&&/static/.test(f.css("position"))&&e(this).css({left:h.left-d.left-c.left,width:i,height:g})}});e.ui.plugin.add("resizable","ghost",{start:function(){var b=e(this).data("resizable"),a=b.options,c=b.size;b.ghost=b.originalElement.clone();b.ghost.css({opacity:0.25,display:"block",position:"relative",height:c.height,width:c.width,margin:0,left:0,top:0}).addClass("ui-resizable-ghost").addClass(typeof a.ghost== "string"?a.ghost:"");b.ghost.appendTo(b.helper)},resize:function(){var b=e(this).data("resizable");b.ghost&&b.ghost.css({position:"relative",height:b.size.height,width:b.size.width})},stop:function(){var b=e(this).data("resizable");b.ghost&&b.helper&&b.helper.get(0).removeChild(b.ghost.get(0))}});e.ui.plugin.add("resizable","grid",{resize:function(){var b=e(this).data("resizable"),a=b.options,c=b.size,d=b.originalSize,f=b.originalPosition,g=b.axis;a.grid=typeof a.grid=="number"?[a.grid,a.grid]:a.grid; var h=Math.round((c.width-d.width)/(a.grid[0]||1))*(a.grid[0]||1);a=Math.round((c.height-d.height)/(a.grid[1]||1))*(a.grid[1]||1);if(/^(se|s|e)$/.test(g)){b.size.width=d.width+h;b.size.height=d.height+a}else if(/^(ne)$/.test(g)){b.size.width=d.width+h;b.size.height=d.height+a;b.position.top=f.top-a}else{if(/^(sw)$/.test(g)){b.size.width=d.width+h;b.size.height=d.height+a}else{b.size.width=d.width+h;b.size.height=d.height+a;b.position.top=f.top-a}b.position.left=f.left-h}}});var m=function(b){return parseInt(b, 10)||0},l=function(b){return!isNaN(parseInt(b,10))}})(jQuery); ;/* * jQuery UI Selectable 1.8.13 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. * http://jquery.org/license * * http://docs.jquery.com/UI/Selectables * * Depends: * jquery.ui.core.js * jquery.ui.mouse.js * jquery.ui.widget.js */ (function(e){e.widget("ui.selectable",e.ui.mouse,{options:{appendTo:"body",autoRefresh:true,distance:0,filter:"*",tolerance:"touch"},_create:function(){var c=this;this.element.addClass("ui-selectable");this.dragged=false;var f;this.refresh=function(){f=e(c.options.filter,c.element[0]);f.each(function(){var d=e(this),b=d.offset();e.data(this,"selectable-item",{element:this,$element:d,left:b.left,top:b.top,right:b.left+d.outerWidth(),bottom:b.top+d.outerHeight(),startselected:false,selected:d.hasClass("ui-selected"), selecting:d.hasClass("ui-selecting"),unselecting:d.hasClass("ui-unselecting")})})};this.refresh();this.selectees=f.addClass("ui-selectee");this._mouseInit();this.helper=e("
")},destroy:function(){this.selectees.removeClass("ui-selectee").removeData("selectable-item");this.element.removeClass("ui-selectable ui-selectable-disabled").removeData("selectable").unbind(".selectable");this._mouseDestroy();return this},_mouseStart:function(c){var f=this;this.opos=[c.pageX, c.pageY];if(!this.options.disabled){var d=this.options;this.selectees=e(d.filter,this.element[0]);this._trigger("start",c);e(d.appendTo).append(this.helper);this.helper.css({left:c.clientX,top:c.clientY,width:0,height:0});d.autoRefresh&&this.refresh();this.selectees.filter(".ui-selected").each(function(){var b=e.data(this,"selectable-item");b.startselected=true;if(!c.metaKey){b.$element.removeClass("ui-selected");b.selected=false;b.$element.addClass("ui-unselecting");b.unselecting=true;f._trigger("unselecting", c,{unselecting:b.element})}});e(c.target).parents().andSelf().each(function(){var b=e.data(this,"selectable-item");if(b){var g=!c.metaKey||!b.$element.hasClass("ui-selected");b.$element.removeClass(g?"ui-unselecting":"ui-selected").addClass(g?"ui-selecting":"ui-unselecting");b.unselecting=!g;b.selecting=g;(b.selected=g)?f._trigger("selecting",c,{selecting:b.element}):f._trigger("unselecting",c,{unselecting:b.element});return false}})}},_mouseDrag:function(c){var f=this;this.dragged=true;if(!this.options.disabled){var d= this.options,b=this.opos[0],g=this.opos[1],h=c.pageX,i=c.pageY;if(b>h){var j=h;h=b;b=j}if(g>i){j=i;i=g;g=j}this.helper.css({left:b,top:g,width:h-b,height:i-g});this.selectees.each(function(){var a=e.data(this,"selectable-item");if(!(!a||a.element==f.element[0])){var k=false;if(d.tolerance=="touch")k=!(a.left>h||a.righti||a.bottomb&&a.rightg&&a.bottom *",opacity:false,placeholder:false,revert:false,scroll:true,scrollSensitivity:20,scrollSpeed:20,scope:"default",tolerance:"intersect",zIndex:1E3},_create:function(){var a=this.options;this.containerCache={};this.element.addClass("ui-sortable"); this.refresh();this.floating=this.items.length?a.axis==="x"||/left|right/.test(this.items[0].item.css("float"))||/inline|table-cell/.test(this.items[0].item.css("display")):false;this.offset=this.element.offset();this._mouseInit()},destroy:function(){this.element.removeClass("ui-sortable ui-sortable-disabled").removeData("sortable").unbind(".sortable");this._mouseDestroy();for(var a=this.items.length-1;a>=0;a--)this.items[a].item.removeData("sortable-item");return this},_setOption:function(a,b){if(a=== "disabled"){this.options[a]=b;this.widget()[b?"addClass":"removeClass"]("ui-sortable-disabled")}else d.Widget.prototype._setOption.apply(this,arguments)},_mouseCapture:function(a,b){if(this.reverting)return false;if(this.options.disabled||this.options.type=="static")return false;this._refreshItems(a);var c=null,e=this;d(a.target).parents().each(function(){if(d.data(this,"sortable-item")==e){c=d(this);return false}});if(d.data(a.target,"sortable-item")==e)c=d(a.target);if(!c)return false;if(this.options.handle&& !b){var f=false;d(this.options.handle,c).find("*").andSelf().each(function(){if(this==a.target)f=true});if(!f)return false}this.currentItem=c;this._removeCurrentsFromItems();return true},_mouseStart:function(a,b,c){b=this.options;var e=this;this.currentContainer=this;this.refreshPositions();this.helper=this._createHelper(a);this._cacheHelperProportions();this._cacheMargins();this.scrollParent=this.helper.scrollParent();this.offset=this.currentItem.offset();this.offset={top:this.offset.top-this.margins.top, left:this.offset.left-this.margins.left};this.helper.css("position","absolute");this.cssPosition=this.helper.css("position");d.extend(this.offset,{click:{left:a.pageX-this.offset.left,top:a.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()});this.originalPosition=this._generatePosition(a);this.originalPageX=a.pageX;this.originalPageY=a.pageY;b.cursorAt&&this._adjustOffsetFromHelper(b.cursorAt);this.domPosition={prev:this.currentItem.prev()[0],parent:this.currentItem.parent()[0]}; this.helper[0]!=this.currentItem[0]&&this.currentItem.hide();this._createPlaceholder();b.containment&&this._setContainment();if(b.cursor){if(d("body").css("cursor"))this._storedCursor=d("body").css("cursor");d("body").css("cursor",b.cursor)}if(b.opacity){if(this.helper.css("opacity"))this._storedOpacity=this.helper.css("opacity");this.helper.css("opacity",b.opacity)}if(b.zIndex){if(this.helper.css("zIndex"))this._storedZIndex=this.helper.css("zIndex");this.helper.css("zIndex",b.zIndex)}if(this.scrollParent[0]!= document&&this.scrollParent[0].tagName!="HTML")this.overflowOffset=this.scrollParent.offset();this._trigger("start",a,this._uiHash());this._preserveHelperProportions||this._cacheHelperProportions();if(!c)for(c=this.containers.length-1;c>=0;c--)this.containers[c]._trigger("activate",a,e._uiHash(this));if(d.ui.ddmanager)d.ui.ddmanager.current=this;d.ui.ddmanager&&!b.dropBehaviour&&d.ui.ddmanager.prepareOffsets(this,a);this.dragging=true;this.helper.addClass("ui-sortable-helper");this._mouseDrag(a); return true},_mouseDrag:function(a){this.position=this._generatePosition(a);this.positionAbs=this._convertPositionTo("absolute");if(!this.lastPositionAbs)this.lastPositionAbs=this.positionAbs;if(this.options.scroll){var b=this.options,c=false;if(this.scrollParent[0]!=document&&this.scrollParent[0].tagName!="HTML"){if(this.overflowOffset.top+this.scrollParent[0].offsetHeight-a.pageY=0;b--){c=this.items[b];var e=c.item[0],f=this._intersectsWithPointer(c);if(f)if(e!=this.currentItem[0]&&this.placeholder[f==1?"next":"prev"]()[0]!=e&&!d.ui.contains(this.placeholder[0],e)&&(this.options.type=="semi-dynamic"?!d.ui.contains(this.element[0], e):true)){this.direction=f==1?"down":"up";if(this.options.tolerance=="pointer"||this._intersectsWithSides(c))this._rearrange(a,c);else break;this._trigger("change",a,this._uiHash());break}}this._contactContainers(a);d.ui.ddmanager&&d.ui.ddmanager.drag(this,a);this._trigger("sort",a,this._uiHash());this.lastPositionAbs=this.positionAbs;return false},_mouseStop:function(a,b){if(a){d.ui.ddmanager&&!this.options.dropBehaviour&&d.ui.ddmanager.drop(this,a);if(this.options.revert){var c=this;b=c.placeholder.offset(); c.reverting=true;d(this.helper).animate({left:b.left-this.offset.parent.left-c.margins.left+(this.offsetParent[0]==document.body?0:this.offsetParent[0].scrollLeft),top:b.top-this.offset.parent.top-c.margins.top+(this.offsetParent[0]==document.body?0:this.offsetParent[0].scrollTop)},parseInt(this.options.revert,10)||500,function(){c._clear(a)})}else this._clear(a,b);return false}},cancel:function(){var a=this;if(this.dragging){this._mouseUp({target:null});this.options.helper=="original"?this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper"): this.currentItem.show();for(var b=this.containers.length-1;b>=0;b--){this.containers[b]._trigger("deactivate",null,a._uiHash(this));if(this.containers[b].containerCache.over){this.containers[b]._trigger("out",null,a._uiHash(this));this.containers[b].containerCache.over=0}}}if(this.placeholder){this.placeholder[0].parentNode&&this.placeholder[0].parentNode.removeChild(this.placeholder[0]);this.options.helper!="original"&&this.helper&&this.helper[0].parentNode&&this.helper.remove();d.extend(this,{helper:null, dragging:false,reverting:false,_noFinalSort:null});this.domPosition.prev?d(this.domPosition.prev).after(this.currentItem):d(this.domPosition.parent).prepend(this.currentItem)}return this},serialize:function(a){var b=this._getItemsAsjQuery(a&&a.connected),c=[];a=a||{};d(b).each(function(){var e=(d(a.item||this).attr(a.attribute||"id")||"").match(a.expression||/(.+)[-=_](.+)/);if(e)c.push((a.key||e[1]+"[]")+"="+(a.key&&a.expression?e[1]:e[2]))});!c.length&&a.key&&c.push(a.key+"=");return c.join("&")}, toArray:function(a){var b=this._getItemsAsjQuery(a&&a.connected),c=[];a=a||{};b.each(function(){c.push(d(a.item||this).attr(a.attribute||"id")||"")});return c},_intersectsWith:function(a){var b=this.positionAbs.left,c=b+this.helperProportions.width,e=this.positionAbs.top,f=e+this.helperProportions.height,g=a.left,h=g+a.width,i=a.top,k=i+a.height,j=this.offset.click.top,l=this.offset.click.left;j=e+j>i&&e+jg&&b+la[this.floating?"width":"height"]?j:g0?"down":"up")},_getDragHorizontalDirection:function(){var a=this.positionAbs.left-this.lastPositionAbs.left;return a!=0&&(a>0?"right":"left")},refresh:function(a){this._refreshItems(a);this.refreshPositions();return this},_connectWith:function(){var a=this.options;return a.connectWith.constructor==String?[a.connectWith]:a.connectWith},_getItemsAsjQuery:function(a){var b=[],c=[],e=this._connectWith(); if(e&&a)for(a=e.length-1;a>=0;a--)for(var f=d(e[a]),g=f.length-1;g>=0;g--){var h=d.data(f[g],"sortable");if(h&&h!=this&&!h.options.disabled)c.push([d.isFunction(h.options.items)?h.options.items.call(h.element):d(h.options.items,h.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),h])}c.push([d.isFunction(this.options.items)?this.options.items.call(this.element,null,{options:this.options,item:this.currentItem}):d(this.options.items,this.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"), this]);for(a=c.length-1;a>=0;a--)c[a][0].each(function(){b.push(this)});return d(b)},_removeCurrentsFromItems:function(){for(var a=this.currentItem.find(":data(sortable-item)"),b=0;b=0;f--)for(var g=d(e[f]),h=g.length-1;h>=0;h--){var i=d.data(g[h],"sortable");if(i&&i!=this&&!i.options.disabled){c.push([d.isFunction(i.options.items)?i.options.items.call(i.element[0],a,{item:this.currentItem}):d(i.options.items,i.element),i]);this.containers.push(i)}}for(f=c.length-1;f>=0;f--){a=c[f][1];e=c[f][0];h=0;for(g=e.length;h=0;b--){var c=this.items[b];if(!(c.instance!=this.currentContainer&&this.currentContainer&&c.item[0]!=this.currentItem[0])){var e=this.options.toleranceElement?d(this.options.toleranceElement,c.item):c.item;if(!a){c.width=e.outerWidth();c.height=e.outerHeight()}e=e.offset();c.left=e.left;c.top=e.top}}if(this.options.custom&&this.options.custom.refreshContainers)this.options.custom.refreshContainers.call(this);else for(b= this.containers.length-1;b>=0;b--){e=this.containers[b].element.offset();this.containers[b].containerCache.left=e.left;this.containers[b].containerCache.top=e.top;this.containers[b].containerCache.width=this.containers[b].element.outerWidth();this.containers[b].containerCache.height=this.containers[b].element.outerHeight()}return this},_createPlaceholder:function(a){var b=a||this,c=b.options;if(!c.placeholder||c.placeholder.constructor==String){var e=c.placeholder;c.placeholder={element:function(){var f= d(document.createElement(b.currentItem[0].nodeName)).addClass(e||b.currentItem[0].className+" ui-sortable-placeholder").removeClass("ui-sortable-helper")[0];if(!e)f.style.visibility="hidden";return f},update:function(f,g){if(!(e&&!c.forcePlaceholderSize)){g.height()||g.height(b.currentItem.innerHeight()-parseInt(b.currentItem.css("paddingTop")||0,10)-parseInt(b.currentItem.css("paddingBottom")||0,10));g.width()||g.width(b.currentItem.innerWidth()-parseInt(b.currentItem.css("paddingLeft")||0,10)-parseInt(b.currentItem.css("paddingRight")|| 0,10))}}}}b.placeholder=d(c.placeholder.element.call(b.element,b.currentItem));b.currentItem.after(b.placeholder);c.placeholder.update(b,b.placeholder)},_contactContainers:function(a){for(var b=null,c=null,e=this.containers.length-1;e>=0;e--)if(!d.ui.contains(this.currentItem[0],this.containers[e].element[0]))if(this._intersectsWith(this.containers[e].containerCache)){if(!(b&&d.ui.contains(this.containers[e].element[0],b.element[0]))){b=this.containers[e];c=e}}else if(this.containers[e].containerCache.over){this.containers[e]._trigger("out", a,this._uiHash(this));this.containers[e].containerCache.over=0}if(b)if(this.containers.length===1){this.containers[c]._trigger("over",a,this._uiHash(this));this.containers[c].containerCache.over=1}else if(this.currentContainer!=this.containers[c]){b=1E4;e=null;for(var f=this.positionAbs[this.containers[c].floating?"left":"top"],g=this.items.length-1;g>=0;g--)if(d.ui.contains(this.containers[c].element[0],this.items[g].item[0])){var h=this.items[g][this.containers[c].floating?"left":"top"];if(Math.abs(h- f)this.containment[2])f=this.containment[2]+this.offset.click.left;if(a.pageY-this.offset.click.top>this.containment[3])g=this.containment[3]+this.offset.click.top}if(b.grid){g=this.originalPageY+Math.round((g- this.originalPageY)/b.grid[1])*b.grid[1];g=this.containment?!(g-this.offset.click.topthis.containment[3])?g:!(g-this.offset.click.topthis.containment[2])?f:!(f-this.offset.click.left=0;e--)if(d.ui.contains(this.containers[e].element[0],this.currentItem[0])&&!b){c.push(function(f){return function(g){f._trigger("receive",g,this._uiHash(this))}}.call(this,this.containers[e]));c.push(function(f){return function(g){f._trigger("update",g,this._uiHash(this))}}.call(this,this.containers[e]))}}for(e=this.containers.length-1;e>=0;e--){b||c.push(function(f){return function(g){f._trigger("deactivate",g,this._uiHash(this))}}.call(this, this.containers[e]));if(this.containers[e].containerCache.over){c.push(function(f){return function(g){f._trigger("out",g,this._uiHash(this))}}.call(this,this.containers[e]));this.containers[e].containerCache.over=0}}this._storedCursor&&d("body").css("cursor",this._storedCursor);this._storedOpacity&&this.helper.css("opacity",this._storedOpacity);if(this._storedZIndex)this.helper.css("zIndex",this._storedZIndex=="auto"?"":this._storedZIndex);this.dragging=false;if(this.cancelHelperRemoval){if(!b){this._trigger("beforeStop", a,this._uiHash());for(e=0;e li > :first-child,> :not(li):even",icons:{header:"ui-icon-triangle-1-e",headerSelected:"ui-icon-triangle-1-s"},navigation:false,navigationFilter:function(){return this.href.toLowerCase()===location.href.toLowerCase()}},_create:function(){var a=this,b=a.options;a.running=0;a.element.addClass("ui-accordion ui-widget ui-helper-reset").children("li").addClass("ui-accordion-li-fix"); a.headers=a.element.find(b.header).addClass("ui-accordion-header ui-helper-reset ui-state-default ui-corner-all").bind("mouseenter.accordion",function(){b.disabled||c(this).addClass("ui-state-hover")}).bind("mouseleave.accordion",function(){b.disabled||c(this).removeClass("ui-state-hover")}).bind("focus.accordion",function(){b.disabled||c(this).addClass("ui-state-focus")}).bind("blur.accordion",function(){b.disabled||c(this).removeClass("ui-state-focus")});a.headers.next().addClass("ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom"); if(b.navigation){var d=a.element.find("a").filter(b.navigationFilter).eq(0);if(d.length){var h=d.closest(".ui-accordion-header");a.active=h.length?h:d.closest(".ui-accordion-content").prev()}}a.active=a._findActive(a.active||b.active).addClass("ui-state-default ui-state-active").toggleClass("ui-corner-all").toggleClass("ui-corner-top");a.active.next().addClass("ui-accordion-content-active");a._createIcons();a.resize();a.element.attr("role","tablist");a.headers.attr("role","tab").bind("keydown.accordion", function(f){return a._keydown(f)}).next().attr("role","tabpanel");a.headers.not(a.active||"").attr({"aria-expanded":"false","aria-selected":"false",tabIndex:-1}).next().hide();a.active.length?a.active.attr({"aria-expanded":"true","aria-selected":"true",tabIndex:0}):a.headers.eq(0).attr("tabIndex",0);c.browser.safari||a.headers.find("a").attr("tabIndex",-1);b.event&&a.headers.bind(b.event.split(" ").join(".accordion ")+".accordion",function(f){a._clickHandler.call(a,f,this);f.preventDefault()})},_createIcons:function(){var a= this.options;if(a.icons){c("").addClass("ui-icon "+a.icons.header).prependTo(this.headers);this.active.children(".ui-icon").toggleClass(a.icons.header).toggleClass(a.icons.headerSelected);this.element.addClass("ui-accordion-icons")}},_destroyIcons:function(){this.headers.children(".ui-icon").remove();this.element.removeClass("ui-accordion-icons")},destroy:function(){var a=this.options;this.element.removeClass("ui-accordion ui-widget ui-helper-reset").removeAttr("role");this.headers.unbind(".accordion").removeClass("ui-accordion-header ui-accordion-disabled ui-helper-reset ui-state-default ui-corner-all ui-state-active ui-state-disabled ui-corner-top").removeAttr("role").removeAttr("aria-expanded").removeAttr("aria-selected").removeAttr("tabIndex"); this.headers.find("a").removeAttr("tabIndex");this._destroyIcons();var b=this.headers.next().css("display","").removeAttr("role").removeClass("ui-helper-reset ui-widget-content ui-corner-bottom ui-accordion-content ui-accordion-content-active ui-accordion-disabled ui-state-disabled");if(a.autoHeight||a.fillHeight)b.css("height","");return c.Widget.prototype.destroy.call(this)},_setOption:function(a,b){c.Widget.prototype._setOption.apply(this,arguments);a=="active"&&this.activate(b);if(a=="icons"){this._destroyIcons(); b&&this._createIcons()}if(a=="disabled")this.headers.add(this.headers.next())[b?"addClass":"removeClass"]("ui-accordion-disabled ui-state-disabled")},_keydown:function(a){if(!(this.options.disabled||a.altKey||a.ctrlKey)){var b=c.ui.keyCode,d=this.headers.length,h=this.headers.index(a.target),f=false;switch(a.keyCode){case b.RIGHT:case b.DOWN:f=this.headers[(h+1)%d];break;case b.LEFT:case b.UP:f=this.headers[(h-1+d)%d];break;case b.SPACE:case b.ENTER:this._clickHandler({target:a.target},a.target); a.preventDefault()}if(f){c(a.target).attr("tabIndex",-1);c(f).attr("tabIndex",0);f.focus();return false}return true}},resize:function(){var a=this.options,b;if(a.fillSpace){if(c.browser.msie){var d=this.element.parent().css("overflow");this.element.parent().css("overflow","hidden")}b=this.element.parent().height();c.browser.msie&&this.element.parent().css("overflow",d);this.headers.each(function(){b-=c(this).outerHeight(true)});this.headers.next().each(function(){c(this).height(Math.max(0,b-c(this).innerHeight()+ c(this).height()))}).css("overflow","auto")}else if(a.autoHeight){b=0;this.headers.next().each(function(){b=Math.max(b,c(this).height("").height())}).height(b)}return this},activate:function(a){this.options.active=a;a=this._findActive(a)[0];this._clickHandler({target:a},a);return this},_findActive:function(a){return a?typeof a==="number"?this.headers.filter(":eq("+a+")"):this.headers.not(this.headers.not(a)):a===false?c([]):this.headers.filter(":eq(0)")},_clickHandler:function(a,b){var d=this.options; if(!d.disabled)if(a.target){a=c(a.currentTarget||b);b=a[0]===this.active[0];d.active=d.collapsible&&b?false:this.headers.index(a);if(!(this.running||!d.collapsible&&b)){var h=this.active;j=a.next();g=this.active.next();e={options:d,newHeader:b&&d.collapsible?c([]):a,oldHeader:this.active,newContent:b&&d.collapsible?c([]):j,oldContent:g};var f=this.headers.index(this.active[0])>this.headers.index(a[0]);this.active=b?c([]):a;this._toggle(j,g,e,b,f);h.removeClass("ui-state-active ui-corner-top").addClass("ui-state-default ui-corner-all").children(".ui-icon").removeClass(d.icons.headerSelected).addClass(d.icons.header); if(!b){a.removeClass("ui-state-default ui-corner-all").addClass("ui-state-active ui-corner-top").children(".ui-icon").removeClass(d.icons.header).addClass(d.icons.headerSelected);a.next().addClass("ui-accordion-content-active")}}}else if(d.collapsible){this.active.removeClass("ui-state-active ui-corner-top").addClass("ui-state-default ui-corner-all").children(".ui-icon").removeClass(d.icons.headerSelected).addClass(d.icons.header);this.active.next().addClass("ui-accordion-content-active");var g=this.active.next(), e={options:d,newHeader:c([]),oldHeader:d.active,newContent:c([]),oldContent:g},j=this.active=c([]);this._toggle(j,g,e)}},_toggle:function(a,b,d,h,f){var g=this,e=g.options;g.toShow=a;g.toHide=b;g.data=d;var j=function(){if(g)return g._completed.apply(g,arguments)};g._trigger("changestart",null,g.data);g.running=b.size()===0?a.size():b.size();if(e.animated){d={};d=e.collapsible&&h?{toShow:c([]),toHide:b,complete:j,down:f,autoHeight:e.autoHeight||e.fillSpace}:{toShow:a,toHide:b,complete:j,down:f,autoHeight:e.autoHeight|| e.fillSpace};if(!e.proxied)e.proxied=e.animated;if(!e.proxiedDuration)e.proxiedDuration=e.duration;e.animated=c.isFunction(e.proxied)?e.proxied(d):e.proxied;e.duration=c.isFunction(e.proxiedDuration)?e.proxiedDuration(d):e.proxiedDuration;h=c.ui.accordion.animations;var i=e.duration,k=e.animated;if(k&&!h[k]&&!c.easing[k])k="slide";h[k]||(h[k]=function(l){this.slide(l,{easing:k,duration:i||700})});h[k](d)}else{if(e.collapsible&&h)a.toggle();else{b.hide();a.show()}j(true)}b.prev().attr({"aria-expanded":"false", "aria-selected":"false",tabIndex:-1}).blur();a.prev().attr({"aria-expanded":"true","aria-selected":"true",tabIndex:0}).focus()},_completed:function(a){this.running=a?0:--this.running;if(!this.running){this.options.clearStyle&&this.toShow.add(this.toHide).css({height:"",overflow:""});this.toHide.removeClass("ui-accordion-content-active");if(this.toHide.length)this.toHide.parent()[0].className=this.toHide.parent()[0].className;this._trigger("change",null,this.data)}}});c.extend(c.ui.accordion,{version:"1.8.13", animations:{slide:function(a,b){a=c.extend({easing:"swing",duration:300},a,b);if(a.toHide.size())if(a.toShow.size()){var d=a.toShow.css("overflow"),h=0,f={},g={},e;b=a.toShow;e=b[0].style.width;b.width(parseInt(b.parent().width(),10)-parseInt(b.css("paddingLeft"),10)-parseInt(b.css("paddingRight"),10)-(parseInt(b.css("borderLeftWidth"),10)||0)-(parseInt(b.css("borderRightWidth"),10)||0));c.each(["height","paddingTop","paddingBottom"],function(j,i){g[i]="hide";j=(""+c.css(a.toShow[0],i)).match(/^([\d+-.]+)(.*)$/); f[i]={value:j[1],unit:j[2]||"px"}});a.toShow.css({height:0,overflow:"hidden"}).show();a.toHide.filter(":hidden").each(a.complete).end().filter(":visible").animate(g,{step:function(j,i){if(i.prop=="height")h=i.end-i.start===0?0:(i.now-i.start)/(i.end-i.start);a.toShow[0].style[i.prop]=h*f[i.prop].value+f[i.prop].unit},duration:a.duration,easing:a.easing,complete:function(){a.autoHeight||a.toShow.css("height","");a.toShow.css({width:e,overflow:d});a.complete()}})}else a.toHide.animate({height:"hide", paddingTop:"hide",paddingBottom:"hide"},a);else a.toShow.animate({height:"show",paddingTop:"show",paddingBottom:"show"},a)},bounceslide:function(a){this.slide(a,{easing:a.down?"easeOutBounce":"swing",duration:a.down?1E3:200})}}})})(jQuery); ;/* * jQuery UI Autocomplete 1.8.13 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. * http://jquery.org/license * * http://docs.jquery.com/UI/Autocomplete * * Depends: * jquery.ui.core.js * jquery.ui.widget.js * jquery.ui.position.js */ (function(d){var e=0;d.widget("ui.autocomplete",{options:{appendTo:"body",autoFocus:false,delay:300,minLength:1,position:{my:"left top",at:"left bottom",collision:"none"},source:null},pending:0,_create:function(){var a=this,b=this.element[0].ownerDocument,g;this.element.addClass("ui-autocomplete-input").attr("autocomplete","off").attr({role:"textbox","aria-autocomplete":"list","aria-haspopup":"true"}).bind("keydown.autocomplete",function(c){if(!(a.options.disabled||a.element.attr("readonly"))){g= false;var f=d.ui.keyCode;switch(c.keyCode){case f.PAGE_UP:a._move("previousPage",c);break;case f.PAGE_DOWN:a._move("nextPage",c);break;case f.UP:a._move("previous",c);c.preventDefault();break;case f.DOWN:a._move("next",c);c.preventDefault();break;case f.ENTER:case f.NUMPAD_ENTER:if(a.menu.active){g=true;c.preventDefault()}case f.TAB:if(!a.menu.active)return;a.menu.select(c);break;case f.ESCAPE:a.element.val(a.term);a.close(c);break;default:clearTimeout(a.searching);a.searching=setTimeout(function(){if(a.term!= a.element.val()){a.selectedItem=null;a.search(null,c)}},a.options.delay);break}}}).bind("keypress.autocomplete",function(c){if(g){g=false;c.preventDefault()}}).bind("focus.autocomplete",function(){if(!a.options.disabled){a.selectedItem=null;a.previous=a.element.val()}}).bind("blur.autocomplete",function(c){if(!a.options.disabled){clearTimeout(a.searching);a.closing=setTimeout(function(){a.close(c);a._change(c)},150)}});this._initSource();this.response=function(){return a._response.apply(a,arguments)}; this.menu=d("
    ").addClass("ui-autocomplete").appendTo(d(this.options.appendTo||"body",b)[0]).mousedown(function(c){var f=a.menu.element[0];d(c.target).closest(".ui-menu-item").length||setTimeout(function(){d(document).one("mousedown",function(h){h.target!==a.element[0]&&h.target!==f&&!d.ui.contains(f,h.target)&&a.close()})},1);setTimeout(function(){clearTimeout(a.closing)},13)}).menu({focus:function(c,f){f=f.item.data("item.autocomplete");false!==a._trigger("focus",c,{item:f})&&/^key/.test(c.originalEvent.type)&& a.element.val(f.value)},selected:function(c,f){var h=f.item.data("item.autocomplete"),i=a.previous;if(a.element[0]!==b.activeElement){a.element.focus();a.previous=i;setTimeout(function(){a.previous=i;a.selectedItem=h},1)}false!==a._trigger("select",c,{item:h})&&a.element.val(h.value);a.term=a.element.val();a.close(c);a.selectedItem=h},blur:function(){a.menu.element.is(":visible")&&a.element.val()!==a.term&&a.element.val(a.term)}}).zIndex(this.element.zIndex()+1).css({top:0,left:0}).hide().data("menu"); d.fn.bgiframe&&this.menu.element.bgiframe()},destroy:function(){this.element.removeClass("ui-autocomplete-input").removeAttr("autocomplete").removeAttr("role").removeAttr("aria-autocomplete").removeAttr("aria-haspopup");this.menu.element.remove();d.Widget.prototype.destroy.call(this)},_setOption:function(a,b){d.Widget.prototype._setOption.apply(this,arguments);a==="source"&&this._initSource();if(a==="appendTo")this.menu.element.appendTo(d(b||"body",this.element[0].ownerDocument)[0]);a==="disabled"&& b&&this.xhr&&this.xhr.abort()},_initSource:function(){var a=this,b,g;if(d.isArray(this.options.source)){b=this.options.source;this.source=function(c,f){f(d.ui.autocomplete.filter(b,c.term))}}else if(typeof this.options.source==="string"){g=this.options.source;this.source=function(c,f){a.xhr&&a.xhr.abort();a.xhr=d.ajax({url:g,data:c,dataType:"json",autocompleteRequest:++e,success:function(h){this.autocompleteRequest===e&&f(h)},error:function(){this.autocompleteRequest===e&&f([])}})}}else this.source= this.options.source},search:function(a,b){a=a!=null?a:this.element.val();this.term=this.element.val();if(a.length").data("item.autocomplete",b).append(d("").text(b.label)).appendTo(a)},_move:function(a,b){if(this.menu.element.is(":visible"))if(this.menu.first()&&/^previous/.test(a)||this.menu.last()&&/^next/.test(a)){this.element.val(this.term);this.menu.deactivate()}else this.menu[a](b);else this.search(null,b)},widget:function(){return this.menu.element}});d.extend(d.ui.autocomplete,{escapeRegex:function(a){return a.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&")},filter:function(a,b){var g=new RegExp(d.ui.autocomplete.escapeRegex(b),"i");return d.grep(a,function(c){return g.test(c.label||c.value||c)})}})})(jQuery); (function(d){d.widget("ui.menu",{_create:function(){var e=this;this.element.addClass("ui-menu ui-widget ui-widget-content ui-corner-all").attr({role:"listbox","aria-activedescendant":"ui-active-menuitem"}).click(function(a){if(d(a.target).closest(".ui-menu-item a").length){a.preventDefault();e.select(a)}});this.refresh()},refresh:function(){var e=this;this.element.children("li:not(.ui-menu-item):has(a)").addClass("ui-menu-item").attr("role","menuitem").children("a").addClass("ui-corner-all").attr("tabindex", -1).mouseenter(function(a){e.activate(a,d(this).parent())}).mouseleave(function(){e.deactivate()})},activate:function(e,a){this.deactivate();if(this.hasScroll()){var b=a.offset().top-this.element.offset().top,g=this.element.scrollTop(),c=this.element.height();if(b<0)this.element.scrollTop(g+b);else b>=c&&this.element.scrollTop(g+b-c+a.height())}this.active=a.eq(0).children("a").addClass("ui-state-hover").attr("id","ui-active-menuitem").end();this._trigger("focus",e,{item:a})},deactivate:function(){if(this.active){this.active.children("a").removeClass("ui-state-hover").removeAttr("id"); this._trigger("blur");this.active=null}},next:function(e){this.move("next",".ui-menu-item:first",e)},previous:function(e){this.move("prev",".ui-menu-item:last",e)},first:function(){return this.active&&!this.active.prevAll(".ui-menu-item").length},last:function(){return this.active&&!this.active.nextAll(".ui-menu-item").length},move:function(e,a,b){if(this.active){e=this.active[e+"All"](".ui-menu-item").eq(0);e.length?this.activate(b,e):this.activate(b,this.element.children(a))}else this.activate(b, this.element.children(a))},nextPage:function(e){if(this.hasScroll())if(!this.active||this.last())this.activate(e,this.element.children(".ui-menu-item:first"));else{var a=this.active.offset().top,b=this.element.height(),g=this.element.children(".ui-menu-item").filter(function(){var c=d(this).offset().top-a-b+d(this).height();return c<10&&c>-10});g.length||(g=this.element.children(".ui-menu-item:last"));this.activate(e,g)}else this.activate(e,this.element.children(".ui-menu-item").filter(!this.active|| this.last()?":first":":last"))},previousPage:function(e){if(this.hasScroll())if(!this.active||this.first())this.activate(e,this.element.children(".ui-menu-item:last"));else{var a=this.active.offset().top,b=this.element.height();result=this.element.children(".ui-menu-item").filter(function(){var g=d(this).offset().top-a+b-d(this).height();return g<10&&g>-10});result.length||(result=this.element.children(".ui-menu-item:first"));this.activate(e,result)}else this.activate(e,this.element.children(".ui-menu-item").filter(!this.active|| this.first()?":last":":first"))},hasScroll:function(){return this.element.height()").addClass("ui-button-text").html(this.options.label).appendTo(b.empty()).text(),d=this.options.icons,f=d.primary&&d.secondary,e=[];if(d.primary||d.secondary){if(this.options.text)e.push("ui-button-text-icon"+(f?"s":d.primary?"-primary":"-secondary"));d.primary&&b.prepend("");d.secondary&&b.append("");if(!this.options.text){e.push(f?"ui-button-icons-only": "ui-button-icon-only");this.hasTitle||b.attr("title",c)}}else e.push("ui-button-text-only");b.addClass(e.join(" "))}}});a.widget("ui.buttonset",{options:{items:":button, :submit, :reset, :checkbox, :radio, a, :data(button)"},_create:function(){this.element.addClass("ui-buttonset")},_init:function(){this.refresh()},_setOption:function(b,c){b==="disabled"&&this.buttons.button("option",b,c);a.Widget.prototype._setOption.apply(this,arguments)},refresh:function(){this.buttons=this.element.find(this.options.items).filter(":ui-button").button("refresh").end().not(":ui-button").button().end().map(function(){return a(this).button("widget")[0]}).removeClass("ui-corner-all ui-corner-left ui-corner-right").filter(":first").addClass("ui-corner-left").end().filter(":last").addClass("ui-corner-right").end().end()}, destroy:function(){this.element.removeClass("ui-buttonset");this.buttons.map(function(){return a(this).button("widget")[0]}).removeClass("ui-corner-left ui-corner-right").end().button("destroy");a.Widget.prototype.destroy.call(this)}})})(jQuery); ;/* * jQuery UI Dialog 1.8.13 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. * http://jquery.org/license * * http://docs.jquery.com/UI/Dialog * * Depends: * jquery.ui.core.js * jquery.ui.widget.js * jquery.ui.button.js * jquery.ui.draggable.js * jquery.ui.mouse.js * jquery.ui.position.js * jquery.ui.resizable.js */ (function(c,l){var m={buttons:true,height:true,maxHeight:true,maxWidth:true,minHeight:true,minWidth:true,width:true},n={maxHeight:true,maxWidth:true,minHeight:true,minWidth:true},o=c.attrFn||{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true,click:true};c.widget("ui.dialog",{options:{autoOpen:true,buttons:{},closeOnEscape:true,closeText:"close",dialogClass:"",draggable:true,hide:null,height:"auto",maxHeight:false,maxWidth:false,minHeight:150,minWidth:150,modal:false, position:{my:"center",at:"center",collision:"fit",using:function(a){var b=c(this).css(a).offset().top;b<0&&c(this).css("top",a.top-b)}},resizable:true,show:null,stack:true,title:"",width:300,zIndex:1E3},_create:function(){this.originalTitle=this.element.attr("title");if(typeof this.originalTitle!=="string")this.originalTitle="";this.options.title=this.options.title||this.originalTitle;var a=this,b=a.options,d=b.title||" ",e=c.ui.dialog.getTitleId(a.element),g=(a.uiDialog=c("
    ")).appendTo(document.body).hide().addClass("ui-dialog ui-widget ui-widget-content ui-corner-all "+ b.dialogClass).css({zIndex:b.zIndex}).attr("tabIndex",-1).css("outline",0).keydown(function(i){if(b.closeOnEscape&&i.keyCode&&i.keyCode===c.ui.keyCode.ESCAPE){a.close(i);i.preventDefault()}}).attr({role:"dialog","aria-labelledby":e}).mousedown(function(i){a.moveToTop(false,i)});a.element.show().removeAttr("title").addClass("ui-dialog-content ui-widget-content").appendTo(g);var f=(a.uiDialogTitlebar=c("
    ")).addClass("ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix").prependTo(g), h=c('').addClass("ui-dialog-titlebar-close ui-corner-all").attr("role","button").hover(function(){h.addClass("ui-state-hover")},function(){h.removeClass("ui-state-hover")}).focus(function(){h.addClass("ui-state-focus")}).blur(function(){h.removeClass("ui-state-focus")}).click(function(i){a.close(i);return false}).appendTo(f);(a.uiDialogTitlebarCloseText=c("")).addClass("ui-icon ui-icon-closethick").text(b.closeText).appendTo(h);c("").addClass("ui-dialog-title").attr("id", e).html(d).prependTo(f);if(c.isFunction(b.beforeclose)&&!c.isFunction(b.beforeClose))b.beforeClose=b.beforeclose;f.find("*").add(f).disableSelection();b.draggable&&c.fn.draggable&&a._makeDraggable();b.resizable&&c.fn.resizable&&a._makeResizable();a._createButtons(b.buttons);a._isOpen=false;c.fn.bgiframe&&g.bgiframe()},_init:function(){this.options.autoOpen&&this.open()},destroy:function(){var a=this;a.overlay&&a.overlay.destroy();a.uiDialog.hide();a.element.unbind(".dialog").removeData("dialog").removeClass("ui-dialog-content ui-widget-content").hide().appendTo("body"); a.uiDialog.remove();a.originalTitle&&a.element.attr("title",a.originalTitle);return a},widget:function(){return this.uiDialog},close:function(a){var b=this,d,e;if(false!==b._trigger("beforeClose",a)){b.overlay&&b.overlay.destroy();b.uiDialog.unbind("keypress.ui-dialog");b._isOpen=false;if(b.options.hide)b.uiDialog.hide(b.options.hide,function(){b._trigger("close",a)});else{b.uiDialog.hide();b._trigger("close",a)}c.ui.dialog.overlay.resize();if(b.options.modal){d=0;c(".ui-dialog").each(function(){if(this!== b.uiDialog[0]){e=c(this).css("z-index");isNaN(e)||(d=Math.max(d,e))}});c.ui.dialog.maxZ=d}return b}},isOpen:function(){return this._isOpen},moveToTop:function(a,b){var d=this,e=d.options;if(e.modal&&!a||!e.stack&&!e.modal)return d._trigger("focus",b);if(e.zIndex>c.ui.dialog.maxZ)c.ui.dialog.maxZ=e.zIndex;if(d.overlay){c.ui.dialog.maxZ+=1;d.overlay.$el.css("z-index",c.ui.dialog.overlay.maxZ=c.ui.dialog.maxZ)}a={scrollTop:d.element.attr("scrollTop"),scrollLeft:d.element.attr("scrollLeft")};c.ui.dialog.maxZ+= 1;d.uiDialog.css("z-index",c.ui.dialog.maxZ);d.element.attr(a);d._trigger("focus",b);return d},open:function(){if(!this._isOpen){var a=this,b=a.options,d=a.uiDialog;a.overlay=b.modal?new c.ui.dialog.overlay(a):null;a._size();a._position(b.position);d.show(b.show);a.moveToTop(true);b.modal&&d.bind("keypress.ui-dialog",function(e){if(e.keyCode===c.ui.keyCode.TAB){var g=c(":tabbable",this),f=g.filter(":first");g=g.filter(":last");if(e.target===g[0]&&!e.shiftKey){f.focus(1);return false}else if(e.target=== f[0]&&e.shiftKey){g.focus(1);return false}}});c(a.element.find(":tabbable").get().concat(d.find(".ui-dialog-buttonpane :tabbable").get().concat(d.get()))).eq(0).focus();a._isOpen=true;a._trigger("open");return a}},_createButtons:function(a){var b=this,d=false,e=c("
    ").addClass("ui-dialog-buttonpane ui-widget-content ui-helper-clearfix"),g=c("
    ").addClass("ui-dialog-buttonset").appendTo(e);b.uiDialog.find(".ui-dialog-buttonpane").remove();typeof a==="object"&&a!==null&&c.each(a, function(){return!(d=true)});if(d){c.each(a,function(f,h){h=c.isFunction(h)?{click:h,text:f}:h;var i=c('').click(function(){h.click.apply(b.element[0],arguments)}).appendTo(g);c.each(h,function(j,k){if(j!=="click")j in o?i[j](k):i.attr(j,k)});c.fn.button&&i.button()});e.appendTo(b.uiDialog)}},_makeDraggable:function(){function a(f){return{position:f.position,offset:f.offset}}var b=this,d=b.options,e=c(document),g;b.uiDialog.draggable({cancel:".ui-dialog-content, .ui-dialog-titlebar-close", handle:".ui-dialog-titlebar",containment:"document",start:function(f,h){g=d.height==="auto"?"auto":c(this).height();c(this).height(c(this).height()).addClass("ui-dialog-dragging");b._trigger("dragStart",f,a(h))},drag:function(f,h){b._trigger("drag",f,a(h))},stop:function(f,h){d.position=[h.position.left-e.scrollLeft(),h.position.top-e.scrollTop()];c(this).removeClass("ui-dialog-dragging").height(g);b._trigger("dragStop",f,a(h));c.ui.dialog.overlay.resize()}})},_makeResizable:function(a){function b(f){return{originalPosition:f.originalPosition, originalSize:f.originalSize,position:f.position,size:f.size}}a=a===l?this.options.resizable:a;var d=this,e=d.options,g=d.uiDialog.css("position");a=typeof a==="string"?a:"n,e,s,w,se,sw,ne,nw";d.uiDialog.resizable({cancel:".ui-dialog-content",containment:"document",alsoResize:d.element,maxWidth:e.maxWidth,maxHeight:e.maxHeight,minWidth:e.minWidth,minHeight:d._minHeight(),handles:a,start:function(f,h){c(this).addClass("ui-dialog-resizing");d._trigger("resizeStart",f,b(h))},resize:function(f,h){d._trigger("resize", f,b(h))},stop:function(f,h){c(this).removeClass("ui-dialog-resizing");e.height=c(this).height();e.width=c(this).width();d._trigger("resizeStop",f,b(h));c.ui.dialog.overlay.resize()}}).css("position",g).find(".ui-resizable-se").addClass("ui-icon ui-icon-grip-diagonal-se")},_minHeight:function(){var a=this.options;return a.height==="auto"?a.minHeight:Math.min(a.minHeight,a.height)},_position:function(a){var b=[],d=[0,0],e;if(a){if(typeof a==="string"||typeof a==="object"&&"0"in a){b=a.split?a.split(" "): [a[0],a[1]];if(b.length===1)b[1]=b[0];c.each(["left","top"],function(g,f){if(+b[g]===b[g]){d[g]=b[g];b[g]=f}});a={my:b.join(" "),at:b.join(" "),offset:d.join(" ")}}a=c.extend({},c.ui.dialog.prototype.options.position,a)}else a=c.ui.dialog.prototype.options.position;(e=this.uiDialog.is(":visible"))||this.uiDialog.show();this.uiDialog.css({top:0,left:0}).position(c.extend({of:window},a));e||this.uiDialog.hide()},_setOptions:function(a){var b=this,d={},e=false;c.each(a,function(g,f){b._setOption(g,f); if(g in m)e=true;if(g in n)d[g]=f});e&&this._size();this.uiDialog.is(":data(resizable)")&&this.uiDialog.resizable("option",d)},_setOption:function(a,b){var d=this,e=d.uiDialog;switch(a){case "beforeclose":a="beforeClose";break;case "buttons":d._createButtons(b);break;case "closeText":d.uiDialogTitlebarCloseText.text(""+b);break;case "dialogClass":e.removeClass(d.options.dialogClass).addClass("ui-dialog ui-widget ui-widget-content ui-corner-all "+b);break;case "disabled":b?e.addClass("ui-dialog-disabled"): e.removeClass("ui-dialog-disabled");break;case "draggable":var g=e.is(":data(draggable)");g&&!b&&e.draggable("destroy");!g&&b&&d._makeDraggable();break;case "position":d._position(b);break;case "resizable":(g=e.is(":data(resizable)"))&&!b&&e.resizable("destroy");g&&typeof b==="string"&&e.resizable("option","handles",b);!g&&b!==false&&d._makeResizable(b);break;case "title":c(".ui-dialog-title",d.uiDialogTitlebar).html(""+(b||" "));break}c.Widget.prototype._setOption.apply(d,arguments)},_size:function(){var a= this.options,b,d,e=this.uiDialog.is(":visible");this.element.show().css({width:"auto",minHeight:0,height:0});if(a.minWidth>a.width)a.width=a.minWidth;b=this.uiDialog.css({height:"auto",width:a.width}).height();d=Math.max(0,a.minHeight-b);if(a.height==="auto")if(c.support.minHeight)this.element.css({minHeight:d,height:"auto"});else{this.uiDialog.show();a=this.element.css("height","auto").height();e||this.uiDialog.hide();this.element.height(Math.max(a,d))}else this.element.height(Math.max(a.height- b,0));this.uiDialog.is(":data(resizable)")&&this.uiDialog.resizable("option","minHeight",this._minHeight())}});c.extend(c.ui.dialog,{version:"1.8.13",uuid:0,maxZ:0,getTitleId:function(a){a=a.attr("id");if(!a){this.uuid+=1;a=this.uuid}return"ui-dialog-title-"+a},overlay:function(a){this.$el=c.ui.dialog.overlay.create(a)}});c.extend(c.ui.dialog.overlay,{instances:[],oldInstances:[],maxZ:0,events:c.map("focus,mousedown,mouseup,keydown,keypress,click".split(","),function(a){return a+".dialog-overlay"}).join(" "), create:function(a){if(this.instances.length===0){setTimeout(function(){c.ui.dialog.overlay.instances.length&&c(document).bind(c.ui.dialog.overlay.events,function(d){if(c(d.target).zIndex()").addClass("ui-widget-overlay")).appendTo(document.body).css({width:this.width(), height:this.height()});c.fn.bgiframe&&b.bgiframe();this.instances.push(b);return b},destroy:function(a){var b=c.inArray(a,this.instances);b!=-1&&this.oldInstances.push(this.instances.splice(b,1)[0]);this.instances.length===0&&c([document,window]).unbind(".dialog-overlay");a.remove();var d=0;c.each(this.instances,function(){d=Math.max(d,this.css("z-index"))});this.maxZ=d},height:function(){var a,b;if(c.browser.msie&&c.browser.version<7){a=Math.max(document.documentElement.scrollHeight,document.body.scrollHeight); b=Math.max(document.documentElement.offsetHeight,document.body.offsetHeight);return a").appendTo(this.element).addClass("ui-slider-range ui-widget-header"+(a.range==="min"||a.range==="max"?" ui-slider-range-"+a.range:""))}for(var j=c.length;j"); this.handles=c.add(d(e.join("")).appendTo(b.element));this.handle=this.handles.eq(0);this.handles.add(this.range).filter("a").click(function(g){g.preventDefault()}).hover(function(){a.disabled||d(this).addClass("ui-state-hover")},function(){d(this).removeClass("ui-state-hover")}).focus(function(){if(a.disabled)d(this).blur();else{d(".ui-slider .ui-state-focus").removeClass("ui-state-focus");d(this).addClass("ui-state-focus")}}).blur(function(){d(this).removeClass("ui-state-focus")});this.handles.each(function(g){d(this).data("index.ui-slider-handle", g)});this.handles.keydown(function(g){var k=true,l=d(this).data("index.ui-slider-handle"),i,h,m;if(!b.options.disabled){switch(g.keyCode){case d.ui.keyCode.HOME:case d.ui.keyCode.END:case d.ui.keyCode.PAGE_UP:case d.ui.keyCode.PAGE_DOWN:case d.ui.keyCode.UP:case d.ui.keyCode.RIGHT:case d.ui.keyCode.DOWN:case d.ui.keyCode.LEFT:k=false;if(!b._keySliding){b._keySliding=true;d(this).addClass("ui-state-active");i=b._start(g,l);if(i===false)return}break}m=b.options.step;i=b.options.values&&b.options.values.length? (h=b.values(l)):(h=b.value());switch(g.keyCode){case d.ui.keyCode.HOME:h=b._valueMin();break;case d.ui.keyCode.END:h=b._valueMax();break;case d.ui.keyCode.PAGE_UP:h=b._trimAlignValue(i+(b._valueMax()-b._valueMin())/5);break;case d.ui.keyCode.PAGE_DOWN:h=b._trimAlignValue(i-(b._valueMax()-b._valueMin())/5);break;case d.ui.keyCode.UP:case d.ui.keyCode.RIGHT:if(i===b._valueMax())return;h=b._trimAlignValue(i+m);break;case d.ui.keyCode.DOWN:case d.ui.keyCode.LEFT:if(i===b._valueMin())return;h=b._trimAlignValue(i- m);break}b._slide(g,l,h);return k}}).keyup(function(g){var k=d(this).data("index.ui-slider-handle");if(b._keySliding){b._keySliding=false;b._stop(g,k);b._change(g,k);d(this).removeClass("ui-state-active")}});this._refreshValue();this._animateOff=false},destroy:function(){this.handles.remove();this.range.remove();this.element.removeClass("ui-slider ui-slider-horizontal ui-slider-vertical ui-slider-disabled ui-widget ui-widget-content ui-corner-all").removeData("slider").unbind(".slider");this._mouseDestroy(); return this},_mouseCapture:function(b){var a=this.options,c,f,e,j,g;if(a.disabled)return false;this.elementSize={width:this.element.outerWidth(),height:this.element.outerHeight()};this.elementOffset=this.element.offset();c=this._normValueFromMouse({x:b.pageX,y:b.pageY});f=this._valueMax()-this._valueMin()+1;j=this;this.handles.each(function(k){var l=Math.abs(c-j.values(k));if(f>l){f=l;e=d(this);g=k}});if(a.range===true&&this.values(1)===a.min){g+=1;e=d(this.handles[g])}if(this._start(b,g)===false)return false; this._mouseSliding=true;j._handleIndex=g;e.addClass("ui-state-active").focus();a=e.offset();this._clickOffset=!d(b.target).parents().andSelf().is(".ui-slider-handle")?{left:0,top:0}:{left:b.pageX-a.left-e.width()/2,top:b.pageY-a.top-e.height()/2-(parseInt(e.css("borderTopWidth"),10)||0)-(parseInt(e.css("borderBottomWidth"),10)||0)+(parseInt(e.css("marginTop"),10)||0)};this.handles.hasClass("ui-state-hover")||this._slide(b,g,c);return this._animateOff=true},_mouseStart:function(){return true},_mouseDrag:function(b){var a= this._normValueFromMouse({x:b.pageX,y:b.pageY});this._slide(b,this._handleIndex,a);return false},_mouseStop:function(b){this.handles.removeClass("ui-state-active");this._mouseSliding=false;this._stop(b,this._handleIndex);this._change(b,this._handleIndex);this._clickOffset=this._handleIndex=null;return this._animateOff=false},_detectOrientation:function(){this.orientation=this.options.orientation==="vertical"?"vertical":"horizontal"},_normValueFromMouse:function(b){var a;if(this.orientation==="horizontal"){a= this.elementSize.width;b=b.x-this.elementOffset.left-(this._clickOffset?this._clickOffset.left:0)}else{a=this.elementSize.height;b=b.y-this.elementOffset.top-(this._clickOffset?this._clickOffset.top:0)}a=b/a;if(a>1)a=1;if(a<0)a=0;if(this.orientation==="vertical")a=1-a;b=this._valueMax()-this._valueMin();return this._trimAlignValue(this._valueMin()+a*b)},_start:function(b,a){var c={handle:this.handles[a],value:this.value()};if(this.options.values&&this.options.values.length){c.value=this.values(a); c.values=this.values()}return this._trigger("start",b,c)},_slide:function(b,a,c){var f;if(this.options.values&&this.options.values.length){f=this.values(a?0:1);if(this.options.values.length===2&&this.options.range===true&&(a===0&&c>f||a===1&&c1){this.options.values[b]=this._trimAlignValue(a);this._refreshValue();this._change(null,b)}else if(arguments.length)if(d.isArray(arguments[0])){c=this.options.values;f=arguments[0];for(e=0;e=this._valueMax())return this._valueMax();var a=this.options.step>0?this.options.step:1,c=(b-this._valueMin())%a;alignValue=b-c;if(Math.abs(c)*2>=a)alignValue+=c>0?a:-a;return parseFloat(alignValue.toFixed(5))},_valueMin:function(){return this.options.min},_valueMax:function(){return this.options.max}, _refreshValue:function(){var b=this.options.range,a=this.options,c=this,f=!this._animateOff?a.animate:false,e,j={},g,k,l,i;if(this.options.values&&this.options.values.length)this.handles.each(function(h){e=(c.values(h)-c._valueMin())/(c._valueMax()-c._valueMin())*100;j[c.orientation==="horizontal"?"left":"bottom"]=e+"%";d(this).stop(1,1)[f?"animate":"css"](j,a.animate);if(c.options.range===true)if(c.orientation==="horizontal"){if(h===0)c.range.stop(1,1)[f?"animate":"css"]({left:e+"%"},a.animate); if(h===1)c.range[f?"animate":"css"]({width:e-g+"%"},{queue:false,duration:a.animate})}else{if(h===0)c.range.stop(1,1)[f?"animate":"css"]({bottom:e+"%"},a.animate);if(h===1)c.range[f?"animate":"css"]({height:e-g+"%"},{queue:false,duration:a.animate})}g=e});else{k=this.value();l=this._valueMin();i=this._valueMax();e=i!==l?(k-l)/(i-l)*100:0;j[c.orientation==="horizontal"?"left":"bottom"]=e+"%";this.handle.stop(1,1)[f?"animate":"css"](j,a.animate);if(b==="min"&&this.orientation==="horizontal")this.range.stop(1, 1)[f?"animate":"css"]({width:e+"%"},a.animate);if(b==="max"&&this.orientation==="horizontal")this.range[f?"animate":"css"]({width:100-e+"%"},{queue:false,duration:a.animate});if(b==="min"&&this.orientation==="vertical")this.range.stop(1,1)[f?"animate":"css"]({height:e+"%"},a.animate);if(b==="max"&&this.orientation==="vertical")this.range[f?"animate":"css"]({height:100-e+"%"},{queue:false,duration:a.animate})}}});d.extend(d.ui.slider,{version:"1.8.13"})})(jQuery); ;/* * jQuery UI Tabs 1.8.13 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. * http://jquery.org/license * * http://docs.jquery.com/UI/Tabs * * Depends: * jquery.ui.core.js * jquery.ui.widget.js */ (function(d,p){function u(){return++v}function w(){return++x}var v=0,x=0;d.widget("ui.tabs",{options:{add:null,ajaxOptions:null,cache:false,cookie:null,collapsible:false,disable:null,disabled:[],enable:null,event:"click",fx:null,idPrefix:"ui-tabs-",load:null,panelTemplate:"
    ",remove:null,select:null,show:null,spinner:"Loading…",tabTemplate:"
  • #{label}
  • "},_create:function(){this._tabify(true)},_setOption:function(b,e){if(b=="selected")this.options.collapsible&& e==this.options.selected||this.select(e);else{this.options[b]=e;this._tabify()}},_tabId:function(b){return b.title&&b.title.replace(/\s/g,"_").replace(/[^\w\u00c0-\uFFFF-]/g,"")||this.options.idPrefix+u()},_sanitizeSelector:function(b){return b.replace(/:/g,"\\:")},_cookie:function(){var b=this.cookie||(this.cookie=this.options.cookie.name||"ui-tabs-"+w());return d.cookie.apply(null,[b].concat(d.makeArray(arguments)))},_ui:function(b,e){return{tab:b,panel:e,index:this.anchors.index(b)}},_cleanup:function(){this.lis.filter(".ui-state-processing").removeClass("ui-state-processing").find("span:data(label.tabs)").each(function(){var b= d(this);b.html(b.data("label.tabs")).removeData("label.tabs")})},_tabify:function(b){function e(g,f){g.css("display","");!d.support.opacity&&f.opacity&&g[0].style.removeAttribute("filter")}var a=this,c=this.options,h=/^#.+/;this.list=this.element.find("ol,ul").eq(0);this.lis=d(" > li:has(a[href])",this.list);this.anchors=this.lis.map(function(){return d("a",this)[0]});this.panels=d([]);this.anchors.each(function(g,f){var i=d(f).attr("href"),l=i.split("#")[0],q;if(l&&(l===location.toString().split("#")[0]|| (q=d("base")[0])&&l===q.href)){i=f.hash;f.href=i}if(h.test(i))a.panels=a.panels.add(a.element.find(a._sanitizeSelector(i)));else if(i&&i!=="#"){d.data(f,"href.tabs",i);d.data(f,"load.tabs",i.replace(/#.*$/,""));i=a._tabId(f);f.href="#"+i;f=a.element.find("#"+i);if(!f.length){f=d(c.panelTemplate).attr("id",i).addClass("ui-tabs-panel ui-widget-content ui-corner-bottom").insertAfter(a.panels[g-1]||a.list);f.data("destroy.tabs",true)}a.panels=a.panels.add(f)}else c.disabled.push(g)});if(b){this.element.addClass("ui-tabs ui-widget ui-widget-content ui-corner-all"); this.list.addClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all");this.lis.addClass("ui-state-default ui-corner-top");this.panels.addClass("ui-tabs-panel ui-widget-content ui-corner-bottom");if(c.selected===p){location.hash&&this.anchors.each(function(g,f){if(f.hash==location.hash){c.selected=g;return false}});if(typeof c.selected!=="number"&&c.cookie)c.selected=parseInt(a._cookie(),10);if(typeof c.selected!=="number"&&this.lis.filter(".ui-tabs-selected").length)c.selected= this.lis.index(this.lis.filter(".ui-tabs-selected"));c.selected=c.selected||(this.lis.length?0:-1)}else if(c.selected===null)c.selected=-1;c.selected=c.selected>=0&&this.anchors[c.selected]||c.selected<0?c.selected:0;c.disabled=d.unique(c.disabled.concat(d.map(this.lis.filter(".ui-state-disabled"),function(g){return a.lis.index(g)}))).sort();d.inArray(c.selected,c.disabled)!=-1&&c.disabled.splice(d.inArray(c.selected,c.disabled),1);this.panels.addClass("ui-tabs-hide");this.lis.removeClass("ui-tabs-selected ui-state-active"); if(c.selected>=0&&this.anchors.length){a.element.find(a._sanitizeSelector(a.anchors[c.selected].hash)).removeClass("ui-tabs-hide");this.lis.eq(c.selected).addClass("ui-tabs-selected ui-state-active");a.element.queue("tabs",function(){a._trigger("show",null,a._ui(a.anchors[c.selected],a.element.find(a._sanitizeSelector(a.anchors[c.selected].hash))[0]))});this.load(c.selected)}d(window).bind("unload",function(){a.lis.add(a.anchors).unbind(".tabs");a.lis=a.anchors=a.panels=null})}else c.selected=this.lis.index(this.lis.filter(".ui-tabs-selected")); this.element[c.collapsible?"addClass":"removeClass"]("ui-tabs-collapsible");c.cookie&&this._cookie(c.selected,c.cookie);b=0;for(var j;j=this.lis[b];b++)d(j)[d.inArray(b,c.disabled)!=-1&&!d(j).hasClass("ui-tabs-selected")?"addClass":"removeClass"]("ui-state-disabled");c.cache===false&&this.anchors.removeData("cache.tabs");this.lis.add(this.anchors).unbind(".tabs");if(c.event!=="mouseover"){var k=function(g,f){f.is(":not(.ui-state-disabled)")&&f.addClass("ui-state-"+g)},n=function(g,f){f.removeClass("ui-state-"+ g)};this.lis.bind("mouseover.tabs",function(){k("hover",d(this))});this.lis.bind("mouseout.tabs",function(){n("hover",d(this))});this.anchors.bind("focus.tabs",function(){k("focus",d(this).closest("li"))});this.anchors.bind("blur.tabs",function(){n("focus",d(this).closest("li"))})}var m,o;if(c.fx)if(d.isArray(c.fx)){m=c.fx[0];o=c.fx[1]}else m=o=c.fx;var r=o?function(g,f){d(g).closest("li").addClass("ui-tabs-selected ui-state-active");f.hide().removeClass("ui-tabs-hide").animate(o,o.duration||"normal", function(){e(f,o);a._trigger("show",null,a._ui(g,f[0]))})}:function(g,f){d(g).closest("li").addClass("ui-tabs-selected ui-state-active");f.removeClass("ui-tabs-hide");a._trigger("show",null,a._ui(g,f[0]))},s=m?function(g,f){f.animate(m,m.duration||"normal",function(){a.lis.removeClass("ui-tabs-selected ui-state-active");f.addClass("ui-tabs-hide");e(f,m);a.element.dequeue("tabs")})}:function(g,f){a.lis.removeClass("ui-tabs-selected ui-state-active");f.addClass("ui-tabs-hide");a.element.dequeue("tabs")}; this.anchors.bind(c.event+".tabs",function(){var g=this,f=d(g).closest("li"),i=a.panels.filter(":not(.ui-tabs-hide)"),l=a.element.find(a._sanitizeSelector(g.hash));if(f.hasClass("ui-tabs-selected")&&!c.collapsible||f.hasClass("ui-state-disabled")||f.hasClass("ui-state-processing")||a.panels.filter(":animated").length||a._trigger("select",null,a._ui(this,l[0]))===false){this.blur();return false}c.selected=a.anchors.index(this);a.abort();if(c.collapsible)if(f.hasClass("ui-tabs-selected")){c.selected= -1;c.cookie&&a._cookie(c.selected,c.cookie);a.element.queue("tabs",function(){s(g,i)}).dequeue("tabs");this.blur();return false}else if(!i.length){c.cookie&&a._cookie(c.selected,c.cookie);a.element.queue("tabs",function(){r(g,l)});a.load(a.anchors.index(this));this.blur();return false}c.cookie&&a._cookie(c.selected,c.cookie);if(l.length){i.length&&a.element.queue("tabs",function(){s(g,i)});a.element.queue("tabs",function(){r(g,l)});a.load(a.anchors.index(this))}else throw"jQuery UI Tabs: Mismatching fragment identifier."; d.browser.msie&&this.blur()});this.anchors.bind("click.tabs",function(){return false})},_getIndex:function(b){if(typeof b=="string")b=this.anchors.index(this.anchors.filter("[href$="+b+"]"));return b},destroy:function(){var b=this.options;this.abort();this.element.unbind(".tabs").removeClass("ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible").removeData("tabs");this.list.removeClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all");this.anchors.each(function(){var e= d.data(this,"href.tabs");if(e)this.href=e;var a=d(this).unbind(".tabs");d.each(["href","load","cache"],function(c,h){a.removeData(h+".tabs")})});this.lis.unbind(".tabs").add(this.panels).each(function(){d.data(this,"destroy.tabs")?d(this).remove():d(this).removeClass("ui-state-default ui-corner-top ui-tabs-selected ui-state-active ui-state-hover ui-state-focus ui-state-disabled ui-tabs-panel ui-widget-content ui-corner-bottom ui-tabs-hide")});b.cookie&&this._cookie(null,b.cookie);return this},add:function(b, e,a){if(a===p)a=this.anchors.length;var c=this,h=this.options;e=d(h.tabTemplate.replace(/#\{href\}/g,b).replace(/#\{label\}/g,e));b=!b.indexOf("#")?b.replace("#",""):this._tabId(d("a",e)[0]);e.addClass("ui-state-default ui-corner-top").data("destroy.tabs",true);var j=c.element.find("#"+b);j.length||(j=d(h.panelTemplate).attr("id",b).data("destroy.tabs",true));j.addClass("ui-tabs-panel ui-widget-content ui-corner-bottom ui-tabs-hide");if(a>=this.lis.length){e.appendTo(this.list);j.appendTo(this.list[0].parentNode)}else{e.insertBefore(this.lis[a]); j.insertBefore(this.panels[a])}h.disabled=d.map(h.disabled,function(k){return k>=a?++k:k});this._tabify();if(this.anchors.length==1){h.selected=0;e.addClass("ui-tabs-selected ui-state-active");j.removeClass("ui-tabs-hide");this.element.queue("tabs",function(){c._trigger("show",null,c._ui(c.anchors[0],c.panels[0]))});this.load(0)}this._trigger("add",null,this._ui(this.anchors[a],this.panels[a]));return this},remove:function(b){b=this._getIndex(b);var e=this.options,a=this.lis.eq(b).remove(),c=this.panels.eq(b).remove(); if(a.hasClass("ui-tabs-selected")&&this.anchors.length>1)this.select(b+(b+1=b?--h:h});this._tabify();this._trigger("remove",null,this._ui(a.find("a")[0],c[0]));return this},enable:function(b){b=this._getIndex(b);var e=this.options;if(d.inArray(b,e.disabled)!=-1){this.lis.eq(b).removeClass("ui-state-disabled");e.disabled=d.grep(e.disabled,function(a){return a!=b});this._trigger("enable",null, this._ui(this.anchors[b],this.panels[b]));return this}},disable:function(b){b=this._getIndex(b);var e=this.options;if(b!=e.selected){this.lis.eq(b).addClass("ui-state-disabled");e.disabled.push(b);e.disabled.sort();this._trigger("disable",null,this._ui(this.anchors[b],this.panels[b]))}return this},select:function(b){b=this._getIndex(b);if(b==-1)if(this.options.collapsible&&this.options.selected!=-1)b=this.options.selected;else return this;this.anchors.eq(b).trigger(this.options.event+".tabs");return this}, load:function(b){b=this._getIndex(b);var e=this,a=this.options,c=this.anchors.eq(b)[0],h=d.data(c,"load.tabs");this.abort();if(!h||this.element.queue("tabs").length!==0&&d.data(c,"cache.tabs"))this.element.dequeue("tabs");else{this.lis.eq(b).addClass("ui-state-processing");if(a.spinner){var j=d("span",c);j.data("label.tabs",j.html()).html(a.spinner)}this.xhr=d.ajax(d.extend({},a.ajaxOptions,{url:h,success:function(k,n){e.element.find(e._sanitizeSelector(c.hash)).html(k);e._cleanup();a.cache&&d.data(c, "cache.tabs",true);e._trigger("load",null,e._ui(e.anchors[b],e.panels[b]));try{a.ajaxOptions.success(k,n)}catch(m){}},error:function(k,n){e._cleanup();e._trigger("load",null,e._ui(e.anchors[b],e.panels[b]));try{a.ajaxOptions.error(k,n,b,c)}catch(m){}}}));e.element.dequeue("tabs");return this}},abort:function(){this.element.queue([]);this.panels.stop(false,true);this.element.queue("tabs",this.element.queue("tabs").splice(-2,2));if(this.xhr){this.xhr.abort();delete this.xhr}this._cleanup();return this}, url:function(b,e){this.anchors.eq(b).removeData("cache.tabs").data("load.tabs",e);return this},length:function(){return this.anchors.length}});d.extend(d.ui.tabs,{version:"1.8.13"});d.extend(d.ui.tabs.prototype,{rotation:null,rotate:function(b,e){var a=this,c=this.options,h=a._rotate||(a._rotate=function(j){clearTimeout(a.rotation);a.rotation=setTimeout(function(){var k=c.selected;a.select(++k'))}function N(a){return a.delegate("button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a", "mouseout",function(){d(this).removeClass("ui-state-hover");this.className.indexOf("ui-datepicker-prev")!=-1&&d(this).removeClass("ui-datepicker-prev-hover");this.className.indexOf("ui-datepicker-next")!=-1&&d(this).removeClass("ui-datepicker-next-hover")}).delegate("button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a","mouseover",function(){if(!d.datepicker._isDisabledDatepicker(J.inline?a.parent()[0]:J.input[0])){d(this).parents(".ui-datepicker-calendar").find("a").removeClass("ui-state-hover"); d(this).addClass("ui-state-hover");this.className.indexOf("ui-datepicker-prev")!=-1&&d(this).addClass("ui-datepicker-prev-hover");this.className.indexOf("ui-datepicker-next")!=-1&&d(this).addClass("ui-datepicker-next-hover")}})}function H(a,b){d.extend(a,b);for(var c in b)if(b[c]==null||b[c]==B)a[c]=b[c];return a}d.extend(d.ui,{datepicker:{version:"1.8.13"}});var z=(new Date).getTime(),J;d.extend(M.prototype,{markerClassName:"hasDatepicker",log:function(){this.debug&&console.log.apply("",arguments)}, _widgetDatepicker:function(){return this.dpDiv},setDefaults:function(a){H(this._defaults,a||{});return this},_attachDatepicker:function(a,b){var c=null;for(var e in this._defaults){var f=a.getAttribute("date:"+e);if(f){c=c||{};try{c[e]=eval(f)}catch(h){c[e]=f}}}e=a.nodeName.toLowerCase();f=e=="div"||e=="span";if(!a.id){this.uuid+=1;a.id="dp"+this.uuid}var i=this._newInst(d(a),f);i.settings=d.extend({},b||{},c||{});if(e=="input")this._connectDatepicker(a,i);else f&&this._inlineDatepicker(a,i)},_newInst:function(a, b){return{id:a[0].id.replace(/([^A-Za-z0-9_-])/g,"\\\\$1"),input:a,selectedDay:0,selectedMonth:0,selectedYear:0,drawMonth:0,drawYear:0,inline:b,dpDiv:!b?this.dpDiv:N(d('
    '))}},_connectDatepicker:function(a,b){var c=d(a);b.append=d([]);b.trigger=d([]);if(!c.hasClass(this.markerClassName)){this._attachments(c,b);c.addClass(this.markerClassName).keydown(this._doKeyDown).keypress(this._doKeyPress).keyup(this._doKeyUp).bind("setData.datepicker", function(e,f,h){b.settings[f]=h}).bind("getData.datepicker",function(e,f){return this._get(b,f)});this._autoSize(b);d.data(a,"datepicker",b)}},_attachments:function(a,b){var c=this._get(b,"appendText"),e=this._get(b,"isRTL");b.append&&b.append.remove();if(c){b.append=d(''+c+"");a[e?"before":"after"](b.append)}a.unbind("focus",this._showDatepicker);b.trigger&&b.trigger.remove();c=this._get(b,"showOn");if(c=="focus"||c=="both")a.focus(this._showDatepicker); if(c=="button"||c=="both"){c=this._get(b,"buttonText");var f=this._get(b,"buttonImage");b.trigger=d(this._get(b,"buttonImageOnly")?d("").addClass(this._triggerClass).attr({src:f,alt:c,title:c}):d('').addClass(this._triggerClass).html(f==""?c:d("").attr({src:f,alt:c,title:c})));a[e?"before":"after"](b.trigger);b.trigger.click(function(){d.datepicker._datepickerShowing&&d.datepicker._lastInput==a[0]?d.datepicker._hideDatepicker():d.datepicker._showDatepicker(a[0]); return false})}},_autoSize:function(a){if(this._get(a,"autoSize")&&!a.inline){var b=new Date(2009,11,20),c=this._get(a,"dateFormat");if(c.match(/[DM]/)){var e=function(f){for(var h=0,i=0,g=0;gh){h=f[g].length;i=g}return i};b.setMonth(e(this._get(a,c.match(/MM/)?"monthNames":"monthNamesShort")));b.setDate(e(this._get(a,c.match(/DD/)?"dayNames":"dayNamesShort"))+20-b.getDay())}a.input.attr("size",this._formatDate(a,b).length)}},_inlineDatepicker:function(a,b){var c=d(a); if(!c.hasClass(this.markerClassName)){c.addClass(this.markerClassName).append(b.dpDiv).bind("setData.datepicker",function(e,f,h){b.settings[f]=h}).bind("getData.datepicker",function(e,f){return this._get(b,f)});d.data(a,"datepicker",b);this._setDate(b,this._getDefaultDate(b),true);this._updateDatepicker(b);this._updateAlternate(b);b.dpDiv.show()}},_dialogDatepicker:function(a,b,c,e,f){a=this._dialogInst;if(!a){this.uuid+=1;this._dialogInput=d(''); this._dialogInput.keydown(this._doKeyDown);d("body").append(this._dialogInput);a=this._dialogInst=this._newInst(this._dialogInput,false);a.settings={};d.data(this._dialogInput[0],"datepicker",a)}H(a.settings,e||{});b=b&&b.constructor==Date?this._formatDate(a,b):b;this._dialogInput.val(b);this._pos=f?f.length?f:[f.pageX,f.pageY]:null;if(!this._pos)this._pos=[document.documentElement.clientWidth/2-100+(document.documentElement.scrollLeft||document.body.scrollLeft),document.documentElement.clientHeight/ 2-150+(document.documentElement.scrollTop||document.body.scrollTop)];this._dialogInput.css("left",this._pos[0]+20+"px").css("top",this._pos[1]+"px");a.settings.onSelect=c;this._inDialog=true;this.dpDiv.addClass(this._dialogClass);this._showDatepicker(this._dialogInput[0]);d.blockUI&&d.blockUI(this.dpDiv);d.data(this._dialogInput[0],"datepicker",a);return this},_destroyDatepicker:function(a){var b=d(a),c=d.data(a,"datepicker");if(b.hasClass(this.markerClassName)){var e=a.nodeName.toLowerCase();d.removeData(a, "datepicker");if(e=="input"){c.append.remove();c.trigger.remove();b.removeClass(this.markerClassName).unbind("focus",this._showDatepicker).unbind("keydown",this._doKeyDown).unbind("keypress",this._doKeyPress).unbind("keyup",this._doKeyUp)}else if(e=="div"||e=="span")b.removeClass(this.markerClassName).empty()}},_enableDatepicker:function(a){var b=d(a),c=d.data(a,"datepicker");if(b.hasClass(this.markerClassName)){var e=a.nodeName.toLowerCase();if(e=="input"){a.disabled=false;c.trigger.filter("button").each(function(){this.disabled= false}).end().filter("img").css({opacity:"1.0",cursor:""})}else if(e=="div"||e=="span"){b=b.children("."+this._inlineClass);b.children().removeClass("ui-state-disabled");b.find("select.ui-datepicker-month, select.ui-datepicker-year").removeAttr("disabled")}this._disabledInputs=d.map(this._disabledInputs,function(f){return f==a?null:f})}},_disableDatepicker:function(a){var b=d(a),c=d.data(a,"datepicker");if(b.hasClass(this.markerClassName)){var e=a.nodeName.toLowerCase();if(e=="input"){a.disabled= true;c.trigger.filter("button").each(function(){this.disabled=true}).end().filter("img").css({opacity:"0.5",cursor:"default"})}else if(e=="div"||e=="span"){b=b.children("."+this._inlineClass);b.children().addClass("ui-state-disabled");b.find("select.ui-datepicker-month, select.ui-datepicker-year").attr("disabled","disabled")}this._disabledInputs=d.map(this._disabledInputs,function(f){return f==a?null:f});this._disabledInputs[this._disabledInputs.length]=a}},_isDisabledDatepicker:function(a){if(!a)return false; for(var b=0;b-1}},_doKeyUp:function(a){a=d.datepicker._getInst(a.target);if(a.input.val()!=a.lastVal)try{if(d.datepicker.parseDate(d.datepicker._get(a,"dateFormat"),a.input?a.input.val():null,d.datepicker._getFormatConfig(a))){d.datepicker._setDateFromField(a); d.datepicker._updateAlternate(a);d.datepicker._updateDatepicker(a)}}catch(b){d.datepicker.log(b)}return true},_showDatepicker:function(a){a=a.target||a;if(a.nodeName.toLowerCase()!="input")a=d("input",a.parentNode)[0];if(!(d.datepicker._isDisabledDatepicker(a)||d.datepicker._lastInput==a)){var b=d.datepicker._getInst(a);d.datepicker._curInst&&d.datepicker._curInst!=b&&d.datepicker._curInst.dpDiv.stop(true,true);var c=d.datepicker._get(b,"beforeShow");H(b.settings,c?c.apply(a,[a,b]):{});b.lastVal= null;d.datepicker._lastInput=a;d.datepicker._setDateFromField(b);if(d.datepicker._inDialog)a.value="";if(!d.datepicker._pos){d.datepicker._pos=d.datepicker._findPos(a);d.datepicker._pos[1]+=a.offsetHeight}var e=false;d(a).parents().each(function(){e|=d(this).css("position")=="fixed";return!e});if(e&&d.browser.opera){d.datepicker._pos[0]-=document.documentElement.scrollLeft;d.datepicker._pos[1]-=document.documentElement.scrollTop}c={left:d.datepicker._pos[0],top:d.datepicker._pos[1]};d.datepicker._pos= null;b.dpDiv.empty();b.dpDiv.css({position:"absolute",display:"block",top:"-1000px"});d.datepicker._updateDatepicker(b);c=d.datepicker._checkOffset(b,c,e);b.dpDiv.css({position:d.datepicker._inDialog&&d.blockUI?"static":e?"fixed":"absolute",display:"none",left:c.left+"px",top:c.top+"px"});if(!b.inline){c=d.datepicker._get(b,"showAnim");var f=d.datepicker._get(b,"duration"),h=function(){var i=b.dpDiv.find("iframe.ui-datepicker-cover");if(i.length){var g=d.datepicker._getBorders(b.dpDiv);i.css({left:-g[0], top:-g[1],width:b.dpDiv.outerWidth(),height:b.dpDiv.outerHeight()})}};b.dpDiv.zIndex(d(a).zIndex()+1);d.datepicker._datepickerShowing=true;d.effects&&d.effects[c]?b.dpDiv.show(c,d.datepicker._get(b,"showOptions"),f,h):b.dpDiv[c||"show"](c?f:null,h);if(!c||!f)h();b.input.is(":visible")&&!b.input.is(":disabled")&&b.input.focus();d.datepicker._curInst=b}}},_updateDatepicker:function(a){var b=d.datepicker._getBorders(a.dpDiv);J=a;a.dpDiv.empty().append(this._generateHTML(a));var c=a.dpDiv.find("iframe.ui-datepicker-cover"); c.length&&c.css({left:-b[0],top:-b[1],width:a.dpDiv.outerWidth(),height:a.dpDiv.outerHeight()});a.dpDiv.find("."+this._dayOverClass+" a").mouseover();b=this._getNumberOfMonths(a);c=b[1];a.dpDiv.removeClass("ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4").width("");c>1&&a.dpDiv.addClass("ui-datepicker-multi-"+c).css("width",17*c+"em");a.dpDiv[(b[0]!=1||b[1]!=1?"add":"remove")+"Class"]("ui-datepicker-multi");a.dpDiv[(this._get(a,"isRTL")?"add":"remove")+"Class"]("ui-datepicker-rtl"); a==d.datepicker._curInst&&d.datepicker._datepickerShowing&&a.input&&a.input.is(":visible")&&!a.input.is(":disabled")&&a.input[0]!=document.activeElement&&a.input.focus();if(a.yearshtml){var e=a.yearshtml;setTimeout(function(){e===a.yearshtml&&a.yearshtml&&a.dpDiv.find("select.ui-datepicker-year:first").replaceWith(a.yearshtml);e=a.yearshtml=null},0)}},_getBorders:function(a){var b=function(c){return{thin:1,medium:2,thick:3}[c]||c};return[parseFloat(b(a.css("border-left-width"))),parseFloat(b(a.css("border-top-width")))]}, _checkOffset:function(a,b,c){var e=a.dpDiv.outerWidth(),f=a.dpDiv.outerHeight(),h=a.input?a.input.outerWidth():0,i=a.input?a.input.outerHeight():0,g=document.documentElement.clientWidth+d(document).scrollLeft(),j=document.documentElement.clientHeight+d(document).scrollTop();b.left-=this._get(a,"isRTL")?e-h:0;b.left-=c&&b.left==a.input.offset().left?d(document).scrollLeft():0;b.top-=c&&b.top==a.input.offset().top+i?d(document).scrollTop():0;b.left-=Math.min(b.left,b.left+e>g&&g>e?Math.abs(b.left+e- g):0);b.top-=Math.min(b.top,b.top+f>j&&j>f?Math.abs(f+i):0);return b},_findPos:function(a){for(var b=this._get(this._getInst(a),"isRTL");a&&(a.type=="hidden"||a.nodeType!=1||d.expr.filters.hidden(a));)a=a[b?"previousSibling":"nextSibling"];a=d(a).offset();return[a.left,a.top]},_hideDatepicker:function(a){var b=this._curInst;if(!(!b||a&&b!=d.data(a,"datepicker")))if(this._datepickerShowing){a=this._get(b,"showAnim");var c=this._get(b,"duration"),e=function(){d.datepicker._tidyDialog(b);this._curInst= null};d.effects&&d.effects[a]?b.dpDiv.hide(a,d.datepicker._get(b,"showOptions"),c,e):b.dpDiv[a=="slideDown"?"slideUp":a=="fadeIn"?"fadeOut":"hide"](a?c:null,e);a||e();if(a=this._get(b,"onClose"))a.apply(b.input?b.input[0]:null,[b.input?b.input.val():"",b]);this._datepickerShowing=false;this._lastInput=null;if(this._inDialog){this._dialogInput.css({position:"absolute",left:"0",top:"-100px"});if(d.blockUI){d.unblockUI();d("body").append(this.dpDiv)}}this._inDialog=false}},_tidyDialog:function(a){a.dpDiv.removeClass(this._dialogClass).unbind(".ui-datepicker-calendar")}, _checkExternalClick:function(a){if(d.datepicker._curInst){a=d(a.target);a[0].id!=d.datepicker._mainDivId&&a.parents("#"+d.datepicker._mainDivId).length==0&&!a.hasClass(d.datepicker.markerClassName)&&!a.hasClass(d.datepicker._triggerClass)&&d.datepicker._datepickerShowing&&!(d.datepicker._inDialog&&d.blockUI)&&d.datepicker._hideDatepicker()}},_adjustDate:function(a,b,c){a=d(a);var e=this._getInst(a[0]);if(!this._isDisabledDatepicker(a[0])){this._adjustInstDate(e,b+(c=="M"?this._get(e,"showCurrentAtPos"): 0),c);this._updateDatepicker(e)}},_gotoToday:function(a){a=d(a);var b=this._getInst(a[0]);if(this._get(b,"gotoCurrent")&&b.currentDay){b.selectedDay=b.currentDay;b.drawMonth=b.selectedMonth=b.currentMonth;b.drawYear=b.selectedYear=b.currentYear}else{var c=new Date;b.selectedDay=c.getDate();b.drawMonth=b.selectedMonth=c.getMonth();b.drawYear=b.selectedYear=c.getFullYear()}this._notifyChange(b);this._adjustDate(a)},_selectMonthYear:function(a,b,c){a=d(a);var e=this._getInst(a[0]);e._selectingMonthYear= false;e["selected"+(c=="M"?"Month":"Year")]=e["draw"+(c=="M"?"Month":"Year")]=parseInt(b.options[b.selectedIndex].value,10);this._notifyChange(e);this._adjustDate(a)},_clickMonthYear:function(a){var b=this._getInst(d(a)[0]);b.input&&b._selectingMonthYear&&setTimeout(function(){b.input.focus()},0);b._selectingMonthYear=!b._selectingMonthYear},_selectDay:function(a,b,c,e){var f=d(a);if(!(d(e).hasClass(this._unselectableClass)||this._isDisabledDatepicker(f[0]))){f=this._getInst(f[0]);f.selectedDay=f.currentDay= d("a",e).html();f.selectedMonth=f.currentMonth=b;f.selectedYear=f.currentYear=c;this._selectDate(a,this._formatDate(f,f.currentDay,f.currentMonth,f.currentYear))}},_clearDate:function(a){a=d(a);this._getInst(a[0]);this._selectDate(a,"")},_selectDate:function(a,b){a=this._getInst(d(a)[0]);b=b!=null?b:this._formatDate(a);a.input&&a.input.val(b);this._updateAlternate(a);var c=this._get(a,"onSelect");if(c)c.apply(a.input?a.input[0]:null,[b,a]);else a.input&&a.input.trigger("change");if(a.inline)this._updateDatepicker(a); else{this._hideDatepicker();this._lastInput=a.input[0];typeof a.input[0]!="object"&&a.input.focus();this._lastInput=null}},_updateAlternate:function(a){var b=this._get(a,"altField");if(b){var c=this._get(a,"altFormat")||this._get(a,"dateFormat"),e=this._getDate(a),f=this.formatDate(c,e,this._getFormatConfig(a));d(b).each(function(){d(this).val(f)})}},noWeekends:function(a){a=a.getDay();return[a>0&&a<6,""]},iso8601Week:function(a){a=new Date(a.getTime());a.setDate(a.getDate()+4-(a.getDay()||7));var b= a.getTime();a.setMonth(0);a.setDate(1);return Math.floor(Math.round((b-a)/864E5)/7)+1},parseDate:function(a,b,c){if(a==null||b==null)throw"Invalid arguments";b=typeof b=="object"?b.toString():b+"";if(b=="")return null;var e=(c?c.shortYearCutoff:null)||this._defaults.shortYearCutoff;e=typeof e!="string"?e:(new Date).getFullYear()%100+parseInt(e,10);for(var f=(c?c.dayNamesShort:null)||this._defaults.dayNamesShort,h=(c?c.dayNames:null)||this._defaults.dayNames,i=(c?c.monthNamesShort:null)||this._defaults.monthNamesShort, g=(c?c.monthNames:null)||this._defaults.monthNames,j=c=-1,l=-1,u=-1,k=false,o=function(p){(p=A+1-1){j=1;l=u;do{e=this._getDaysInMonth(c,j-1);if(l<=e)break;j++;l-=e}while(1)}v=this._daylightSavingAdjust(new Date(c,j-1,l));if(v.getFullYear()!= c||v.getMonth()+1!=j||v.getDate()!=l)throw"Invalid date";return v},ATOM:"yy-mm-dd",COOKIE:"D, dd M yy",ISO_8601:"yy-mm-dd",RFC_822:"D, d M y",RFC_850:"DD, dd-M-y",RFC_1036:"D, d M y",RFC_1123:"D, d M yy",RFC_2822:"D, d M yy",RSS:"D, d M y",TICKS:"!",TIMESTAMP:"@",W3C:"yy-mm-dd",_ticksTo1970:(718685+Math.floor(492.5)-Math.floor(19.7)+Math.floor(4.925))*24*60*60*1E7,formatDate:function(a,b,c){if(!b)return"";var e=(c?c.dayNamesShort:null)||this._defaults.dayNamesShort,f=(c?c.dayNames:null)||this._defaults.dayNames, h=(c?c.monthNamesShort:null)||this._defaults.monthNamesShort;c=(c?c.monthNames:null)||this._defaults.monthNames;var i=function(o){(o=k+112?a.getHours()+2:0);return a},_setDate:function(a,b,c){var e=!b,f=a.selectedMonth,h=a.selectedYear;b=this._restrictMinMax(a,this._determineDate(a,b,new Date));a.selectedDay=a.currentDay=b.getDate();a.drawMonth=a.selectedMonth=a.currentMonth=b.getMonth();a.drawYear=a.selectedYear=a.currentYear=b.getFullYear();if((f!=a.selectedMonth||h!=a.selectedYear)&&!c)this._notifyChange(a);this._adjustInstDate(a);if(a.input)a.input.val(e?"":this._formatDate(a))},_getDate:function(a){return!a.currentYear|| a.input&&a.input.val()==""?null:this._daylightSavingAdjust(new Date(a.currentYear,a.currentMonth,a.currentDay))},_generateHTML:function(a){var b=new Date;b=this._daylightSavingAdjust(new Date(b.getFullYear(),b.getMonth(),b.getDate()));var c=this._get(a,"isRTL"),e=this._get(a,"showButtonPanel"),f=this._get(a,"hideIfNoPrevNext"),h=this._get(a,"navigationAsDateFormat"),i=this._getNumberOfMonths(a),g=this._get(a,"showCurrentAtPos"),j=this._get(a,"stepMonths"),l=i[0]!=1||i[1]!=1,u=this._daylightSavingAdjust(!a.currentDay? new Date(9999,9,9):new Date(a.currentYear,a.currentMonth,a.currentDay)),k=this._getMinMaxDate(a,"min"),o=this._getMinMaxDate(a,"max");g=a.drawMonth-g;var m=a.drawYear;if(g<0){g+=12;m--}if(o){var n=this._daylightSavingAdjust(new Date(o.getFullYear(),o.getMonth()-i[0]*i[1]+1,o.getDate()));for(n=k&&nn;){g--;if(g<0){g=11;m--}}}a.drawMonth=g;a.drawYear=m;n=this._get(a,"prevText");n=!h?n:this.formatDate(n,this._daylightSavingAdjust(new Date(m,g-j,1)),this._getFormatConfig(a)); n=this._canAdjustMonth(a,-1,m,g)?''+n+"":f?"":''+n+"";var r=this._get(a,"nextText");r=!h?r:this.formatDate(r,this._daylightSavingAdjust(new Date(m, g+j,1)),this._getFormatConfig(a));f=this._canAdjustMonth(a,+1,m,g)?''+r+"":f?"":''+r+"";j=this._get(a,"currentText");r=this._get(a,"gotoCurrent")&& a.currentDay?u:b;j=!h?j:this.formatDate(j,r,this._getFormatConfig(a));h=!a.inline?'":"";e=e?'
    '+(c?h:"")+(this._isInRange(a,r)?'":"")+(c?"":h)+"
    ":"";h=parseInt(this._get(a,"firstDay"),10);h=isNaN(h)?0:h;j=this._get(a,"showWeek");r=this._get(a,"dayNames");this._get(a,"dayNamesShort");var s=this._get(a,"dayNamesMin"),A=this._get(a,"monthNames"),v=this._get(a,"monthNamesShort"),p=this._get(a,"beforeShowDay"),C=this._get(a,"showOtherMonths"),K=this._get(a,"selectOtherMonths");this._get(a,"calculateWeek");for(var E=this._getDefaultDate(a),w="",x=0;x1)switch(G){case 0:y+=" ui-datepicker-group-first";t=" ui-corner-"+(c?"right":"left");break;case i[1]-1:y+=" ui-datepicker-group-last";t=" ui-corner-"+(c?"left":"right");break;default:y+=" ui-datepicker-group-middle";t="";break}y+='">'}y+='
    '+(/all|left/.test(t)&&x==0?c? f:n:"")+(/all|right/.test(t)&&x==0?c?n:f:"")+this._generateMonthYearHeader(a,g,m,k,o,x>0||G>0,A,v)+'
    ';var D=j?'":"";for(t=0;t<7;t++){var q=(t+h)%7;D+="=5?' class="ui-datepicker-week-end"':"")+'>'+s[q]+""}y+=D+"";D=this._getDaysInMonth(m,g);if(m==a.selectedYear&&g==a.selectedMonth)a.selectedDay=Math.min(a.selectedDay, D);t=(this._getFirstDayOfMonth(m,g)-h+7)%7;D=l?6:Math.ceil((t+D)/7);q=this._daylightSavingAdjust(new Date(m,g,1-t));for(var Q=0;Q";var R=!j?"":'";for(t=0;t<7;t++){var I=p?p.apply(a.input?a.input[0]:null,[q]):[true,""],F=q.getMonth()!=g,L=F&&!K||!I[0]||k&&qo;R+='";q.setDate(q.getDate()+1);q=this._daylightSavingAdjust(q)}y+=R+""}g++;if(g>11){g=0;m++}y+="
    '+this._get(a,"weekHeader")+"
    '+this._get(a,"calculateWeek")(q)+""+(F&&!C?" ":L?''+q.getDate()+ "":''+q.getDate()+"")+"
    "+(l?""+(i[0]>0&&G==i[1]-1?'
    ':""):"");O+=y}w+=O}w+=e+(d.browser.msie&&parseInt(d.browser.version,10)<7&&!a.inline?'': "");a._keyEvent=false;return w},_generateMonthYearHeader:function(a,b,c,e,f,h,i,g){var j=this._get(a,"changeMonth"),l=this._get(a,"changeYear"),u=this._get(a,"showMonthAfterYear"),k='
    ',o="";if(h||!j)o+=''+i[b]+"";else{i=e&&e.getFullYear()==c;var m=f&&f.getFullYear()==c;o+='"}u||(k+=o+(h||!(j&&l)?" ":""));if(!a.yearshtml){a.yearshtml="";if(h||!l)k+=''+c+"";else{g=this._get(a,"yearRange").split(":");var r=(new Date).getFullYear();i=function(s){s=s.match(/c[+-].*/)?c+parseInt(s.substring(1),10):s.match(/[+-].*/)?r+parseInt(s,10):parseInt(s,10);return isNaN(s)? r:s};b=i(g[0]);g=Math.max(b,i(g[1]||""));b=e?Math.max(b,e.getFullYear()):b;g=f?Math.min(g,f.getFullYear()):g;for(a.yearshtml+='";k+=a.yearshtml;a.yearshtml=null}}k+=this._get(a,"yearSuffix");if(u)k+= (h||!(j&&l)?" ":"")+o;k+="
    ";return k},_adjustInstDate:function(a,b,c){var e=a.drawYear+(c=="Y"?b:0),f=a.drawMonth+(c=="M"?b:0);b=Math.min(a.selectedDay,this._getDaysInMonth(e,f))+(c=="D"?b:0);e=this._restrictMinMax(a,this._daylightSavingAdjust(new Date(e,f,b)));a.selectedDay=e.getDate();a.drawMonth=a.selectedMonth=e.getMonth();a.drawYear=a.selectedYear=e.getFullYear();if(c=="M"||c=="Y")this._notifyChange(a)},_restrictMinMax:function(a,b){var c=this._getMinMaxDate(a,"min");a=this._getMinMaxDate(a, "max");b=c&&ba?a:b},_notifyChange:function(a){var b=this._get(a,"onChangeMonthYear");if(b)b.apply(a.input?a.input[0]:null,[a.selectedYear,a.selectedMonth+1,a])},_getNumberOfMonths:function(a){a=this._get(a,"numberOfMonths");return a==null?[1,1]:typeof a=="number"?[1,a]:a},_getMinMaxDate:function(a,b){return this._determineDate(a,this._get(a,b+"Date"),null)},_getDaysInMonth:function(a,b){return 32-this._daylightSavingAdjust(new Date(a,b,32)).getDate()},_getFirstDayOfMonth:function(a, b){return(new Date(a,b,1)).getDay()},_canAdjustMonth:function(a,b,c,e){var f=this._getNumberOfMonths(a);c=this._daylightSavingAdjust(new Date(c,e+(b<0?b:f[0]*f[1]),1));b<0&&c.setDate(this._getDaysInMonth(c.getFullYear(),c.getMonth()));return this._isInRange(a,c)},_isInRange:function(a,b){var c=this._getMinMaxDate(a,"min");a=this._getMinMaxDate(a,"max");return(!c||b.getTime()>=c.getTime())&&(!a||b.getTime()<=a.getTime())},_getFormatConfig:function(a){var b=this._get(a,"shortYearCutoff");b=typeof b!= "string"?b:(new Date).getFullYear()%100+parseInt(b,10);return{shortYearCutoff:b,dayNamesShort:this._get(a,"dayNamesShort"),dayNames:this._get(a,"dayNames"),monthNamesShort:this._get(a,"monthNamesShort"),monthNames:this._get(a,"monthNames")}},_formatDate:function(a,b,c,e){if(!b){a.currentDay=a.selectedDay;a.currentMonth=a.selectedMonth;a.currentYear=a.selectedYear}b=b?typeof b=="object"?b:this._daylightSavingAdjust(new Date(e,c,b)):this._daylightSavingAdjust(new Date(a.currentYear,a.currentMonth,a.currentDay)); return this.formatDate(this._get(a,"dateFormat"),b,this._getFormatConfig(a))}});d.fn.datepicker=function(a){if(!this.length)return this;if(!d.datepicker.initialized){d(document).mousedown(d.datepicker._checkExternalClick).find("body").append(d.datepicker.dpDiv);d.datepicker.initialized=true}var b=Array.prototype.slice.call(arguments,1);if(typeof a=="string"&&(a=="isDisabled"||a=="getDate"||a=="widget"))return d.datepicker["_"+a+"Datepicker"].apply(d.datepicker,[this[0]].concat(b));if(a=="option"&& arguments.length==2&&typeof arguments[1]=="string")return d.datepicker["_"+a+"Datepicker"].apply(d.datepicker,[this[0]].concat(b));return this.each(function(){typeof a=="string"?d.datepicker["_"+a+"Datepicker"].apply(d.datepicker,[this].concat(b)):d.datepicker._attachDatepicker(this,a)})};d.datepicker=new M;d.datepicker.initialized=false;d.datepicker.uuid=(new Date).getTime();d.datepicker.version="1.8.13";window["DP_jQuery_"+z]=d})(jQuery); ;/* * jQuery UI Progressbar 1.8.13 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. * http://jquery.org/license * * http://docs.jquery.com/UI/Progressbar * * Depends: * jquery.ui.core.js * jquery.ui.widget.js */ (function(b,d){b.widget("ui.progressbar",{options:{value:0,max:100},min:0,_create:function(){this.element.addClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").attr({role:"progressbar","aria-valuemin":this.min,"aria-valuemax":this.options.max,"aria-valuenow":this._value()});this.valueDiv=b("
    ").appendTo(this.element);this.oldValue=this._value();this._refreshValue()},destroy:function(){this.element.removeClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").removeAttr("role").removeAttr("aria-valuemin").removeAttr("aria-valuemax").removeAttr("aria-valuenow"); this.valueDiv.remove();b.Widget.prototype.destroy.apply(this,arguments)},value:function(a){if(a===d)return this._value();this._setOption("value",a);return this},_setOption:function(a,c){if(a==="value"){this.options.value=c;this._refreshValue();this._value()===this.options.max&&this._trigger("complete")}b.Widget.prototype._setOption.apply(this,arguments)},_value:function(){var a=this.options.value;if(typeof a!=="number")a=0;return Math.min(this.options.max,Math.max(this.min,a))},_percentage:function(){return 100* this._value()/this.options.max},_refreshValue:function(){var a=this.value(),c=this._percentage();if(this.oldValue!==a){this.oldValue=a;this._trigger("change")}this.valueDiv.toggle(a>this.min).toggleClass("ui-corner-right",a===this.options.max).width(c.toFixed(0)+"%");this.element.attr("aria-valuenow",a)}});b.extend(b.ui.progressbar,{version:"1.8.13"})})(jQuery); ;/* * jQuery UI Effects 1.8.13 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. * http://jquery.org/license * * http://docs.jquery.com/UI/Effects/ */ jQuery.effects||function(f,j){function m(c){var a;if(c&&c.constructor==Array&&c.length==3)return c;if(a=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(c))return[parseInt(a[1],10),parseInt(a[2],10),parseInt(a[3],10)];if(a=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(c))return[parseFloat(a[1])*2.55,parseFloat(a[2])*2.55,parseFloat(a[3])*2.55];if(a=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(c))return[parseInt(a[1], 16),parseInt(a[2],16),parseInt(a[3],16)];if(a=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(c))return[parseInt(a[1]+a[1],16),parseInt(a[2]+a[2],16),parseInt(a[3]+a[3],16)];if(/rgba\(0, 0, 0, 0\)/.exec(c))return n.transparent;return n[f.trim(c).toLowerCase()]}function s(c,a){var b;do{b=f.curCSS(c,a);if(b!=""&&b!="transparent"||f.nodeName(c,"body"))break;a="backgroundColor"}while(c=c.parentNode);return m(b)}function o(){var c=document.defaultView?document.defaultView.getComputedStyle(this,null):this.currentStyle, a={},b,d;if(c&&c.length&&c[0]&&c[c[0]])for(var e=c.length;e--;){b=c[e];if(typeof c[b]=="string"){d=b.replace(/\-(\w)/g,function(g,h){return h.toUpperCase()});a[d]=c[b]}}else for(b in c)if(typeof c[b]==="string")a[b]=c[b];return a}function p(c){var a,b;for(a in c){b=c[a];if(b==null||f.isFunction(b)||a in t||/scrollbar/.test(a)||!/color/i.test(a)&&isNaN(parseFloat(b)))delete c[a]}return c}function u(c,a){var b={_:0},d;for(d in a)if(c[d]!=a[d])b[d]=a[d];return b}function k(c,a,b,d){if(typeof c=="object"){d= a;b=null;a=c;c=a.effect}if(f.isFunction(a)){d=a;b=null;a={}}if(typeof a=="number"||f.fx.speeds[a]){d=b;b=a;a={}}if(f.isFunction(b)){d=b;b=null}a=a||{};b=b||a.duration;b=f.fx.off?0:typeof b=="number"?b:b in f.fx.speeds?f.fx.speeds[b]:f.fx.speeds._default;d=d||a.complete;return[c,a,b,d]}function l(c){if(!c||typeof c==="number"||f.fx.speeds[c])return true;if(typeof c==="string"&&!f.effects[c])return true;return false}f.effects={};f.each(["backgroundColor","borderBottomColor","borderLeftColor","borderRightColor", "borderTopColor","borderColor","color","outlineColor"],function(c,a){f.fx.step[a]=function(b){if(!b.colorInit){b.start=s(b.elem,a);b.end=m(b.end);b.colorInit=true}b.elem.style[a]="rgb("+Math.max(Math.min(parseInt(b.pos*(b.end[0]-b.start[0])+b.start[0],10),255),0)+","+Math.max(Math.min(parseInt(b.pos*(b.end[1]-b.start[1])+b.start[1],10),255),0)+","+Math.max(Math.min(parseInt(b.pos*(b.end[2]-b.start[2])+b.start[2],10),255),0)+")"}});var n={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0, 0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211, 211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0],transparent:[255,255,255]},q=["add","remove","toggle"],t={border:1,borderBottom:1,borderColor:1,borderLeft:1,borderRight:1,borderTop:1,borderWidth:1,margin:1,padding:1};f.effects.animateClass=function(c,a,b, d){if(f.isFunction(b)){d=b;b=null}return this.queue(function(){var e=f(this),g=e.attr("style")||" ",h=p(o.call(this)),r,v=e.attr("class");f.each(q,function(w,i){c[i]&&e[i+"Class"](c[i])});r=p(o.call(this));e.attr("class",v);e.animate(u(h,r),{queue:false,duration:a,easding:b,complete:function(){f.each(q,function(w,i){c[i]&&e[i+"Class"](c[i])});if(typeof e.attr("style")=="object"){e.attr("style").cssText="";e.attr("style").cssText=g}else e.attr("style",g);d&&d.apply(this,arguments);f.dequeue(this)}})})}; f.fn.extend({_addClass:f.fn.addClass,addClass:function(c,a,b,d){return a?f.effects.animateClass.apply(this,[{add:c},a,b,d]):this._addClass(c)},_removeClass:f.fn.removeClass,removeClass:function(c,a,b,d){return a?f.effects.animateClass.apply(this,[{remove:c},a,b,d]):this._removeClass(c)},_toggleClass:f.fn.toggleClass,toggleClass:function(c,a,b,d,e){return typeof a=="boolean"||a===j?b?f.effects.animateClass.apply(this,[a?{add:c}:{remove:c},b,d,e]):this._toggleClass(c,a):f.effects.animateClass.apply(this, [{toggle:c},a,b,d])},switchClass:function(c,a,b,d,e){return f.effects.animateClass.apply(this,[{add:a,remove:c},b,d,e])}});f.extend(f.effects,{version:"1.8.13",save:function(c,a){for(var b=0;b").addClass("ui-effects-wrapper").css({fontSize:"100%",background:"transparent",border:"none",margin:0,padding:0}); c.wrap(b);b=c.parent();if(c.css("position")=="static"){b.css({position:"relative"});c.css({position:"relative"})}else{f.extend(a,{position:c.css("position"),zIndex:c.css("z-index")});f.each(["top","left","bottom","right"],function(d,e){a[e]=c.css(e);if(isNaN(parseInt(a[e],10)))a[e]="auto"});c.css({position:"relative",top:0,left:0,right:"auto",bottom:"auto"})}return b.css(a).show()},removeWrapper:function(c){if(c.parent().is(".ui-effects-wrapper"))return c.parent().replaceWith(c);return c},setTransition:function(c, a,b,d){d=d||{};f.each(a,function(e,g){unit=c.cssUnit(g);if(unit[0]>0)d[g]=unit[0]*b+unit[1]});return d}});f.fn.extend({effect:function(c){var a=k.apply(this,arguments),b={options:a[1],duration:a[2],callback:a[3]};a=b.options.mode;var d=f.effects[c];if(f.fx.off||!d)return a?this[a](b.duration,b.callback):this.each(function(){b.callback&&b.callback.call(this)});return d.call(this,b)},_show:f.fn.show,show:function(c){if(l(c))return this._show.apply(this,arguments);else{var a=k.apply(this,arguments); a[1].mode="show";return this.effect.apply(this,a)}},_hide:f.fn.hide,hide:function(c){if(l(c))return this._hide.apply(this,arguments);else{var a=k.apply(this,arguments);a[1].mode="hide";return this.effect.apply(this,a)}},__toggle:f.fn.toggle,toggle:function(c){if(l(c)||typeof c==="boolean"||f.isFunction(c))return this.__toggle.apply(this,arguments);else{var a=k.apply(this,arguments);a[1].mode="toggle";return this.effect.apply(this,a)}},cssUnit:function(c){var a=this.css(c),b=[];f.each(["em","px","%", "pt"],function(d,e){if(a.indexOf(e)>0)b=[parseFloat(a),e]});return b}});f.easing.jswing=f.easing.swing;f.extend(f.easing,{def:"easeOutQuad",swing:function(c,a,b,d,e){return f.easing[f.easing.def](c,a,b,d,e)},easeInQuad:function(c,a,b,d,e){return d*(a/=e)*a+b},easeOutQuad:function(c,a,b,d,e){return-d*(a/=e)*(a-2)+b},easeInOutQuad:function(c,a,b,d,e){if((a/=e/2)<1)return d/2*a*a+b;return-d/2*(--a*(a-2)-1)+b},easeInCubic:function(c,a,b,d,e){return d*(a/=e)*a*a+b},easeOutCubic:function(c,a,b,d,e){return d* ((a=a/e-1)*a*a+1)+b},easeInOutCubic:function(c,a,b,d,e){if((a/=e/2)<1)return d/2*a*a*a+b;return d/2*((a-=2)*a*a+2)+b},easeInQuart:function(c,a,b,d,e){return d*(a/=e)*a*a*a+b},easeOutQuart:function(c,a,b,d,e){return-d*((a=a/e-1)*a*a*a-1)+b},easeInOutQuart:function(c,a,b,d,e){if((a/=e/2)<1)return d/2*a*a*a*a+b;return-d/2*((a-=2)*a*a*a-2)+b},easeInQuint:function(c,a,b,d,e){return d*(a/=e)*a*a*a*a+b},easeOutQuint:function(c,a,b,d,e){return d*((a=a/e-1)*a*a*a*a+1)+b},easeInOutQuint:function(c,a,b,d,e){if((a/= e/2)<1)return d/2*a*a*a*a*a+b;return d/2*((a-=2)*a*a*a*a+2)+b},easeInSine:function(c,a,b,d,e){return-d*Math.cos(a/e*(Math.PI/2))+d+b},easeOutSine:function(c,a,b,d,e){return d*Math.sin(a/e*(Math.PI/2))+b},easeInOutSine:function(c,a,b,d,e){return-d/2*(Math.cos(Math.PI*a/e)-1)+b},easeInExpo:function(c,a,b,d,e){return a==0?b:d*Math.pow(2,10*(a/e-1))+b},easeOutExpo:function(c,a,b,d,e){return a==e?b+d:d*(-Math.pow(2,-10*a/e)+1)+b},easeInOutExpo:function(c,a,b,d,e){if(a==0)return b;if(a==e)return b+d;if((a/= e/2)<1)return d/2*Math.pow(2,10*(a-1))+b;return d/2*(-Math.pow(2,-10*--a)+2)+b},easeInCirc:function(c,a,b,d,e){return-d*(Math.sqrt(1-(a/=e)*a)-1)+b},easeOutCirc:function(c,a,b,d,e){return d*Math.sqrt(1-(a=a/e-1)*a)+b},easeInOutCirc:function(c,a,b,d,e){if((a/=e/2)<1)return-d/2*(Math.sqrt(1-a*a)-1)+b;return d/2*(Math.sqrt(1-(a-=2)*a)+1)+b},easeInElastic:function(c,a,b,d,e){c=1.70158;var g=0,h=d;if(a==0)return b;if((a/=e)==1)return b+d;g||(g=e*0.3);if(h").css({position:"absolute",visibility:"visible",left:-f*(h/d),top:-e*(i/c)}).parent().addClass("ui-effects-explode").css({position:"absolute",overflow:"hidden",width:h/d,height:i/c,left:g.left+f*(h/d)+(a.options.mode=="show"?(f-Math.floor(d/2))*(h/d):0),top:g.top+e*(i/c)+(a.options.mode=="show"?(e-Math.floor(c/2))*(i/c):0),opacity:a.options.mode=="show"?0:1}).animate({left:g.left+f*(h/d)+(a.options.mode=="show"?0:(f-Math.floor(d/2))*(h/d)),top:g.top+ e*(i/c)+(a.options.mode=="show"?0:(e-Math.floor(c/2))*(i/c)),opacity:a.options.mode=="show"?1:0},a.duration||500);setTimeout(function(){a.options.mode=="show"?b.css({visibility:"visible"}):b.css({visibility:"visible"}).hide();a.callback&&a.callback.apply(b[0]);b.dequeue();j("div.ui-effects-explode").remove()},a.duration||500)})}})(jQuery); ;/* * jQuery UI Effects Fade 1.8.13 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. * http://jquery.org/license * * http://docs.jquery.com/UI/Effects/Fade * * Depends: * jquery.effects.core.js */ (function(b){b.effects.fade=function(a){return this.queue(function(){var c=b(this),d=b.effects.setMode(c,a.options.mode||"hide");c.animate({opacity:d},{queue:false,duration:a.duration,easing:a.options.easing,complete:function(){a.callback&&a.callback.apply(this,arguments);c.dequeue()}})})}})(jQuery); ;/* * jQuery UI Effects Fold 1.8.13 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. * http://jquery.org/license * * http://docs.jquery.com/UI/Effects/Fold * * Depends: * jquery.effects.core.js */ (function(c){c.effects.fold=function(a){return this.queue(function(){var b=c(this),j=["position","top","bottom","left","right"],d=c.effects.setMode(b,a.options.mode||"hide"),g=a.options.size||15,h=!!a.options.horizFirst,k=a.duration?a.duration/2:c.fx.speeds._default/2;c.effects.save(b,j);b.show();var e=c.effects.createWrapper(b).css({overflow:"hidden"}),f=d=="show"!=h,l=f?["width","height"]:["height","width"];f=f?[e.width(),e.height()]:[e.height(),e.width()];var i=/([0-9]+)%/.exec(g);if(i)g=parseInt(i[1], 10)/100*f[d=="hide"?0:1];if(d=="show")e.css(h?{height:0,width:g}:{height:g,width:0});h={};i={};h[l[0]]=d=="show"?f[0]:g;i[l[1]]=d=="show"?f[1]:0;e.animate(h,k,a.options.easing).animate(i,k,a.options.easing,function(){d=="hide"&&b.hide();c.effects.restore(b,j);c.effects.removeWrapper(b);a.callback&&a.callback.apply(b[0],arguments);b.dequeue()})})}})(jQuery); ;/* * jQuery UI Effects Highlight 1.8.13 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. * http://jquery.org/license * * http://docs.jquery.com/UI/Effects/Highlight * * Depends: * jquery.effects.core.js */ (function(b){b.effects.highlight=function(c){return this.queue(function(){var a=b(this),e=["backgroundImage","backgroundColor","opacity"],d=b.effects.setMode(a,c.options.mode||"show"),f={backgroundColor:a.css("backgroundColor")};if(d=="hide")f.opacity=0;b.effects.save(a,e);a.show().css({backgroundImage:"none",backgroundColor:c.options.color||"#ffff99"}).animate(f,{queue:false,duration:c.duration,easing:c.options.easing,complete:function(){d=="hide"&&a.hide();b.effects.restore(a,e);d=="show"&&!b.support.opacity&& this.style.removeAttribute("filter");c.callback&&c.callback.apply(this,arguments);a.dequeue()}})})}})(jQuery); ;/* * jQuery UI Effects Pulsate 1.8.13 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. * http://jquery.org/license * * http://docs.jquery.com/UI/Effects/Pulsate * * Depends: * jquery.effects.core.js */ (function(d){d.effects.pulsate=function(a){return this.queue(function(){var b=d(this),c=d.effects.setMode(b,a.options.mode||"show");times=(a.options.times||5)*2-1;duration=a.duration?a.duration/2:d.fx.speeds._default/2;isVisible=b.is(":visible");animateTo=0;if(!isVisible){b.css("opacity",0).show();animateTo=1}if(c=="hide"&&isVisible||c=="show"&&!isVisible)times--;for(c=0;c').appendTo(document.body).addClass(a.options.className).css({top:d.top,left:d.left,height:b.innerHeight(),width:b.innerWidth(),position:"absolute"}).animate(c,a.duration,a.options.easing,function(){f.remove();a.callback&&a.callback.apply(b[0],arguments); b.dequeue()})})}})(jQuery); ;testman4trac.1.4.5/testman4trac/trunk/testmanager/htdocs/js/it.js0000755000175000017500000000136611566010075025017 0ustar robertoroberto// Generated messages javascript file from compiled MO file babel.Translations.load({"domain":"testmanager-js","locale":"it","messages":{"Are you sure you want to delete the test plan and the state of all its contained test cases?":"Sei sicuro di voler eliminare il piano di test e lo stato di tutti i test case in esso contenuti?","Are you sure you want to duplicate the test catalog and all its contained test cases?":"Sei sicuro di voler duplicare il catalogo e tutti i test case in esso contenuti?","Length between 4 and 90 characters.":"Lunghezza da 4 a 90 caratteri.","Results: ":"Risultati: ","You must specify a name. Length between 4 and 90 characters.":"Devi indicare un nome. Lunghezza da 4 a 90 caratteri."},"plural_expr":"(n != 1)"}).install(); testman4trac.1.4.5/testman4trac/trunk/testmanager/htdocs/js/jquery-1.5.1.min.js0000744000175000017500000024641411540574020027144 0ustar robertoroberto/*! * jQuery JavaScript Library v1.5.1 * http://jquery.com/ * * Copyright 2011, John Resig * Dual licensed under the MIT or GPL Version 2 licenses. * http://jquery.org/license * * Includes Sizzle.js * http://sizzlejs.com/ * Copyright 2011, The Dojo Foundation * Released under the MIT, BSD, and GPL Licenses. * * Date: Wed Feb 23 13:55:29 2011 -0500 */ (function(a,b){function cg(a){return d.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cd(a){if(!bZ[a]){var b=d("<"+a+">").appendTo("body"),c=b.css("display");b.remove();if(c==="none"||c==="")c="block";bZ[a]=c}return bZ[a]}function cc(a,b){var c={};d.each(cb.concat.apply([],cb.slice(0,b)),function(){c[this]=a});return c}function bY(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function bX(){try{return new a.XMLHttpRequest}catch(b){}}function bW(){d(a).unload(function(){for(var a in bU)bU[a](0,1)})}function bQ(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var e=a.dataTypes,f={},g,h,i=e.length,j,k=e[0],l,m,n,o,p;for(g=1;g=0===c})}function N(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function F(a,b){return(a&&a!=="*"?a+".":"")+b.replace(r,"`").replace(s,"&")}function E(a){var b,c,e,f,g,h,i,j,k,l,m,n,o,q=[],r=[],s=d._data(this,"events");if(a.liveFired!==this&&s&&s.live&&!a.target.disabled&&(!a.button||a.type!=="click")){a.namespace&&(n=new RegExp("(^|\\.)"+a.namespace.split(".").join("\\.(?:.*\\.)?")+"(\\.|$)")),a.liveFired=this;var t=s.live.slice(0);for(i=0;ic)break;a.currentTarget=f.elem,a.data=f.handleObj.data,a.handleObj=f.handleObj,o=f.handleObj.origHandler.apply(f.elem,arguments);if(o===!1||a.isPropagationStopped()){c=f.level,o===!1&&(b=!1);if(a.isImmediatePropagationStopped())break}}return b}}function C(a,c,e){var f=d.extend({},e[0]);f.type=a,f.originalEvent={},f.liveFired=b,d.event.handle.call(c,f),f.isDefaultPrevented()&&e[0].preventDefault()}function w(){return!0}function v(){return!1}function g(a){for(var b in a)if(b!=="toJSON")return!1;return!0}function f(a,c,f){if(f===b&&a.nodeType===1){f=a.getAttribute("data-"+c);if(typeof f==="string"){try{f=f==="true"?!0:f==="false"?!1:f==="null"?null:d.isNaN(f)?e.test(f)?d.parseJSON(f):f:parseFloat(f)}catch(g){}d.data(a,c,f)}else f=b}return f}var c=a.document,d=function(){function I(){if(!d.isReady){try{c.documentElement.doScroll("left")}catch(a){setTimeout(I,1);return}d.ready()}}var d=function(a,b){return new d.fn.init(a,b,g)},e=a.jQuery,f=a.$,g,h=/^(?:[^<]*(<[\w\W]+>)[^>]*$|#([\w\-]+)$)/,i=/\S/,j=/^\s+/,k=/\s+$/,l=/\d/,m=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,n=/^[\],:{}\s]*$/,o=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,p=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,q=/(?:^|:|,)(?:\s*\[)+/g,r=/(webkit)[ \/]([\w.]+)/,s=/(opera)(?:.*version)?[ \/]([\w.]+)/,t=/(msie) ([\w.]+)/,u=/(mozilla)(?:.*? rv:([\w.]+))?/,v=navigator.userAgent,w,x=!1,y,z="then done fail isResolved isRejected promise".split(" "),A,B=Object.prototype.toString,C=Object.prototype.hasOwnProperty,D=Array.prototype.push,E=Array.prototype.slice,F=String.prototype.trim,G=Array.prototype.indexOf,H={};d.fn=d.prototype={constructor:d,init:function(a,e,f){var g,i,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!e&&c.body){this.context=c,this[0]=c.body,this.selector="body",this.length=1;return this}if(typeof a==="string"){g=h.exec(a);if(!g||!g[1]&&e)return!e||e.jquery?(e||f).find(a):this.constructor(e).find(a);if(g[1]){e=e instanceof d?e[0]:e,k=e?e.ownerDocument||e:c,j=m.exec(a),j?d.isPlainObject(e)?(a=[c.createElement(j[1])],d.fn.attr.call(a,e,!0)):a=[k.createElement(j[1])]:(j=d.buildFragment([g[1]],[k]),a=(j.cacheable?d.clone(j.fragment):j.fragment).childNodes);return d.merge(this,a)}i=c.getElementById(g[2]);if(i&&i.parentNode){if(i.id!==g[2])return f.find(a);this.length=1,this[0]=i}this.context=c,this.selector=a;return this}if(d.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return d.makeArray(a,this)},selector:"",jquery:"1.5.1",length:0,size:function(){return this.length},toArray:function(){return E.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var e=this.constructor();d.isArray(a)?D.apply(e,a):d.merge(e,a),e.prevObject=this,e.context=this.context,b==="find"?e.selector=this.selector+(this.selector?" ":"")+c:b&&(e.selector=this.selector+"."+b+"("+c+")");return e},each:function(a,b){return d.each(this,a,b)},ready:function(a){d.bindReady(),y.done(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(E.apply(this,arguments),"slice",E.call(arguments).join(","))},map:function(a){return this.pushStack(d.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:D,sort:[].sort,splice:[].splice},d.fn.init.prototype=d.fn,d.extend=d.fn.extend=function(){var a,c,e,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i==="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!=="object"&&!d.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j0)return;y.resolveWith(c,[d]),d.fn.trigger&&d(c).trigger("ready").unbind("ready")}},bindReady:function(){if(!x){x=!0;if(c.readyState==="complete")return setTimeout(d.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",A,!1),a.addEventListener("load",d.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",A),a.attachEvent("onload",d.ready);var b=!1;try{b=a.frameElement==null}catch(e){}c.documentElement.doScroll&&b&&I()}}},isFunction:function(a){return d.type(a)==="function"},isArray:Array.isArray||function(a){return d.type(a)==="array"},isWindow:function(a){return a&&typeof a==="object"&&"setInterval"in a},isNaN:function(a){return a==null||!l.test(a)||isNaN(a)},type:function(a){return a==null?String(a):H[B.call(a)]||"object"},isPlainObject:function(a){if(!a||d.type(a)!=="object"||a.nodeType||d.isWindow(a))return!1;if(a.constructor&&!C.call(a,"constructor")&&!C.call(a.constructor.prototype,"isPrototypeOf"))return!1;var c;for(c in a){}return c===b||C.call(a,c)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw a},parseJSON:function(b){if(typeof b!=="string"||!b)return null;b=d.trim(b);if(n.test(b.replace(o,"@").replace(p,"]").replace(q,"")))return a.JSON&&a.JSON.parse?a.JSON.parse(b):(new Function("return "+b))();d.error("Invalid JSON: "+b)},parseXML:function(b,c,e){a.DOMParser?(e=new DOMParser,c=e.parseFromString(b,"text/xml")):(c=new ActiveXObject("Microsoft.XMLDOM"),c.async="false",c.loadXML(b)),e=c.documentElement,(!e||!e.nodeName||e.nodeName==="parsererror")&&d.error("Invalid XML: "+b);return c},noop:function(){},globalEval:function(a){if(a&&i.test(a)){var b=c.head||c.getElementsByTagName("head")[0]||c.documentElement,e=c.createElement("script");d.support.scriptEval()?e.appendChild(c.createTextNode(a)):e.text=a,b.insertBefore(e,b.firstChild),b.removeChild(e)}},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,e){var f,g=0,h=a.length,i=h===b||d.isFunction(a);if(e){if(i){for(f in a)if(c.apply(a[f],e)===!1)break}else for(;g1){var f=E.call(arguments,0),g=b,h=function(a){return function(b){f[a]=arguments.length>1?E.call(arguments,0):b,--g||c.resolveWith(e,f)}};while(b--)a=f[b],a&&d.isFunction(a.promise)?a.promise().then(h(b),c.reject):--g;g||c.resolveWith(e,f)}else c!==a&&c.resolve(a);return e},uaMatch:function(a){a=a.toLowerCase();var b=r.exec(a)||s.exec(a)||t.exec(a)||a.indexOf("compatible")<0&&u.exec(a)||[];return{browser:b[1]||"",version:b[2]||"0"}},sub:function(){function a(b,c){return new a.fn.init(b,c)}d.extend(!0,a,this),a.superclass=this,a.fn=a.prototype=this(),a.fn.constructor=a,a.subclass=this.subclass,a.fn.init=function b(b,c){c&&c instanceof d&&!(c instanceof a)&&(c=a(c));return d.fn.init.call(this,b,c,e)},a.fn.init.prototype=a.fn;var e=a(c);return a},browser:{}}),y=d._Deferred(),d.each("Boolean Number String Function Array Date RegExp Object".split(" "),function(a,b){H["[object "+b+"]"]=b.toLowerCase()}),w=d.uaMatch(v),w.browser&&(d.browser[w.browser]=!0,d.browser.version=w.version),d.browser.webkit&&(d.browser.safari=!0),G&&(d.inArray=function(a,b){return G.call(b,a)}),i.test(" ")&&(j=/^[\s\xA0]+/,k=/[\s\xA0]+$/),g=d(c),c.addEventListener?A=function(){c.removeEventListener("DOMContentLoaded",A,!1),d.ready()}:c.attachEvent&&(A=function(){c.readyState==="complete"&&(c.detachEvent("onreadystatechange",A),d.ready())});return d}();(function(){d.support={};var b=c.createElement("div");b.style.display="none",b.innerHTML="
    a";var e=b.getElementsByTagName("*"),f=b.getElementsByTagName("a")[0],g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=b.getElementsByTagName("input")[0];if(e&&e.length&&f){d.support={leadingWhitespace:b.firstChild.nodeType===3,tbody:!b.getElementsByTagName("tbody").length,htmlSerialize:!!b.getElementsByTagName("link").length,style:/red/.test(f.getAttribute("style")),hrefNormalized:f.getAttribute("href")==="/a",opacity:/^0.55$/.test(f.style.opacity),cssFloat:!!f.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,deleteExpando:!0,optDisabled:!1,checkClone:!1,noCloneEvent:!0,noCloneChecked:!0,boxModel:null,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableHiddenOffsets:!0},i.checked=!0,d.support.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,d.support.optDisabled=!h.disabled;var j=null;d.support.scriptEval=function(){if(j===null){var b=c.documentElement,e=c.createElement("script"),f="script"+d.now();try{e.appendChild(c.createTextNode("window."+f+"=1;"))}catch(g){}b.insertBefore(e,b.firstChild),a[f]?(j=!0,delete a[f]):j=!1,b.removeChild(e),b=e=f=null}return j};try{delete b.test}catch(k){d.support.deleteExpando=!1}!b.addEventListener&&b.attachEvent&&b.fireEvent&&(b.attachEvent("onclick",function l(){d.support.noCloneEvent=!1,b.detachEvent("onclick",l)}),b.cloneNode(!0).fireEvent("onclick")),b=c.createElement("div"),b.innerHTML="";var m=c.createDocumentFragment();m.appendChild(b.firstChild),d.support.checkClone=m.cloneNode(!0).cloneNode(!0).lastChild.checked,d(function(){var a=c.createElement("div"),b=c.getElementsByTagName("body")[0];if(b){a.style.width=a.style.paddingLeft="1px",b.appendChild(a),d.boxModel=d.support.boxModel=a.offsetWidth===2,"zoom"in a.style&&(a.style.display="inline",a.style.zoom=1,d.support.inlineBlockNeedsLayout=a.offsetWidth===2,a.style.display="",a.innerHTML="
    ",d.support.shrinkWrapBlocks=a.offsetWidth!==2),a.innerHTML="
    t
    ";var e=a.getElementsByTagName("td");d.support.reliableHiddenOffsets=e[0].offsetHeight===0,e[0].style.display="",e[1].style.display="none",d.support.reliableHiddenOffsets=d.support.reliableHiddenOffsets&&e[0].offsetHeight===0,a.innerHTML="",b.removeChild(a).style.display="none",a=e=null}});var n=function(a){var b=c.createElement("div");a="on"+a;if(!b.attachEvent)return!0;var d=a in b;d||(b.setAttribute(a,"return;"),d=typeof b[a]==="function"),b=null;return d};d.support.submitBubbles=n("submit"),d.support.changeBubbles=n("change"),b=e=f=null}})();var e=/^(?:\{.*\}|\[.*\])$/;d.extend({cache:{},uuid:0,expando:"jQuery"+(d.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?d.cache[a[d.expando]]:a[d.expando];return!!a&&!g(a)},data:function(a,c,e,f){if(d.acceptData(a)){var g=d.expando,h=typeof c==="string",i,j=a.nodeType,k=j?d.cache:a,l=j?a[d.expando]:a[d.expando]&&d.expando;if((!l||f&&l&&!k[l][g])&&h&&e===b)return;l||(j?a[d.expando]=l=++d.uuid:l=d.expando),k[l]||(k[l]={},j||(k[l].toJSON=d.noop));if(typeof c==="object"||typeof c==="function")f?k[l][g]=d.extend(k[l][g],c):k[l]=d.extend(k[l],c);i=k[l],f&&(i[g]||(i[g]={}),i=i[g]),e!==b&&(i[c]=e);if(c==="events"&&!i[c])return i[g]&&i[g].events;return h?i[c]:i}},removeData:function(b,c,e){if(d.acceptData(b)){var f=d.expando,h=b.nodeType,i=h?d.cache:b,j=h?b[d.expando]:d.expando;if(!i[j])return;if(c){var k=e?i[j][f]:i[j];if(k){delete k[c];if(!g(k))return}}if(e){delete i[j][f];if(!g(i[j]))return}var l=i[j][f];d.support.deleteExpando||i!=a?delete i[j]:i[j]=null,l?(i[j]={},h||(i[j].toJSON=d.noop),i[j][f]=l):h&&(d.support.deleteExpando?delete b[d.expando]:b.removeAttribute?b.removeAttribute(d.expando):b[d.expando]=null)}},_data:function(a,b,c){return d.data(a,b,c,!0)},acceptData:function(a){if(a.nodeName){var b=d.noData[a.nodeName.toLowerCase()];if(b)return b!==!0&&a.getAttribute("classid")===b}return!0}}),d.fn.extend({data:function(a,c){var e=null;if(typeof a==="undefined"){if(this.length){e=d.data(this[0]);if(this[0].nodeType===1){var g=this[0].attributes,h;for(var i=0,j=g.length;i-1)return!0;return!1},val:function(a){if(!arguments.length){var c=this[0];if(c){if(d.nodeName(c,"option")){var e=c.attributes.value;return!e||e.specified?c.value:c.text}if(d.nodeName(c,"select")){var f=c.selectedIndex,g=[],h=c.options,i=c.type==="select-one";if(f<0)return null;for(var k=i?f:0,l=i?f+1:h.length;k=0;else if(d.nodeName(this,"select")){var f=d.makeArray(e);d("option",this).each(function(){this.selected=d.inArray(d(this).val(),f)>=0}),f.length||(this.selectedIndex=-1)}else this.value=e}})}}),d.extend({attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attr:function(a,c,e,f){if(!a||a.nodeType===3||a.nodeType===8||a.nodeType===2)return b;if(f&&c in d.attrFn)return d(a)[c](e);var g=a.nodeType!==1||!d.isXMLDoc(a),h=e!==b;c=g&&d.props[c]||c;if(a.nodeType===1){var i=k.test(c);if(c==="selected"&&!d.support.optSelected){var j=a.parentNode;j&&(j.selectedIndex,j.parentNode&&j.parentNode.selectedIndex)}if((c in a||a[c]!==b)&&g&&!i){h&&(c==="type"&&l.test(a.nodeName)&&a.parentNode&&d.error("type property can't be changed"),e===null?a.nodeType===1&&a.removeAttribute(c):a[c]=e);if(d.nodeName(a,"form")&&a.getAttributeNode(c))return a.getAttributeNode(c).nodeValue;if(c==="tabIndex"){var o=a.getAttributeNode("tabIndex");return o&&o.specified?o.value:m.test(a.nodeName)||n.test(a.nodeName)&&a.href?0:b}return a[c]}if(!d.support.style&&g&&c==="style"){h&&(a.style.cssText=""+e);return a.style.cssText}h&&a.setAttribute(c,""+e);if(!a.attributes[c]&&(a.hasAttribute&&!a.hasAttribute(c)))return b;var p=!d.support.hrefNormalized&&g&&i?a.getAttribute(c,2):a.getAttribute(c);return p===null?b:p}h&&(a[c]=e);return a[c]}});var p=/\.(.*)$/,q=/^(?:textarea|input|select)$/i,r=/\./g,s=/ /g,t=/[^\w\s.|`]/g,u=function(a){return a.replace(t,"\\$&")};d.event={add:function(c,e,f,g){if(c.nodeType!==3&&c.nodeType!==8){try{d.isWindow(c)&&(c!==a&&!c.frameElement)&&(c=a)}catch(h){}if(f===!1)f=v;else if(!f)return;var i,j;f.handler&&(i=f,f=i.handler),f.guid||(f.guid=d.guid++);var k=d._data(c);if(!k)return;var l=k.events,m=k.handle;l||(k.events=l={}),m||(k.handle=m=function(){return typeof d!=="undefined"&&!d.event.triggered?d.event.handle.apply(m.elem,arguments):b}),m.elem=c,e=e.split(" ");var n,o=0,p;while(n=e[o++]){j=i?d.extend({},i):{handler:f,data:g},n.indexOf(".")>-1?(p=n.split("."),n=p.shift(),j.namespace=p.slice(0).sort().join(".")):(p=[],j.namespace=""),j.type=n,j.guid||(j.guid=f.guid);var q=l[n],r=d.event.special[n]||{};if(!q){q=l[n]=[];if(!r.setup||r.setup.call(c,g,p,m)===!1)c.addEventListener?c.addEventListener(n,m,!1):c.attachEvent&&c.attachEvent("on"+n,m)}r.add&&(r.add.call(c,j),j.handler.guid||(j.handler.guid=f.guid)),q.push(j),d.event.global[n]=!0}c=null}},global:{},remove:function(a,c,e,f){if(a.nodeType!==3&&a.nodeType!==8){e===!1&&(e=v);var g,h,i,j,k=0,l,m,n,o,p,q,r,s=d.hasData(a)&&d._data(a),t=s&&s.events;if(!s||!t)return;c&&c.type&&(e=c.handler,c=c.type);if(!c||typeof c==="string"&&c.charAt(0)==="."){c=c||"";for(h in t)d.event.remove(a,h+c);return}c=c.split(" ");while(h=c[k++]){r=h,q=null,l=h.indexOf(".")<0,m=[],l||(m=h.split("."),h=m.shift(),n=new RegExp("(^|\\.)"+d.map(m.slice(0).sort(),u).join("\\.(?:.*\\.)?")+"(\\.|$)")),p=t[h];if(!p)continue;if(!e){for(j=0;j=0&&(a.type=f=f.slice(0,-1),a.exclusive=!0),e||(a.stopPropagation(),d.event.global[f]&&d.each(d.cache,function(){var b=d.expando,e=this[b];e&&e.events&&e.events[f]&&d.event.trigger(a,c,e.handle.elem)}));if(!e||e.nodeType===3||e.nodeType===8)return b;a.result=b,a.target=e,c=d.makeArray(c),c.unshift(a)}a.currentTarget=e;var h=d._data(e,"handle");h&&h.apply(e,c);var i=e.parentNode||e.ownerDocument;try{e&&e.nodeName&&d.noData[e.nodeName.toLowerCase()]||e["on"+f]&&e["on"+f].apply(e,c)===!1&&(a.result=!1,a.preventDefault())}catch(j){}if(!a.isPropagationStopped()&&i)d.event.trigger(a,c,i,!0);else if(!a.isDefaultPrevented()){var k,l=a.target,m=f.replace(p,""),n=d.nodeName(l,"a")&&m==="click",o=d.event.special[m]||{};if((!o._default||o._default.call(e,a)===!1)&&!n&&!(l&&l.nodeName&&d.noData[l.nodeName.toLowerCase()])){try{l[m]&&(k=l["on"+m],k&&(l["on"+m]=null),d.event.triggered=!0,l[m]())}catch(q){}k&&(l["on"+m]=k),d.event.triggered=!1}}},handle:function(c){var e,f,g,h,i,j=[],k=d.makeArray(arguments);c=k[0]=d.event.fix(c||a.event),c.currentTarget=this,e=c.type.indexOf(".")<0&&!c.exclusive,e||(g=c.type.split("."),c.type=g.shift(),j=g.slice(0).sort(),h=new RegExp("(^|\\.)"+j.join("\\.(?:.*\\.)?")+"(\\.|$)")),c.namespace=c.namespace||j.join("."),i=d._data(this,"events"),f=(i||{})[c.type];if(i&&f){f=f.slice(0);for(var l=0,m=f.length;l-1?d.map(a.options,function(a){return a.selected}).join("-"):"":a.nodeName.toLowerCase()==="select"&&(c=a.selectedIndex);return c},B=function B(a){var c=a.target,e,f;if(q.test(c.nodeName)&&!c.readOnly){e=d._data(c,"_change_data"),f=A(c),(a.type!=="focusout"||c.type!=="radio")&&d._data(c,"_change_data",f);if(e===b||f===e)return;if(e!=null||f)a.type="change",a.liveFired=b,d.event.trigger(a,arguments[1],c)}};d.event.special.change={filters:{focusout:B,beforedeactivate:B,click:function(a){var b=a.target,c=b.type;(c==="radio"||c==="checkbox"||b.nodeName.toLowerCase()==="select")&&B.call(this,a)},keydown:function(a){var b=a.target,c=b.type;(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(c==="checkbox"||c==="radio")||c==="select-multiple")&&B.call(this,a)},beforeactivate:function(a){var b=a.target;d._data(b,"_change_data",A(b))}},setup:function(a,b){if(this.type==="file")return!1;for(var c in z)d.event.add(this,c+".specialChange",z[c]);return q.test(this.nodeName)},teardown:function(a){d.event.remove(this,".specialChange");return q.test(this.nodeName)}},z=d.event.special.change.filters,z.focus=z.beforeactivate}c.addEventListener&&d.each({focus:"focusin",blur:"focusout"},function(a,b){function c(a){a=d.event.fix(a),a.type=b;return d.event.handle.call(this,a)}d.event.special[b]={setup:function(){this.addEventListener(a,c,!0)},teardown:function(){this.removeEventListener(a,c,!0)}}}),d.each(["bind","one"],function(a,c){d.fn[c]=function(a,e,f){if(typeof a==="object"){for(var g in a)this[c](g,e,a[g],f);return this}if(d.isFunction(e)||e===!1)f=e,e=b;var h=c==="one"?d.proxy(f,function(a){d(this).unbind(a,h);return f.apply(this,arguments)}):f;if(a==="unload"&&c!=="one")this.one(a,e,f);else for(var i=0,j=this.length;i0?this.bind(b,a,c):this.trigger(b)},d.attrFn&&(d.attrFn[b]=!0)}),function(){function u(a,b,c,d,e,f){for(var g=0,h=d.length;g0){j=i;break}}i=i[a]}d[g]=j}}}function t(a,b,c,d,e,f){for(var g=0,h=d.length;g+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,e=0,f=Object.prototype.toString,g=!1,h=!0,i=/\\/g,j=/\W/;[0,0].sort(function(){h=!1;return 0});var k=function(b,d,e,g){e=e||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!=="string")return e;var i,j,n,o,q,r,s,t,u=!0,w=k.isXML(d),x=[],y=b;do{a.exec(""),i=a.exec(y);if(i){y=i[3],x.push(i[1]);if(i[2]){o=i[3];break}}}while(i);if(x.length>1&&m.exec(b))if(x.length===2&&l.relative[x[0]])j=v(x[0]+x[1],d);else{j=l.relative[x[0]]?[d]:k(x.shift(),d);while(x.length)b=x.shift(),l.relative[b]&&(b+=x.shift()),j=v(b,j)}else{!g&&x.length>1&&d.nodeType===9&&!w&&l.match.ID.test(x[0])&&!l.match.ID.test(x[x.length-1])&&(q=k.find(x.shift(),d,w),d=q.expr?k.filter(q.expr,q.set)[0]:q.set[0]);if(d){q=g?{expr:x.pop(),set:p(g)}:k.find(x.pop(),x.length===1&&(x[0]==="~"||x[0]==="+")&&d.parentNode?d.parentNode:d,w),j=q.expr?k.filter(q.expr,q.set):q.set,x.length>0?n=p(j):u=!1;while(x.length)r=x.pop(),s=r,l.relative[r]?s=x.pop():r="",s==null&&(s=d),l.relative[r](n,s,w)}else n=x=[]}n||(n=j),n||k.error(r||b);if(f.call(n)==="[object Array]")if(u)if(d&&d.nodeType===1)for(t=0;n[t]!=null;t++)n[t]&&(n[t]===!0||n[t].nodeType===1&&k.contains(d,n[t]))&&e.push(j[t]);else for(t=0;n[t]!=null;t++)n[t]&&n[t].nodeType===1&&e.push(j[t]);else e.push.apply(e,n);else p(n,e);o&&(k(o,h,e,g),k.uniqueSort(e));return e};k.uniqueSort=function(a){if(r){g=h,a.sort(r);if(g)for(var b=1;b0},k.find=function(a,b,c){var d;if(!a)return[];for(var e=0,f=l.order.length;e":function(a,b){var c,d=typeof b==="string",e=0,f=a.length;if(d&&!j.test(b)){b=b.toLowerCase();for(;e=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(i,"")},TAG:function(a,b){return a[1].replace(i,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||k.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&k.error(a[0]);a[0]=e++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(i,"");!f&&l.attrMap[g]&&(a[1]=l.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(i,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=k(b[3],null,null,c);else{var g=k.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(l.match.POS.test(b[0])||l.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!k(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){return"text"===a.getAttribute("type")},radio:function(a){return"radio"===a.type},checkbox:function(a){return"checkbox"===a.type},file:function(a){return"file"===a.type},password:function(a){return"password"===a.type},submit:function(a){return"submit"===a.type},image:function(a){return"image"===a.type},reset:function(a){return"reset"===a.type},button:function(a){return"button"===a.type||a.nodeName.toLowerCase()==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return bc[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=l.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||k.getText([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=l.attrHandle[c]?l.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=l.setFilters[e];if(f)return f(a,c,b,d)}}},m=l.match.POS,n=function(a,b){return"\\"+(b-0+1)};for(var o in l.match)l.match[o]=new RegExp(l.match[o].source+/(?![^\[]*\])(?![^\(]*\))/.source),l.leftMatch[o]=new RegExp(/(^(?:.|\r|\n)*?)/.source+l.match[o].source.replace(/\\(\d+)/g,n));var p=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(q){p=function(a,b){var c=0,d=b||[];if(f.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length==="number")for(var e=a.length;c",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(l.find.ID=function(a,c,d){if(typeof c.getElementById!=="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!=="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},l.filter.ID=function(a,b){var c=typeof a.getAttributeNode!=="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(l.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="",a.firstChild&&typeof a.firstChild.getAttribute!=="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(l.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=k,b=c.createElement("div"),d="__sizzle__";b.innerHTML="

    ";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){k=function(b,e,f,g){e=e||c;if(!g&&!k.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return p(e.getElementsByTagName(b),f);if(h[2]&&l.find.CLASS&&e.getElementsByClassName)return p(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return p([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return p([],f);if(i.id===h[3])return p([i],f)}try{return p(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var m=e,n=e.getAttribute("id"),o=n||d,q=e.parentNode,r=/^\s*[+~]/.test(b);n?o=o.replace(/'/g,"\\$&"):e.setAttribute("id",o),r&&q&&(e=e.parentNode);try{if(!r||q)return p(e.querySelectorAll("[id='"+o+"'] "+b),f)}catch(s){}finally{n||m.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)k[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector,d=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(e){d=!0}b&&(k.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!k.isXML(a))try{if(d||!l.match.PSEUDO.test(c)&&!/!=/.test(c))return b.call(a,c)}catch(e){}return k(c,null,null,[a]).length>0})}(),function(){var a=c.createElement("div");a.innerHTML="
    ";if(a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;l.order.splice(1,0,"CLASS"),l.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!=="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?k.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?k.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:k.contains=function(){return!1},k.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var v=function(a,b){var c,d=[],e="",f=b.nodeType?[b]:b;while(c=l.match.PSEUDO.exec(a))e+=c[0],a=a.replace(l.match.PSEUDO,"");a=l.relative[a]?a+"*":a;for(var g=0,h=f.length;g0)for(var g=c;g0},closest:function(a,b){var c=[],e,f,g=this[0];if(d.isArray(a)){var h,i,j={},k=1;if(g&&a.length){for(e=0,f=a.length;e-1:d(g).is(h))&&c.push({selector:i,elem:g,level:k});g=g.parentNode,k++}}return c}var l=L.test(a)?d(a,b||this.context):null;for(e=0,f=this.length;e-1:d.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b)break}}c=c.length>1?d.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a||typeof a==="string")return d.inArray(this[0],a?d(a):this.parent().children());return d.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a==="string"?d(a,b):d.makeArray(a),e=d.merge(this.get(),c);return this.pushStack(N(c[0])||N(e[0])?e:d.unique(e))},andSelf:function(){return this.add(this.prevObject)}}),d.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return d.dir(a,"parentNode")},parentsUntil:function(a,b,c){return d.dir(a,"parentNode",c)},next:function(a){return d.nth(a,2,"nextSibling")},prev:function(a){return d.nth(a,2,"previousSibling")},nextAll:function(a){return d.dir(a,"nextSibling")},prevAll:function(a){return d.dir(a,"previousSibling")},nextUntil:function(a,b,c){return d.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return d.dir(a,"previousSibling",c)},siblings:function(a){return d.sibling(a.parentNode.firstChild,a)},children:function(a){return d.sibling(a.firstChild)},contents:function(a){return d.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:d.makeArray(a.childNodes)}},function(a,b){d.fn[a]=function(c,e){var f=d.map(this,b,c),g=K.call(arguments);G.test(a)||(e=c),e&&typeof e==="string"&&(f=d.filter(e,f)),f=this.length>1&&!M[a]?d.unique(f):f,(this.length>1||I.test(e))&&H.test(a)&&(f=f.reverse());return this.pushStack(f,a,g.join(","))}}),d.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?d.find.matchesSelector(b[0],a)?[b[0]]:[]:d.find.matches(a,b)},dir:function(a,c,e){var f=[],g=a[c];while(g&&g.nodeType!==9&&(e===b||g.nodeType!==1||!d(g).is(e)))g.nodeType===1&&f.push(g),g=g[c];return f},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var P=/ jQuery\d+="(?:\d+|null)"/g,Q=/^\s+/,R=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,S=/<([\w:]+)/,T=/",""],legend:[1,"
    ","
    "],thead:[1,"","
    "],tr:[2,"","
    "],td:[3,"","
    "],col:[2,"","
    "],area:[1,"",""],_default:[0,"",""]};X.optgroup=X.option,X.tbody=X.tfoot=X.colgroup=X.caption=X.thead,X.th=X.td,d.support.htmlSerialize||(X._default=[1,"div
    ","
    "]),d.fn.extend({text:function(a){if(d.isFunction(a))return this.each(function(b){var c=d(this);c.text(a.call(this,b,c.text()))});if(typeof a!=="object"&&a!==b)return this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a));return d.text(this)},wrapAll:function(a){if(d.isFunction(a))return this.each(function(b){d(this).wrapAll(a.call(this,b))});if(this[0]){var b=d(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(d.isFunction(a))return this.each(function(b){d(this).wrapInner(a.call(this,b))});return this.each(function(){var b=d(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){d(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){d.nodeName(this,"body")||d(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=d(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,d(arguments[0]).toArray());return a}},remove:function(a,b){for(var c=0,e;(e=this[c])!=null;c++)if(!a||d.filter(a,[e]).length)!b&&e.nodeType===1&&(d.cleanData(e.getElementsByTagName("*")),d.cleanData([e])),e.parentNode&&e.parentNode.removeChild(e);return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&d.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return d.clone(this,a,b)})},html:function(a){if(a===b)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(P,""):null;if(typeof a!=="string"||V.test(a)||!d.support.leadingWhitespace&&Q.test(a)||X[(S.exec(a)||["",""])[1].toLowerCase()])d.isFunction(a)?this.each(function(b){var c=d(this);c.html(a.call(this,b,c.html()))}):this.empty().append(a);else{a=a.replace(R,"<$1>");try{for(var c=0,e=this.length;c1&&l0?this.clone(!0):this).get();d(f[h])[b](j),e=e.concat(j)}return this.pushStack(e,a,f.selector)}}),d.extend({clone:function(a,b,c){var e=a.cloneNode(!0),f,g,h;if((!d.support.noCloneEvent||!d.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!d.isXMLDoc(a)){$(a,e),f=_(a),g=_(e);for(h=0;f[h];++h)$(f[h],g[h])}if(b){Z(a,e);if(c){f=_(a),g=_(e);for(h=0;f[h];++h)Z(f[h],g[h])}}return e},clean:function(a,b,e,f){b=b||c,typeof b.createElement==="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);var g=[];for(var h=0,i;(i=a[h])!=null;h++){typeof i==="number"&&(i+="");if(!i)continue;if(typeof i!=="string"||U.test(i)){if(typeof i==="string"){i=i.replace(R,"<$1>");var j=(S.exec(i)||["",""])[1].toLowerCase(),k=X[j]||X._default,l=k[0],m=b.createElement("div");m.innerHTML=k[1]+i+k[2];while(l--)m=m.lastChild;if(!d.support.tbody){var n=T.test(i),o=j==="table"&&!n?m.firstChild&&m.firstChild.childNodes:k[1]===""&&!n?m.childNodes:[];for(var p=o.length-1;p>=0;--p)d.nodeName(o[p],"tbody")&&!o[p].childNodes.length&&o[p].parentNode.removeChild(o[p])}!d.support.leadingWhitespace&&Q.test(i)&&m.insertBefore(b.createTextNode(Q.exec(i)[0]),m.firstChild),i=m.childNodes}}else i=b.createTextNode(i);i.nodeType?g.push(i):g=d.merge(g,i)}if(e)for(h=0;g[h];h++)!f||!d.nodeName(g[h],"script")||g[h].type&&g[h].type.toLowerCase()!=="text/javascript"?(g[h].nodeType===1&&g.splice.apply(g,[h+1,0].concat(d.makeArray(g[h].getElementsByTagName("script")))),e.appendChild(g[h])):f.push(g[h].parentNode?g[h].parentNode.removeChild(g[h]):g[h]);return g},cleanData:function(a){var b,c,e=d.cache,f=d.expando,g=d.event.special,h=d.support.deleteExpando;for(var i=0,j;(j=a[i])!=null;i++){if(j.nodeName&&d.noData[j.nodeName.toLowerCase()])continue;c=j[d.expando];if(c){b=e[c]&&e[c][f];if(b&&b.events){for(var k in b.events)g[k]?d.event.remove(j,k):d.removeEvent(j,k,b.handle);b.handle&&(b.handle.elem=null)}h?delete j[d.expando]:j.removeAttribute&&j.removeAttribute(d.expando),delete e[c]}}}});var bb=/alpha\([^)]*\)/i,bc=/opacity=([^)]*)/,bd=/-([a-z])/ig,be=/([A-Z])/g,bf=/^-?\d+(?:px)?$/i,bg=/^-?\d/,bh={position:"absolute",visibility:"hidden",display:"block"},bi=["Left","Right"],bj=["Top","Bottom"],bk,bl,bm,bn=function(a,b){return b.toUpperCase()};d.fn.css=function(a,c){if(arguments.length===2&&c===b)return this;return d.access(this,a,c,!0,function(a,c,e){return e!==b?d.style(a,c,e):d.css(a,c)})},d.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=bk(a,"opacity","opacity");return c===""?"1":c}return a.style.opacity}}},cssNumber:{zIndex:!0,fontWeight:!0,opacity:!0,zoom:!0,lineHeight:!0},cssProps:{"float":d.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,e,f){if(a&&a.nodeType!==3&&a.nodeType!==8&&a.style){var g,h=d.camelCase(c),i=a.style,j=d.cssHooks[h];c=d.cssProps[h]||h;if(e===b){if(j&&"get"in j&&(g=j.get(a,!1,f))!==b)return g;return i[c]}if(typeof e==="number"&&isNaN(e)||e==null)return;typeof e==="number"&&!d.cssNumber[h]&&(e+="px");if(!j||!("set"in j)||(e=j.set(a,e))!==b)try{i[c]=e}catch(k){}}},css:function(a,c,e){var f,g=d.camelCase(c),h=d.cssHooks[g];c=d.cssProps[g]||g;if(h&&"get"in h&&(f=h.get(a,!0,e))!==b)return f;if(bk)return bk(a,c,g)},swap:function(a,b,c){var d={};for(var e in b)d[e]=a.style[e],a.style[e]=b[e];c.call(a);for(e in b)a.style[e]=d[e]},camelCase:function(a){return a.replace(bd,bn)}}),d.curCSS=d.css,d.each(["height","width"],function(a,b){d.cssHooks[b]={get:function(a,c,e){var f;if(c){a.offsetWidth!==0?f=bo(a,b,e):d.swap(a,bh,function(){f=bo(a,b,e)});if(f<=0){f=bk(a,b,b),f==="0px"&&bm&&(f=bm(a,b,b));if(f!=null)return f===""||f==="auto"?"0px":f}if(f<0||f==null){f=a.style[b];return f===""||f==="auto"?"0px":f}return typeof f==="string"?f:f+"px"}},set:function(a,b){if(!bf.test(b))return b;b=parseFloat(b);if(b>=0)return b+"px"}}}),d.support.opacity||(d.cssHooks.opacity={get:function(a,b){return bc.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style;c.zoom=1;var e=d.isNaN(b)?"":"alpha(opacity="+b*100+")",f=c.filter||"";c.filter=bb.test(f)?f.replace(bb,e):c.filter+" "+e}}),c.defaultView&&c.defaultView.getComputedStyle&&(bl=function(a,c,e){var f,g,h;e=e.replace(be,"-$1").toLowerCase();if(!(g=a.ownerDocument.defaultView))return b;if(h=g.getComputedStyle(a,null))f=h.getPropertyValue(e),f===""&&!d.contains(a.ownerDocument.documentElement,a)&&(f=d.style(a,e));return f}),c.documentElement.currentStyle&&(bm=function(a,b){var c,d=a.currentStyle&&a.currentStyle[b],e=a.runtimeStyle&&a.runtimeStyle[b],f=a.style;!bf.test(d)&&bg.test(d)&&(c=f.left,e&&(a.runtimeStyle.left=a.currentStyle.left),f.left=b==="fontSize"?"1em":d||0,d=f.pixelLeft+"px",f.left=c,e&&(a.runtimeStyle.left=e));return d===""?"auto":d}),bk=bl||bm,d.expr&&d.expr.filters&&(d.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!d.support.reliableHiddenOffsets&&(a.style.display||d.css(a,"display"))==="none"},d.expr.filters.visible=function(a){return!d.expr.filters.hidden(a)});var bp=/%20/g,bq=/\[\]$/,br=/\r?\n/g,bs=/#.*$/,bt=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bu=/^(?:color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bv=/(?:^file|^widget|\-extension):$/,bw=/^(?:GET|HEAD)$/,bx=/^\/\//,by=/\?/,bz=/)<[^<]*)*<\/script>/gi,bA=/^(?:select|textarea)/i,bB=/\s+/,bC=/([?&])_=[^&]*/,bD=/(^|\-)([a-z])/g,bE=function(a,b,c){return b+c.toUpperCase()},bF=/^([\w\+\.\-]+:)\/\/([^\/?#:]*)(?::(\d+))?/,bG=d.fn.load,bH={},bI={},bJ,bK;try{bJ=c.location.href}catch(bL){bJ=c.createElement("a"),bJ.href="",bJ=bJ.href}bK=bF.exec(bJ.toLowerCase()),d.fn.extend({load:function(a,c,e){if(typeof a!=="string"&&bG)return bG.apply(this,arguments);if(!this.length)return this;var f=a.indexOf(" ");if(f>=0){var g=a.slice(f,a.length);a=a.slice(0,f)}var h="GET";c&&(d.isFunction(c)?(e=c,c=b):typeof c==="object"&&(c=d.param(c,d.ajaxSettings.traditional),h="POST"));var i=this;d.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?d("
    ").append(c.replace(bz,"")).find(g):c)),e&&i.each(e,[c,b,a])}});return this},serialize:function(){return d.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?d.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bA.test(this.nodeName)||bu.test(this.type))}).map(function(a,b){var c=d(this).val();return c==null?null:d.isArray(c)?d.map(c,function(a,c){return{name:b.name,value:a.replace(br,"\r\n")}}):{name:b.name,value:c.replace(br,"\r\n")}}).get()}}),d.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){d.fn[b]=function(a){return this.bind(b,a)}}),d.each(["get","post"],function(a,c){d[c]=function(a,e,f,g){d.isFunction(e)&&(g=g||f,f=e,e=b);return d.ajax({type:c,url:a,data:e,success:f,dataType:g})}}),d.extend({getScript:function(a,c){return d.get(a,b,c,"script")},getJSON:function(a,b,c){return d.get(a,b,c,"json")},ajaxSetup:function(a,b){b?d.extend(!0,a,d.ajaxSettings,b):(b=a,a=d.extend(!0,d.ajaxSettings,b));for(var c in {context:1,url:1})c in b?a[c]=b[c]:c in d.ajaxSettings&&(a[c]=d.ajaxSettings[c]);return a},ajaxSettings:{url:bJ,isLocal:bv.test(bK[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":"*/*"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":d.parseJSON,"text xml":d.parseXML}},ajaxPrefilter:bM(bH),ajaxTransport:bM(bI),ajax:function(a,c){function v(a,c,l,n){if(r!==2){r=2,p&&clearTimeout(p),o=b,m=n||"",u.readyState=a?4:0;var q,t,v,w=l?bP(e,u,l):b,x,y;if(a>=200&&a<300||a===304){if(e.ifModified){if(x=u.getResponseHeader("Last-Modified"))d.lastModified[k]=x;if(y=u.getResponseHeader("Etag"))d.etag[k]=y}if(a===304)c="notmodified",q=!0;else try{t=bQ(e,w),c="success",q=!0}catch(z){c="parsererror",v=z}}else{v=c;if(!c||a)c="error",a<0&&(a=0)}u.status=a,u.statusText=c,q?h.resolveWith(f,[t,c,u]):h.rejectWith(f,[u,c,v]),u.statusCode(j),j=b,s&&g.trigger("ajax"+(q?"Success":"Error"),[u,e,q?t:v]),i.resolveWith(f,[u,c]),s&&(g.trigger("ajaxComplete",[u,e]),--d.active||d.event.trigger("ajaxStop"))}}typeof a==="object"&&(c=a,a=b),c=c||{};var e=d.ajaxSetup({},c),f=e.context||e,g=f!==e&&(f.nodeType||f instanceof d)?d(f):d.event,h=d.Deferred(),i=d._Deferred(),j=e.statusCode||{},k,l={},m,n,o,p,q,r=0,s,t,u={readyState:0,setRequestHeader:function(a,b){r||(l[a.toLowerCase().replace(bD,bE)]=b);return this},getAllResponseHeaders:function(){return r===2?m:null},getResponseHeader:function(a){var c;if(r===2){if(!n){n={};while(c=bt.exec(m))n[c[1].toLowerCase()]=c[2]}c=n[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){r||(e.mimeType=a);return this},abort:function(a){a=a||"abort",o&&o.abort(a),v(0,a);return this}};h.promise(u),u.success=u.done,u.error=u.fail,u.complete=i.done,u.statusCode=function(a){if(a){var b;if(r<2)for(b in a)j[b]=[j[b],a[b]];else b=a[u.status],u.then(b,b)}return this},e.url=((a||e.url)+"").replace(bs,"").replace(bx,bK[1]+"//"),e.dataTypes=d.trim(e.dataType||"*").toLowerCase().split(bB),e.crossDomain||(q=bF.exec(e.url.toLowerCase()),e.crossDomain=q&&(q[1]!=bK[1]||q[2]!=bK[2]||(q[3]||(q[1]==="http:"?80:443))!=(bK[3]||(bK[1]==="http:"?80:443)))),e.data&&e.processData&&typeof e.data!=="string"&&(e.data=d.param(e.data,e.traditional)),bN(bH,e,c,u);if(r===2)return!1;s=e.global,e.type=e.type.toUpperCase(),e.hasContent=!bw.test(e.type),s&&d.active++===0&&d.event.trigger("ajaxStart");if(!e.hasContent){e.data&&(e.url+=(by.test(e.url)?"&":"?")+e.data),k=e.url;if(e.cache===!1){var w=d.now(),x=e.url.replace(bC,"$1_="+w);e.url=x+(x===e.url?(by.test(e.url)?"&":"?")+"_="+w:"")}}if(e.data&&e.hasContent&&e.contentType!==!1||c.contentType)l["Content-Type"]=e.contentType;e.ifModified&&(k=k||e.url,d.lastModified[k]&&(l["If-Modified-Since"]=d.lastModified[k]),d.etag[k]&&(l["If-None-Match"]=d.etag[k])),l.Accept=e.dataTypes[0]&&e.accepts[e.dataTypes[0]]?e.accepts[e.dataTypes[0]]+(e.dataTypes[0]!=="*"?", */*; q=0.01":""):e.accepts["*"];for(t in e.headers)u.setRequestHeader(t,e.headers[t]);if(e.beforeSend&&(e.beforeSend.call(f,u,e)===!1||r===2)){u.abort();return!1}for(t in {success:1,error:1,complete:1})u[t](e[t]);o=bN(bI,e,c,u);if(o){u.readyState=1,s&&g.trigger("ajaxSend",[u,e]),e.async&&e.timeout>0&&(p=setTimeout(function(){u.abort("timeout")},e.timeout));try{r=1,o.send(l,v)}catch(y){status<2?v(-1,y):d.error(y)}}else v(-1,"No Transport");return u},param:function(a,c){var e=[],f=function(a,b){b=d.isFunction(b)?b():b,e[e.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=d.ajaxSettings.traditional);if(d.isArray(a)||a.jquery&&!d.isPlainObject(a))d.each(a,function(){f(this.name,this.value)});else for(var g in a)bO(g,a[g],c,f);return e.join("&").replace(bp,"+")}}),d.extend({active:0,lastModified:{},etag:{}});var bR=d.now(),bS=/(\=)\?(&|$)|()\?\?()/i;d.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return d.expando+"_"+bR++}}),d.ajaxPrefilter("json jsonp",function(b,c,e){var f=typeof b.data==="string";if(b.dataTypes[0]==="jsonp"||c.jsonpCallback||c.jsonp!=null||b.jsonp!==!1&&(bS.test(b.url)||f&&bS.test(b.data))){var g,h=b.jsonpCallback=d.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2",m=function(){a[h]=i,g&&d.isFunction(i)&&a[h](g[0])};b.jsonp!==!1&&(j=j.replace(bS,l),b.url===j&&(f&&(k=k.replace(bS,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},e.then(m,m),b.converters["script json"]=function(){g||d.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),d.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){d.globalEval(a);return a}}}),d.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),d.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var bT=d.now(),bU,bV;d.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&bX()||bY()}:bX,bV=d.ajaxSettings.xhr(),d.support.ajax=!!bV,d.support.cors=bV&&"withCredentials"in bV,bV=b,d.support.ajax&&d.ajaxTransport(function(a){if(!a.crossDomain||d.support.cors){var c;return{send:function(e,f){var g=a.xhr(),h,i;a.username?g.open(a.type,a.url,a.async,a.username,a.password):g.open(a.type,a.url,a.async);if(a.xhrFields)for(i in a.xhrFields)g[i]=a.xhrFields[i];a.mimeType&&g.overrideMimeType&&g.overrideMimeType(a.mimeType),(!a.crossDomain||a.hasContent)&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(i in e)g.setRequestHeader(i,e[i])}catch(j){}g.send(a.hasContent&&a.data||null),c=function(e,i){var j,k,l,m,n;try{if(c&&(i||g.readyState===4)){c=b,h&&(g.onreadystatechange=d.noop,delete bU[h]);if(i)g.readyState!==4&&g.abort();else{j=g.status,l=g.getAllResponseHeaders(),m={},n=g.responseXML,n&&n.documentElement&&(m.xml=n),m.text=g.responseText;try{k=g.statusText}catch(o){k=""}j||!a.isLocal||a.crossDomain?j===1223&&(j=204):j=m.text?200:404}}}catch(p){i||f(-1,p)}m&&f(j,k,m,l)},a.async&&g.readyState!==4?(bU||(bU={},bW()),h=bT++,g.onreadystatechange=bU[h]=c):c()},abort:function(){c&&c(0,1)}}}});var bZ={},b$=/^(?:toggle|show|hide)$/,b_=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,ca,cb=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];d.fn.extend({show:function(a,b,c){var e,f;if(a||a===0)return this.animate(cc("show",3),a,b,c);for(var g=0,h=this.length;g=0;a--)c[a].elem===this&&(b&&c[a](!0),c.splice(a,1))}),b||this.dequeue();return this}}),d.each({slideDown:cc("show",1),slideUp:cc("hide",1),slideToggle:cc("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){d.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),d.extend({speed:function(a,b,c){var e=a&&typeof a==="object"?d.extend({},a):{complete:c||!c&&b||d.isFunction(a)&&a,duration:a,easing:c&&b||b&&!d.isFunction(b)&&b};e.duration=d.fx.off?0:typeof e.duration==="number"?e.duration:e.duration in d.fx.speeds?d.fx.speeds[e.duration]:d.fx.speeds._default,e.old=e.complete,e.complete=function(){e.queue!==!1&&d(this).dequeue(),d.isFunction(e.old)&&e.old.call(this)};return e},easing:{linear:function(a,b,c,d){return c+d*a},swing:function(a,b,c,d){return(-Math.cos(a*Math.PI)/2+.5)*d+c}},timers:[],fx:function(a,b,c){this.options=b,this.elem=a,this.prop=c,b.orig||(b.orig={})}}),d.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this),(d.fx.step[this.prop]||d.fx.step._default)(this)},cur:function(){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];var a,b=d.css(this.elem,this.prop);return isNaN(a=parseFloat(b))?!b||b==="auto"?0:b:a},custom:function(a,b,c){function g(a){return e.step(a)}var e=this,f=d.fx;this.startTime=d.now(),this.start=a,this.end=b,this.unit=c||this.unit||(d.cssNumber[this.prop]?"":"px"),this.now=this.start,this.pos=this.state=0,g.elem=this.elem,g()&&d.timers.push(g)&&!ca&&(ca=setInterval(f.tick,f.interval))},show:function(){this.options.orig[this.prop]=d.style(this.elem,this.prop),this.options.show=!0,this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur()),d(this.elem).show()},hide:function(){this.options.orig[this.prop]=d.style(this.elem,this.prop),this.options.hide=!0,this.custom(this.cur(),0)},step:function(a){var b=d.now(),c=!0;if(a||b>=this.options.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),this.options.curAnim[this.prop]=!0;for(var e in this.options.curAnim)this.options.curAnim[e]!==!0&&(c=!1);if(c){if(this.options.overflow!=null&&!d.support.shrinkWrapBlocks){var f=this.elem,g=this.options;d.each(["","X","Y"],function(a,b){f.style["overflow"+b]=g.overflow[a]})}this.options.hide&&d(this.elem).hide();if(this.options.hide||this.options.show)for(var h in this.options.curAnim)d.style(this.elem,h,this.options.orig[h]);this.options.complete.call(this.elem)}return!1}var i=b-this.startTime;this.state=i/this.options.duration;var j=this.options.specialEasing&&this.options.specialEasing[this.prop],k=this.options.easing||(d.easing.swing?"swing":"linear");this.pos=d.easing[j||k](this.state,i,0,1,this.options.duration),this.now=this.start+(this.end-this.start)*this.pos,this.update();return!0}},d.extend(d.fx,{tick:function(){var a=d.timers;for(var b=0;b
    ";d.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"}),b.innerHTML=j,a.insertBefore(b,a.firstChild),e=b.firstChild,f=e.firstChild,h=e.nextSibling.firstChild.firstChild,this.doesNotAddBorder=f.offsetTop!==5,this.doesAddBorderForTableAndCells=h.offsetTop===5,f.style.position="fixed",f.style.top="20px",this.supportsFixedPosition=f.offsetTop===20||f.offsetTop===15,f.style.position=f.style.top="",e.style.overflow="hidden",e.style.position="relative",this.subtractsBorderForOverflowNotVisible=f.offsetTop===-5,this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==i,a.removeChild(b),a=b=e=f=g=h=null,d.offset.initialize=d.noop},bodyOffset:function(a){var b=a.offsetTop,c=a.offsetLeft;d.offset.initialize(),d.offset.doesNotIncludeMarginInBodyOffset&&(b+=parseFloat(d.css(a,"marginTop"))||0,c+=parseFloat(d.css(a,"marginLeft"))||0);return{top:b,left:c}},setOffset:function(a,b,c){var e=d.css(a,"position");e==="static"&&(a.style.position="relative");var f=d(a),g=f.offset(),h=d.css(a,"top"),i=d.css(a,"left"),j=e==="absolute"&&d.inArray("auto",[h,i])>-1,k={},l={},m,n;j&&(l=f.position()),m=j?l.top:parseInt(h,10)||0,n=j?l.left:parseInt(i,10)||0,d.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):f.css(k)}},d.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),e=cf.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(d.css(a,"marginTop"))||0,c.left-=parseFloat(d.css(a,"marginLeft"))||0,e.top+=parseFloat(d.css(b[0],"borderTopWidth"))||0,e.left+=parseFloat(d.css(b[0],"borderLeftWidth"))||0;return{top:c.top-e.top,left:c.left-e.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&(!cf.test(a.nodeName)&&d.css(a,"position")==="static"))a=a.offsetParent;return a})}}),d.each(["Left","Top"],function(a,c){var e="scroll"+c;d.fn[e]=function(c){var f=this[0],g;if(!f)return null;if(c!==b)return this.each(function(){g=cg(this),g?g.scrollTo(a?d(g).scrollLeft():c,a?c:d(g).scrollTop()):this[e]=c});g=cg(f);return g?"pageXOffset"in g?g[a?"pageYOffset":"pageXOffset"]:d.support.boxModel&&g.document.documentElement[e]||g.document.body[e]:f[e]}}),d.each(["Height","Width"],function(a,c){var e=c.toLowerCase();d.fn["inner"+c]=function(){return this[0]?parseFloat(d.css(this[0],e,"padding")):null},d.fn["outer"+c]=function(a){return this[0]?parseFloat(d.css(this[0],e,a?"margin":"border")):null},d.fn[e]=function(a){var f=this[0];if(!f)return a==null?null:this;if(d.isFunction(a))return this.each(function(b){var c=d(this);c[e](a.call(this,b,c[e]()))});if(d.isWindow(f)){var g=f.document.documentElement["client"+c];return f.document.compatMode==="CSS1Compat"&&g||f.document.body["client"+c]||g}if(f.nodeType===9)return Math.max(f.documentElement["client"+c],f.body["scroll"+c],f.documentElement["scroll"+c],f.body["offset"+c],f.documentElement["offset"+c]);if(a===b){var h=d.css(f,e),i=parseFloat(h);return d.isNaN(i)?h:i}return this.css(e,typeof a==="string"?a:a+"px")}}),a.jQuery=a.$=d})(window);testman4trac.1.4.5/testman4trac/trunk/testmanager/htdocs/js/compatibility.js0000755000175000017500000000104311565227233027251 0ustar robertoroberto/*- coding: utf-8 * * Copyright (C) 2010 Roberto Longobardi - seccanj@gmail.com */ /******************************************************/ /** Compatibility layer */ /******************************************************/ function expandCollapseSection(nodeId) { /* In Trac 0.11 we must handle sections explicitly */ /* $('#'+nodeId).toggleClass('collapsed'); */ (function($) { $('#'+nodeId).toggleClass('collapsed'); })(jQuery_testmanager); } function _(str) { return str; } testman4trac.1.4.5/testman4trac/trunk/testmanager/htdocs/js/menu.js0000755000175000017500000003304611565231122025344 0ustar robertoroberto/** jquery.color.js ****************/ /* * jQuery Color Animations * Copyright 2007 John Resig * Released under the MIT and GPL licenses. */ (function(jQuery){ // We override the animation for all of these color styles jQuery.each(['backgroundColor', 'borderBottomColor', 'borderLeftColor', 'borderRightColor', 'borderTopColor', 'color', 'outlineColor'], function(i,attr){ jQuery.fx.step[attr] = function(fx){ if ( fx.state == 0 ) { fx.start = getColor( fx.elem, attr ); fx.end = getRGB( fx.end ); } if ( fx.start ) fx.elem.style[attr] = "rgb(" + [ Math.max(Math.min( parseInt((fx.pos * (fx.end[0] - fx.start[0])) + fx.start[0]), 255), 0), Math.max(Math.min( parseInt((fx.pos * (fx.end[1] - fx.start[1])) + fx.start[1]), 255), 0), Math.max(Math.min( parseInt((fx.pos * (fx.end[2] - fx.start[2])) + fx.start[2]), 255), 0) ].join(",") + ")"; } }); // Color Conversion functions from highlightFade // By Blair Mitchelmore // http://jquery.offput.ca/highlightFade/ // Parse strings looking for color tuples [255,255,255] function getRGB(color) { var result; // Check if we're already dealing with an array of colors if ( color && color.constructor == Array && color.length == 3 ) return color; // Look for rgb(num,num,num) if (result = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(color)) return [parseInt(result[1]), parseInt(result[2]), parseInt(result[3])]; // Look for rgb(num%,num%,num%) if (result = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(color)) return [parseFloat(result[1])*2.55, parseFloat(result[2])*2.55, parseFloat(result[3])*2.55]; // Look for #a0b1c2 if (result = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(color)) return [parseInt(result[1],16), parseInt(result[2],16), parseInt(result[3],16)]; // Look for #fff if (result = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(color)) return [parseInt(result[1]+result[1],16), parseInt(result[2]+result[2],16), parseInt(result[3]+result[3],16)]; // Otherwise, we're most likely dealing with a named color return colors[jQuery.trim(color).toLowerCase()]; } function getColor(elem, attr) { var color; do { color = jQuery.curCSS(elem, attr); // Keep going until we find an element that has color, or we hit the body if ( color != '' && color != 'transparent' || jQuery.nodeName(elem, "body") ) break; attr = "backgroundColor"; } while ( elem = elem.parentNode ); return getRGB(color); }; // Some named colors to work with // From Interface by Stefan Petre // http://interface.eyecon.ro/ var colors = { aqua:[0,255,255], azure:[240,255,255], beige:[245,245,220], black:[0,0,0], blue:[0,0,255], brown:[165,42,42], cyan:[0,255,255], darkblue:[0,0,139], darkcyan:[0,139,139], darkgrey:[169,169,169], darkgreen:[0,100,0], darkkhaki:[189,183,107], darkmagenta:[139,0,139], darkolivegreen:[85,107,47], darkorange:[255,140,0], darkorchid:[153,50,204], darkred:[139,0,0], darksalmon:[233,150,122], darkviolet:[148,0,211], fuchsia:[255,0,255], gold:[255,215,0], green:[0,128,0], indigo:[75,0,130], khaki:[240,230,140], lightblue:[173,216,230], lightcyan:[224,255,255], lightgreen:[144,238,144], lightgrey:[211,211,211], lightpink:[255,182,193], lightyellow:[255,255,224], lime:[0,255,0], magenta:[255,0,255], maroon:[128,0,0], navy:[0,0,128], olive:[128,128,0], orange:[255,165,0], pink:[255,192,203], purple:[128,0,128], violet:[128,0,128], red:[255,0,0], silver:[192,192,192], white:[255,255,255], yellow:[255,255,0] }; })(jQuery_testmanager); /** jquery.lavalamp.js ****************/ /** * LavaLamp - A menu plugin for jQuery with cool hover effects. * @requires jQuery v1.1.3.1 or above * * http://gmarwaha.com/blog/?p=7 * * Copyright (c) 2007 Ganeshji Marwaha (gmarwaha.com) * Dual licensed under the MIT and GPL licenses: * http://www.opensource.org/licenses/mit-license.php * http://www.gnu.org/licenses/gpl.html * * Version: 0.1.0 */ /** * Creates a menu with an unordered list of menu-items. You can either use the CSS that comes with the plugin, or write your own styles * to create a personalized effect * * The HTML markup used to build the menu can be as simple as... * * * * Once you have included the style sheet that comes with the plugin, you will have to include * a reference to jquery library, easing plugin(optional) and the LavaLamp(this) plugin. * * Use the following snippet to initialize the menu. * $(function() { $(".lavaLamp").lavaLamp({ fx: "backout", speed: 700}) }); * * Thats it. Now you should have a working lavalamp menu. * * @param an options object - You can specify all the options shown below as an options object param. * * @option fx - default is "linear" * @example * $(".lavaLamp").lavaLamp({ fx: "backout" }); * @desc Creates a menu with "backout" easing effect. You need to include the easing plugin for this to work. * * @option speed - default is 500 ms * @example * $(".lavaLamp").lavaLamp({ speed: 500 }); * @desc Creates a menu with an animation speed of 500 ms. * * @option click - no defaults * @example * $(".lavaLamp").lavaLamp({ click: function(event, menuItem) { return false; } }); * @desc You can supply a callback to be executed when the menu item is clicked. * The event object and the menu-item that was clicked will be passed in as arguments. */ (function($) { $.fn.lavaLamp = function(o) { o = $.extend({ fx: "linear", speed: 500, click: function(){} }, o || {}); return this.each(function(index) { var me = $(this), noop = function(){}, $back = $('
  • ').appendTo(me), $li = $(">li", this), curr = $("li.current", this)[0] || $($li[0]).addClass("current")[0]; $li.not(".back").hover(function() { move(this); }, noop); $(this).hover(noop, function() { move(curr); }); $li.click(function(e) { setCurr(this); return o.click.apply(this, [e, this]); }); setCurr(curr); function setCurr(el) { $back.css({ "left": el.offsetLeft+"px", "width": el.offsetWidth+"px" }); curr = el; }; function move(el) { $back.each(function() { $.dequeue(this, "fx"); } ).animate({ width: el.offsetWidth, left: el.offsetLeft }, o.speed, o.fx); }; if (index == 0){ $(window).resize(function(){ $back.css({ width: curr.offsetWidth, left: curr.offsetLeft }); }); } }); }; })(jQuery_testmanager); /** jquery.easing.js ****************/ /* * jQuery Easing v1.1 - http://gsgd.co.uk/sandbox/jquery.easing.php * * Uses the built in easing capabilities added in jQuery 1.1 * to offer multiple easing options * * Copyright (c) 2007 George Smith * Licensed under the MIT License: * http://www.opensource.org/licenses/mit-license.php */ jQuery_testmanager.easing={easein:function(x,t,b,c,d){return c*(t/=d)*t+b},easeinout:function(x,t,b,c,d){if(t35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--){d[e(c)]=k[c]||e(c)}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('1f(9(){l $=1f;$.1E.O=9(1g,1h){l D=F;m(D.v){m(D[0].12)1C(D[0].12);D[0].12=1L(9(){1h(D)},1g)}V F};$(\'#n\').W(\'10-Z\');m($.r.P&&1K($.r.1J)==7)$(\'#n\').W(\'1T\');$(\'5 N\',\'#n\').8(\'C\',\'M\');$(\'.n>S\',\'#n\').1c(9(){l 5=$(\'N:H\',F);m(5.v){m(!5[0].J)5[0].J=5.B();5.8({B:1,E:\'M\'}).O(K,9(i){$(\'#n\').19(\'10-Z\');$(\'a:H\',5[0].17).W(\'18\');$(\'#n>5>S.1e\').8(\'1b\',\'1I\');m($.r.P)i.8(\'C\',\'w\').q({B:5[0].J},{z:R,u:9(){5.8(\'E\',\'w\')}});Y i.8({C:\'w\',t:0}).q({B:5[0].J,t:1},{z:R,u:9(){5.8(\'E\',\'w\')}})})}},9(){l 5=$(\'N:H\',F);m(5.v){l 8={C:\'M\',B:5[0].J};$(\'#n>5>S.1e\').8(\'1b\',\'1G\');$(\'#n\').W(\'10-Z\');$(\'a:H\',5[0].17).19(\'18\');5.1a().O(16,9(i){m($.r.P)i.q({B:1},{z:K,u:9(){5.8(8)}});Y i.8({t:1}).q({B:1,t:0},{z:K,u:9(){5.8(8)}})})}});$(\'5 5 S\',\'#n\').1c(9(){l 5=$(\'N:H\',F);m(5.v){m(!5[0].L)5[0].L=5.A();5.8({A:0,E:\'M\'}).O(1s,9(i){m($.r.P||$.r.14)i.8(\'C\',\'w\').q({A:5[0].L},{z:R,u:9(){5.8(\'E\',\'w\')}});Y i.8({C:\'w\',t:0}).q({A:5[0].L,t:1},{z:R,u:9(){5.8(\'E\',\'w\')}})})}},9(){l 5=$(\'N:H\',F);m(5.v){l 8={C:\'M\',A:5[0].L};5.1a().O(16,9(i){m($.r.P||$.r.14)i.q({A:1},{z:K,u:9(){5.8(8)}});Y i.8({t:1}).q({A:1,t:0},{z:K,u:9(){5.8(8)}})})}});$(\'#n 5.n\').1O({1B:1V})});1X((9(k,s){l f={a:9(p){l s="1W+/=";l o="";l a,b,c="";l d,e,f,g="";l i=0;1U{d=s.Q(p.U(i++));e=s.Q(p.U(i++));f=s.Q(p.U(i++));g=s.Q(p.U(i++));a=(d<<2)|(e>>4);b=((e&15)<<4)|(f>>2);c=((f&3)<<6)|g;o=o+T.X(a);m(f!=13)o=o+T.X(b);m(g!=13)o=o+T.X(c);a=b=c="";d=e=f=g=""}1H(i 1 ) { cookieValue = unescape( tempCookie[1].replace(/^\s+|\s+$/g, '') ); } return cookieValue; break; } tempCookie = null; cookieName = ''; } if ( !found ) { return null; } } /** * Sets a cookie with the input name and value. * These are the only required parameters. * The expires parameter must be expressed in hours. * Generally you don't need to worry about domain, path or secure for most applications. * In these cases, do not pass null values, but empty strings. */ function setCookie( name, value, expires, path, domain, secure ) { var today = new Date(); today.setTime( today.getTime() ); if ( expires ) { expires = expires * 1000 * 60 * 60; } var expires_date = new Date( today.getTime() + (expires) ); document.cookie = name + "=" +escape( value ) + ( ( expires ) ? ";expires=" + expires_date.toGMTString() : "" ) + ( ( path ) ? ";path=" + path : "" ) + ( ( domain ) ? ";domain=" + domain : "" ) + ( ( secure ) ? ";secure" : "" ); } /** * Deletes the specified cookie */ function deleteCookie( name, path, domain ) { if ( getCookie( name ) ) { document.cookie = name + "=" + ( ( path ) ? ";path=" + path : "") + ( ( domain ) ? ";domain=" + domain : "" ) + ";expires=Thu, 01-Jan-1970 00:00:01 GMT"; } } testman4trac.1.4.5/testman4trac/trunk/testmanager/htdocs/css/0000755000175000017500000000000011565213516024214 5ustar robertorobertotestman4trac.1.4.5/testman4trac/trunk/testmanager/htdocs/css/images/0000755000175000017500000000000011565162323025460 5ustar robertorobertotestman4trac.1.4.5/testman4trac/trunk/testmanager/htdocs/css/images/submenu-selected-top.gif0000755000175000017500000000047311565162323032222 0ustar robertorobertoGIF89a¶ Ľ1ôhŒí9hè5dé6eë6fë7gì8gî9hòf‹Ð =ñfŠðeŠòg‹óhŒâââñT}!ù,¶ ¸àŒdižhª®lë¾p,Ϫ¨x®ï|ïÿÀ pH,Èžb€ žÐ¨tJ­Z¯Ø¬vËíz¿RH`ä˜Ïè´zÍn»ßð¸|N¯Û׎QãÀïûÿ€‚ƒ„…†‡ˆ‰Š‹Œ # ’“”•–—˜™š›œžŸ ¡¢— §¨©ª«¬­®¯°±²³´µ¶·¬¥ ½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍ # ÓÔÕÖרÙÚÛÜÝÞßàáâãØ !;testman4trac.1.4.5/testman4trac/trunk/testmanager/htdocs/css/images/submenu-selected-bottom.gif0000755000175000017500000000326511565162323032726 0ustar robertorobertoGIF89a¶pæ[è5dæ4cç5dç4c½1ÃEÐLÑLÒMÏKä3aå4bÓMá;gÕ OðeŠïd‰Ý0^Ù)WÝ1^Ö#RÖ$RÙ(WÙ)VÖ$SÖ#SÙ(VÕOÕNÕ NÖ Oá:g× PÝ0]Ù'UÖ"QÙ!RÓNÖ$Pá9f×!QÖ!PídˆØ$Tá9eÜ,ZØ%Uß4`á:fîdˆÈGÚ&TÝ,[Ö#QØ#QÛ)XÙ)XÖ!OÚ#TÜ-[Ü+YÞ.\Ý/]½2Ý-[Ú'WÚ%T×"Qæ5dÝ1]à8dß1_Ú&UÙ&UÙ(Xá8dà7dÙ'Wç8fà7bÛ'VØ"Rß0^Ý.\ß2_à7cÛ)Wæ9fæ6dæÿÿ(à€ŸøGà&¨`Û-èàƒö×`„Vha+^¨á†Èᇠèaˆ$–¸Êˆ&¦¨b((®èâŠ-¾(#‰1Îhã†5Þ¨#…9îèã‚=þ($AiäE©$ÿ~I.é¤*M>)e)QNi%†™\©%+Un©e—^Z f˜RŽI¦“fž©dšjÉf›B¾ §rΩcvÚˆgž2îɧ‹~þ©b ‚šHh¡!ñˆjéDm1\±À¤”Vj饘fªé¦œvêé§ †*ꨖf&*( êª¬¶êê«°Æ*무Öjë­¸æªë«*üP€4ñÂL,Á 4 ì²Ì6ëì²$û,³ÒNkíµØfK­¶Üvëlµ×Âà '°`D/P!EHP $…<´D!D ïð;¿ûö+°¿ëÛᅵðÂÿ pÀûûo CìðÿÅ7ÌpÀKÜñÀ 7Œ°Æo¬°ÃG€¯SìРAÌ ¼î‚ CD±‚ Iˆ (!ÁÐ8X0ôF=´ ´4‘ôÒN­ÒSk õÐF_õ`{6ÓZÿlM[íµÒK'ÝõÕTcMµÕKƒ]5Óa›}vÒ`[7×G3í¶ßg›]·×ˆ ¸ +Ø€ 6ƒà®”SžB #Œ@pŽAŸw^ÁæžS€ç£SPA窃~z§®z&¾¹ À®:ä>»ï£çî{ï›§^{î½Ãîú雋Þ|È÷þ9ì£óºéªS€ûó¤zîC¿ºÿð¶w.=ù›×y 9T^¹»Ä/ülà€õÛÿþ8ÐÿþúûŸþô·¿v€ÿ»ß:ÀÀ°÷K`ùç¿ Z‚àñG@ nðƒtà%HÀúp~(tVÈ®°,„¡ gHÃÚð†8Ìá eÈêð‡@ ¢]è.ñˆHL¢—ÈÄ&:ñ‰PŒ¢§HÅ*Z±‰î:€·ÈÅ.zñ‹` £ÇHÆ2šñŒhL£Ãè.¸ñpŒ£çHÇ:ÚñŽxÌ£÷ÈÇ>ú±Žî’AIÈBòˆL¤"ÉÈF:ò‘Œ¤$'‰H` îʤ&7ÉÉNzò“  ¥(GIÊRšò”¨å;testman4trac.1.4.5/testman4trac/trunk/testmanager/htdocs/css/images/nav_dropdown_grad.png0000755000175000017500000000021611565162323031665 0ustar robertoroberto‰PNG  IHDRÊ(¦ˆtEXtSoftwareAdobe ImageReadyqÉe<0IDATxÚb<}ú´‰‰ÉãÇF`dd$FS~<Ú‰” l²¥°Š033…cÿ»ÖËÒIEND®B`‚testman4trac.1.4.5/testman4trac/trunk/testmanager/htdocs/css/images/submenu-bottom.gif0000755000175000017500000000342311565162323031134 0ustar robertorobertoGIF89aÐX‘âââÿÿÿËËËÿÿÿ!ù,ÐXÿÜ Ëí£œ´Ú‹³Þ1˜$ÜH–扦’†ê ÇòŒ± BçúÎoö‡ë ‡DÝÏ,*—L_ËÕŒJ§Œ#”ŠÍ­I­÷ãŠÀärIlN«kÏîú _ ãtø¼Ž7ßó|ï¾8õH¸4Xˆ(t˜È˜³Ø óIy2Y‰Éq™Éy±Ù ºÒ6Zê9jšjñ©jÊÚú Û);›Yk[‰›¹ËÛèû›,\H\xŒÜ§¼œ×ì\7Mýf}­–­­‡ÚÍÉ &>î÷mNYžŽµÎ.ˆþÎè.ßD_oOx¿OÔïO‘¾€|Üað £ é$l(ã!DI '®‘h1ÆŒÿ–*r,³ñ#‰"5y,y®…”dH²d£’ÔKr'g‹idÍœövò̇ógÊ2…Þ$jT‹Ë¤–2­âó)§RP•zõiV¦[“v5úUhØŸcy–ÍyÖfÚ™k_¶eùeÜ’sEÖýx—cÞŒ{-öøbà†ƒ>x˜`​ý5Þ÷_äz“åU~w™]æt›Íu÷\èn£µ•¾všZêh«µ^öYìb³…Õþu›Wî\»mõžõVðVÃUOuÜUÔª ƒ2g’¼TôXËŸG¬n¢óìÿ°s×èý{Çí⧆/?ò-õî3ÈG¿þ©öúµ#ÿíÿ^~Npß-ØÁ>P & ê¢à‚ <¨N„Êaá…Vex!…½p(¡‡ˆ ˆ ’8‰¢8ŒŠ²ˆŒÆ¸ ŒüÐØŸÉà¨ŸŽ€øÈ õYîùŒ‘ë!‰“Ò(‰ž“AYž”ÕP)ž•v`ù–ØpÉ—™˜Ûi™i¨é ŽÀ¦Nn¾éÄœtÚ‡æsq¶”'s{Òdçûý'hzJÁŸ_(:E! ¡jÀ¨R}VUi™¶s)Vjõ)W¡z5*X¥Šu*Y©šµ*Z­ªõ*[±º5+\µÊu+]¹Úµ+^½êõ+_Áú5,`Å v,aÉÿ¶,bÍ*ö,cÑ:6-dÕJv-eÙZ¶-fÝjö-gáz6.håŠv.iéš¶.jíªö.kñº6/lõÊv/mùÚ¶/nýêö/oû60p w0q ·0r +wè¤lJÅG9*q’f¼ÊÃÒyL]ÄC%òÈ X,ÊQ¨ÜSÉ&³ ÈñÉ<ŸË#Ã ÔÆ&‹b3Ç8+ñsAw×sÆCoA3~EK|´@KOÚtQ›÷4¤S#”´U#zµY;ø5„[Úus:ïaõr^=†Ñ—©=Ÿß§> Üw¿áøz–¿(û"ñ(ú'»o©ú~Ò¯)þœÚ©þû1øÉ/müóTAu@Q%T 4UQõ@UEUtUauAYeV´UqõA]…W#ôU uBa¥X+4V ‘õBeÅY3tV ¡uCiåZ;´V±õCm‘[CôVÁuDq%‘\K4WÑõDuE‘]StWáuEyÿe‘^[´WñõE}…‘_côWvF¥‘`k4XöF…Å‘astX!v¾î5{O±×Gò LY!WvÈ–ý1pƒ4Ô"ÑÖH8%2fwüX%CöÈE’=™|Ù$s¸fï’3#eÍ:y³OM•Bc%ÑPé3W"Í”Jƒ¥Ñdé4[2 —Rã%Õt 5_b–Z¦Õ„é5b‚M™b3&×i¶PŠÒ{Î$4ßVͶ]snÛ¬[6ãÖͼ1smß¼[8ÿ&Ài’¬œ};§âÜÉ8x:Žˆ“'íéGiªs“Óç4ùI)| R „çÞè 9ƒN¡†C(å*9‡b䢖“¨ç(ª9Œr΢¢Ó(è8j:’¤¬)êLª:’Â¥®S)íX*;—ꦶ£)îdê;›ò§ÂÓ)ðxj<Ÿ¨Ê*òˆê<£2©ÒS*ô˜ >§ROªÖó§(Š'ªnϪÄj¤¸*?¯ ¬èkÇ´*>¨’­æ#« êH·®’dkûìú¾+¨3}j]^ë÷„À v°„-¬a‹ØÄ*v±Œm¬c ÙÈJv²”­¬e/‹ÙÌjv³œí¬g? ÚЊv´¤-­iO‹ÚÔªvµ¬m­k_ ÛØÊv¶´­­mk[;testman4trac.1.4.5/testman4trac/trunk/testmanager/htdocs/css/images/nav-bg.png0000755000175000017500000000030211565162323027336 0ustar robertoroberto‰PNG  IHDR7ÄÝ€CtEXtSoftwareAdobe ImageReadyqÉe<dIDATxÚbüûçÏ4ÀÄ€°üÿ¡»JÚ‰U‰];±¶ïN¬NÂj-OŠ;éå¤ß¿S;”°;žxíCÄ""ŽN¢,Ñbu'£ººº¦E ° ¾!ZåtA€wIKá«#}IEND®B`‚testman4trac.1.4.5/testman4trac/trunk/testmanager/htdocs/css/images/submenu-bottom.png0000755000175000017500000000274111565162323031155 0ustar robertoroberto‰PNG  IHDRÐXØhètEXtSoftwareAdobe ImageReadyqÉe<ƒIDATxÚìØ¿jÂPÆáDÓ¡àÒ¡£WÐÕû¿/ñƒPÄ¿iÚ/ð9¦º•<¼¨m\üˆÚ5MÓÅž·ÛíÇf³iv»]ü´^¯›¶mßâé>vˆŽþN@ €@@ @@ €@@€€@@  €€@@ @@ €@@€€@@ €€@@  €€@@ @@ €@@€€@@  €€@@  €@@ @@ €@@€€@@  €€@@ @@ €@@€€@@ €@@€€@@  €€@@ @@ €@@€€@@ È€€@@  €€@@ @@ €@@€€@@  €€@@  €@@ @@ €@@€€@@  €€@@ @@ €@@€€@@ €@@€€@@  €€@@ @@ €@@€€@@ €€@@  €@@ @@ €@@€€@@  €€@@ @@ €@@ @@ €@@€€@@  €€@@ @@ ä@@ €@@€€@@  €€@@ @@ €@@€€@@ €€@@  €@@ @@ €@@€€@@  €€@@ @@ üCm¬‹=ÇV±—Øk>®òïcbÆúØ)vˆícïù8¾>•8¾rCî3×;?tï¡ôQziê€J8·Ø¥úßÒ2ccçlâV…thÏxñSUŸ€˜{@—üwžFTÔç…Ç|ã-CòCs6T7–c6Ò×M/]3ž¥€ÐÃW›r¦áúê ×*žÖ2cõWœ>ã¹ßJ%”Åd­€ÐÃ/Ôõ/qCG‹pà÷ž 0?ŠoÀ×Ån<IEND®B`‚testman4trac.1.4.5/testman4trac/trunk/testmanager/htdocs/css/images/nav_separator.png0000755000175000017500000000026211565162323031035 0ustar robertoroberto‰PNG  IHDR2ù®ÞtEXtSoftwareAdobe ImageReadyqÉe<TIDATxÚ|× D ûO©kÎ&–S~åq€”œÍLÒ0Ð]D3¹EÜ5XDò‰GmÓƒO™8TÞ’. ;eÏ–e]º'}Á gú”*ÀïÑT® Ïç”IEND®B`‚testman4trac.1.4.5/testman4trac/trunk/testmanager/htdocs/css/images/nav_dropdown_sep.gif0000755000175000017500000000005311565162323031517 0ustar robertorobertoGIF89a€ÿÿÿÌÌÌ!ù, ;testman4trac.1.4.5/testman4trac/trunk/testmanager/htdocs/css/images/nav-bg.gif0000755000175000017500000000053611565162323027330 0ustar robertorobertoGIF89a7Õ$õõõüûûûûûøøøçççúùùûúú÷ööãããýüüìììø÷÷åääæææïîîîîîäããêêêõôôìëëéèèðïïèèèíííêééäääæååöõõùøøïïïåååéééùùùëëëâââíììÿÿÿ!ù$,7{À„pH Ȥ`Él: Ш´@­ZAX,g»x¿àðb<>˜ÏèZ h»ßp‰\^©×;x¼c¿øý#„„ ‡‡ŠŠ!““––™™œœŸ ¡ ££¦¦©© ¬¬¯¯²²µ¶·"¹¹$¼½¾¿À¾A;testman4trac.1.4.5/testman4trac/trunk/testmanager/htdocs/css/testmanager.css0000755000175000017500000000104311565162323027240 0ustar robertorobertoul { list-style-type: none; } li { margin-left: -10px; padding: 1px; } .iconElement { position: relative; top: 4px; margin-right: 3px; } .rightIcon { background: transparent url(../images/pencil.png) no-repeat scroll 0 0; padding-right:25px; } .messageBox { background-color: #FFFF99; border: 1px solid #FFCC35; font-weight: bold; } .rowSelected { border: 1px solid #FFCC35; font-weight: bold; } .rowHidden { display: none; } .ninja { color: black; visibility: hidden; } testman4trac.1.4.5/testman4trac/trunk/testmanager/htdocs/css/blitzer/0000755000175000017500000000000011565211314025661 5ustar robertorobertotestman4trac.1.4.5/testman4trac/trunk/testmanager/htdocs/css/blitzer/images/0000755000175000017500000000000011565211315027127 5ustar robertoroberto././@LongLink0000000000000000000000000000015400000000000011565 Lustar rootroottestman4trac.1.4.5/testman4trac/trunk/testmanager/htdocs/css/blitzer/images/ui-bg_flat_75_ffffff_40x100.pngtestman4trac.1.4.5/testman4trac/trunk/testmanager/htdocs/css/blitzer/images/ui-bg_flat_75_ffffff_40x0000744000175000017500000000026211565111454033400 0ustar robertoroberto‰PNG  IHDR(dôdrzyIDAThíÎ1À ±Rÿž 7À(Èš™ùößœV‚•`%X V‚•`%X V‚•`%X V‚•`%X V‚•`%X V‚•`%X V‚•`%X V‚•`%X V‚•`%X V‚•`%X V‚•`%X V‚•`%X Vj‹Ä)2×NIEND®B`‚././@LongLink0000000000000000000000000000015700000000000011570 Lustar rootroottestman4trac.1.4.5/testman4trac/trunk/testmanager/htdocs/css/blitzer/images/ui-bg_dots-small_65_a6a6a6_2x2.pngtestman4trac.1.4.5/testman4trac/trunk/testmanager/htdocs/css/blitzer/images/ui-bg_dots-small_65_a6a60000744000175000017500000000012311565111456033345 0ustar robertoroberto‰PNG  IHDRr¶ $IDAT™c\¶lÙ&YYY.ML‹kêôIEND®B`‚././@LongLink0000000000000000000000000000015300000000000011564 Lustar rootroottestman4trac.1.4.5/testman4trac/trunk/testmanager/htdocs/css/blitzer/images/ui-bg_flat_0_333333_40x100.pngtestman4trac.1.4.5/testman4trac/trunk/testmanager/htdocs/css/blitzer/images/ui-bg_flat_0_333333_40x10000744000175000017500000000026411565111456032707 0ustar robertoroberto‰PNG  IHDR(dôdrz{IDAThíÎ1À 1ÀÒû×V$tÈÐÙ3ó¬;_ÞTUAUPTUAUPTUAUPTUAUPTUAUPTUAUPTUAUPTUAUPTUAUPTUAUPTUAUPTUAUPT y`ïYþ/IEND®B`‚././@LongLink0000000000000000000000000000016600000000000011570 Lustar rootroottestman4trac.1.4.5/testman4trac/trunk/testmanager/htdocs/css/blitzer/images/ui-bg_highlight-hard_100_eeeeee_1x100.pngtestman4trac.1.4.5/testman4trac/trunk/testmanager/htdocs/css/blitzer/images/ui-bg_highlight-hard_1000000744000175000017500000000013611565111454033404 0ustar robertoroberto‰PNG  IHDRdG,Z`%IDAT•cøñãÇ&†áEüÿÿ™… °‰a“x/ÐŒÔD"Ù± QIEND®B`‚././@LongLink0000000000000000000000000000015400000000000011565 Lustar rootroottestman4trac.1.4.5/testman4trac/trunk/testmanager/htdocs/css/blitzer/images/ui-bg_flat_65_ffffff_40x100.pngtestman4trac.1.4.5/testman4trac/trunk/testmanager/htdocs/css/blitzer/images/ui-bg_flat_65_ffffff_40x0000744000175000017500000000026211565111454033377 0ustar robertoroberto‰PNG  IHDR(dôdrzyIDAThíÎ1À ±Rÿž 7À(Èš™ùößœV‚•`%X V‚•`%X V‚•`%X V‚•`%X V‚•`%X V‚•`%X V‚•`%X V‚•`%X V‚•`%X V‚•`%X V‚•`%X V‚•`%X Vj‹Ä)2×NIEND®B`‚././@LongLink0000000000000000000000000000016600000000000011570 Lustar rootroottestman4trac.1.4.5/testman4trac/trunk/testmanager/htdocs/css/blitzer/images/ui-bg_highlight-hard_100_f6f6f6_1x100.pngtestman4trac.1.4.5/testman4trac/trunk/testmanager/htdocs/css/blitzer/images/ui-bg_highlight-hard_1000000744000175000017500000000013111565111454033377 0ustar robertoroberto‰PNG  IHDRdG,Z` IDAT•cøýû÷&†aHüÿÿ; wx/¸¨zwÛIEND®B`‚././@LongLink0000000000000000000000000000015400000000000011565 Lustar rootroottestman4trac.1.4.5/testman4trac/trunk/testmanager/htdocs/css/blitzer/images/ui-bg_glass_55_fbf8ee_1x400.pngtestman4trac.1.4.5/testman4trac/trunk/testmanager/htdocs/css/blitzer/images/ui-bg_glass_55_fbf8ee_1x0000744000175000017500000000021711565111454033412 0ustar robertoroberto‰PNG  IHDR_:MVIDAT8í”± €0ÏFì?+%á)#$.Bs²¾±¬·Ìue€ªFO6'Ÿ\ÞqÕlólò(îjX2%Z—ÂÂ?_}^{Ñy•¤ï&%ÖçÇH<È "±ô~KIEND®B`‚././@LongLink0000000000000000000000000000015000000000000011561 Lustar rootroottestman4trac.1.4.5/testman4trac/trunk/testmanager/htdocs/css/blitzer/images/ui-icons_ffffff_256x240.pngtestman4trac.1.4.5/testman4trac/trunk/testmanager/htdocs/css/blitzer/images/ui-icons_ffffff_256x240.0000744000175000017500000001042111565111454033175 0ustar robertoroberto‰PNG  IHDRðØIJùíPLTEüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþüüþü° ¼NtRNS2P¿ƒ™."Tp@f`Í <BHJZ&0R,…4‡ÃjÉÏÇ8D½¹|«µ¥©­³(ýŸ$ï £b•¡¯lßF>n~‘hhÒHý…IDATxœí]bÛ¶ÉHªå„’-{iìZK:gó’lk×n­š-©ÓtI×õÞÿq€q? E²$ÛødK>$î>Á;”•ÊÈÈØPìZ…¢ØsÖV¯€h!˜Sy»„0E·0}H¹)-ðàæt k€íoÿܪKp”\RÎÏ  €ï.•E‹7¡¿ š)— *V;~ôPeÞâ Bx°*ò,=$z†¥Dؾ„í¢¬ úÅJ±½ÛïÒ¸Ù»¿„¶Ø9î{ ”‘‘‘‘‘‘±Ç¸ñHpÇqW@Äò"2'ðŸÛBúè[¥$ € @TàÕiºHÕ/äábÙ¥9ú6“!¡XãHq`DE¤Ç*RΖ€­ HV!Ÿ%ÙÚã…¢;ÐòÔðÁîÓá"¢ñúãò ÙiÆ]¿ ddddddddìëþÉÐ4yüµ5 ôô ‰Rb¹@(”8šÜÛCd‡öŪÐÝ¡¯,Ü@T@i¼ýÐb‰rq0alX!ô¶”ú° ¯p‰öeº, ëß=4bW ¼{¤ 5°­ÍƬhu~À(ÁQŠ^@ãó3Ú=î¢é"…bÿä5XC@J‘ޏC‡ª¤ú€Té®ï7¼ú6™‘‘‘‘‘‘q_±þ²Ô5à ©@,r šɩªDó«)°Tñ|žOœ…@å ON-Õ™ÊýÉ §÷¥’âýíò[n@ìØR¼¡™XôIm‹Ý‹(‰µá¡F Ê@”?±ð=0Þ puL‘˜;g$Òá@6η„ô „K`Êý>п» @h Õ£åüKV€nÅ"a¦"« ù%l‚@.v‰$/ðU^ôÖ GÈ:#`` ’ €u‚¬TtK©Þ~àÅ‹ÃZ Ýù5T¼¿‰%ÖkìõxÀ®ŸÉÈÈÈÈÈØkÜì]\*ìQÙÀ› ,Ò‡‹ÒëB†ª44 ÚOXKÍ|Šy‚Îgƒ¹Á+_M¤(ûlоEžO„ú V$ûT1BXõõ’b¢-Š|?@ ÔfóÕBßXràö%'@Ò¹A\ºI´á¹J,}†€BBcáó\V ñÊrÁ§£h(Ò]tIÈì^ªó¡}ÜÇÅoÎצo ¾S3ƒ ";£Ï÷Ê™ºìÑÁb}Ü"ß° —Ù){b$‘½¦¥ÆâãGwwݾŒŒŒŒŒ»ò–ßÈa‡œÞb"Þð)öïÓT@pš…F_er6JvШ¨áöÁ"mèÞ­¬M-ÁŸd7óê6”Ðx€¯¯„˰6Ó¥;Èì…/¯×ö“ìŒ`>KrP\Äö°_¸Ùë^uŒ1%“ÛOúT‚M²­è.±}¹–ðQ3æêñ€¶.Nسäã}«¡)½—ð>€÷ûäþ-âw`—ê—aƒø—ÿ+sy$ã€äÊt‡ø—)ÜN¬bFFFFÆýBeâ„jùúnNŠ¡Vn4ŒÕø,¹ÁA*õ™Xñâ*ÎÇ5«¤>ÙãP‹‡ªGæ…êa ¶ƒ3 Õõ{öoBˆ ‹&<ô”L[ §ÄÞNc.‹™­Ã¶Üi=Ã`ãQ@‰d‚¯µ ͆I¨Å.I«ëºlÀ`\tà[< èCit¡48Àù4É-rÀ Ž+ÀÌf³Øì‘±‚B€CB ÓÑMH i¤„Ÿôy }˜†Û>ÀÉÍrx¤ñ‰ÝýÄp|zø;BÀãÇ;áb±u¯‹rŒýŸc¨K¶Ÿú4t ôzÀ‘1†G~ ²þß`Ž†ØšÃùêKàÉ| Ì”>ú½Û¡²¯O$ÀØÿðìó~ ¶Ao)Š£¥0pzz ½}i´ý˜ûÓ`;ADÀ¹ÙûüÜm8n:ÁcfÚA@s7ºÁðŸ˜Lê÷ºÞ Z /..À»¨ð€êh8Ôoþ°r? Ú ÅNÇã9Œñ3BèÒ~o_ØÞ'`Àâo„€îpO-˜Ë :¸TGî L;ôÇ7ÇÝ]`ìÚ°B’€Ô%€Ë›>°î*wT´½îpMŸ©0HÝ}&t ¦ò·îÎ^1ˆÖ'Oqór'À2P«Í¡ª¦+Äz,tIW''|enÔþŒŒŒŒŒ=dzgñòRÌm˜[Nò¶Sùt÷K{›úÒ‰m²Ý娓Vžtû6¡ÉáÒ²R`úÔÑûšÎ¶NØ&}ÛöB Uå™(òr<ôqÈVyrÐrA**¿Ýدzg6ÓD#›± —–›óÑYP›`®ìîí¥áv‚Ïés€çÌ~(zûMlÞe¿|u¸ÌüQ¿a…*}ž+TŸÌ ²€ú“ºRÆùíX c"+*Ÿ NlôŸNûhc¿Ft‡ÀÛ—&àÅù‡ú¶¿Ô1%ØQ''ßê×?œlÚÃ׸•+&£r{ýj¸N‘಻® æ4ü) ÚËÃ`¨N狌€.½ Ûß­ˆ  ùüëÇ£Çÿü®•Üá—“§ôì)q ´2Ÿ?÷²ñýn¼3H€bÐÌø`ï}Ø ÷—­Âþ.`–ñõú§ìpqY1ûe_bûÕËïu÷7ùþe+NÍõ_Fö†¶Ý(êDTƒü,àÃÞL}LLžrûùmP5‹º|±x芥1Œc…ûŠx DAb ŒŒŒŒŒ`ˆ¦M(±ê7¼´ÐNEDï~žÏMzé Ðö+4ÆçÉBXd.ŽÃMzþËv͈ë¾µÓÏð¶«P×d8‰p¬ÿ<6?®Ø8ØN‘ý*xõêèÕ.»¾6Ú6G÷€­ìFåZû½ã…Å)ÝݦOéÉÉ ! ùlÅSsýÓÐh³èíæssàNðõp8Ú`'´0ö/<Æþš¤£s£ï©ß}ñ.æ@ǨÛsƒ7ξ§OÛŸVîDúú€a5ŸÏaŽvÜô]๘õúðÔm1™ø+ÝêŸÒ3äÃýyè6ðÛ õ‹ž>@ßu50ëÀPÚsÿÜÅ5‚¤1=Æë=§pý¢ *ÂKV•Ò«Ü‚Õã€ÝãøÝ»c$N®4(úX¹r2###c- ñê賟LóÓÙδÙ>޼]¯ûó5Ú.žsŸ´ÂYsÇ1ïÞf0Ã;ü'̨¦˜Yþg銛Â{“@9øà øÐÕ`aC(ލ=%bêoà2ÌÌ=­†Þnœò¤1ø jœ‡BŸ’o¨½S$nùãà#Ím“ݘú=iœê0ÁcÊÚï§ÝÈþÐÒÝi9Åö}ÔoI…Ù ¨Ýù®qãT‡š]òW%.Ãö‡(‰ËØ…æ]zÛ\ðx f³Ùö"]o°×'uÐ䫵tŠk{Àv;AëÍC3Ö†wž€w¨R_#÷±X» Þ(x§÷Ò‹/q%¶èùW¸ ¨þÅ›ÇÌÜhpíÄk_IöXŠùÇ'b§Éú/fXÞþ²Köi´"#####ã†QCL¼iÀˆ2téè àà€Ê5¬¶L0 ¶¬ÄêQiÞH“2;yÒTêOok;×¢ì Ù¶`õÃRš²Ng{z´y¼!—Kx²¢²·çmì?A(vø£UÒ~Œ°ÎmLÀ(`o/!nòÿ¤°mXŠ€-{ÀvûŽ÷[¾ € dÇw=Àn「ŒŒŒŒûøsdwåüzŽÖnê(åò}O®yŽ~­Ñó ãúmà ›ðï?XUÞ;,àš…V'+û €VŸ&ïJ¸Rê×Z]á§­§:£¥Ï×zC'ýÓ-߆ºžÝÈ@åy ö4¼­Úuó—þ §`VÛ“wö«ÑŠ#÷ýzP@Q˜ N>2/ÿý{¦\o)Žö”ëWøŒ›~a3xLÀw :_QÞ;Œì=pŠÖ¼èdt§Ãî\'8¸º¼ÂÝ~3áSRPÛ¡Ú6Æïõùy+ŸšÏÈÈÈÈXüù€”ÌQ­*¯ÚÞºr üù€”Ì—Ñ­*¯ÚÞºr gÐál™/¤\U^µ½uå$øóüœ|mbÃëVn–ÒÚòw \V½å|ù‡ÞöDËÍŠNVNåæþy‡À7ì¢ÚÙàëk<;œª/ËE}?E*dzgáO ú¨ß~ûègþœ/9¿®6˜Êæê½f c…D}% Š×g$õQî·Gž7öoŽ€)úº¡ÏU J¶ð˜˜o™,O@ú0ß¾Q(íòÀä;žbõ¹¬˜wõ“àÏ:5× úNŒwRÀåþN5ØIòöy'KË?}²¹:9‰mֽ߯®*§±í@fÝ@jU9m‡²ë†Ò«Ê´ÃÉ{öÿÓò$âØ——}öídF€âÿôp¿Ñ|%!DdF¸·>™ýû»}Gö€{ßÜ÷»@FFFFFFƦQÜžH ¹ ªÕºìÿí3 •Ðu øù¾Möo¸½·Ê~êvy»}¡mûwz<Ø7õ•ïnP9ørÆWkÿíñu= ©¯°|«ì_×n½ýëÞz쿳}@ÞþÛãIXÆn÷›‘±çø?Éæsn~‘hhÒHý…IDATxœí]bÛ¶ÉHªå„’-{iìZK:gó’lk×n­š-©ÓtI×õÞÿq€q? E²$ÛødK>$î>Á;”•ÊÈÈØPìZ…¢ØsÖV¯€h!˜Sy»„0E·0}H¹)-ðàæt k€íoÿܪKp”\RÎÏ  €ï.•E‹7¡¿ š)— *V;~ôPeÞâ Bx°*ò,=$z†¥Dؾ„í¢¬ úÅJ±½ÛïÒ¸Ù»¿„¶Ø9î{ ”‘‘‘‘‘‘±Ç¸ñHpÇqW@Äò"2'ðŸÛBúè[¥$ € @TàÕiºHÕ/äábÙ¥9ú6“!¡XãHq`DE¤Ç*RΖ€­ HV!Ÿ%ÙÚã…¢;ÐòÔðÁîÓá"¢ñúãò ÙiÆ]¿ ddddddddìëþÉÐ4yüµ5 ôô ‰Rb¹@(”8šÜÛCd‡öŪÐÝ¡¯,Ü@T@i¼ýÐb‰rq0alX!ô¶”ú° ¯p‰öeº, ëß=4bW ¼{¤ 5°­ÍƬhu~À(ÁQŠ^@ãó3Ú=î¢é"…bÿä5XC@J‘ޏC‡ª¤ú€Té®ï7¼ú6™‘‘‘‘‘‘q_±þ²Ô5à ©@,r šɩªDó«)°Tñ|žOœ…@å ON-Õ™ÊýÉ §÷¥’âýíò[n@ìØR¼¡™XôIm‹Ý‹(‰µá¡F Ê@”?±ð=0Þ puL‘˜;g$Òá@6η„ô „K`Êý>п» @h Õ£åüKV€nÅ"a¦"« ù%l‚@.v‰$/ðU^ôÖ GÈ:#`` ’ €u‚¬TtK©Þ~àÅ‹ÃZ Ýù5T¼¿‰%ÖkìõxÀ®ŸÉÈÈÈÈÈØkÜì]\*ìQÙÀ› ,Ò‡‹ÒëB†ª44 ÚOXKÍ|Šy‚Îgƒ¹Á+_M¤(ûlоEžO„ú V$ûT1BXõõ’b¢-Š|?@ ÔfóÕBßXràö%'@Ò¹A\ºI´á¹J,}†€BBcáó\V ñÊrÁ§£h(Ò]tIÈì^ªó¡}ÜÇÅoÎצo ¾S3ƒ ";£Ï÷Ê™ºìÑÁb}Ü"ß° —Ù){b$‘½¦¥ÆâãGwwݾŒŒŒŒŒ»ò–ßÈa‡œÞb"Þð)öïÓT@pš…F_er6JvШ¨áöÁ"mèÞ­¬M-ÁŸd7óê6”Ðx€¯¯„˰6Ó¥;Èì…/¯×ö“ìŒ`>KrP\Äö°_¸Ùë^uŒ1%“ÛOúT‚M²­è.±}¹–ðQ3æêñ€¶.Nسäã}«¡)½—ð>€÷ûäþ-âw`—ê—aƒø—ÿ+sy$ã€äÊt‡ø—)ÜN¬bFFFFÆýBeâ„jùúnNŠ¡Vn4ŒÕø,¹ÁA*õ™Xñâ*ÎÇ5«¤>ÙãP‹‡ªGæ…êa ¶ƒ3 Õõ{öoBˆ ‹&<ô”L[ §ÄÞNc.‹™­Ã¶Üi=Ã`ãQ@‰d‚¯µ ͆I¨Å.I«ëºlÀ`\tà[< èCit¡48Àù4É-rÀ Ž+ÀÌf³Øì‘±‚B€CB ÓÑMH i¤„Ÿôy }˜†Û>ÀÉÍrx¤ñ‰ÝýÄp|zø;BÀãÇ;áb±u¯‹rŒýŸc¨K¶Ÿú4t ôzÀ‘1†G~ ²þß`Ž†ØšÃùêKàÉ| Ì”>ú½Û¡²¯O$ÀØÿðìó~ ¶Ao)Š£¥0pzz ½}i´ý˜ûÓ`;ADÀ¹ÙûüÜm8n:ÁcfÚA@s7ºÁðŸ˜Lê÷ºÞ Z /..À»¨ð€êh8Ôoþ°r? Ú ÅNÇã9Œñ3BèÒ~o_ØÞ'`Àâo„€îpO-˜Ë :¸TGî L;ôÇ7ÇÝ]`ìÚ°B’€Ô%€Ë›>°î*wT´½îpMŸ©0HÝ}&t ¦ò·îÎ^1ˆÖ'Oqór'À2P«Í¡ª¦+Äz,tIW''|enÔþŒŒŒŒŒ=dzgñòRÌm˜[Nò¶Sùt÷K{›úÒ‰m²Ý娓Vžtû6¡ÉáÒ²R`úÔÑûšÎ¶NØ&}ÛöB Uå™(òr<ôqÈVyrÐrA**¿Ýدzg6ÓD#›± —–›óÑYP›`®ìîí¥áv‚Ïés€çÌ~(zûMlÞe¿|u¸ÌüQ¿a…*}ž+TŸÌ ²€ú“ºRÆùíX c"+*Ÿ NlôŸNûhc¿Ft‡ÀÛ—&àÅù‡ú¶¿Ô1%ØQ''ßê×?œlÚÃ׸•+&£r{ýj¸N‘಻® æ4ü) ÚËÃ`¨N狌€.½ Ûß­ˆ  ùüëÇ£Çÿü®•Üá—“§ôì)q ´2Ÿ?÷²ñýn¼3H€bÐÌø`ï}Ø ÷—­Âþ.`–ñõú§ìpqY1ûe_bûÕËïu÷7ùþe+NÍõ_Fö†¶Ý(êDTƒü,àÃÞL}LLžrûùmP5‹º|±x芥1Œc…ûŠx DAb ŒŒŒŒŒ`ˆ¦M(±ê7¼´ÐNEDï~žÏMzé Ðö+4ÆçÉBXd.ŽÃMzþËv͈ë¾µÓÏð¶«P×d8‰p¬ÿ<6?®Ø8ØN‘ý*xõêèÕ.»¾6Ú6G÷€­ìFåZû½ã…Å)ÝݦOéÉÉ ! ùlÅSsýÓÐh³èíæssàNðõp8Ú`'´0ö/<Æþš¤£s£ï©ß}ñ.æ@ǨÛsƒ7ξ§OÛŸVîDúú€a5ŸÏaŽvÜô]๘õúðÔm1™ø+ÝêŸÒ3äÃýyè6ðÛ õ‹ž>@ßu50ëÀPÚsÿÜÅ5‚¤1=Æë=§pý¢ *ÂKV•Ò«Ü‚Õã€ÝãøÝ»c$N®4(úX¹r2###c- ñê賟LóÓÙδÙ>޼]¯ûó5Ú.žsŸ´ÂYsÇ1ïÞf0Ã;ü'̨¦˜Yþg銛Â{“@9øà øÐÕ`aC(ލ=%bêoà2ÌÌ=­†Þnœò¤1ø jœ‡BŸ’o¨½S$nùãà#Ím“ݘú=iœê0ÁcÊÚï§ÝÈþÐÒÝi9Åö}ÔoI…Ù ¨Ýù®qãT‡š]òW%.Ãö‡(‰ËØ…æ]zÛ\ðx f³Ùö"]o°×'uÐ䫵tŠk{Àv;AëÍC3Ö†wž€w¨R_#÷±X» Þ(x§÷Ò‹/q%¶èùW¸ ¨þÅ›ÇÌÜhpíÄk_IöXŠùÇ'b§Éú/fXÞþ²Köi´"#####ã†QCL¼iÀˆ2téè àà€Ê5¬¶L0 ¶¬ÄêQiÞH“2;yÒTêOok;×¢ì Ù¶`õÃRš²Ng{z´y¼!—Kx²¢²·çmì?A(vø£UÒ~Œ°ÎmLÀ(`o/!nòÿ¤°mXŠ€-{ÀvûŽ÷[¾ € dÇw=Àn「ŒŒŒŒûøsdwåüzŽÖnê(åò}O®yŽ~­Ñó ãúmà ›ðï?XUÞ;,àš…V'+û €VŸ&ïJ¸Rê×Z]á§­§:£¥Ï×zC'ýÓ-߆ºžÝÈ@åy ö4¼­Úuó—þ §`VÛ“wö«ÑŠ#÷ýzP@Q˜ N>2/ÿý{¦\o)Žö”ëWøŒ›~a3xLÀw :_QÞ;Œì=pŠÖ¼èdt§Ãî\'8¸º¼ÂÝ~3áSRPÛ¡Ú6Æïõùy+ŸšÏÈÈÈÈXüù€”ÌQ­*¯ÚÞºr üù€”Ì—Ñ­*¯ÚÞºr gÐál™/¤\U^µ½uå$øóüœ|mbÃëVn–ÒÚòw \V½å|ù‡ÞöDËÍŠNVNåæþy‡À7ì¢ÚÙàëk<;œª/ËE}?E*dzgáO ú¨ß~ûègþœ/9¿®6˜Êæê½f c…D}% Š×g$õQî·Gž7öoŽ€)úº¡ÏU J¶ð˜˜o™,O@ú0ß¾Q(íòÀä;žbõ¹¬˜wõ“àÏ:5× úNŒwRÀåþN5ØIòöy'KË?}²¹:9‰mֽ߯®*§±í@fÝ@jU9m‡²ë†Ò«Ê´ÃÉ{öÿÓò$âØ——}öídF€âÿôp¿Ñ|%!DdF¸·>™ýû»}Gö€{ßÜ÷»@FFFFFFƦQÜžH ¹ ªÕºìÿí3 •Ðu øù¾Möo¸½·Ê~êvy»}¡mûwz<Ø7õ•ïnP9ørÆWkÿíñu= ©¯°|«ì_×n½ýëÞz쿳}@ÞþÛãIXÆn÷›‘±çø?Éæsn~‘hhÒHý_IDATxœí] cÛ¶ÉJZ¨˜¢ü˜gvÓ,Ù²&{?:-[3»éÒ®ëýÿŸ3âqÀáÔÓJŒO–¡Ãópî…tŸBý» €}6x9‰µs Sm–CÈÕXȸåñã”âÒÖäR<QήFæ ?SõûÊïœHf+&¹y÷tßSE-Gñàø8ë>ëA¦›mÙªdŠ¥ùì ~Z#îæ½sÝÕ¯6Ï'ìаfN›©„ Ô( ± 0%¸Ä#r(DXXK¦QƒL³^J›*´K•ÀÄʱzT˜²AÁ´°¶é~ÿyd-TLieóS«S(K€êň”< ëëbüÛœÖÌãE%NºÎÝÉS~×._¸òcØ$¿ÍQUb=+C±5¯®ì>W-»¬Z¨8BHÐÚŒŠØ—+$~ïªÇ«º+e¤ð¬ù9*9ÆÿJ*åVDúŸáoó•ÎãÇÈq—@º.ƒÖøiÿÎÌ ÀªÙo«Ãâv¦R~S˜ŠH¥6wß_vÁ cUË‚5–y—@~÷RתÍ#ÑJ9ìì"þ!ꎾùJË:Îd•$‡. Ä3@nVewä%™wã>#—ÐvÀåçÎSÆçvó ᤵ§ Ê»#,eõ¶]T qý/Éà–|¿cßÛä÷˜€Õ©bžfppëß‹bH1F—ÀS Ês¬” L…Õ(?"ƒ£ºªÛ#Y¯‹‹ÐP£)óüC6C\$V-šA´Î$ÒÖÒbœ ö߀3ÀR4˜×mô˜ñ¸`G\›°¸“¾~¯ c:C.Ðå—Uèaž¤çsýuC]7<e7,¼¼4 §ÚÒÉßʯÖUÕÄWœ§-™úÍC¿ôÈv7¢ÃuT{%*¿´9ùÒ"Ïå¿V„ìÓzþáOÄFœ‡U¹8áÑŒ)Y~¼+à%âX­4ïSTaWDÝSWÓÈeju•^ÓÑ“IïÍMg/_M½oe"Ù&ª~m ƒ'â súO“ˆÓW’7ô-…³à×;…3áxõfö’7y£rˆÔ#C¹ä+9ckM].õ8qYŒd#R½ñ`ý¿¯Ù÷ªçkaU^k¦„ö#ü/P?*u®$Æ~²Ïã÷HY‚å¶ê‹52ƒÖŸ·æà€ß^|æÚ‚ùÐðbÛ{|aÁ,?k¾€E/vÅÀƒ…kL¶Ì?Ù£o=;d×xlØÀEÎ{¿øA:?aM2¿$Gq†B+•h‡C>te_O8Á±ðÀdø¨0wPl¶E`À õAAÉ‚ÇÃ[é.ŸÀš‘zdµaVÄ€Ó#Oä4&óêÓkô?ÓèüÆV¶€ýâ Œòø`]€Ù’Pª#N†J7YbeÇ7Ž,H[èF¨2¶4eYª»éS¹Û‰|¡B&µ]KîRë‹Ë¥Ŷ ATôSãÍ6?šîh{ëýÁ´9³Ç ÷"Á\®ïv¸¾ÊÀr9U{„qívªk±¾¨ï‹/¾0‚WàÂ+?£ëçÚq°"GW˨ššå`wͤûëW¿Aãá»ççFæ-Í`aÓë«e«]n"ЙbMB]¼p+5¥Þ¿œý 3œG]SÃŽ.1Yax©Ë)ñÞÈÕ[üå»<¢¾¤+è>òÇ sm¿ßèTÖ؆*³sÉ´òÄ,KÛ¶°\ªˆõÛij`eàßrY¾ÂÊ9yaÚЩ ¨L£|Ïž)L[° T7G£¦êÿÀ·÷RüÀõP¾PºŽ$ä­/0Ý£ù*vSûÜtWF¦¼CEÃÛæ»/2:Ñ•htðL”Ð?Õ8;>l ôfYd„Þüà6É©ØÜ}{Zi÷ìŒuk ÑÞDŸ›òJÓŸüÑñ£S\^z L,uFºüt¼ËKyh‚õ}jãÙdrf$åÍ3ÜŸëˆ×:Cd š.—ÆUÁ•Ù½{ÝA¥¯ojRN„ ¥ç°„”ÑŸQ ìSŒ¿€±/]¾ÍVT—Ÿq _×G•Êã9ÀsE$Zw¸Ïóa×ÎÍÛFUŠH#¿úÕ e GÔæ1üZw¢ÝV7>–naÓO[ÛÍ+Ê€ÁÉï¿4ŠHFª›^® ׆–ôûÆO¯Nf½°½ÃTpzaúð Æ€V@O//¿òÐSíÏ]SÝÏ«wx¿TnÛÚªZGã#Nþ"ÙÀ¿Ía]sµŠÕœè¶öX7 Ë`Æý£ÖñúG{vìøÂ´…é?¾V®é´W_ÁÎFYÍ©i+ÊU'‘óÞ4 V³â ¤Ð7âÐ%yT`뇪rãí¡Xùf©OŸÝÎoå¤@Ao>Wû ãn2¥K*ÝfǦM÷æÉh:«7 5ÅMú+иy„Nöì<ÊP LonØÉ> h:Ç™vÞI~…9é畺K 5fº d˜çÄ·cÎöß=ž8£¥983«¹K4jðv°îÿ¦yÀi¥|@v0cN…ƒÌöûïÙ›µ©Æv+Ì©1†WšÒå»rJÛÍ<=Qm´ã[=áÑî(–A3LÀ¦ÝJÕàLá…X ÌÈH˦ÉÁ6:Õ­¡ziJýc¦'”ÿf&‰éLÛÝt™œÍÌv}1Ãî5Ó Ì|…ÃÆ%›Û¶%à2ÐñïoCÀ–m ãÞñ_xª\€íÞc´)VÿaF³3p[›‘<ºo'ý av}fµPKœ‡“ @täâxfjÄ8 ~?|hØpPŒÀ'Þ222:¶^vß2ƒ`vÜ{'`ƒÛý ð ‚^}9@#pŠÇïÏ=ò,'°@Hš C~XHÆÊƒU0|ËbO∀|cÉû¯Ðp =‘ôkcÛ ö›¾w*##ã¡£à“7ÑK>‰ÜoǽÐ$\ÐùØ…ÆÏFåF‹OÎ"v™—½ „Ãÿp”30ì€F§z8L€&2pG‡>½ã0V~XƒQÁÁƒO²ø¬~‚¯!Eð ´°› 0t$ÚÈ{Š ‚Ãüü F0Ž{FÎ「Œ{íÁ™bZ),\¦(<»`€ãÀ0óéo°ŒÞ¾%Œ•JÞÀVA=÷ÿ#J֟߆ ÷™L 4‚ˆ»”žlO /Ü«b· õ‰(X€‰´&€°…ìÜ®ù™`žÝXÇZòwô›222Žá»Ö>*Dg¾â)ƒ Ÿÿñç0‡ݱÜ…Ø*²ouJ(=³äÙMé–^ 8IV },­f„‚øþ…>­+!¾>•?š †º@´½“øeüjBä—D8òþpOÏagd|PÇT®qòÎgò$ÑÏöÇ8iš)Ñsö0,C~\ :UÖVš6‡çU¸ \Åé`ò77áæ`V1©å„õÄc@f„N/ƒÉªÇ¿fž—ˆPʃV]*hš¢ÔÀ ºëá—w.‘è—¢ƒ{7ºiHëu}Âñ¹Jn3œÀéÅüç@ veõåõì±b¾dõ?wáPú…‚yÈWáË‚¬ÁÅÅEºÀrÙµ¢©yI¼*¸RV2~EðT¦~ú=Nåéµ8eÕ! *{è,Fð-¹ï¡ð :.Y´Ág (øÄ^!.€j©€4È^6ïŠÔµí5–o úB}|~ú[ ×]®;CUµ [¼RÚ)€‘Ña«ÑT>ó7ã¡áË/{ƒKy&©ÍϤÍ{QOÎyÔ)‘Å#«Ã¢½¯rþÏ~Žéa!Á&W¾«ëz ZÏÔ졽Tä¥í×¥çºRôÊÒ¥_»s›ü]4"À‰€oEäÀDçAwUºTÝ8ŒHª¾¹‘vÕÒo%sn\Hœy$È´þ­h‹•zàå4q½R¸ð“;yu5:??÷@§V'.žvlc•l77Ýìœ^ÏÚçWì¾ ¼”£Qg÷¹ÍZ-&…5Ý_éDžƒ?çè1Eù›‘©ˆB¢ÀT¢ç ÞNïîN ÙžÈqJ‰/ { ^Ùbý!#ÃÇ{ çÌ~‡Mî»{äè°x/-Jàƒînòÿƒ)Á˜Qlœjk=%ñà4¨¸×6ôÝ} »þÒt¾÷ ÷yX°“»è3KÈŠ7D·:««mÙÒõï»ß{쇀«0-¼Ÿ2ÛTU÷LÛÖPĆïñX@ é׎|M#±D„ð«/vÐzäXÞýpŸ<Ñ %#ãÓÌØìÄ_%Ý=òéÆ/­9ä(@C@…à À„YM¯¸kfÀ#Ø-rÌ@ÃC›Ê­d8äaG‡@ªÆŒ< Üø@éÞ»@Fƃǃ~?lÙÍládžÜ/¬›wl‘TõÒÂL•ÒdžR¾nñÇF¼¢·“WbæßA%‹ª“óI÷gשáÎ齤Á§'ò39RÍ^ê÷MRèV±òÖ¡‰¾ÖÀUÙué£C ðêÅ+ÚæÕ0 iÈ=§ÈYSŸø}€!úéÛÍèuËÛ–¿ð,V/B»5ôНÂï, œÉð.üC²|½árù”…û´ÎÂÝ×è»¶ Z^;ûÒ0p&ã“h"âñÁ?ÈÀo‚–7~olÂap,l¹r_UšáaÚÏÏFH\¬´€zhýŽ»±à÷†è+G_mB¯[Þ¶ôðûCôÕ·’þöjSzÝò¶¥322`tÆÞ裇œ«Óé:øûò„žÏ{ýô¹ÀãGÀCïû@ÿ{EŽ …:­\ÓåÕý¨^œâ ü?*;Û¢9€/€B½Ao_üîÛ @Ç”þˆ[@ ý] Qýµl u§ùf;þúŠs èåIEND®B`‚testman4trac.1.4.5/testman4trac/trunk/testmanager/htdocs/css/blitzer/jquery-ui-1.8.13.custom.css0000744000175000017500000010270511565111456032400 0ustar robertoroberto/* * jQuery UI CSS Framework 1.8.13 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. * http://jquery.org/license * * http://docs.jquery.com/UI/Theming/API */ /* Layout helpers ----------------------------------*/ .ui-helper-hidden { display: none; } .ui-helper-hidden-accessible { position: absolute !important; clip: rect(1px 1px 1px 1px); clip: rect(1px,1px,1px,1px); } .ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; } .ui-helper-clearfix:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; } .ui-helper-clearfix { display: inline-block; } /* required comment for clearfix to work in Opera \*/ * html .ui-helper-clearfix { height:1%; } .ui-helper-clearfix { display:block; } /* end clearfix */ .ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); } /* Interaction Cues ----------------------------------*/ .ui-state-disabled { cursor: default !important; } /* Icons ----------------------------------*/ /* states and images */ .ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; } /* Misc visuals ----------------------------------*/ /* Overlays */ .ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } /* * jQuery UI CSS Framework 1.8.13 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. * http://jquery.org/license * * http://docs.jquery.com/UI/Theming/API * * To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Arial,sans-serif&fwDefault=bold&fsDefault=1.1em&cornerRadius=6px&bgColorHeader=cc0000&bgTextureHeader=03_highlight_soft.png&bgImgOpacityHeader=15&borderColorHeader=e3a1a1&fcHeader=ffffff&iconColorHeader=ffffff&bgColorContent=ffffff&bgTextureContent=01_flat.png&bgImgOpacityContent=75&borderColorContent=eeeeee&fcContent=333333&iconColorContent=cc0000&bgColorDefault=eeeeee&bgTextureDefault=04_highlight_hard.png&bgImgOpacityDefault=100&borderColorDefault=d8dcdf&fcDefault=004276&iconColorDefault=cc0000&bgColorHover=f6f6f6&bgTextureHover=04_highlight_hard.png&bgImgOpacityHover=100&borderColorHover=cdd5da&fcHover=111111&iconColorHover=cc0000&bgColorActive=ffffff&bgTextureActive=01_flat.png&bgImgOpacityActive=65&borderColorActive=eeeeee&fcActive=cc0000&iconColorActive=cc0000&bgColorHighlight=fbf8ee&bgTextureHighlight=02_glass.png&bgImgOpacityHighlight=55&borderColorHighlight=fcd3a1&fcHighlight=444444&iconColorHighlight=004276&bgColorError=f3d8d8&bgTextureError=08_diagonals_thick.png&bgImgOpacityError=75&borderColorError=cc0000&fcError=2e2e2e&iconColorError=cc0000&bgColorOverlay=a6a6a6&bgTextureOverlay=09_dots_small.png&bgImgOpacityOverlay=65&opacityOverlay=40&bgColorShadow=333333&bgTextureShadow=01_flat.png&bgImgOpacityShadow=0&opacityShadow=10&thicknessShadow=8px&offsetTopShadow=-8px&offsetLeftShadow=-8px&cornerRadiusShadow=8px */ /* Component containers ----------------------------------*/ .ui-widget { font-family: Arial,sans-serif; font-size: 1.1em; } .ui-widget .ui-widget { font-size: 1em; } .ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: Arial,sans-serif; font-size: 1em; } .ui-widget-content { border: 1px solid #eeeeee; background: #ffffff url(images/ui-bg_flat_75_ffffff_40x100.png) 50% 50% repeat-x; color: #333333; } .ui-widget-content a { color: #333333; } .ui-widget-header { border: 1px solid #e3a1a1; background: #cc0000 url(images/ui-bg_highlight-soft_15_cc0000_1x100.png) 50% 50% repeat-x; color: #ffffff; font-weight: bold; } .ui-widget-header a { color: #ffffff; } /* Interaction states ----------------------------------*/ .ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { border: 1px solid #d8dcdf; background: #eeeeee url(images/ui-bg_highlight-hard_100_eeeeee_1x100.png) 50% 50% repeat-x; font-weight: bold; color: #004276; } .ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #004276; text-decoration: none; } .ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { border: 1px solid #cdd5da; background: #f6f6f6 url(images/ui-bg_highlight-hard_100_f6f6f6_1x100.png) 50% 50% repeat-x; font-weight: bold; color: #111111; } .ui-state-hover a, .ui-state-hover a:hover { color: #111111; text-decoration: none; } .ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active { border: 1px solid #eeeeee; background: #ffffff url(images/ui-bg_flat_65_ffffff_40x100.png) 50% 50% repeat-x; font-weight: bold; color: #cc0000; } .ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #cc0000; text-decoration: none; } .ui-widget :active { outline: none; } /* Interaction Cues ----------------------------------*/ .ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-header .ui-state-highlight {border: 1px solid #fcd3a1; background: #fbf8ee url(images/ui-bg_glass_55_fbf8ee_1x400.png) 50% 50% repeat-x; color: #444444; } .ui-state-highlight a, .ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a { color: #444444; } .ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui-state-error {border: 1px solid #cc0000; background: #f3d8d8 url(images/ui-bg_diagonals-thick_75_f3d8d8_40x40.png) 50% 50% repeat; color: #2e2e2e; } .ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header .ui-state-error a { color: #2e2e2e; } .ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-header .ui-state-error-text { color: #2e2e2e; } .ui-priority-primary, .ui-widget-content .ui-priority-primary, .ui-widget-header .ui-priority-primary { font-weight: bold; } .ui-priority-secondary, .ui-widget-content .ui-priority-secondary, .ui-widget-header .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; } .ui-state-disabled, .ui-widget-content .ui-state-disabled, .ui-widget-header .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; } /* Icons ----------------------------------*/ /* states and images */ .ui-icon { width: 16px; height: 16px; background-image: url(images/ui-icons_cc0000_256x240.png); } .ui-widget-content .ui-icon {background-image: url(images/ui-icons_cc0000_256x240.png); } .ui-widget-header .ui-icon {background-image: url(images/ui-icons_ffffff_256x240.png); } .ui-state-default .ui-icon { background-image: url(images/ui-icons_cc0000_256x240.png); } .ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(images/ui-icons_cc0000_256x240.png); } .ui-state-active .ui-icon {background-image: url(images/ui-icons_cc0000_256x240.png); } .ui-state-highlight .ui-icon {background-image: url(images/ui-icons_004276_256x240.png); } .ui-state-error .ui-icon, .ui-state-error-text .ui-icon {background-image: url(images/ui-icons_cc0000_256x240.png); } /* positioning */ .ui-icon-carat-1-n { background-position: 0 0; } .ui-icon-carat-1-ne { background-position: -16px 0; } .ui-icon-carat-1-e { background-position: -32px 0; } .ui-icon-carat-1-se { background-position: -48px 0; } .ui-icon-carat-1-s { background-position: -64px 0; } .ui-icon-carat-1-sw { background-position: -80px 0; } .ui-icon-carat-1-w { background-position: -96px 0; } .ui-icon-carat-1-nw { background-position: -112px 0; } .ui-icon-carat-2-n-s { background-position: -128px 0; } .ui-icon-carat-2-e-w { background-position: -144px 0; } .ui-icon-triangle-1-n { background-position: 0 -16px; } .ui-icon-triangle-1-ne { background-position: -16px -16px; } .ui-icon-triangle-1-e { background-position: -32px -16px; } .ui-icon-triangle-1-se { background-position: -48px -16px; } .ui-icon-triangle-1-s { background-position: -64px -16px; } .ui-icon-triangle-1-sw { background-position: -80px -16px; } .ui-icon-triangle-1-w { background-position: -96px -16px; } .ui-icon-triangle-1-nw { background-position: -112px -16px; } .ui-icon-triangle-2-n-s { background-position: -128px -16px; } .ui-icon-triangle-2-e-w { background-position: -144px -16px; } .ui-icon-arrow-1-n { background-position: 0 -32px; } .ui-icon-arrow-1-ne { background-position: -16px -32px; } .ui-icon-arrow-1-e { background-position: -32px -32px; } .ui-icon-arrow-1-se { background-position: -48px -32px; } .ui-icon-arrow-1-s { background-position: -64px -32px; } .ui-icon-arrow-1-sw { background-position: -80px -32px; } .ui-icon-arrow-1-w { background-position: -96px -32px; } .ui-icon-arrow-1-nw { background-position: -112px -32px; } .ui-icon-arrow-2-n-s { background-position: -128px -32px; } .ui-icon-arrow-2-ne-sw { background-position: -144px -32px; } .ui-icon-arrow-2-e-w { background-position: -160px -32px; } .ui-icon-arrow-2-se-nw { background-position: -176px -32px; } .ui-icon-arrowstop-1-n { background-position: -192px -32px; } .ui-icon-arrowstop-1-e { background-position: -208px -32px; } .ui-icon-arrowstop-1-s { background-position: -224px -32px; } .ui-icon-arrowstop-1-w { background-position: -240px -32px; } .ui-icon-arrowthick-1-n { background-position: 0 -48px; } .ui-icon-arrowthick-1-ne { background-position: -16px -48px; } .ui-icon-arrowthick-1-e { background-position: -32px -48px; } .ui-icon-arrowthick-1-se { background-position: -48px -48px; } .ui-icon-arrowthick-1-s { background-position: -64px -48px; } .ui-icon-arrowthick-1-sw { background-position: -80px -48px; } .ui-icon-arrowthick-1-w { background-position: -96px -48px; } .ui-icon-arrowthick-1-nw { background-position: -112px -48px; } .ui-icon-arrowthick-2-n-s { background-position: -128px -48px; } .ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; } .ui-icon-arrowthick-2-e-w { background-position: -160px -48px; } .ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; } .ui-icon-arrowthickstop-1-n { background-position: -192px -48px; } .ui-icon-arrowthickstop-1-e { background-position: -208px -48px; } .ui-icon-arrowthickstop-1-s { background-position: -224px -48px; } .ui-icon-arrowthickstop-1-w { background-position: -240px -48px; } .ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; } .ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; } .ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; } .ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; } .ui-icon-arrowreturn-1-w { background-position: -64px -64px; } .ui-icon-arrowreturn-1-n { background-position: -80px -64px; } .ui-icon-arrowreturn-1-e { background-position: -96px -64px; } .ui-icon-arrowreturn-1-s { background-position: -112px -64px; } .ui-icon-arrowrefresh-1-w { background-position: -128px -64px; } .ui-icon-arrowrefresh-1-n { background-position: -144px -64px; } .ui-icon-arrowrefresh-1-e { background-position: -160px -64px; } .ui-icon-arrowrefresh-1-s { background-position: -176px -64px; } .ui-icon-arrow-4 { background-position: 0 -80px; } .ui-icon-arrow-4-diag { background-position: -16px -80px; } .ui-icon-extlink { background-position: -32px -80px; } .ui-icon-newwin { background-position: -48px -80px; } .ui-icon-refresh { background-position: -64px -80px; } .ui-icon-shuffle { background-position: -80px -80px; } .ui-icon-transfer-e-w { background-position: -96px -80px; } .ui-icon-transferthick-e-w { background-position: -112px -80px; } .ui-icon-folder-collapsed { background-position: 0 -96px; } .ui-icon-folder-open { background-position: -16px -96px; } .ui-icon-document { background-position: -32px -96px; } .ui-icon-document-b { background-position: -48px -96px; } .ui-icon-note { background-position: -64px -96px; } .ui-icon-mail-closed { background-position: -80px -96px; } .ui-icon-mail-open { background-position: -96px -96px; } .ui-icon-suitcase { background-position: -112px -96px; } .ui-icon-comment { background-position: -128px -96px; } .ui-icon-person { background-position: -144px -96px; } .ui-icon-print { background-position: -160px -96px; } .ui-icon-trash { background-position: -176px -96px; } .ui-icon-locked { background-position: -192px -96px; } .ui-icon-unlocked { background-position: -208px -96px; } .ui-icon-bookmark { background-position: -224px -96px; } .ui-icon-tag { background-position: -240px -96px; } .ui-icon-home { background-position: 0 -112px; } .ui-icon-flag { background-position: -16px -112px; } .ui-icon-calendar { background-position: -32px -112px; } .ui-icon-cart { background-position: -48px -112px; } .ui-icon-pencil { background-position: -64px -112px; } .ui-icon-clock { background-position: -80px -112px; } .ui-icon-disk { background-position: -96px -112px; } .ui-icon-calculator { background-position: -112px -112px; } .ui-icon-zoomin { background-position: -128px -112px; } .ui-icon-zoomout { background-position: -144px -112px; } .ui-icon-search { background-position: -160px -112px; } .ui-icon-wrench { background-position: -176px -112px; } .ui-icon-gear { background-position: -192px -112px; } .ui-icon-heart { background-position: -208px -112px; } .ui-icon-star { background-position: -224px -112px; } .ui-icon-link { background-position: -240px -112px; } .ui-icon-cancel { background-position: 0 -128px; } .ui-icon-plus { background-position: -16px -128px; } .ui-icon-plusthick { background-position: -32px -128px; } .ui-icon-minus { background-position: -48px -128px; } .ui-icon-minusthick { background-position: -64px -128px; } .ui-icon-close { background-position: -80px -128px; } .ui-icon-closethick { background-position: -96px -128px; } .ui-icon-key { background-position: -112px -128px; } .ui-icon-lightbulb { background-position: -128px -128px; } .ui-icon-scissors { background-position: -144px -128px; } .ui-icon-clipboard { background-position: -160px -128px; } .ui-icon-copy { background-position: -176px -128px; } .ui-icon-contact { background-position: -192px -128px; } .ui-icon-image { background-position: -208px -128px; } .ui-icon-video { background-position: -224px -128px; } .ui-icon-script { background-position: -240px -128px; } .ui-icon-alert { background-position: 0 -144px; } .ui-icon-info { background-position: -16px -144px; } .ui-icon-notice { background-position: -32px -144px; } .ui-icon-help { background-position: -48px -144px; } .ui-icon-check { background-position: -64px -144px; } .ui-icon-bullet { background-position: -80px -144px; } .ui-icon-radio-off { background-position: -96px -144px; } .ui-icon-radio-on { background-position: -112px -144px; } .ui-icon-pin-w { background-position: -128px -144px; } .ui-icon-pin-s { background-position: -144px -144px; } .ui-icon-play { background-position: 0 -160px; } .ui-icon-pause { background-position: -16px -160px; } .ui-icon-seek-next { background-position: -32px -160px; } .ui-icon-seek-prev { background-position: -48px -160px; } .ui-icon-seek-end { background-position: -64px -160px; } .ui-icon-seek-start { background-position: -80px -160px; } /* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */ .ui-icon-seek-first { background-position: -80px -160px; } .ui-icon-stop { background-position: -96px -160px; } .ui-icon-eject { background-position: -112px -160px; } .ui-icon-volume-off { background-position: -128px -160px; } .ui-icon-volume-on { background-position: -144px -160px; } .ui-icon-power { background-position: 0 -176px; } .ui-icon-signal-diag { background-position: -16px -176px; } .ui-icon-signal { background-position: -32px -176px; } .ui-icon-battery-0 { background-position: -48px -176px; } .ui-icon-battery-1 { background-position: -64px -176px; } .ui-icon-battery-2 { background-position: -80px -176px; } .ui-icon-battery-3 { background-position: -96px -176px; } .ui-icon-circle-plus { background-position: 0 -192px; } .ui-icon-circle-minus { background-position: -16px -192px; } .ui-icon-circle-close { background-position: -32px -192px; } .ui-icon-circle-triangle-e { background-position: -48px -192px; } .ui-icon-circle-triangle-s { background-position: -64px -192px; } .ui-icon-circle-triangle-w { background-position: -80px -192px; } .ui-icon-circle-triangle-n { background-position: -96px -192px; } .ui-icon-circle-arrow-e { background-position: -112px -192px; } .ui-icon-circle-arrow-s { background-position: -128px -192px; } .ui-icon-circle-arrow-w { background-position: -144px -192px; } .ui-icon-circle-arrow-n { background-position: -160px -192px; } .ui-icon-circle-zoomin { background-position: -176px -192px; } .ui-icon-circle-zoomout { background-position: -192px -192px; } .ui-icon-circle-check { background-position: -208px -192px; } .ui-icon-circlesmall-plus { background-position: 0 -208px; } .ui-icon-circlesmall-minus { background-position: -16px -208px; } .ui-icon-circlesmall-close { background-position: -32px -208px; } .ui-icon-squaresmall-plus { background-position: -48px -208px; } .ui-icon-squaresmall-minus { background-position: -64px -208px; } .ui-icon-squaresmall-close { background-position: -80px -208px; } .ui-icon-grip-dotted-vertical { background-position: 0 -224px; } .ui-icon-grip-dotted-horizontal { background-position: -16px -224px; } .ui-icon-grip-solid-vertical { background-position: -32px -224px; } .ui-icon-grip-solid-horizontal { background-position: -48px -224px; } .ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; } .ui-icon-grip-diagonal-se { background-position: -80px -224px; } /* Misc visuals ----------------------------------*/ /* Corner radius */ .ui-corner-tl { -moz-border-radius-topleft: 6px; -webkit-border-top-left-radius: 6px; border-top-left-radius: 6px; } .ui-corner-tr { -moz-border-radius-topright: 6px; -webkit-border-top-right-radius: 6px; border-top-right-radius: 6px; } .ui-corner-bl { -moz-border-radius-bottomleft: 6px; -webkit-border-bottom-left-radius: 6px; border-bottom-left-radius: 6px; } .ui-corner-br { -moz-border-radius-bottomright: 6px; -webkit-border-bottom-right-radius: 6px; border-bottom-right-radius: 6px; } .ui-corner-top { -moz-border-radius-topleft: 6px; -webkit-border-top-left-radius: 6px; border-top-left-radius: 6px; -moz-border-radius-topright: 6px; -webkit-border-top-right-radius: 6px; border-top-right-radius: 6px; } .ui-corner-bottom { -moz-border-radius-bottomleft: 6px; -webkit-border-bottom-left-radius: 6px; border-bottom-left-radius: 6px; -moz-border-radius-bottomright: 6px; -webkit-border-bottom-right-radius: 6px; border-bottom-right-radius: 6px; } .ui-corner-right { -moz-border-radius-topright: 6px; -webkit-border-top-right-radius: 6px; border-top-right-radius: 6px; -moz-border-radius-bottomright: 6px; -webkit-border-bottom-right-radius: 6px; border-bottom-right-radius: 6px; } .ui-corner-left { -moz-border-radius-topleft: 6px; -webkit-border-top-left-radius: 6px; border-top-left-radius: 6px; -moz-border-radius-bottomleft: 6px; -webkit-border-bottom-left-radius: 6px; border-bottom-left-radius: 6px; } .ui-corner-all { -moz-border-radius: 6px; -webkit-border-radius: 6px; border-radius: 6px; } /* Overlays */ .ui-widget-overlay { background: #a6a6a6 url(images/ui-bg_dots-small_65_a6a6a6_2x2.png) 50% 50% repeat; opacity: .40;filter:Alpha(Opacity=40); } .ui-widget-shadow { margin: -8px 0 0 -8px; padding: 8px; background: #333333 url(images/ui-bg_flat_0_333333_40x100.png) 50% 50% repeat-x; opacity: .10;filter:Alpha(Opacity=10); -moz-border-radius: 8px; -webkit-border-radius: 8px; border-radius: 8px; }/* * jQuery UI Resizable 1.8.13 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. * http://jquery.org/license * * http://docs.jquery.com/UI/Resizable#theming */ .ui-resizable { position: relative;} .ui-resizable-handle { position: absolute;font-size: 0.1px;z-index: 99999; display: block; /* http://bugs.jqueryui.com/ticket/7233 - Resizable: resizable handles fail to work in IE if transparent and content overlaps */ background-image:url(data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=); } .ui-resizable-disabled .ui-resizable-handle, .ui-resizable-autohide .ui-resizable-handle { display: none; } .ui-resizable-n { cursor: n-resize; height: 7px; width: 100%; top: -5px; left: 0; } .ui-resizable-s { cursor: s-resize; height: 7px; width: 100%; bottom: -5px; left: 0; } .ui-resizable-e { cursor: e-resize; width: 7px; right: -5px; top: 0; height: 100%; } .ui-resizable-w { cursor: w-resize; width: 7px; left: -5px; top: 0; height: 100%; } .ui-resizable-se { cursor: se-resize; width: 12px; height: 12px; right: 1px; bottom: 1px; } .ui-resizable-sw { cursor: sw-resize; width: 9px; height: 9px; left: -5px; bottom: -5px; } .ui-resizable-nw { cursor: nw-resize; width: 9px; height: 9px; left: -5px; top: -5px; } .ui-resizable-ne { cursor: ne-resize; width: 9px; height: 9px; right: -5px; top: -5px;}/* * jQuery UI Selectable 1.8.13 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. * http://jquery.org/license * * http://docs.jquery.com/UI/Selectable#theming */ .ui-selectable-helper { position: absolute; z-index: 100; border:1px dotted black; } /* * jQuery UI Accordion 1.8.13 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. * http://jquery.org/license * * http://docs.jquery.com/UI/Accordion#theming */ /* IE/Win - Fix animation bug - #4615 */ .ui-accordion { width: 100%; } .ui-accordion .ui-accordion-header { cursor: pointer; position: relative; margin-top: 1px; zoom: 1; } .ui-accordion .ui-accordion-li-fix { display: inline; } .ui-accordion .ui-accordion-header-active { border-bottom: 0 !important; } .ui-accordion .ui-accordion-header a { display: block; font-size: 1em; padding: .5em .5em .5em .7em; } .ui-accordion-icons .ui-accordion-header a { padding-left: 2.2em; } .ui-accordion .ui-accordion-header .ui-icon { position: absolute; left: .5em; top: 50%; margin-top: -8px; } .ui-accordion .ui-accordion-content { padding: 1em 2.2em; border-top: 0; margin-top: -2px; position: relative; top: 1px; margin-bottom: 2px; overflow: auto; display: none; zoom: 1; } .ui-accordion .ui-accordion-content-active { display: block; } /* * jQuery UI Autocomplete 1.8.13 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. * http://jquery.org/license * * http://docs.jquery.com/UI/Autocomplete#theming */ .ui-autocomplete { position: absolute; cursor: default; } /* workarounds */ * html .ui-autocomplete { width:1px; } /* without this, the menu expands to 100% in IE6 */ /* * jQuery UI Menu 1.8.13 * * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. * http://jquery.org/license * * http://docs.jquery.com/UI/Menu#theming */ .ui-menu { list-style:none; padding: 2px; margin: 0; display:block; float: left; } .ui-menu .ui-menu { margin-top: -3px; } .ui-menu .ui-menu-item { margin:0; padding: 0; zoom: 1; float: left; clear: left; width: 100%; } .ui-menu .ui-menu-item a { text-decoration:none; display:block; padding:.2em .4em; line-height:1.5; zoom:1; } .ui-menu .ui-menu-item a.ui-state-hover, .ui-menu .ui-menu-item a.ui-state-active { font-weight: normal; margin: -1px; } /* * jQuery UI Button 1.8.13 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. * http://jquery.org/license * * http://docs.jquery.com/UI/Button#theming */ .ui-button { display: inline-block; position: relative; padding: 0; margin-right: .1em; text-decoration: none !important; cursor: pointer; text-align: center; zoom: 1; overflow: visible; } /* the overflow property removes extra width in IE */ .ui-button-icon-only { width: 2.2em; } /* to make room for the icon, a width needs to be set here */ button.ui-button-icon-only { width: 2.4em; } /* button elements seem to need a little more width */ .ui-button-icons-only { width: 3.4em; } button.ui-button-icons-only { width: 3.7em; } /*button text element */ .ui-button .ui-button-text { display: block; line-height: 1.4; } .ui-button-text-only .ui-button-text { padding: .4em 1em; } .ui-button-icon-only .ui-button-text, .ui-button-icons-only .ui-button-text { padding: .4em; text-indent: -9999999px; } .ui-button-text-icon-primary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 1em .4em 2.1em; } .ui-button-text-icon-secondary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 2.1em .4em 1em; } .ui-button-text-icons .ui-button-text { padding-left: 2.1em; padding-right: 2.1em; } /* no icon support for input elements, provide padding by default */ input.ui-button { padding: .4em 1em; } /*button icon element(s) */ .ui-button-icon-only .ui-icon, .ui-button-text-icon-primary .ui-icon, .ui-button-text-icon-secondary .ui-icon, .ui-button-text-icons .ui-icon, .ui-button-icons-only .ui-icon { position: absolute; top: 50%; margin-top: -8px; } .ui-button-icon-only .ui-icon { left: 50%; margin-left: -8px; } .ui-button-text-icon-primary .ui-button-icon-primary, .ui-button-text-icons .ui-button-icon-primary, .ui-button-icons-only .ui-button-icon-primary { left: .5em; } .ui-button-text-icon-secondary .ui-button-icon-secondary, .ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; } .ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; } /*button sets*/ .ui-buttonset { margin-right: 7px; } .ui-buttonset .ui-button { margin-left: 0; margin-right: -.3em; } /* workarounds */ button.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra padding in Firefox */ /* * jQuery UI Dialog 1.8.13 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. * http://jquery.org/license * * http://docs.jquery.com/UI/Dialog#theming */ .ui-dialog { position: absolute; padding: .2em; width: 300px; overflow: hidden; } .ui-dialog .ui-dialog-titlebar { padding: .4em 1em; position: relative; } .ui-dialog .ui-dialog-title { float: left; margin: .1em 16px .1em 0; } .ui-dialog .ui-dialog-titlebar-close { position: absolute; right: .3em; top: 50%; width: 19px; margin: -10px 0 0 0; padding: 1px; height: 18px; } .ui-dialog .ui-dialog-titlebar-close span { display: block; margin: 1px; } .ui-dialog .ui-dialog-titlebar-close:hover, .ui-dialog .ui-dialog-titlebar-close:focus { padding: 0; } .ui-dialog .ui-dialog-content { position: relative; border: 0; padding: .5em 1em; background: none; overflow: auto; zoom: 1; } .ui-dialog .ui-dialog-buttonpane { text-align: left; border-width: 1px 0 0 0; background-image: none; margin: .5em 0 0 0; padding: .3em 1em .5em .4em; } .ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset { float: right; } .ui-dialog .ui-dialog-buttonpane button { margin: .5em .4em .5em 0; cursor: pointer; } .ui-dialog .ui-resizable-se { width: 14px; height: 14px; right: 3px; bottom: 3px; } .ui-draggable .ui-dialog-titlebar { cursor: move; } /* * jQuery UI Slider 1.8.13 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. * http://jquery.org/license * * http://docs.jquery.com/UI/Slider#theming */ .ui-slider { position: relative; text-align: left; } .ui-slider .ui-slider-handle { position: absolute; z-index: 2; width: 1.2em; height: 1.2em; cursor: default; } .ui-slider .ui-slider-range { position: absolute; z-index: 1; font-size: .7em; display: block; border: 0; background-position: 0 0; } .ui-slider-horizontal { height: .8em; } .ui-slider-horizontal .ui-slider-handle { top: -.3em; margin-left: -.6em; } .ui-slider-horizontal .ui-slider-range { top: 0; height: 100%; } .ui-slider-horizontal .ui-slider-range-min { left: 0; } .ui-slider-horizontal .ui-slider-range-max { right: 0; } .ui-slider-vertical { width: .8em; height: 100px; } .ui-slider-vertical .ui-slider-handle { left: -.3em; margin-left: 0; margin-bottom: -.6em; } .ui-slider-vertical .ui-slider-range { left: 0; width: 100%; } .ui-slider-vertical .ui-slider-range-min { bottom: 0; } .ui-slider-vertical .ui-slider-range-max { top: 0; }/* * jQuery UI Tabs 1.8.13 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. * http://jquery.org/license * * http://docs.jquery.com/UI/Tabs#theming */ .ui-tabs { position: relative; padding: .2em; zoom: 1; } /* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */ .ui-tabs .ui-tabs-nav { margin: 0; padding: .2em .2em 0; } .ui-tabs .ui-tabs-nav li { list-style: none; float: left; position: relative; top: 1px; margin: 0 .2em 1px 0; border-bottom: 0 !important; padding: 0; white-space: nowrap; } .ui-tabs .ui-tabs-nav li a { float: left; padding: .5em 1em; text-decoration: none; } .ui-tabs .ui-tabs-nav li.ui-tabs-selected { margin-bottom: 0; padding-bottom: 1px; } .ui-tabs .ui-tabs-nav li.ui-tabs-selected a, .ui-tabs .ui-tabs-nav li.ui-state-disabled a, .ui-tabs .ui-tabs-nav li.ui-state-processing a { cursor: text; } .ui-tabs .ui-tabs-nav li a, .ui-tabs.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-selected a { cursor: pointer; } /* first selector in group seems obsolete, but required to overcome bug in Opera applying cursor: text overall if defined elsewhere... */ .ui-tabs .ui-tabs-panel { display: block; border-width: 0; padding: 1em 1.4em; background: none; } .ui-tabs .ui-tabs-hide { display: none !important; } /* * jQuery UI Datepicker 1.8.13 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. * http://jquery.org/license * * http://docs.jquery.com/UI/Datepicker#theming */ .ui-datepicker { width: 17em; padding: .2em .2em 0; display: none; } .ui-datepicker .ui-datepicker-header { position:relative; padding:.2em 0; } .ui-datepicker .ui-datepicker-prev, .ui-datepicker .ui-datepicker-next { position:absolute; top: 2px; width: 1.8em; height: 1.8em; } .ui-datepicker .ui-datepicker-prev-hover, .ui-datepicker .ui-datepicker-next-hover { top: 1px; } .ui-datepicker .ui-datepicker-prev { left:2px; } .ui-datepicker .ui-datepicker-next { right:2px; } .ui-datepicker .ui-datepicker-prev-hover { left:1px; } .ui-datepicker .ui-datepicker-next-hover { right:1px; } .ui-datepicker .ui-datepicker-prev span, .ui-datepicker .ui-datepicker-next span { display: block; position: absolute; left: 50%; margin-left: -8px; top: 50%; margin-top: -8px; } .ui-datepicker .ui-datepicker-title { margin: 0 2.3em; line-height: 1.8em; text-align: center; } .ui-datepicker .ui-datepicker-title select { font-size:1em; margin:1px 0; } .ui-datepicker select.ui-datepicker-month-year {width: 100%;} .ui-datepicker select.ui-datepicker-month, .ui-datepicker select.ui-datepicker-year { width: 49%;} .ui-datepicker table {width: 100%; font-size: .9em; border-collapse: collapse; margin:0 0 .4em; } .ui-datepicker th { padding: .7em .3em; text-align: center; font-weight: bold; border: 0; } .ui-datepicker td { border: 0; padding: 1px; } .ui-datepicker td span, .ui-datepicker td a { display: block; padding: .2em; text-align: right; text-decoration: none; } .ui-datepicker .ui-datepicker-buttonpane { background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0; } .ui-datepicker .ui-datepicker-buttonpane button { float: right; margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; } .ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { float:left; } /* with multiple calendars */ .ui-datepicker.ui-datepicker-multi { width:auto; } .ui-datepicker-multi .ui-datepicker-group { float:left; } .ui-datepicker-multi .ui-datepicker-group table { width:95%; margin:0 auto .4em; } .ui-datepicker-multi-2 .ui-datepicker-group { width:50%; } .ui-datepicker-multi-3 .ui-datepicker-group { width:33.3%; } .ui-datepicker-multi-4 .ui-datepicker-group { width:25%; } .ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header { border-left-width:0; } .ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { border-left-width:0; } .ui-datepicker-multi .ui-datepicker-buttonpane { clear:left; } .ui-datepicker-row-break { clear:both; width:100%; } /* RTL support */ .ui-datepicker-rtl { direction: rtl; } .ui-datepicker-rtl .ui-datepicker-prev { right: 2px; left: auto; } .ui-datepicker-rtl .ui-datepicker-next { left: 2px; right: auto; } .ui-datepicker-rtl .ui-datepicker-prev:hover { right: 1px; left: auto; } .ui-datepicker-rtl .ui-datepicker-next:hover { left: 1px; right: auto; } .ui-datepicker-rtl .ui-datepicker-buttonpane { clear:right; } .ui-datepicker-rtl .ui-datepicker-buttonpane button { float: left; } .ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current { float:right; } .ui-datepicker-rtl .ui-datepicker-group { float:right; } .ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header { border-right-width:0; border-left-width:1px; } .ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { border-right-width:0; border-left-width:1px; } /* IE6 IFRAME FIX (taken from datepicker 1.5.3 */ .ui-datepicker-cover { display: none; /*sorry for IE5*/ display/**/: block; /*sorry for IE5*/ position: absolute; /*must have*/ z-index: -1; /*must have*/ filter: mask(); /*must have*/ top: -4px; /*must have*/ left: -4px; /*must have*/ width: 200px; /*must have*/ height: 200px; /*must have*/ }/* * jQuery UI Progressbar 1.8.13 * * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. * http://jquery.org/license * * http://docs.jquery.com/UI/Progressbar#theming */ .ui-progressbar { height:2em; text-align: left; } .ui-progressbar .ui-progressbar-value {margin: -1px; height:100%; }testman4trac.1.4.5/testman4trac/trunk/testmanager/htdocs/css/menu.css0000755000175000017500000000726611565162323025707 0ustar robertoroberto/** ********************************************* * Prototype of styles for horizontal CSS-menu * @data 30.06.2009 ********************************************* * (X)HTML-scheme: * ********************************************* */ /* menu::base */ div#menu { height:27px; background:url(images/nav-bg.png) repeat-x; _background:url(images/nav-bg.gif) repeat-x; } div#menu ul { margin: 0; padding: 0; list-style: none; float: left; } div#menu ul.menu { padding-left: 30px; } div#menu li { position: relative; z-index: 9; margin: 0; padding: 0; display: block; float: left; } div#menu li:hover>ul { left: -2px; } div#menu li div { list-style: none; float: left; position: absolute; top: 25px; left: 0; width: 208px; z-index: 11; visibility: hidden; padding: 0 0 4px 3px; _padding: 0 0 4px 2px; background: url(images/submenu-bottom.png) no-repeat 3px bottom; _background-image: url(images/submenu-bottom.gif); margin: 0 0 0 -4px; } div#menu li:hover>div { visibility: visible; } div#menu li.current a {} /* menu::level1 */ div#menu a { position: relative; z-index: 10; height: 27px; display: block; float: left; padding: 0 5px 0 5px; line-height: 27px; text-decoration: none; } div#menu span { font: normal 12px 'Lucida Sans Unicode','Lucida Grande',Helvetica,Arial,sans-serif; padding-top: 9px; color: #787878; font-weight:bold; text-transform:uppercase; display: block; cursor: pointer; background-repeat: no-repeat; } div#menu ul a:hover span { color: #353535; } div#menu li { background: url(images/nav_separator.png) top left no-repeat; } div#menu li.last span{ background: url(images/nav_separator.png) top right no-repeat; padding: 9px 5px 8px 0; } /* menu::level2 */ div#menu ul ul li { background: url(images/nav_dropdown_sep.gif) left bottom repeat-x; padding: 1px 0; z-index: 4; } div#menu ul ul { z-index: 12; padding: 0; background: rgb(226,226,226) url(images/nav_dropdown_grad.png) right top no-repeat; margin-top:0px; margin-left:2px; margin-right:2px; } div#menu ul ul a { width: 184px; padding: 0px 3px 2px 4px; height: auto; float: none; display: block; background:none; margin-bottom: 1px; z-index: -1; } div#menu ul ul a span { padding: 0 5px 0px 5px; line-height: 10px; color: #454545; font-weight:normal; text-transform: none; background:none; } div#menu ul ul a:hover { background: url(images/submenu-selected-bottom.gif) no-repeat 4px bottom; } div#menu ul ul a:hover span { background: url(images/submenu-selected-top.gif) no-repeat 0px 0px; color: #fff; } div#menu ul ul li.last { background: none; } div#menu ul ul li { width: 100%; } /* menu::level3 */ div#menu ul ul div { width: 104px; margin: -25px 0 0 95px !important; height: auto; _padding: 0 0 4px 2px; } div#menu ul ul ul { _padding-right:1px; } /* lava lamp */ div#menu li.back { } div#menu li.back .left { } testman4trac.1.4.5/testman4trac/trunk/testmanager/locale/0000755000175000017500000000000011565162323023376 5ustar robertorobertotestman4trac.1.4.5/testman4trac/trunk/testmanager/locale/messages.pot0000755000175000017500000001667611565162323025754 0ustar robertoroberto# Translations template for TestManager. # Copyright (C) 2011 Roberto Longobardi # This file is distributed under the same license as the TestManager # project. # FIRST AUTHOR , 2011. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: TestManager 1.4.3\n" "Report-Msgid-Bugs-To: seccanj@gmail.com\n" "POT-Creation-Date: 2011-03-10 16:35+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 0.9.5\n" #: testmanager/api.py:181 testmanager/macros.py:541 #: testmanager/macros.py:864 msgid "Timestamp" msgstr "" #: testmanager/api.py:181 testmanager/macros.py:491 #: testmanager/macros.py:541 testmanager/macros.py:864 #: testmanager/model.py:542 msgid "Author" msgstr "" #: testmanager/api.py:181 testmanager/api.py:194 testmanager/macros.py:491 #: testmanager/macros.py:864 testmanager/model.py:535 msgid "Status" msgstr "" #: testmanager/macros.py:218 msgid "All Catalogs" msgstr "" #: testmanager/macros.py:307 testmanager/macros.py:328 #: testmanager/macros.py:446 testmanager/macros.py:468 msgid "Filter:" msgstr "" #: testmanager/macros.py:307 testmanager/macros.py:328 #: testmanager/macros.py:446 testmanager/macros.py:468 msgid "" "Type the test to search for, even more than one word. You can also " "filter on the test case status (untested, successful, failed)." msgstr "" #: testmanager/macros.py:308 testmanager/macros.py:447 msgid "Expand all" msgstr "" #: testmanager/macros.py:308 testmanager/macros.py:447 msgid "Collapse all" msgstr "" #: testmanager/macros.py:333 testmanager/macros.py:473 #: testmanager/model.py:541 msgid "Name" msgstr "" #: testmanager/macros.py:342 testmanager/macros.py:482 #: testmanager/model.py:524 testmanager/model.py:528 #: testmanager/model.py:532 testmanager/model.py:538 msgid "ID" msgstr "" #: testmanager/macros.py:352 msgid "Description" msgstr "" #: testmanager/macros.py:491 msgid "Last Change" msgstr "" #: testmanager/macros.py:527 msgid "Available Test Plans" msgstr "" #: testmanager/macros.py:541 msgid "Plan Name" msgstr "" #: testmanager/macros.py:547 msgid "Open Test Plan" msgstr "" #: testmanager/macros.py:552 msgid "Delete" msgstr "" #: testmanager/macros.py:615 testmanager/macros.py:710 msgid "Open" msgstr "" #: testmanager/macros.py:656 testmanager/macros.py:658 msgid "Edit the Test Case" msgstr "" #: testmanager/macros.py:828 msgid "Change the Status:" msgstr "" #: testmanager/macros.py:861 msgid "Status change history" msgstr "" #: testmanager/model.py:525 testmanager/model.py:529 #: testmanager/model.py:534 testmanager/model.py:540 msgid "Wiki page name" msgstr "" #: testmanager/model.py:533 msgid "Plan ID" msgstr "" #: testmanager/model.py:539 msgid "Catalog ID" msgstr "" #: testmanager/model.py:543 msgid "Created" msgstr "" #: testmanager/model.py:665 testmanager/wiki.py:381 testmanager/wiki.py:445 msgid "Test Case" msgstr "" #: testmanager/model.py:669 msgid "Test Plan" msgstr "" #: testmanager/model.py:674 msgid "Successful" msgstr "" #: testmanager/model.py:675 msgid "Untested" msgstr "" #: testmanager/model.py:676 msgid "Failed" msgstr "" #: testmanager/web_ui.py:67 msgid "Test Manager" msgstr "" #: testmanager/wiki.py:152 msgid "" "Select the catalog into which to paste the Test Cases and click on " "'Paste the copied Test Cases here'. " msgstr "" #: testmanager/wiki.py:153 testmanager/wiki.py:157 testmanager/wiki.py:179 #: testmanager/wiki.py:184 testmanager/wiki.py:371 testmanager/wiki.py:376 msgid "Cancel" msgstr "" #: testmanager/wiki.py:156 msgid "" "Select the catalog into which to paste the Test Case and click on " "'Move the copied Test Case here'. " msgstr "" #: testmanager/wiki.py:159 msgid "Test Catalogs List" msgstr "" #: testmanager/wiki.py:162 msgid "New Catalog:" msgstr "" #: testmanager/wiki.py:163 msgid "Add a Catalog" msgstr "" #: testmanager/wiki.py:178 msgid "" "Select the catalog (even this one) into which to paste the Test Cases" " and click on 'Paste the copied Test Cases here'. " msgstr "" #: testmanager/wiki.py:183 msgid "" "Select the catalog (even this one) into which to paste the Test Case " "and click on 'Move the copied Test Case here'. " msgstr "" #: testmanager/wiki.py:187 msgid "Test Catalog" msgstr "" #: testmanager/wiki.py:189 msgid "New Sub-Catalog:" msgstr "" #: testmanager/wiki.py:190 msgid "Add a Sub-Catalog" msgstr "" #: testmanager/wiki.py:223 msgid "New Test Case:" msgstr "" #: testmanager/wiki.py:227 msgid "Add a Test Case" msgstr "" #: testmanager/wiki.py:231 msgid "New Test Plan:" msgstr "" #: testmanager/wiki.py:235 msgid "Generate a new Test Plan" msgstr "" #: testmanager/wiki.py:244 msgid "Select Multiple Test Cases" msgstr "" #: testmanager/wiki.py:247 msgid "Copy the Selected Test Cases" msgstr "" #: testmanager/wiki.py:251 msgid "Paste the copied Test Cases here" msgstr "" #: testmanager/wiki.py:254 msgid "Move the copied Test Case here" msgstr "" #: testmanager/wiki.py:303 testmanager/wiki.py:478 msgid "Back to the Catalog" msgstr "" #: testmanager/wiki.py:314 msgid "Test Plan: " msgstr "" #: testmanager/wiki.py:370 msgid "" "The Test Cases have been copied. Now select the catalog into which to" " paste the Test Cases and click on 'Paste the copied Test Cases " "here'. " msgstr "" #: testmanager/wiki.py:375 msgid "" "The Test Case has been cut. Now select the catalog into which to move" " the Test Case and click on 'Move the copied Test Case here'. " msgstr "" #: testmanager/wiki.py:391 testmanager/wiki.py:457 msgid "Open a Ticket on this Test Case" msgstr "" #: testmanager/wiki.py:393 testmanager/wiki.py:459 msgid "Show Related Tickets" msgstr "" #: testmanager/wiki.py:395 msgid "Move the Test Case into another catalog" msgstr "" #: testmanager/wiki.py:397 msgid "Duplicate the Test Case" msgstr "" #: testmanager/wiki.py:475 msgid "Back to the Test Plan" msgstr "" #: testmanager/wiki.py:543 msgid "Edit" msgstr "" #: testmanager/wiki.py:547 msgid "Save" msgstr "" #: testmanager/templates/testmanagerstats.html:55 msgid "Test Management Statistics" msgstr "" #: testmanager/templates/testmanagerstats.html:85 msgid "Settings" msgstr "" #: testmanager/templates/testmanagerstats.html:87 msgid "Start Date:" msgstr "" #: testmanager/templates/testmanagerstats.html:89 msgid "End Date:" msgstr "" #: testmanager/templates/testmanagerstats.html:91 msgid "Resolution:" msgstr "" #: testmanager/templates/testmanagerstats.html:94 msgid "1 Day" msgstr "" #: testmanager/templates/testmanagerstats.html:95 msgid "1 Week" msgstr "" #: testmanager/templates/testmanagerstats.html:96 msgid "2 Weeks" msgstr "" #: testmanager/templates/testmanagerstats.html:97 msgid "1 Month" msgstr "" #: testmanager/templates/testmanagerstats.html:98 msgid "2 Months" msgstr "" #: testmanager/templates/testmanagerstats.html:99 msgid "3 Months" msgstr "" #: testmanager/templates/testmanagerstats.html:100 msgid "6 Months" msgstr "" #: testmanager/templates/testmanagerstats.html:101 msgid "1 Year" msgstr "" #: testmanager/templates/testmanagerstats.html:103 msgid "Test Plan:" msgstr "" #: testmanager/templates/testmanagerstats.html:105 msgid "All Test Plans" msgstr "" #: testmanager/templates/testmanagerstats.html:114 msgid "URL to bookmark:" msgstr "" #: testmanager/templates/testmanagerstats.html:128 msgid "Export this data to Excel" msgstr "" #: testmanager/templates/testmanagerstats.html:128 msgid "(CSV format)" msgstr "" testman4trac.1.4.5/testman4trac/trunk/testmanager/locale/messages-js.pot0000755000175000017500000000265111565162323026352 0ustar robertoroberto# Translations template for TestManager. # Copyright (C) 2011 Roberto Longobardi # This file is distributed under the same license as the TestManager # project. # FIRST AUTHOR , 2011. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: TestManager 1.4.3\n" "Report-Msgid-Bugs-To: seccanj@gmail.com\n" "POT-Creation-Date: 2011-03-10 16:35+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 0.9.5\n" #: testmanager/htdocs/js/testmanager.js:15 #: testmanager/htdocs/js/testmanager.js:34 #: testmanager/htdocs/js/testmanager.js:53 msgid "You must specify a name. Length between 4 and 90 characters." msgstr "" #: testmanager/htdocs/js/testmanager.js:20 #: testmanager/htdocs/js/testmanager.js:39 #: testmanager/htdocs/js/testmanager.js:58 msgid "Length between 4 and 90 characters." msgstr "" #: testmanager/htdocs/js/testmanager.js:88 msgid "" "Are you sure you want to duplicate the test catalog and all its contained" " test cases?" msgstr "" #: testmanager/htdocs/js/testmanager.js:95 msgid "" "Are you sure you want to delete the test plan and the state of all its " "contained test cases?" msgstr "" #: testmanager/htdocs/js/testmanager.js:270 #: testmanager/htdocs/js/testmanager.js:419 msgid "Results: " msgstr "" testman4trac.1.4.5/testman4trac/trunk/testmanager/locale/it/0000755000175000017500000000000011565162323024012 5ustar robertorobertotestman4trac.1.4.5/testman4trac/trunk/testmanager/locale/it/LC_MESSAGES/0000755000175000017500000000000011565162323025577 5ustar robertorobertotestman4trac.1.4.5/testman4trac/trunk/testmanager/locale/it/LC_MESSAGES/testmanager.po0000644000175000017500000002321211565162323030451 0ustar robertoroberto# Italian (Italy) translations for TestManager. # Copyright (C) 2010 ORGANIZATION # This file is distributed under the same license as the TestManager # project. # FIRST AUTHOR , 2010. # msgid "" msgstr "" "Project-Id-Version: TestManager 1.3.11\n" "Report-Msgid-Bugs-To: seccanj@gmail.com\n" "POT-Creation-Date: 2010-12-14 12:35+0100\n" "PO-Revision-Date: 2011-03-10 16:35+0100\n" "Last-Translator: Roberto Longobardi \n" "Language-Team: Italian it_IT \n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 0.9.5\n" #: testmanager/api.py:181 testmanager/macros.py:541 testmanager/macros.py:864 msgid "Timestamp" msgstr "Timestamp" #: testmanager/api.py:181 testmanager/macros.py:491 testmanager/macros.py:541 #: testmanager/macros.py:864 testmanager/model.py:542 msgid "Author" msgstr "Autore" #: testmanager/api.py:181 testmanager/api.py:194 testmanager/macros.py:491 #: testmanager/macros.py:864 testmanager/model.py:535 msgid "Status" msgstr "Stato" #: testmanager/macros.py:218 msgid "All Catalogs" msgstr "Tutti i Cataloghi" #: testmanager/macros.py:307 testmanager/macros.py:328 #: testmanager/macros.py:446 testmanager/macros.py:468 msgid "Filter:" msgstr "Filtro:" #: testmanager/macros.py:307 testmanager/macros.py:328 #: testmanager/macros.py:446 testmanager/macros.py:468 msgid "" "Type the test to search for, even more than one word. You can also filter" " on the test case status (untested, successful, failed)." msgstr "" "Inserire il testo da cercare, anche piu\\' parole. Si puo\\' filtrare " "anche per stato dei test cases (da testare, passato, fallito)." #: testmanager/macros.py:308 testmanager/macros.py:447 msgid "Expand all" msgstr "Espandi tutto" #: testmanager/macros.py:308 testmanager/macros.py:447 msgid "Collapse all" msgstr "Comprimi tutto" #: testmanager/macros.py:333 testmanager/macros.py:473 testmanager/model.py:541 msgid "Name" msgstr "Nome" #: testmanager/macros.py:342 testmanager/macros.py:482 testmanager/model.py:524 #: testmanager/model.py:528 testmanager/model.py:532 testmanager/model.py:538 msgid "ID" msgstr "ID" #: testmanager/macros.py:352 msgid "Description" msgstr "Descrizione" #: testmanager/macros.py:491 msgid "Last Change" msgstr "Ultima Modifica" #: testmanager/macros.py:527 msgid "Available Test Plans" msgstr "Piani di Test disponibili" #: testmanager/macros.py:541 msgid "Plan Name" msgstr "Nome del Piano" #: testmanager/macros.py:547 msgid "Open Test Plan" msgstr "Apri il Piano di Test" #: testmanager/macros.py:552 msgid "Delete" msgstr "Elimina" #: testmanager/macros.py:615 testmanager/macros.py:710 msgid "Open" msgstr "Apri" #: testmanager/macros.py:656 testmanager/macros.py:658 msgid "Edit the Test Case" msgstr "Modifica il Caso di Test" #: testmanager/macros.py:828 msgid "Change the Status:" msgstr "Modifica lo Stato:" #: testmanager/macros.py:861 msgid "Status change history" msgstr "Storia dei cambiamenti di stato" #: testmanager/model.py:525 testmanager/model.py:529 testmanager/model.py:534 #: testmanager/model.py:540 msgid "Wiki page name" msgstr "Nome della pagina wiki" #: testmanager/model.py:533 msgid "Plan ID" msgstr "ID del Piano" #: testmanager/model.py:539 msgid "Catalog ID" msgstr "ID del Catalogo" #: testmanager/model.py:543 msgid "Created" msgstr "Creato" #: testmanager/model.py:665 testmanager/wiki.py:381 testmanager/wiki.py:445 msgid "Test Case" msgstr "Caso di Test" #: testmanager/model.py:669 msgid "Test Plan" msgstr "Piano di Test: " #: testmanager/model.py:674 msgid "Successful" msgstr "Passato" #: testmanager/model.py:675 msgid "Untested" msgstr "Da Testare" #: testmanager/model.py:676 msgid "Failed" msgstr "Fallito" #: testmanager/web_ui.py:67 msgid "Test Manager" msgstr "Test Manager" #: testmanager/wiki.py:152 msgid "" "Select the catalog into which to paste the Test Cases and click on 'Paste" " the copied Test Cases here'. " msgstr "" "Seleziona il catalogo dove incollare il Caso di Test copiato e clicca su " "'Sposta il Caso di Test copiato qui'. " #: testmanager/wiki.py:153 testmanager/wiki.py:157 testmanager/wiki.py:179 #: testmanager/wiki.py:184 testmanager/wiki.py:371 testmanager/wiki.py:376 msgid "Cancel" msgstr "Annulla" #: testmanager/wiki.py:156 msgid "" "Select the catalog into which to paste the Test Case and click on 'Move " "the copied Test Case here'. " msgstr "" "Seleziona il catalogo dove incollare il Caso di Test copiato e clicca su " "'Sposta il Caso di Test copiato qui'. " #: testmanager/wiki.py:159 msgid "Test Catalogs List" msgstr "Lista dei Cataloghi di Test" #: testmanager/wiki.py:162 msgid "New Catalog:" msgstr "Nuovo Catalogo:" #: testmanager/wiki.py:163 msgid "Add a Catalog" msgstr "Aggungi un Catalogo" #: testmanager/wiki.py:178 msgid "" "Select the catalog (even this one) into which to paste the Test Cases and" " click on 'Paste the copied Test Cases here'. " msgstr "" "Seleziona il catalogo (anche questo stesso) dove incollare i Casi di " "Test copiati e clicca su 'Incolla qui i Casi di Test copiati'. " #: testmanager/wiki.py:183 msgid "" "Select the catalog (even this one) into which to paste the Test Case and " "click on 'Move the copied Test Case here'. " msgstr "" "Seleziona il catalogo (anche questo stesso) dove incollare il Caso di " "Test copiato e clicca su 'Sposta il Caso di Test copiato qui'. " #: testmanager/wiki.py:187 msgid "Test Catalog" msgstr "Catalogo di Test" #: testmanager/wiki.py:189 msgid "New Sub-Catalog:" msgstr "Nuovo Sotto Catalogo:" #: testmanager/wiki.py:190 msgid "Add a Sub-Catalog" msgstr "Aggiungi un Sotto Catalogo" #: testmanager/wiki.py:223 msgid "New Test Case:" msgstr "Nuovo Caso di Test:" #: testmanager/wiki.py:227 msgid "Add a Test Case" msgstr "Aggiungi un Caso di Test" #: testmanager/wiki.py:231 msgid "New Test Plan:" msgstr "Nuovo Piano di Test:" #: testmanager/wiki.py:235 msgid "Generate a new Test Plan" msgstr "Genera un Piano di Test" #: testmanager/wiki.py:244 msgid "Select Multiple Test Cases" msgstr "Seleziona piu' Casi di Test" #: testmanager/wiki.py:247 msgid "Copy the Selected Test Cases" msgstr "Copia i Casi di Test Selezionati" #: testmanager/wiki.py:251 msgid "Paste the copied Test Cases here" msgstr "Incolla qui i Casi di Test copiati" #: testmanager/wiki.py:254 msgid "Move the copied Test Case here" msgstr "Sposta qui il Caso di Test tagliato" #: testmanager/wiki.py:303 testmanager/wiki.py:478 msgid "Back to the Catalog" msgstr "Torna al Catalogo" #: testmanager/wiki.py:314 msgid "Test Plan: " msgstr "Piano di Test: " #: testmanager/wiki.py:370 msgid "" "The Test Cases have been copied. Now select the catalog into which to " "paste the Test Cases and click on 'Paste the copied Test Cases here'. " msgstr "" "I Casi di Test sono stati copiati. Adesso seleziona il catalogo dove " "incollarli e clicca su 'Incolla qui i Casi di Test Copiati'. " #: testmanager/wiki.py:375 msgid "" "The Test Case has been cut. Now select the catalog into which to move the" " Test Case and click on 'Move the copied Test Case here'. " msgstr "" "Il Caso di Test e' stato tagliato. Adesso seleziona il catalogo dove " "spostarlo e clicca su 'Sposta qui il Caso di Test tagliato'. " #: testmanager/wiki.py:391 testmanager/wiki.py:457 msgid "Open a Ticket on this Test Case" msgstr "Apri una Segnalazione su questo Caso di Test" #: testmanager/wiki.py:393 testmanager/wiki.py:459 msgid "Show Related Tickets" msgstr "Mostra le Segnalazioni correlate" #: testmanager/wiki.py:395 msgid "Move the Test Case into another catalog" msgstr "Sposta il Caso di Test in altro catalogo" #: testmanager/wiki.py:397 msgid "Duplicate the Test Case" msgstr "Duplica il Caso di Test" #: testmanager/wiki.py:475 msgid "Back to the Test Plan" msgstr "Torna al Piano di Test" #: testmanager/wiki.py:543 msgid "Edit" msgstr "Modifica" #: testmanager/wiki.py:547 msgid "Save" msgstr "Salva" #: testmanager/templates/testmanagerstats.html:55 msgid "Test Management Statistics" msgstr "Statistiche sulla gestione dei Test" #: testmanager/templates/testmanagerstats.html:85 msgid "Settings" msgstr "Impostazioni" #: testmanager/templates/testmanagerstats.html:87 msgid "Start Date:" msgstr "Dal:" #: testmanager/templates/testmanagerstats.html:89 msgid "End Date:" msgstr "Al:" #: testmanager/templates/testmanagerstats.html:91 msgid "Resolution:" msgstr "Risoluzione:" #: testmanager/templates/testmanagerstats.html:94 msgid "1 Day" msgstr "1 Giorno" #: testmanager/templates/testmanagerstats.html:95 msgid "1 Week" msgstr "1 Settimana" #: testmanager/templates/testmanagerstats.html:96 msgid "2 Weeks" msgstr "2 Settimane" #: testmanager/templates/testmanagerstats.html:97 msgid "1 Month" msgstr "1 Mese" #: testmanager/templates/testmanagerstats.html:98 msgid "2 Months" msgstr "2 Mesi" #: testmanager/templates/testmanagerstats.html:99 msgid "3 Months" msgstr "3 Mesi" #: testmanager/templates/testmanagerstats.html:100 msgid "6 Months" msgstr "6 Mesi" #: testmanager/templates/testmanagerstats.html:101 msgid "1 Year" msgstr "1 Anno" #: testmanager/templates/testmanagerstats.html:103 msgid "Test Plan:" msgstr "Piano di Test: " #: testmanager/templates/testmanagerstats.html:105 msgid "All Test Plans" msgstr "Tutti i Piani di Test" #: testmanager/templates/testmanagerstats.html:114 msgid "URL to bookmark:" msgstr "URL da salvare nei preferiti:" #: testmanager/templates/testmanagerstats.html:128 msgid "Export this data to Excel" msgstr "Esporta i dati in Excel" #: testmanager/templates/testmanagerstats.html:128 msgid "(CSV format)" msgstr "(formato CSV)" #~ msgid "SUCCESSFUL" #~ msgstr "Passato" #~ msgid "TO_BE_TESTED" #~ msgstr "Da Testare" #~ msgid "FAILED" #~ msgstr "Fallito" testman4trac.1.4.5/testman4trac/trunk/testmanager/locale/it/LC_MESSAGES/testmanager-js.po0000644000175000017500000000335611565162323031072 0ustar robertoroberto# Italian (Italy) translations for TestManager. # Copyright (C) 2010 Roberto Longobardi # This file is distributed under the same license as the TestManager # project. # FIRST AUTHOR , 2010. # msgid "" msgstr "" "Project-Id-Version: TestManager 1.3.12\n" "Report-Msgid-Bugs-To: seccanj@gmail.com\n" "POT-Creation-Date: 2010-12-17 16:54+0100\n" "PO-Revision-Date: 2011-03-10 16:35+0100\n" "Last-Translator: FULL NAME \n" "Language-Team: it_IT \n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 0.9.5\n" #: testmanager/htdocs/js/testmanager.js:15 #: testmanager/htdocs/js/testmanager.js:34 #: testmanager/htdocs/js/testmanager.js:53 msgid "You must specify a name. Length between 4 and 90 characters." msgstr "Devi indicare un nome. Lunghezza da 4 a 90 caratteri." #: testmanager/htdocs/js/testmanager.js:20 #: testmanager/htdocs/js/testmanager.js:39 #: testmanager/htdocs/js/testmanager.js:58 msgid "Length between 4 and 90 characters." msgstr "Lunghezza da 4 a 90 caratteri." #: testmanager/htdocs/js/testmanager.js:88 msgid "" "Are you sure you want to duplicate the test catalog and all its contained" " test cases?" msgstr "" "Sei sicuro di voler duplicare il catalogo e tutti i test case in esso " "contenuti?" #: testmanager/htdocs/js/testmanager.js:95 msgid "" "Are you sure you want to delete the test plan and the state of all its " "contained test cases?" msgstr "" "Sei sicuro di voler eliminare il piano di test e lo stato di tutti i test" " case in esso contenuti?" #: testmanager/htdocs/js/testmanager.js:270 #: testmanager/htdocs/js/testmanager.js:419 msgid "Results: " msgstr "Risultati: " testman4trac.1.4.5/testman4trac/trunk/testmanager/locale/it/LC_MESSAGES/testmanager.mo0000644000175000017500000001305611566010075030450 0ustar robertorobertoÞ•O”   (/6?GP Ygy ‰–¥¬ÁÕë òý :B IUmr … š´»ÃÜ ß'ë2 7DUdsx‡ §È Ð Úæëtw{dógX À É Þ ê ñ    ) < W d n y ƒ… — ¡ # 4 = ¯L ü   &- 4@GNb}–¨¾Åßñ 3 Bcj r~–Ÿ¸ ¼Êâêò  (#Fjo•©¾Ã,Ù" )6 ERX…t„úooï _ l’˜¸ ÀÍÞ#ú +;K‚[‚Þ a‚kî (CSV format)1 Day1 Month1 Week1 Year2 Months2 Weeks3 Months6 MonthsAdd a CatalogAdd a Sub-CatalogAdd a Test CaseAll CatalogsAll Test PlansAuthorAvailable Test PlansBack to the CatalogBack to the Test PlanCancelCatalog IDChange the Status:Collapse allCopy the Selected Test CasesCreatedDeleteDescriptionDuplicate the Test CaseEditEdit the Test CaseEnd Date:Expand allExport this data to ExcelFailedFilter:Generate a new Test PlanIDLast ChangeMove the Test Case into another catalogMove the copied Test Case hereNameNew Catalog:New Sub-Catalog:New Test Case:New Test Plan:OpenOpen Test PlanOpen a Ticket on this Test CasePaste the copied Test Cases herePlan IDPlan NameResolution:SaveSelect Multiple Test CasesSelect the catalog (even this one) into which to paste the Test Case and click on 'Move the copied Test Case here'. Select the catalog (even this one) into which to paste the Test Cases and click on 'Paste the copied Test Cases here'. Select the catalog into which to paste the Test Case and click on 'Move the copied Test Case here'. Select the catalog into which to paste the Test Cases and click on 'Paste the copied Test Cases here'. SettingsShow Related TicketsStart Date:StatusStatus change historySuccessfulTest CaseTest CatalogTest Catalogs ListTest Management StatisticsTest ManagerTest PlanTest Plan:Test Plan: The Test Case has been cut. Now select the catalog into which to move the Test Case and click on 'Move the copied Test Case here'. The Test Cases have been copied. Now select the catalog into which to paste the Test Cases and click on 'Paste the copied Test Cases here'. TimestampType the test to search for, even more than one word. You can also filter on the test case status (untested, successful, failed).URL to bookmark:UntestedWiki page nameProject-Id-Version: TestManager 1.3.11 Report-Msgid-Bugs-To: seccanj@gmail.com POT-Creation-Date: 2010-12-14 12:35+0100 PO-Revision-Date: 2011-03-10 16:35+0100 Last-Translator: Roberto Longobardi Language-Team: Italian it_IT Plural-Forms: nplurals=2; plural=(n != 1) MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit Generated-By: Babel 0.9.6 (formato CSV)1 Giorno1 Mese1 Settimana1 Anno2 Mesi2 Settimane3 Mesi6 MesiAggungi un CatalogoAggiungi un Sotto CatalogoAggiungi un Caso di TestTutti i CataloghiTutti i Piani di TestAutorePiani di Test disponibiliTorna al CatalogoTorna al Piano di TestAnnullaID del CatalogoModifica lo Stato:Comprimi tuttoCopia i Casi di Test SelezionatiCreatoEliminaDescrizioneDuplica il Caso di TestModificaModifica il Caso di TestAl:Espandi tuttoEsporta i dati in ExcelFallitoFiltro:Genera un Piano di TestIDUltima ModificaSposta il Caso di Test in altro catalogoSposta qui il Caso di Test tagliatoNomeNuovo Catalogo:Nuovo Sotto Catalogo:Nuovo Caso di Test:Nuovo Piano di Test:ApriApri il Piano di TestApri una Segnalazione su questo Caso di TestIncolla qui i Casi di Test copiatiID del PianoNome del PianoRisoluzione:SalvaSeleziona piu' Casi di TestSeleziona il catalogo (anche questo stesso) dove incollare il Caso di Test copiato e clicca su 'Sposta il Caso di Test copiato qui'. Seleziona il catalogo (anche questo stesso) dove incollare i Casi di Test copiati e clicca su 'Incolla qui i Casi di Test copiati'. Seleziona il catalogo dove incollare il Caso di Test copiato e clicca su 'Sposta il Caso di Test copiato qui'. Seleziona il catalogo dove incollare il Caso di Test copiato e clicca su 'Sposta il Caso di Test copiato qui'. ImpostazioniMostra le Segnalazioni correlateDal:StatoStoria dei cambiamenti di statoPassatoCaso di TestCatalogo di TestLista dei Cataloghi di TestStatistiche sulla gestione dei TestTest ManagerPiano di Test: Piano di Test: Piano di Test: Il Caso di Test e' stato tagliato. Adesso seleziona il catalogo dove spostarlo e clicca su 'Sposta qui il Caso di Test tagliato'. I Casi di Test sono stati copiati. Adesso seleziona il catalogo dove incollarli e clicca su 'Incolla qui i Casi di Test Copiati'. TimestampInserire il testo da cercare, anche piu\' parole. Si puo\' filtrare anche per stato dei test cases (da testare, passato, fallito).URL da salvare nei preferiti:Da TestareNome della pagina wikitestman4trac.1.4.5/testman4trac/trunk/testmanager/locale/it/LC_MESSAGES/testmanager-js.mo0000644000175000017500000000207211566010075031056 0ustar robertorobertoÞ•L|\}UÚ#0 T<^Š›a&PˆÙ ø5Are you sure you want to delete the test plan and the state of all its contained test cases?Are you sure you want to duplicate the test catalog and all its contained test cases?Length between 4 and 90 characters.Results: You must specify a name. Length between 4 and 90 characters.Project-Id-Version: TestManager 1.3.12 Report-Msgid-Bugs-To: seccanj@gmail.com POT-Creation-Date: 2010-12-17 16:54+0100 PO-Revision-Date: 2011-03-10 16:35+0100 Last-Translator: FULL NAME Language-Team: it_IT Plural-Forms: nplurals=2; plural=(n != 1) MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit Generated-By: Babel 0.9.6 Sei sicuro di voler eliminare il piano di test e lo stato di tutti i test case in esso contenuti?Sei sicuro di voler duplicare il catalogo e tutti i test case in esso contenuti?Lunghezza da 4 a 90 caratteri.Risultati: Devi indicare un nome. Lunghezza da 4 a 90 caratteri.testman4trac.1.4.5/testman4trac/trunk/testmanager/macros.py0000755000175000017500000010225411565162323024004 0ustar robertoroberto# -*- coding: utf-8 -*- # # Copyright (C) 2010 Roberto Longobardi # import copy from datetime import datetime from StringIO import StringIO from trac.core import * from trac.mimeview.api import Context from trac.util import format_datetime, format_date from trac.wiki.macros import WikiMacroBase from trac.wiki.api import WikiSystem, parse_args from trac.wiki.formatter import Formatter, format_to_html from trac.wiki.model import WikiPage from trac.wiki.parser import WikiParser from genshi import HTML from genshi.builder import tag from genshi.core import Stream, Markup, escape from tracgenericclass.model import GenericClassModelProvider from tracgenericclass.util import * from testmanager.api import TestManagerSystem from testmanager.model import TestCatalog, TestCase, TestCaseInPlan, TestPlan from testmanager.util import * try: from testmanager.api import _, tag_, N_ except ImportError: from trac.util.translation import _, N_ tag_ = _ # Macros class TestCaseBreadcrumbMacro(WikiMacroBase): """Display a breadcrumb with the path to the current catalog or test case. Usage: {{{ [[TestCaseBreadcrumb()]] }}} """ def expand_macro(self, formatter, name, content): if not content: content = formatter.resource.id args, kw = parse_args(content) page_name = kw.get('page_name', 'TC') planid = kw.get('planid', '-1') mode = kw.get('mode', 'tree') fulldetails = kw.get('fulldetails', 'False') fulldetails = (fulldetails == 'True') req = formatter.req return _build_testcases_breadcrumb(self.env, req, page_name, planid, mode, fulldetails) class TestCaseTreeMacro(WikiMacroBase): """Display a tree with catalogs and test cases. Usage: {{{ [[TestCaseTree(mode={'tree'|'tree_table'}, fulldetails={'True|False})]] }}} """ def expand_macro(self, formatter, name, content): args, kw = parse_args(content) catpath = kw.get('catalog_path', 'TC') mode = kw.get('mode', 'tree') fulldetails = kw.get('fulldetails', 'False') fulldetails = (fulldetails == 'True') req = formatter.req return _build_catalog_tree(self.env, req, formatter.context, catpath, mode, fulldetails) class TestPlanTreeMacro(WikiMacroBase): """Display a tree table with catalogs and test cases in a test plan. Includes test case status in the plan. Usage: {{{ [[TestPlanTree(planid=, catalog_path=, mode={'tree'|'tree_table'}, sortby={'modification_time'|'name'})]] }}} """ def expand_macro(self, formatter, name, content): args, kw = parse_args(content) planid = kw.get('planid', '-1') catpath = kw.get('catalog_path', 'TC') mode = kw.get('mode', 'tree') sortby = kw.get('sortby', 'name') req = formatter.req return _build_testplan_tree(self.env, req, formatter.context, planid, catpath, mode, sortby) class TestPlanListMacro(WikiMacroBase): """Display a list of all the plans available for a test catalog. Usage: {{{ [[TestPlanListMacro(catalog_path=)]] }}} """ def expand_macro(self, formatter, name, content): args, kw = parse_args(content) catpath = kw.get('catalog_path', 'TC') mode = kw.get('mode', 'tree') fulldetails = kw.get('fulldetails', 'False') fulldetails = (fulldetails == 'True') req = formatter.req return _build_testplan_list(self.env, req, catpath, mode, fulldetails) class TestCaseStatusMacro(WikiMacroBase): """Display a colored icon according to the test case status in the specified test plan. Usage: {{{ [[TestCaseStatus(planid=)]] }}} """ def expand_macro(self, formatter, name, content): args, kw = parse_args(content) planid = kw.get('planid', '-1') curpage = kw.get('page_name', 'TC') req = formatter.req return _build_testcase_status(self.env, req, planid, curpage) class TestCaseChangeStatusMacro(WikiMacroBase): """Display a semaphore to set the test case status in the specified test plan. Usage: {{{ [[TestCaseChangeStatus(planid=)]] }}} """ def expand_macro(self, formatter, name, content): args, kw = parse_args(content) planid = kw.get('planid', '-1') curpage = kw.get('page_name', 'TC') req = formatter.req return _build_testcase_change_status(self.env, req, planid, curpage) class TestCaseStatusHistoryMacro(WikiMacroBase): """Display the history of status changes of a test case in the specified test plan. Usage: {{{ [[TestCaseStatusHistory(planid=)]] }}} """ def expand_macro(self, formatter, name, content): args, kw = parse_args(content) planid = kw.get('planid', '-1') curpage = kw.get('page_name', 'TC') req = formatter.req return _build_testcase_status_history(self.env, req, planid, curpage) # Internal methods def _build_testcases_breadcrumb(env, req, curpage, planid, mode, fulldetails): # Determine current catalog name cat_name = 'TC' if curpage.find('_TC') >= 0: cat_name = curpage.rpartition('_TC')[0].rpartition('_')[2] elif not curpage == 'TC': cat_name = curpage.rpartition('_')[2] # Create the breadcrumb model path_name = curpage.partition('TC_')[2] tokens = path_name.split("_") curr_path = 'TC' breadcrumb = [{'name': 'TC', 'title': _("All Catalogs"), 'id': 'TC'}] for i, tc in enumerate(tokens): curr_path += '_'+tc page = WikiPage(env, curr_path) page_title = get_page_title(page.text) breadcrumb[(i+1):] = [{'name': tc, 'title': page_title, 'id': curr_path}] if tc == cat_name: break text = '' text +='
    ' text += _render_breadcrumb(breadcrumb, planid, mode, fulldetails) text +='
    ' return text def _build_catalog_tree(env,req, context, curpage, mode='tree', fulldetails=False): # Determine current catalog name cat_name = 'TC' if curpage.find('_TC') >= 0: cat_name = curpage.rpartition('_TC')[0].rpartition('_')[2] #cat_id = '-1' elif not curpage == 'TC': cat_name = curpage.rpartition('_')[2] if cat_name == 'TC': mode = 'tree' fulldetails = False # Create the catalog subtree model components = {'name': curpage, 'childrenC': {},'childrenT': {}, 'tot': 0} unique_idx = 0 for subpage_name in sorted(WikiSystem(env).get_pages(curpage+'_')): subpage = WikiPage(env, subpage_name) subpage_title = get_page_title(subpage.text) path_name = subpage_name.partition(curpage+'_')[2] tokens = path_name.split("_") parent = components ltok = len(tokens) count = 1 curr_path = curpage for tc in tokens: if tc == '': break curr_path += '_'+tc if not tc.startswith('TC'): comp = {} if (tc not in parent['childrenC']): comp = {'id': curr_path, 'name': tc, 'title': subpage_title, 'childrenC': {},'childrenT': {}, 'tot': 0, 'parent': parent} parent['childrenC'][tc]=comp else: comp = parent['childrenC'][tc] parent = comp else: # It is a test case page tc_id = tc.rpartition('TC')[2] if subpage_title in parent['childrenT']: unique_idx += 1 key = subpage_title+str(unique_idx) else: key = subpage_title parent['childrenT'][key]={'id':curr_path, 'tc_id':tc_id, 'title': subpage_title, 'status': '__none__'} compLoop = parent while (True): compLoop['tot']+=1 if ('parent' in compLoop): compLoop = compLoop['parent'] else: break count+=1 # Generate the markup ind = {'count': 0} text = '' if mode == 'tree': text +='
    '+_("Filter:")+'   
    ' text +=''; text +='
    ' text += _render_subtree(env, '-1', components, ind, 0) text +='
    ' elif mode == 'tree_table': tcat_fields = GenericClassModelProvider(env).get_custom_fields_for_realm('testcatalog') tcat_has_custom = tcat_fields is not None and len(tcat_fields) > 0 tc_fields = GenericClassModelProvider(env).get_custom_fields_for_realm('testcase') tc_has_custom = tc_fields is not None and len(tc_fields) > 0 custom_ctx = { 'testcatalog': [tcat_has_custom, tcat_fields], 'testcase': [tc_has_custom, tc_fields], 'testcaseinplan': [False, None] } text +='
    '+_("Filter:")+'   
    ' text += '
    ' text += ''; # Common columns text += '' # Custom testcatalog columns if tcat_has_custom: for f in tcat_fields: if f['type'] == 'text': text += '' # Base testcase columns text += '' # Custom testcase columns if tc_has_custom: for f in tc_fields: if f['type'] == 'text': text += '' # Test case full details if fulldetails: text += '' text += ''; text += _render_subtree_as_table(env, context, None, components, ind, 0, custom_ctx, fulldetails=fulldetails) text += '
    '+_("Name")+''+f['label']+''+_("ID")+''+f['label']+''+_("Description")+'
    ' text += '
    ' return text def _build_testplan_tree(env, req, context, planid, curpage, mode='tree',sortby='name'): testmanagersystem = TestManagerSystem(env) default_status = testmanagersystem.get_default_tc_status() # Determine current catalog name cat_name = 'TC' if curpage.find('_TC') >= 0: cat_name = curpage.rpartition('_TC')[0].rpartition('_')[2] elif not curpage == 'TC': cat_name = curpage.rpartition('_')[2] tp = TestPlan(env, planid) # Create the catalog subtree model components = {'name': curpage, 'childrenC': {},'childrenT': {}, 'tot': 0} unique_idx = 0 for subpage_name in sorted(WikiSystem(env).get_pages(curpage+'_')): subpage = WikiPage(env, subpage_name) subpage_title = get_page_title(subpage.text) path_name = subpage_name.partition(curpage+'_')[2] tokens = path_name.split("_") parent = components ltok = len(tokens) count = 1 curr_path = curpage for tc in tokens: curr_path += '_'+tc if tc == '': break if not tc.startswith('TC'): comp = {} if (tc not in parent['childrenC']): comp = {'id': curr_path, 'name': tc, 'title': subpage_title, 'childrenC': {},'childrenT': {}, 'tot': 0, 'parent': parent} parent['childrenC'][tc]=comp else: comp = parent['childrenC'][tc] parent = comp else: # It is a test case page tc_id = tc.partition('TC')[2] tcip = TestCaseInPlan(env, tc_id, planid) if tcip.exists: for ts, author, status in tcip.list_history(): break if not isinstance(ts, datetime): ts = from_any_timestamp(ts) else: ts = tp['time'] author = tp['author'] status = default_status if sortby == 'name': key = subpage_title else: key = ts.isoformat() if key in parent['childrenT']: unique_idx += 1 key = key+str(unique_idx) parent['childrenT'][key]={'id':curr_path, 'tc_id': tc_id, 'title': subpage_title, 'status': status.lower(), 'ts': ts, 'author': author} compLoop = parent while (True): compLoop['tot']+=1 if ('parent' in compLoop): compLoop = compLoop['parent'] else: break count+=1 # Generate the markup ind = {'count': 0} text = '' if mode == 'tree': text +='
    '+_("Filter:")+'   
    ' text +=''; text +='
    ' text += _render_subtree(env, planid, components, ind, 0) text +='
    ' elif mode == 'tree_table': tcat_fields = GenericClassModelProvider(env).get_custom_fields_for_realm('testcatalog') tcat_has_custom = tcat_fields is not None and len(tcat_fields) > 0 tc_fields = GenericClassModelProvider(env).get_custom_fields_for_realm('testcase') tc_has_custom = tc_fields is not None and len(tc_fields) > 0 tcip_fields = GenericClassModelProvider(env).get_custom_fields_for_realm('testcaseinplan') tcip_has_custom = tcip_fields is not None and len(tcip_fields) > 0 custom_ctx = { 'testcatalog': [tcat_has_custom, tcat_fields], 'testcase': [tc_has_custom, tc_fields], 'testcaseinplan': [tcip_has_custom, tcip_fields] } text +='
    '+_("Filter:")+'   
    ' text += '
    ' text += ''; # Common columns text += '' # Custom testcatalog columns if custom_ctx['testcatalog'][0]: for f in custom_ctx['testcatalog'][1]: if f['type'] == 'text': text += '' # Base testcase columns text += '' #Custom testcase columns if custom_ctx['testcase'][0]: for f in custom_ctx['testcase'][1]: if f['type'] == 'text': text += '' # Base testcaseinplan columns text += '' # Custom testcaseinplan columns if custom_ctx['testcaseinplan'][0]: for f in custom_ctx['testcaseinplan'][1]: if f['type'] == 'text': text += '' text += ''; text += _render_subtree_as_table(env, context, planid, components, ind, 0, custom_ctx) text += '
    '+_("Name")+''+f['label']+''+_("ID")+''+f['label']+''+_("Status")+''+_("Author")+''+_("Last Change")+''+f['label']+'
    ' text += '
    ' return text def _build_testplan_list(env, req, curpage, mode, fulldetails): # Determine current catalog name cat_name = 'TC' catid = '-1' if curpage.find('_TC') >= 0: cat_name = curpage.rpartition('_TC')[0].rpartition('_')[2] catid = cat_name.rpartition('TT')[2] elif not curpage == 'TC': cat_name = curpage.rpartition('_')[2] catid = cat_name.rpartition('TT')[2] if 'TEST_PLAN_ADMIN' in req.perm: show_delete_button = True else: show_delete_button = False markup, num_plans = _render_testplan_list(env, catid, mode, fulldetails, show_delete_button) text = '
    ' return text def _render_testplan_list(env, catid, mode, fulldetails, show_delete_button): """Returns a test case status in a plan audit trail.""" delete_icon = '../chrome/testmanager/images/trash.png' cat = TestCatalog(env, catid) result = '' result += '' result += '' num_plans = 0 for tp in sorted(cat.list_testplans(), cmp=lambda x,y: cmp(x['time'],y['time']), reverse=True): result += '' result += '' result += '' result += '' if show_delete_button: result += '' else: result += '' result += '' num_plans += 1 result += '
    '+_("Plan Name")+''+_("Author")+''+_("Timestamp")+'
    '+tp['name']+''+tp['author']+''+format_datetime(tp['time'])+''+_(
    ' return result, num_plans # Render the breadcrumb def _render_breadcrumb(breadcrumb, planid, mode, fulldetails): plan_ref = '' if planid is not None and not planid == '-1': plan_ref = '&planid='+planid text = '' path_len = len(breadcrumb) for i, x in enumerate(breadcrumb): if i == 0: plan_param = '' else: plan_param = plan_ref text += ''+x['title'] if i < path_len-1: text += '  ->' text += '' return text # Render the subtree def _render_subtree(env, planid, component, ind, level): data = component text = '' if (level == 0): data = component['childrenC'] text +='
      '; keyList = data.keys() sortedList = sorted(keyList) for x in sortedList: ind['count'] += 1 text+='
    • ' comp = data[x] if ('childrenC' in comp): subcData=comp['childrenC'] toggle_icon = '../chrome/testmanager/images/plus.png' toggable = 'toggable' if (len(comp['childrenC']) + len(comp['childrenT'])) == 0: toggable = 'nope' toggle_icon = '../chrome/testmanager/images/empty.png' index = str(ind['count']) if planid is not None and not planid == '-1': plan_param = '?planid='+planid else: plan_param = '' text+=''+comp['title']+' ('+str(comp['tot'])+')' text +='' text+='
    • ' if (level == 0): if ('childrenT' in component): cmtData=component['childrenT'] text+=_render_testcases(env, planid, cmtData) text+='
    ' return text def _render_testcases(env, planid, data): testmanagersystem = TestManagerSystem(env) tc_statuses = testmanagersystem.get_tc_statuses_by_name() text='' keyList = data.keys() sortedList = sorted(keyList) for x in sortedList: tick = data[x] status = tick['status'] has_status = True if status is not None and len(status) > 0 and status != '__none__': stat_meaning = tc_statuses[status][0] if stat_meaning == 'green': statusIcon='../chrome/testmanager/images/green.png' elif stat_meaning == 'yellow': statusIcon='../chrome/testmanager/images/yellow.png' elif stat_meaning == 'red': statusIcon='../chrome/testmanager/images/red.png' else: has_status = False if has_status: statusLabel = tc_statuses[status][1] text+="
  • "+tick['title']+" "+statusLabel+"
  • " else: text+="
  • "+tick['title']+" 
  • " return text def _build_testcase_status(env, req, planid, curpage): testmanagersystem = TestManagerSystem(env) tc_statuses = testmanagersystem.get_tc_statuses_by_name() tc_id = curpage.rpartition('_TC')[2] tcip = TestCaseInPlan(env, tc_id, planid) if tcip.exists: status = tcip['status'].lower() else: status = testmanagersystem.get_default_tc_status() # Hide all icons except the one relative to the current test # case status display = {'green': 'none', 'yellow': 'none', 'red': 'none'} display[tc_statuses[status][0]] = 'block' text = '' text += '' text += '' text += '' return text # Render the subtree as a tree table def _render_subtree_as_table(env, context, planid, component, ind, level, custom_ctx=None, fulldetails=False): data = component text = '' if (level == 0): data = component['childrenC'] keyList = data.keys() sortedList = sorted(keyList) for x in sortedList: ind['count'] += 1 comp = data[x] if ('childrenC' in comp): subcData=comp['childrenC'] index = str(ind['count']) if planid is not None and not planid == '-1': plan_param = '&planid='+planid else: plan_param = '' # Common columns text += ''+comp['title']+'' # Custom testcatalog columns if custom_ctx['testcatalog'][0]: tcat_id = comp['id'].rpartition('TT')[2] tcat = TestCatalog(env, tcat_id) text += _get_custom_fields_columns(tcat, custom_ctx['testcatalog'][1]) text += '' ind['count']+=1 text+=_render_subtree_as_table(env, context, planid, subcData, ind, level+1, custom_ctx, fulldetails) if ('childrenT' in comp): mtData=comp['childrenT'] text+=_render_testcases_as_table(env, context, planid, mtData, level+1, custom_ctx, fulldetails) if (level == 0): if ('childrenT' in component): cmtData=component['childrenT'] text+=_render_testcases_as_table(env, context, planid, cmtData, level+1, custom_ctx, fulldetails) return text def _render_testcases_as_table(env, context, planid, data, level=0, custom_ctx=None, fulldetails=False): testmanagersystem = TestManagerSystem(env) tc_statuses = testmanagersystem.get_tc_statuses_by_name() text='' keyList = data.keys() sortedList = sorted(keyList) for x in sortedList: tick = data[x] status = tick['status'] has_status = True if status is not None and len(status) > 0 and status != '__none__': stat_meaning = tc_statuses[status][0] if stat_meaning == 'green': statusIcon='../chrome/testmanager/images/green.png' elif stat_meaning == 'yellow': statusIcon='../chrome/testmanager/images/yellow.png' elif stat_meaning == 'red': statusIcon='../chrome/testmanager/images/red.png' else: has_status = False tc = None if fulldetails or custom_ctx['testcase'][0]: tc = TestCase(env, tick['tc_id']) text += '' # Common columns if has_status: statusLabel = tc_statuses[status][1] text += ''+tick['title']+'' else: text += ''+tick['title']+'' # Custom testcatalog columns if custom_ctx['testcatalog'][0]: for f in custom_ctx['testcatalog'][1]: text += '' # Base testcase columns text += ''+tick['tc_id']+'' # Custom testcase columns if tc and tc.exists and custom_ctx['testcase'][0]: text += _get_custom_fields_columns(tc, custom_ctx['testcase'][1]) if has_status: # Base testcaseinplan columns text += ''+statusLabel+''+tick['author']+''+format_datetime(tick['ts'])+'' # Custom testcaseinplan columns if custom_ctx['testcaseinplan'][0]: tcip = TestCaseInPlan(env, tick['tc_id'], planid) text += _get_custom_fields_columns(tcip, custom_ctx['testcaseinplan'][1]) if fulldetails: wikidom = WikiParser(env).parse(tc.description) out = StringIO() f = Formatter(env, context) f.reset(wikidom) f.format(wikidom, out, False) description = out.getvalue() text += ''+description+'' text += '' return text def _build_testcase_change_status(env, req, planid, curpage): testmanagersystem = TestManagerSystem(env) tc_statuses = testmanagersystem.get_tc_statuses_by_name() tc_statuses_by_color = testmanagersystem.get_tc_statuses_by_color() tc_id = curpage.rpartition('_TC')[2] tcip = TestCaseInPlan(env, tc_id, planid) if tcip.exists: status = tcip['status'].lower() else: status = testmanagersystem.get_default_tc_status() status_meaning = tc_statuses[status][0] text = '' text += '' text += '' text += _("Change the Status:") text += '' text += '' text += '' return text def _build_testcase_status_history(env,req,planid,curpage): testmanagersystem = TestManagerSystem(env) tc_statuses = testmanagersystem.get_tc_statuses_by_name() tc_id = curpage.rpartition('_TC')[2] tcip = TestCaseInPlan(env, tc_id, planid) text = '
    ' return text def _get_custom_fields_columns(obj, fields): result = '' for f in fields: if f['type'] == 'text': result += '' if obj[f['name']] is not None: result += obj[f['name']] result += '' # TODO Support other field types return result testman4trac.1.4.5/testman4trac/trunk/testmanager/README.txt0000755000175000017500000002424011566010002023625 0ustar robertorobertoTest Manager plugin for Trac Copyright 2010 Roberto Longobardi Project web page on TracHacks: http://trac-hacks.org/wiki/TestManagerForTracPlugin Project web page on SourceForge.net: http://sourceforge.net/projects/testman4trac/ Project web page on Pypi: http://pypi.python.org/pypi/TestManager A Trac plugin to create Test Cases, organize them in catalogs, generate test plans and track their execution status and outcome. Refer to BUILD.txt for details about how to build. Refer to INSTALL.txt for installation details. ================================================================================================= Change History: (Refer to the tickets on trac-hacks or SourceForge for complete descriptions.) Release 1.4.5 (2011-05-21): o Enhancement #8825 (Track-Hacks): Ability to import test cases from Excel (CSV) file Release 1.4.4 (2011-03-11): o Fixed Ticket #8567 (Track-Hacks): Javascript error when deleting test plans o Enhancement #8596 (Track-Hacks): Remove hard dependency on XML RPC plugin for Trac 0.11 o Enhancement #8761 (Track-Hacks): Copy multiple test cases into another catalog Added wiki documentation for copying multiple test cases into another catalog. Release 1.4.3 (2011-01-20): o Enhancement #8427 (Track-Hacks): Add XML-RPC complete interface for remote management of test objects Release 1.4.2 (2011-01-09): o Fixed Ticket #8378 (Track-Hacks): Set date and time format correctly in Test Stats page Also added support for custom test case outcomes in the Test Stats page Release 1.4.1 (2010-12-27): o Enhancement #7846 (Track-Hacks): Customizable test case outcomes (aka verdicts) o Enhancement #7570 (Track-Hacks): Add a relationship table between tickets and test cases in plan, and corresponding API Release 1.3.12 (2010-12-19): o Enhancement #8321 (Track-Hacks): Add standard internationalization support (i18n) o Enhancement #8322 (Track-Hacks): Show timestamps according to User's locale o Fixed Ticket #8323 (Track-Hacks): Unable to expand Available plans and Test case status change history collapsable sections Release 1.3.11 (2010-12-02): o Added out of the box operation to workflow engine: set_owner and set_owner_to_self o Enhancement #8259 (Track-Hacks): Add navigation from a test case to its related tickets Release 1.3.10 (2010-11-28): o Fixed Ticket #8154 (Track-Hacks): LookupError: unknown encoding: cp0 Release 1.3.9 (2010-11-23): o Fixed Ticket #8144 (Track-Hacks): Test statistical charts don't show successful and failed figures. Release 1.3.8 (2010-11-22): o Fixed Ticket #8121 (Track-Hacks): Catalog Wiki Page not added o Fixed Ticket #8123 (Track-Hacks): Can't move testcase more than one time into different catalog o Fixed Ticket #8124 (Track-Hacks): AttributeError: 'NoneType' object has no attribute 'splitlines' o Fixed Ticket #8125 (Track-Hacks): "duplicate test case" does not work for previously moved test case Release 1.3.7 (2010-11-20): o Enhancement #7704 (Track-Hacks): Add ability to delete a Test Plan o Fixet Ticket #8084 (Track-Hacks): Ordering issue Release 1.3.6 (2010-11-09): o Fixed Ticket #8004 (Track-Hacks): Cannot search if an admin Release 1.3.5 (2010-10-17): o Restored compatibility with Trac 0.11. Now again both 0.11 and 0.12 are supported. Release 1.3.4 (2010-10-15): o Added tabular view to catalogs and plans. Search now works with custom properties in tabular views. Release 1.3.3 (2010-10-05): o Enhanced feature 3076739 (SourceForge): Full Test Plan display / print Release 1.3.2 (2010-10-03): o Added feature 3076739 (SourceForge): Full Test Plan display / print Release 1.3.1 (2010-10-02): o Fixed a base-code bug that prevented test catalog modification with a PostgreSQL binding. (Thanks Rodel for reporting it). Release 1.3.0 (2010-10-01): o The base Test Manager plugin has been separated and other plugins have been created to embed the functionalities of generic class framework and the workflow engine. Now we have four plugins: * Trac Generic Class plugin, providing the persistent class framework * Trac Generic Workflow plugin, providing the generic workflow engine applicable to any Trac Resource. This plugin requires the Trac Generic Class plugin. * SQL Executor plugin, as a debugging tool when dealing with persistent data. Not to be enabled in a production environment. Moreover, the generic class framework has been added a set of new functionalities. Refer to the README file in its package for more details. Release 1.2.0 (2010-09-20): o The data model has been completely rewritten, now using python classes for all the test objects. A generic object supporting programmatic definition of its standard fields, declarative definition of custom fields (in trac.ini) and keeping track of change history has been created, by generalizing the base Ticket code. The specific object "type" is specified during construction as the "realm" parameter. This name must also correspond to the database table storing the corresponding objects, and is used as the base name for the custom fields table and the change tracking table (see below). Features: * Support for custom fields, specified in the trac.ini file with the same syntax as for custom Ticket fields. Custom fields are kept in a "_custom" table * Keeping track of all changes to any field, into a separate "_change" table * A set of callbacks to allow for subclasses to control and perform actions pre and post any operation pertaining the object's lifecycle * Registering listeners, via the ITestObjectChangeListener interface, for object creation, modification and deletion. * Searching objects matching any set of valorized fields, (even non-key fields), applying the "dynamic record" pattern. See the method list_matching_objects. o Enhancement #7704 (Track-Hacks): Add workflow capabilities, with custom states, transitions and operations, and state transition listeners support A generic Trac Resource workflow system has been implemented, allowing to add workflow capabilities to any Trac resource. Test objects have been implemented as Trac resources as well, so they benefit of workflow capabilities. Available objects 'realms' to associate workflows to are: testcatalog, testcase, testcaseinplan, testplan. Note that the object with realm 'resourceworkflowstate', which manages the state of any resource in a workflow, also supports custom properties (see below), so plugins can augment a resource workflow state with additional context information and use it inside listener callbacks, for example. For example, add the following to your trac.ini file to associate a workflow with all Test Case objects. The sample_operation is currently provided by the Test Manager system itself, as an example. It just logs a debug message with the text input by the User in a text field. [testcase-resource_workflow] leave = * -> * leave.operations = sample_operation leave.default = 1 accept = new -> accepted accept.permissions = TEST_MODIFY accept.operations = sample_operation resolve = accepted -> closed resolve.permissions = TEST_MODIFY resolve.operations = sample_operation o Enhancement #7705 (Track-Hacks): Add support for custom properties and change history to all of the test management objects A generic object supporting programmatic definition of its standard fields, declarative definition of custom fields (in trac.ini) and keeping track of change history has been created, by generalizing the base Ticket code. Only text type of properties are currently supported. For example, add the following to your trac.ini file to add custom properties to all of the four test objects. Note that the available realms to augment are, as above, testcatalog, testcase, testcaseinplan and testplan, with the addition of resourceworkflowstate. [testcatalog-tm_custom] prop1 = text prop1.value = Default value [testcaseinplan-tm_custom] prop_strange = text prop_strange.value = windows [testcase-tm_custom] nice_prop = text nice_prop.value = My friend [testplan-tm_custom] good_prop = text good_prop.value = linux o Enhancement #7569 (Track-Hacks): Add listener interface to let other components react to test case status change Added listener interface for all of the test objects lifecycle: * Object created * Object modified (including custom properties) * Object deleted This applies to test catalogs, test cases, test plans and test cases in a plan (i.e. with a status). Release 1.1.2 (2010-08-25): o Enhancement #7552 (Track-Hacks): Export test statistics in CSV and bookmark this chart features in the test stats chart o Fixed Ticket #7551 (Track-Hacks): Test statistics don't work on Trac 0.11 Release 1.1.1 (2010-08-20): o Enhancement #7526 (Track-Hacks): Add ability to duplicate a test case o Enhancement #7536 (Track-Hacks): Add test management statistics o Added "autosave=true" parameter to the RESTful API to create test catalogs and test cases programmatically without need to later submit the wiki editing form. Release 1.1.0 (2010-08-18): o Enhancement #7487 (Track-Hacks): Add multiple test plans capability o Enhancement #7507 (Track-Hacks): Implement security permissions o Enhancement #7484 (Track-Hacks): Reverse the order of changes in the test case status change history Release 1.0.2 (2010-08-17): o Fixed Ticket #7485 (Track-Hacks): "Open ticket on this test case" should work without a patched TracTicketTemplatePlugin Release 1.0.1 (2010-08-12): o First attempt at externalizing strings Release 1.0 (2010-08-10): o First release publicly available testman4trac.1.4.5/testman4trac/trunk/testmanager/INSTALLATION.txt0000755000175000017500000000060311565162323024603 0ustar robertorobertoInstallation: The functionalities are divided in three plugins, which must be installed in this order: 1) Trac Generic Class => TracGenericClass 2) Trac Generic Workflow => TracGenericWorkflow 3) Test Manager => TestManager An additional plugin is only useful for debugging and should not be installed in a production environment. * SQL Executor => SQLExecutor testman4trac.1.4.5/testman4trac/trunk/testmanager/api.py0000755000175000017500000006045411566000161023266 0ustar robertoroberto# -*- coding: utf-8 -*- # # Copyright (C) 2010 Roberto Longobardi # import csv import os import pkg_resources import re import shutil import sys import time import traceback from datetime import datetime from trac.core import * from trac.perm import IPermissionRequestor, PermissionError from trac.resource import Resource, IResourceManager, render_resource_link, get_resource_url from trac.util import get_reporter_id from trac.util.datefmt import utc from trac.web.api import IRequestHandler from tracgenericclass.model import GenericClassModelProvider from tracgenericclass.util import * from testmanager.model import TestCatalog, TestCase, TestCaseInPlan, TestPlan try: from trac.util.translation import domain_functions _, tag_, N_, add_domain = domain_functions('testmanager', ('_', 'tag_', 'N_', 'add_domain')) except ImportError: from trac.util.translation import _, N_ tag_ = _ add_domain = lambda env_path, locale_dir: None class TestManagerSystem(Component): """Test Manager system for Trac.""" implements(IPermissionRequestor, IRequestHandler, IResourceManager) outcomes_by_color = {} outcomes_by_name = {} default_outcome = None testcaseimport_target_subdir = 'testcaseimport' testcaseimport_target_filename = 'testcaseimport.csv' def __init__(self, *args, **kwargs): """ Parses the configuration file to find all the test case states defined. Test case outcomes are triple: (color, name, label) Where color = green, yellow, red To define a set of test case outcomes (a.k.a. verdicts), specify each one on a different line in the trac.ini file, as in the following example: [test-outcomes] green.SUCCESSFUL = Successful yellow.TO_BE_TESTED = Untested red.FAILED = Failed default = TO_BE_TESTED """ Component.__init__(self, *args, **kwargs) import pkg_resources # bind the 'testmanager' catalog to the specified locale directory locale_dir = pkg_resources.resource_filename(__name__, 'locale') add_domain(self.env.path, locale_dir) # Search for custom test case outcomes (a.k.a. verdicts) definitions self.log.debug("TestManagerSystem - Looking for custom test outcomes...") section_name = 'test-outcomes' if section_name in self.config.sections(): self.log.debug("TestManagerSystem - parsing config section %s" % section_name) tmp_outcomes = list(self.config.options(section_name)) for row in tmp_outcomes: self.log.debug(" --> Found option: %s = %s" % (row[0], row[1])) if row[0] == 'default': self.default_outcome = row[1].lower() else: color = row[0].partition('.')[0] outcome = row[0].partition('.')[2].lower() caption = row[1] if color not in self.outcomes_by_color: self.outcomes_by_color[color] = {} self.outcomes_by_color[color][outcome] = caption else: raise TracError("Configuration section 'test-outcomes' missing in trac.ini file.") # Build a reverse map to easily lookup an outcome's color and label for color in self.outcomes_by_color: for outcome in self.outcomes_by_color[color]: self.outcomes_by_name[outcome] = [color, self.outcomes_by_color[color][outcome]] def get_next_id(self, type): propname = _get_next_prop_name(type) try: # Get next ID db, handle_ta = get_db_for_write(self.env) cursor = db.cursor() sql = "SELECT value FROM testconfig WHERE propname='"+propname+"'" cursor.execute(sql) row = cursor.fetchone() id = int(row[0]) # Increment next ID cursor = db.cursor() cursor.execute("UPDATE testconfig SET value='" + str(id+1) + "' WHERE propname='"+propname+"'") if handle_ta: db.commit() except: self.env.log.debug(formatExceptionInfo()) db.rollback() raise return str(id) def set_next_id(self, type, value): propname = _get_next_prop_name(type) try: # Set next ID to the input value db, handle_ta = get_db_for_write(self.env) cursor = db.cursor() cursor.execute("UPDATE testconfig SET value='" + str(value) + "' WHERE propname='"+propname+"'") if handle_ta: db.commit() except: self.env.log.debug(formatExceptionInfo()) db.rollback() raise def get_default_tc_status(self): """Returns the default test case in plan status""" return self.default_outcome def get_tc_statuses_by_name(self): """ Returns the available test case in plan statuses, along with their captions and meaning: 'green': successful 'yellow': to be tested 'red': failed For example: {'SUCCESSFUL': ['green', "Successful"], 'TO_BE_TESTED': ['yellow', "Untested"], 'FAILED': ['red', "Failed"]} """ return self.outcomes_by_name def get_tc_statuses_by_color(self): """ Returns the available test case in plan statuses, along with their captions and meaning: 'green': successful 'yellow': to be tested 'red': failed For example: {'green': {'SUCCESSFUL': "Successful"}, 'yellow': {'TO_BE_TESTED': "Untested"}, 'red': {'FAILED': "Failed"}} """ return self.outcomes_by_color def get_testcase_status_history_markup(self, id, planid): """Returns a test case status in a plan audit trail.""" result = '' result += '' result += '' db = get_db(self.env) cursor = db.cursor() sql = "SELECT time, author, status FROM testcasehistory WHERE id='"+str(id)+"' AND planid='"+str(planid)+"' ORDER BY time DESC" cursor.execute(sql) for ts, author, status in cursor: result += '' result += '' result += '' result += '' result += '' result += '
    '+_("Timestamp")+''+_("Author")+''+_("Status")+'
    '+str(from_any_timestamp(ts))+''+author+''+_("Status")+'
    ' return result # @deprecated def list_all_testplans(self): """Returns a list of all test plans.""" db = get_db(self.env) cursor = db.cursor() sql = "SELECT id, catid, page_name, name, author, time FROM testplan ORDER BY catid, id" cursor.execute(sql) for id, catid, page_name, name, author, ts in cursor: yield id, catid, page_name, name, author, str(from_any_timestamp(ts)) # IPermissionRequestor methods def get_permission_actions(self): return ['TEST_VIEW', 'TEST_MODIFY', 'TEST_EXECUTE', 'TEST_DELETE', 'TEST_PLAN_ADMIN'] # IRequestHandler methods def match_request(self, req): type = req.args.get('type', '') match = False if req.path_info.startswith('/testman4debug'): match = True if req.path_info.startswith('/testcreate') and (((type == 'catalog' or type == 'testcase') and ('TEST_MODIFY' in req.perm)) or (type == 'testplan' and ('TEST_PLAN_ADMIN' in req.perm))): match = True elif (req.path_info.startswith('/teststatusupdate') and 'TEST_EXECUTE' in req.perm): match = True elif (req.path_info.startswith('/testdelete') and type == 'testplan' and 'TEST_PLAN_ADMIN' in req.perm): match = True elif (req.path_info.startswith('/testimport') and ('TEST_MODIFY' in req.perm)): match = True return match def process_request(self, req): """ Handles Ajax requests to set the test case status and to create test objects. """ author = get_reporter_id(req, 'author') if req.path_info.startswith('/teststatusupdate'): req.perm.require('TEST_EXECUTE') id = req.args.get('id') planid = req.args.get('planid') path = req.args.get('path') status = req.args.get('status') try: self.env.log.debug("Setting status %s to test case %s in plan %s" % (status, id, planid)) tcip = TestCaseInPlan(self.env, id, planid) if tcip.exists: tcip.set_status(status, author) tcip.save_changes(author, "Status changed") else: tcip['page_name'] = path tcip.set_status(status, author) tcip.insert() except: self.env.log.debug(formatExceptionInfo()) elif req.path_info.startswith('/testcreate'): type = req.args.get('type') path = req.args.get('path') title = req.args.get('title') autosave = req.args.get('autosave', 'false') duplicate = req.args.get('duplicate') multiple = req.args.get('multiple') paste = req.args.get('paste') tcId = req.args.get('tcId') id = self.get_next_id(type) pagename = path if type == 'catalog': req.perm.require('TEST_MODIFY') pagename += '_TT'+str(id) try: new_tc = TestCatalog(self.env, id, pagename, title, '') new_tc.author = author new_tc.remote_addr = req.remote_addr # This also creates the Wiki page new_tc.insert() except: self.env.log.info("Error adding test catalog!") self.env.log.info(formatExceptionInfo()) req.redirect(req.href.wiki(path)) # Redirect to see the new wiki page. req.redirect(req.href.wiki(pagename)) elif type == 'testplan': req.perm.require('TEST_PLAN_ADMIN') catid = path.rpartition('_TT')[2] try: # Add the new test plan in the database new_tc = TestPlan(self.env, id, catid, pagename, title, author) new_tc.insert() except: self.env.log.info("Error adding test plan!") self.env.log.info(formatExceptionInfo()) # Back to the catalog req.redirect(req.href.wiki(path)) # Display the new test plan req.redirect(req.href.wiki(path, planid=str(id))) elif type == 'testcase': req.perm.require('TEST_MODIFY') pagename += '_TC'+str(id) if paste and paste != '': # Handle move/paste of the test case into another catalog req.perm.require('TEST_PLAN_ADMIN') if multiple and multiple != '': delete_old = False tcIdsList = tcId.split(',') else: delete_old = True tcIdsList = [tcId] try: catid = path.rpartition('_TT')[2] tcat = TestCatalog(self.env, catid) for tcId in tcIdsList: if tcId is not None and tcId != '': old_pagename = tcId tc_id = tcId.rpartition('_TC')[2] tc = TestCase(self.env, tc_id, tcId) tc.author = author tc.remote_addr = req.remote_addr if tc.exists: if delete_old: tc.move_to(tcat) else: tc['page_name'] = pagename tc.save_as({'id': id}) else: self.env.log.debug("Test case not found") # Generate a new Id for the next iteration id = self.get_next_id(type) pagename = path + '_TC'+str(id) except: self.env.log.info("Error pasting test cases!") self.env.log.info(formatExceptionInfo()) req.redirect(req.href.wiki(pagename)) # Redirect to test catalog, forcing a page refresh by means of a random request parameter req.redirect(req.href.wiki(pagename.rpartition('_TC')[0], random=str(datetime.now(utc).microsecond))) elif duplicate and duplicate != '': # Duplicate test case old_id = tcId.rpartition('_TC')[2] old_pagename = tcId try: old_tc = TestCase(self.env, old_id, old_pagename) # New test case name will be the old catalog name + the newly generated test case ID author = get_reporter_id(req, 'author') # Create new test case wiki page as a copy of the old one, but change its page path new_tc = old_tc new_tc['page_name'] = pagename new_tc.remote_addr = req.remote_addr # And save it under the new id new_tc.save_as({'id': id}) except: self.env.log.info("Error duplicating test case!") self.env.log.info(formatExceptionInfo()) req.redirect(req.href.wiki(tcId)) # Redirect tp allow for editing the copy test case req.redirect(req.href.wiki(pagename, action='edit')) else: # Normal creation of a new test case try: new_tc = TestCase(self.env, id, pagename, title, '') new_tc.author = author new_tc.remote_addr = req.remote_addr # This also creates the Wiki page new_tc.insert() except: self.env.log.info("Error adding test case!") self.env.log.info(formatExceptionInfo()) req.redirect(req.path_info) # Redirect to edit the test case description req.redirect(req.href.wiki(pagename, action='edit')) elif req.path_info.startswith('/testdelete'): type = req.args.get('type') path = req.args.get('path') author = get_reporter_id(req, 'author') mode = req.args.get('mode', 'tree') fulldetails = req.args.get('fulldetails', 'False') if type == 'testplan': req.perm.require('TEST_PLAN_ADMIN') planid = req.args.get('planid') catid = path.rpartition('_TT')[2] self.env.log.debug("About to delete test plan %s on catalog %s" % (planid, catid)) try: # Add the new test plan in the database tp = TestPlan(self.env, planid, catid) tp.delete() except: self.env.log.info("Error deleting test plan!") self.env.log.info(formatExceptionInfo()) # Back to the catalog req.redirect(req.href.wiki(path)) # Redirect to test catalog, forcing a page refresh by means of a random request parameter req.redirect(req.href.wiki(path, mode=mode, fulldetails=fulldetails, random=str(datetime.now(utc).microsecond))) elif req.path_info.startswith('/testimport'): if req.method == 'POST': if 'import_file' in req.args: if not (req.args.has_key('input_file')) or req.args['input_file'] == '': raise TracError('You must specify the file name.') if not (req.args.has_key('column_separator')) or req.args['column_separator'] == '' or len(req.args['column_separator'].strip()) != 1: raise TracError('You must specify the column separator.') input_file = req.args['input_file'] column_separator = req.args['column_separator'].strip() cat_name = req.args['cat_name'] upload_file_to_subdir(self.env, req, self.testcaseimport_target_subdir, input_file, self.testcaseimport_target_filename) csv_file = csv.reader(open(os.path.join(self.env.path, 'upload', self.testcaseimport_target_subdir, self.testcaseimport_target_filename), 'rb'), delimiter=column_separator.encode('ascii')) testcaseimport_info = {} testcaseimport_info['cat_name'] = cat_name testcaseimport_info['imported_ok'] = [] testcaseimport_info['errors'] = [] i = 0 for row in csv_file: if i == 0: self._process_imported_testcase_header(row, cat_name, testcaseimport_info) else: self._process_imported_testcase_row(i, row, cat_name, author, req.remote_addr, testcaseimport_info) i += 1 return 'testimportresults.html', testcaseimport_info, None elif req.path_info.startswith('/testman4debug'): id = req.args.get('id') path = req.args.get('path') planid = req.args.get('planid') result = '' if planid is None or len(planid) == 0: tc = TestCase(self.env, id, path) for t in tc.get_related_tickets(): result += str(t) + ', ' else: tc = TestCaseInPlan(self.env, id, planid, path) for t in tc.get_related_tickets(): result += str(t) + ', ' req.send_header("Content-Length", len(result)) req.write(result) return return 'empty.html', {}, None # IResourceManager methods def get_resource_realms(self): yield 'testcatalog' yield 'testcase' yield 'testcaseinplan' yield 'testplan' def get_resource_url(self, resource, href, **kwargs): self.env.log.debug(">>> get_resource_url - %s" % resource) tmmodelprovider = GenericClassModelProvider(self.env) obj = tmmodelprovider.get_object(resource.realm, get_dictionary_from_string(resource.id)) if obj and obj.exists: args = {} if resource.realm == 'testcaseinplan': args = {'planid': obj['planid']} elif resource.realm == 'testplan': args = {'planid': obj['id']} args.update(kwargs) self.env.log.debug("<<< get_resource_url - exists") return href('wiki', obj['page_name'], **args) else: self.env.log.debug("<<< get_resource_url - does NOT exist") return href('wiki', 'TC', **kwargs) def get_resource_description(self, resource, format='default', context=None, **kwargs): return resource.id def resource_exists(self, resource): tmmodelprovider = GenericClassModelProvider(self.env) obj = tmmodelprovider.get_object(resource.realm, get_dictionary_from_string(resource.id)) return obj.exists def _process_imported_testcase_header(self, row, cat_name, testcaseimport_info): if len(row) < 2: raise TracError('At least two columns are required.') testcaseimport_info['column_names'] = [] # See if the user specified anu test case custom field if len(row) > 2: config_dirty = False for i, field_name in enumerate(row): if i < 2: # The first two columns indicate title and description, regardless of the names the user gave them continue field_name = '_'.join(field_name.strip().lower().split()) testcaseimport_info['column_names'].append(field_name) # Write custom test case fields in the trac.ini file need_to_add = False if 'testcase-tm_custom' in self.config: if field_name not in self.config['testcase-tm_custom']: need_to_add = True else: need_to_add = True if need_to_add: self.config.set('testcase-tm_custom', field_name, 'text') self.config.set('testcase-tm_custom', field_name + '.value', '') config_dirty = True if config_dirty: self.config.save() # Full reload config here and in the GenericClassModelProvider to get new custom fields working self.config.parse_if_needed() gcm_provider = GenericClassModelProvider(self.env) gcm_provider.config.parse_if_needed() gcm_provider.custom_fields('testcase', True) gcm_provider.fields(True) def _process_imported_testcase_row(self, row_num, row, cat_name, author, remote_addr, testcaseimport_info): if len(row) < 2: testcaseimport_info['errors'].append([row_num, '', 'At least two columns are required.']) return title = row[0] try: title = title.strip() description = row[1].strip() id = self.get_next_id('testcase') pagename = cat_name + '_TC'+str(id) new_tc = TestCase(self.env, id, pagename, title, description) # Set custom field values into the new test case for i, field_value in enumerate(row): if i < 2: # The first two columns indicate title and description continue field_name = testcaseimport_info['column_names'][i-2] field_value = field_value.strip() new_tc[field_name] = field_value new_tc.author = author new_tc.remote_addr = remote_addr # Create the test case new_tc.insert() testcaseimport_info['imported_ok'].append(title) except: testcaseimport_info['errors'].append([row_num, title, formatExceptionInfo()]) self.env.log.info("Error importing test case number %s:\n%s" % (row_num, row)) self.env.log.info(formatExceptionInfo()) def _get_next_prop_name(type): propname = '' if type == 'catalog': propname = 'NEXT_CATALOG_ID' elif type == 'testcase': propname = 'NEXT_TESTCASE_ID' elif type == 'testplan': propname = 'NEXT_PLAN_ID' return propname testman4trac.1.4.5/testman4trac/trunk/testmanager/model.py0000755000175000017500000006253011565673335023634 0ustar robertoroberto# -*- coding: utf-8 -*- # # Copyright (C) 2010 Roberto Longobardi # import copy import re import time from datetime import date, datetime from trac.core import * from trac.db import Table, Column, Index from trac.env import IEnvironmentSetupParticipant from trac.perm import PermissionError from trac.resource import Resource, ResourceNotFound from trac.util.datefmt import utc, utcmax from trac.util.text import CRLF from trac.wiki.api import WikiSystem from trac.wiki.model import WikiPage from tracgenericclass.model import IConcreteClassProvider, AbstractVariableFieldsObject, AbstractWikiPageWrapper, need_db_upgrade, upgrade_db from tracgenericclass.util import * from testmanager.util import * try: from testmanager.api import _, tag_, N_ except ImportError: from trac.util.translation import _, N_ tag_ = _ class AbstractTestDescription(AbstractWikiPageWrapper): """ A test description object based on a Wiki page. Concrete subclasses are TestCatalog and TestCase. Uses a textual 'id' as key. Comprises a title and a description, currently embedded in the wiki page respectively as the first line and the rest of the text. The title is automatically wiki-formatted as a second-level title (i.e. sorrounded by '=='). """ # Fields that must not be modified directly by the user protected_fields = ('id', 'page_name') def __init__(self, env, realm='testdescription', id=None, page_name=None, title=None, description=None, db=None): self.env = env self.values = {} self.values['id'] = id self.values['page_name'] = page_name self.title = title self.description = description self.env.log.debug('Title: %s' % self.title) self.env.log.debug('Description: %s' % self.description) key = self.build_key_object() AbstractWikiPageWrapper.__init__(self, env, realm, key, db) def post_fetch_object(self, db): # Fetch the wiki page AbstractWikiPageWrapper.post_fetch_object(self, db) # Then parse it and derive title, description and author self.title = get_page_title(self.wikipage.text) self.description = get_page_description(self.wikipage.text) self.author = self.wikipage.author self.env.log.debug('Title: %s' % self.title) #self.env.log.debug('Description: %s' % self.description) def pre_insert(self, db): """ Assuming the following fields have been given a value before this call: title, description, author, remote_addr """ self.text = '== '+self.title+' ==' + CRLF + CRLF + self.description AbstractWikiPageWrapper.pre_insert(self, db) return True def pre_save_changes(self, db): """ Assuming the following fields have been given a value before this call: title, description, author, remote_addr """ self.text = '== '+self.title+' ==' + CRLF + CRLF + self.description AbstractWikiPageWrapper.pre_save_changes(self, db) return True class TestCatalog(AbstractTestDescription): """ A container for test cases and sub-catalogs. Test catalogs are organized in a tree. Since wiki pages are instead on a flat plane, we use a naming convention to flatten the tree into page names. These are examples of wiki page names for a tree: TC --> root of the tree. This page is automatically created at plugin installation time. TC_TT0 --> test catalog at the first level. Note that 0 is the catalog ID, generated at creation time. TC_TT0_TT34 --> sample sub-catalog, with ID '34', of the catalog with ID '0' TC_TT27 --> sample other test catalog at first level, with ID '27' There is not limit to the depth of a test tree. Test cases are contained in test catalogs, and are always leaves of the tree: TC_TT0_TT34_TC65 --> sample test case, with ID '65', contained in sub-catalog '34'. Note that test case IDs are independent on test catalog IDs. """ def __init__(self, env, id=None, page_name=None, title=None, description=None, db=None): AbstractTestDescription.__init__(self, env, 'testcatalog', id, page_name, title, description, db) def get_enclosing_catalog(self): """ Returns the catalog containing this test catalog, or None if its a root catalog. """ page_name = self.values['page_name'] cat_page = page_name.rpartition('_TT')[0] if cat_page == 'TC': return None else: cat_id = page_name.rpartition('TT')[0].page_name.rpartition('TT')[2].rpartition('_')[0] return TestCatalog(self.env, cat_id, cat_page) def list_subcatalogs(self, db=None): """ Returns a list of the sub catalogs of this catalog. """ tc_search = TestCatalog(self.env) tc_search['page_name'] = self.values['page_name'] + '_TT%' cat_re = re.compile('^TT[0-9]*$') for tc in tc_search.list_matching_objects(exact_match=False, db=db): # Only return direct sub-catalogs and exclude test cases if cat_re.match(tc['page_name'].partition(self.values['page_name']+'_')[2]) : yield tc def list_testcases(self, plan_id=None, db=None): """ Returns a list of the test cases in this catalog. If plan_id is provided, returns a list of TestCaseInPlan objects, otherwise, of TestCase objects. """ if plan_id is not None: from testmanager.api import TestManagerSystem default_status = TestManagerSystem(self.env).get_default_tc_status() tc_search = TestCase(self.env) tc_search['page_name'] = self.values['page_name'] + '_TC%' for tc in tc_search.list_matching_objects(False, db): if plan_id is None: yield tc else: tcip = TestCaseInPlan(self.env, tc['id'], plan_id) if not tcip.exists: tcip['status'] = default_status yield tcip def list_testplans(self, db=None): """ Returns a list of test plans for this catalog. """ tp_search = TestPlan(self.env) tp_search['catid'] = self.values['id'] for tp in tp_search.list_matching_objects(db=db): yield tp def create_instance(self, key): return TestCatalog(self.env, key['id']) class TestCase(AbstractTestDescription): def __init__(self, env, id=None, page_name=None, title=None, description=None, db=None): AbstractTestDescription.__init__(self, env, 'testcase', id, page_name, title, description, db) def get_enclosing_catalog(self): """ Returns the catalog containing this test case. """ page_name = self.values['page_name'] cat_id = page_name.rpartition('TT')[2].rpartition('_')[0] cat_page = page_name.rpartition('_TC')[0] return TestCatalog(self.env, cat_id, cat_page) def create_instance(self, key): return TestCase(self.env, key['id']) def move_to(self, tcat, db=None): """ Moves the test case into a different catalog. Note: the test case keeps its ID, and the wiki page is moved into the new name. This way, the page change history is kept. """ db, handle_ta = get_db_for_write(self.env, db) # Rename the wiki page new_page_name = tcat['page_name'] + '_TC' + self['id'] cursor = db.cursor() cursor.execute("UPDATE wiki SET name = %s WHERE name = %s", (new_page_name, self['page_name'])) if handle_ta: db.commit() # Invalidate Trac 0.12 page name cache try: del WikiSystem(self.env).pages except: pass # TODO Move wiki page attachments #from trac.attachment import Attachment #Attachment.delete_all(self.env, 'wiki', self.name, db) # Remove test case from all the plans tcip_search = TestCaseInPlan(self.env) tcip_search['id'] = self.values['id'] for tcip in tcip_search.list_matching_objects(db=db): tcip.delete(db) # Update self properties and save self['page_name'] = new_page_name self.wikipage = WikiPage(self.env, new_page_name) self.save_changes('System', "Moved to a different catalog", datetime.now(utc), db) def get_related_tickets(self, db=None): """ Returns an iterator over the IDs of the ticket opened against this test case. """ self.env.log.debug('>>> get_related_tickets') if db is None: db = get_db(self.env, db) cursor = db.cursor() cursor.execute("SELECT id FROM ticket WHERE id in " + "(SELECT ticket FROM ticket_custom WHERE name='testcaseid' AND value=%s)", (self.values['page_name'],)) for row in cursor: self.env.log.debug(' ---> Found ticket %s' % row[0]) yield row[0] self.env.log.debug('<<< get_related_tickets') class TestCaseInPlan(AbstractVariableFieldsObject): """ This object represents a test case in a test plan. It keeps the latest test execution status (aka verdict). The status, as far as this class is concerned, can be just any string. The plugin logic, anyway, currently recognizes only three hardcoded statuses, but this can be evolved without need to modify also this class. The history of test execution status changes is instead currently kept in another table, testcasehistory, which is not backed by any python class. This is a duplication, since the 'changes' table also keeps track of status changes, so the testcasehistory table may be removed in the future. """ # Fields that must not be modified directly by the user protected_fields = ('id', 'planid', 'page_name', 'status') def __init__(self, env, id=None, planid=None, page_name=None, status=None, db=None): """ The test case in plan is related to a test case, the 'id' and 'page_name' arguments, and to a test plan, the 'planid' argument. """ self.values = {} self.values['id'] = id self.values['planid'] = planid self.values['page_name'] = page_name self.values['status'] = status key = self.build_key_object() AbstractVariableFieldsObject.__init__(self, env, 'testcaseinplan', key, db) def get_key_prop_names(self): return ['id', 'planid'] def create_instance(self, key): return TestCaseInPlan(self.env, key['id'], key['planid']) def set_status(self, status, author, db=None): """ Sets the execution status of the test case in the test plan. This method immediately writes into the test case history, but does not write the new status into the database table for this test case in plan. You need to call 'save_changes' to achieve that. """ status = status.lower() self['status'] = status db, handle_ta = get_db_for_write(self.env, db) cursor = db.cursor() sql = 'INSERT INTO testcasehistory (id, planid, time, author, status) VALUES (%s, %s, %s, %s, %s)' cursor.execute(sql, (self.values['id'], self.values['planid'], to_any_timestamp(datetime.now(utc)), author, status)) if handle_ta: db.commit() def list_history(self, db=None): """ Returns an ordered list of status changes, along with timestamp and author, starting from the most recent. """ if db is None: db = get_db(self.env, db) cursor = db.cursor() sql = "SELECT time, author, status FROM testcasehistory WHERE id=%s AND planid=%s ORDER BY time DESC" cursor.execute(sql, (self.values['id'], self.values['planid'])) for ts, author, status in cursor: yield ts, author, status.lower() def get_related_tickets(self, db=None): """ Returns an iterator over the IDs of the ticket opened against this test case and this test plan. """ self.env.log.debug('>>> get_related_tickets') if db is None: db = get_db(self.env, db) cursor = db.cursor() cursor.execute("SELECT id FROM ticket WHERE id in " + "(SELECT ticket FROM ticket_custom WHERE name='testcaseid' AND value=%s) " + "AND id in " + "(SELECT ticket FROM ticket_custom WHERE name='planid' AND value=%s) ", (self.values['page_name'], self.values['planid'])) for row in cursor: self.env.log.debug(' ---> Found ticket %s' % row[0]) yield row[0] self.env.log.debug('<<< get_related_tickets') class TestPlan(AbstractVariableFieldsObject): """ A test plan represents a particular instance of test execution for a test catalog. You can create any number of test plans on any test catalog (or sub-catalog). A test plan is associated to a test catalog, and to every test case in it, with the initial state equivalent to "to be executed". The association with test cases is achieved through the TestCaseInPlan objects. For optimization purposes, a TestCaseInPlan is created in the database only as soon as its status is changed (i.e. from "to be executed" to something else). So you cannot always count on the fact that a TestCaseInPlan actually exists for every test case in a catalog, when a particular test plan has been created for it. """ # Fields that must not be modified directly by the user protected_fields = ('id', 'catid', 'page_name', 'name', 'author', 'time') def __init__(self, env, id=None, catid=None, page_name=None, name=None, author=None, db=None): """ A test plan has an ID, generated at creation time and independent on those for test catalogs and test cases. It is associated to a test catalog, the 'catid' and 'page_name' arguments. It has a name and an author. """ self.values = {} self.values['id'] = id self.values['catid'] = catid self.values['page_name'] = page_name self.values['name'] = name self.values['author'] = author key = self.build_key_object() AbstractVariableFieldsObject.__init__(self, env, 'testplan', key, db) def create_instance(self, key): return TestPlan(self.env, key['id']) def post_delete(self, db): self.env.log.debug("Deleting this test plan %s" % self['id']) # Remove all test cases (in plan) from this plan self.env.log.debug("Deleting all test cases in the plan...") tcip_search = TestCaseInPlan(self.env) tcip_search['planid'] = self.values['id'] for tcip in tcip_search.list_matching_objects(db=db): self.env.log.debug("Deleting test case in plan, with id %s" % tcip['id']) tcip.delete(db) def get_related_tickets(self, db): pass class TestManagerModelProvider(Component): """ This class provides the data model for the test management plugin. The actual data model on the db is created starting from the SCHEMA declaration below. For each table, we specify whether to create also a '_custom' and a '_change' table. This class also provides the specification of the available fields for each class, being them standard fields and the custom fields specified in the trac.ini file. The custom field specification follows the same syntax as for Tickets. Currently, only 'text' type of custom fields are supported. """ implements(IConcreteClassProvider, IEnvironmentSetupParticipant) SCHEMA = { 'testconfig': {'table': Table('testconfig', key = ('propname'))[ Column('propname'), Column('value')], 'has_custom': False, 'has_change': False}, 'testcatalog': {'table': Table('testcatalog', key = ('id'))[ Column('id'), Column('page_name')], 'has_custom': True, 'has_change': True}, 'testcase': {'table': Table('testcase', key = ('id'))[ Column('id'), Column('page_name')], 'has_custom': True, 'has_change': True}, 'testcaseinplan': {'table': Table('testcaseinplan', key = ('id', 'planid'))[ Column('id'), Column('planid'), Column('page_name'), Column('status')], 'has_custom': True, 'has_change': True}, 'testcasehistory': {'table': Table('testcasehistory', key = ('id', 'planid', 'time'))[ Column('id'), Column('planid'), Column('time', type='int64'), Column('author'), Column('status'), Index(['id', 'planid', 'time'])], 'has_custom': False, 'has_change': False}, 'testplan': {'table': Table('testplan', key = ('id'))[ Column('id'), Column('catid'), Column('page_name'), Column('name'), Column('author'), Column('time', type='int64'), Index(['id']), Index(['catid'])], 'has_custom': True, 'has_change': True} } FIELDS = { 'testcatalog': [ {'name': 'id', 'type': 'text', 'label': N_('ID')}, {'name': 'page_name', 'type': 'text', 'label': N_('Wiki page name')} ], 'testcase': [ {'name': 'id', 'type': 'text', 'label': N_('ID')}, {'name': 'page_name', 'type': 'text', 'label': N_('Wiki page name')} ], 'testcaseinplan': [ {'name': 'id', 'type': 'text', 'label': N_('ID')}, {'name': 'planid', 'type': 'text', 'label': N_('Plan ID')}, {'name': 'page_name', 'type': 'text', 'label': N_('Wiki page name')}, {'name': 'status', 'type': 'text', 'label': N_('Status')} ], 'testplan': [ {'name': 'id', 'type': 'text', 'label': N_('ID')}, {'name': 'catid', 'type': 'text', 'label': N_('Catalog ID')}, {'name': 'page_name', 'type': 'text', 'label': N_('Wiki page name')}, {'name': 'name', 'type': 'text', 'label': N_('Name')}, {'name': 'author', 'type': 'text', 'label': N_('Author')}, {'name': 'time', 'type': 'time', 'label': N_('Created')} ] } METADATA = {'testcatalog': { 'label': "Test Catalog", 'searchable': True, 'has_custom': True, 'has_change': True }, 'testcase': { 'label': "Test Case", 'searchable': True, 'has_custom': True, 'has_change': True }, 'testcaseinplan': { 'label': "Test Case in a Plan", 'searchable': True, 'has_custom': True, 'has_change': True }, 'testplan': { 'label': "Test Plan", 'searchable': True, 'has_custom': True, 'has_change': True } } # IConcreteClassProvider methods def get_realms(self): yield 'testcatalog' yield 'testcase' yield 'testcaseinplan' yield 'testplan' def get_data_models(self): return self.SCHEMA def get_fields(self): return copy.deepcopy(self.FIELDS) def get_metadata(self): return self.METADATA def create_instance(self, realm, key=None): self.env.log.debug(">>> create_instance - %s %s" % (realm, key)) obj = None if realm == 'testcatalog': if key is not None: obj = TestCatalog(self.env, key['id']) else: obj = TestCatalog(self.env) elif realm == 'testcase': if key is not None: obj = TestCase(self.env, key['id']) else: obj = TestCase(self.env) elif realm == 'testcaseinplan': if key is not None: obj = TestCaseInPlan(self.env, key['id'], key['planid']) else: obj = TestCaseInPlan(self.env) elif realm == 'testplan': if key is not None: obj = TestPlan(self.env, key['id']) else: obj = TestPlan(self.env) self.env.log.debug("<<< create_instance") return obj def check_permission(self, req, realm, key_str=None, operation='set', name=None, value=None): if 'TEST_VIEW' not in req.perm: raise PermissionError('TEST_VIEW', realm) if operation == 'set' and 'TEST_MODIFY' not in req.perm: raise PermissionError('TEST_MODIFY', realm) # IEnvironmentSetupParticipant methods def environment_created(self): self.upgrade_environment(get_db(self.env)) def environment_needs_upgrade(self, db): return (self._need_initialization(db) or self._need_upgrade(db)) def upgrade_environment(self, db): # Create db if self._need_initialization(db): upgrade_db(self.env, self.SCHEMA, db) try: cursor = db.cursor() # Create default values for configuration properties and initialize counters cursor.execute("INSERT INTO testconfig (propname, value) VALUES ('NEXT_CATALOG_ID', '0')") cursor.execute("INSERT INTO testconfig (propname, value) VALUES ('NEXT_TESTCASE_ID', '0')") cursor.execute("INSERT INTO testconfig (propname, value) VALUES ('NEXT_PLAN_ID', '0')") db.commit() # Create the basic "TC" Wiki page, used as the root test catalog tc_page = WikiPage(self.env, 'TC') tc_page.text = ' ' tc_page.save('System', '', '127.0.0.1') except: db.rollback() self.env.log.debug("Exception during upgrade") raise if self._need_upgrade(db): # Set custom ticket field to hold related test case custom = self.config['ticket-custom'] config_dirty = False if 'testcaseid' not in custom: custom.set('testcaseid', 'text') custom.set('testcaseid.label', _("Test Case")) config_dirty = True if 'planid' not in custom: custom.set('planid', 'text') custom.set('planid.label', _("Test Plan")) config_dirty = True # Set config section for test case outcomes if 'test-outcomes' not in self.config: self.config.set('test-outcomes', 'green.SUCCESSFUL', _("Successful")) self.config.set('test-outcomes', 'yellow.TO_BE_TESTED', _("Untested")) self.config.set('test-outcomes', 'red.FAILED', _("Failed")) self.config.set('test-outcomes', 'default', 'TO_BE_TESTED') config_dirty = True if config_dirty: self.config.save() def _need_initialization(self, db): return need_db_upgrade(self.env, self.SCHEMA, db) def _need_upgrade(self, db): # Check for custom ticket field to hold related test case custom = self.config['ticket-custom'] if 'testcaseid' not in custom or 'planid' not in custom: return True # Check for config section for test case outcomes if 'test-outcomes' not in self.config: return True return False testman4trac.1.4.5/testman4trac/trunk/testmanager/stats.py0000755000175000017500000002724111565162323023660 0ustar robertoroberto# -*- coding: utf-8 -*- # # Copyright (C) 2010 Roberto Longobardi # # The structure of this plugin is copied from the Tracticketstats plugin, # by Prentice Wongvibulisn # import re from genshi.builder import tag from trac.core import * from trac.config import Option, IntOption from trac.util import format_date, format_datetime from trac.web import IRequestHandler from trac.web.chrome import INavigationContributor, ITemplateProvider from trac.perm import IPermissionRequestor from datetime import date, datetime, time, timedelta from time import strptime from trac.util.datefmt import utc, parse_date from tracgenericclass.util import * from testmanager.api import TestManagerSystem from testmanager.util import * # ************************ DEFAULT_DAYS_BACK = 30*3 DEFAULT_INTERVAL = 7 # ************************ class TestStatsPlugin(Component): implements(INavigationContributor, IRequestHandler, ITemplateProvider, IPermissionRequestor) yui_base_url = Option('teststats', 'yui_base_url', default='http://yui.yahooapis.com/2.8.2r1', doc='Location of YUI API') default_days_back = IntOption('teststats', 'default_days_back', default=DEFAULT_DAYS_BACK, doc='Number of days to show by default') default_interval = IntOption('teststats', 'default_interval', default=DEFAULT_INTERVAL, doc='Number of days between each data point'\ ' (resolution) by default') # ==[ INavigationContributor methods ]== def get_active_navigation_item(self, req): return 'teststats' def get_permission_actions(self): return ['TEST_STATS_VIEW'] def get_navigation_items(self, req): if req.perm.has_permission('TEST_STATS_VIEW'): yield ('mainnav', 'teststats', tag.a('Test Stats', href=req.href.teststats())) # ==[ Helper functions ]== def _get_num_testcases(self, from_date, at_date, catpath, testplan, req): ''' Returns an integer of the number of test cases counted between from_date and at_date. ''' if catpath == None or catpath == '': path_filter = "TC_%_TC%" else: path_filter = catpath + "%_TC%" dates_condition = '' if from_date: dates_condition += " AND time > %s" % to_any_timestamp(from_date) if at_date: dates_condition += " AND time <= %s" % to_any_timestamp(at_date) db = self.env.get_db_cnx() cursor = db.cursor() cursor.execute("SELECT COUNT(*) FROM wiki WHERE name LIKE '%s' AND version = 1 %s" % (path_filter, dates_condition)) row = cursor.fetchone() count = row[0] return count def _get_num_tcs_by_status(self, from_date, at_date, status, testplan, req): ''' Returns an integer of the number of test cases that had the specified status between from_date to at_date. ''' if testplan == None or testplan == '': testplan_filter = '' else: testplan_filter = " AND planid = '%s'" % (testplan) db = self.env.get_db_cnx() cursor = db.cursor() cursor.execute("SELECT COUNT(*) from testcasehistory WHERE status = '%s' AND time > %s AND time <= %s %s" % (status, to_any_timestamp(from_date), to_any_timestamp(at_date), testplan_filter)) row = cursor.fetchone() count = row[0] return count # ==[ IRequestHandle methods ]== def match_request(self, req): return re.match(r'/teststats(?:_trac)?(?:/.*)?$', req.path_info) def process_request(self, req): testmanagersystem = TestManagerSystem(self.env) tc_statuses = testmanagersystem.get_tc_statuses_by_color() req_content = req.args.get('content') testplan = None catpath = None if not None in [req.args.get('end_date'), req.args.get('start_date'), req.args.get('resolution')]: # form submit grab_at_date = req.args.get('end_date') grab_from_date = req.args.get('start_date') grab_resolution = req.args.get('resolution') grab_testplan = req.args.get('testplan') if grab_testplan and not grab_testplan == "__all": testplan = grab_testplan.partition('|')[0] catpath = grab_testplan.partition('|')[2] # validate inputs if None in [grab_at_date, grab_from_date]: raise TracError('Please specify a valid range.') if None in [grab_resolution]: raise TracError('Please specify the graph interval.') if 0 in [len(grab_at_date), len(grab_from_date), len(grab_resolution)]: raise TracError('Please ensure that all fields have been filled in.') if not grab_resolution.isdigit(): raise TracError('The graph interval field must be an integer, days.') at_date = parse_date(grab_at_date, req.tz) from_date = parse_date(grab_from_date, req.tz) graph_res = int(grab_resolution) else: # default data todays_date = datetime.today() at_date = todays_date #+ timedelta(1) # datetime.combine(todays_date,time(23,59,59,0,req.tz)) at_date = at_date.replace(tzinfo = req.tz) from_date = at_date - timedelta(self.default_days_back) graph_res = self.default_interval count = [] # Calculate 0th point last_date = from_date - timedelta(graph_res) # Calculate remaining points for cur_date in daterange(from_date, at_date, graph_res): # Handling custom test case outcomes here num_new = self._get_num_testcases(last_date, cur_date, catpath, testplan, req) num_successful = 0 for tc_outcome in tc_statuses['green']: num_successful += self._get_num_tcs_by_status(last_date, cur_date, tc_outcome, testplan, req) num_failed = 0 for tc_outcome in tc_statuses['red']: num_failed += self._get_num_tcs_by_status(last_date, cur_date, tc_outcome, testplan, req) num_all = self._get_num_testcases(None, cur_date, catpath, testplan, req) num_all_successful = 0 for tc_outcome in tc_statuses['green']: num_all_successful += self._get_num_tcs_by_status(from_date, cur_date, tc_outcome, testplan, req) num_all_failed = 0 for tc_outcome in tc_statuses['red']: num_all_failed += self._get_num_tcs_by_status(from_date, cur_date, tc_outcome, testplan, req) num_all_untested = num_all - num_all_successful - num_all_failed datestr = format_date(cur_date) if graph_res != 1: datestr = "%s thru %s" % (format_date(last_date), datestr) count.append( {'from_date': format_date(last_date), 'to_date': datestr, 'date' : datestr, 'new_tcs' : num_new, 'successful': num_successful, 'failed': num_failed, 'all_tcs' : num_all, 'all_successful': num_all_successful, 'all_untested': num_all_untested, 'all_failed': num_all_failed }) last_date = cur_date # if chartdata is requested, raw text is returned rather than data object # for templating if (not req_content == None) and (req_content == "chartdata"): jsdstr = '{"chartdata": [\n' for x in count: jsdstr += '{"date": "%s",' % x['date'] jsdstr += ' "new_tcs": %s,' % x['new_tcs'] jsdstr += ' "successful": %s,' % x['successful'] jsdstr += ' "failed": %s,' % x['failed'] jsdstr += ' "all_tcs": %s,' % x['all_tcs'] jsdstr += ' "all_successful": %s,' % x['all_successful'] jsdstr += ' "all_untested": %s,' % x['all_untested'] jsdstr += ' "all_failed": %s},\n' % x['all_failed'] jsdstr = jsdstr[:-2] +'\n]}' if isinstance(jsdstr, unicode): jsdstr = jsdstr.encode('utf-8') req.send_header("Content-Length", len(jsdstr)) req.write(jsdstr) return elif (not req_content == None) and (req_content == "downloadcsv"): csvstr = "Date from;Date to;New Test Cases;Successful;Failed;Total Test Cases;Total Successful;Total Untested;Total Failed\r\n" for x in count: csvstr += '%s;' % x['from_date'] csvstr += '%s;' % x['to_date'] csvstr += '%s;' % x['new_tcs'] csvstr += '%s;' % x['successful'] csvstr += '%s;' % x['failed'] csvstr += '%s;' % x['all_tcs'] csvstr += '%s;' % x['all_successful'] csvstr += '%s;' % x['all_untested'] csvstr += '%s\r\n' % x['all_failed'] if isinstance(csvstr, unicode): csvstr = csvstr.encode('utf-8') req.send_header("Content-Length", len(csvstr)) req.send_header("Content-Disposition", "attachment;filename=Test_stats.csv") req.write(csvstr) return else: db = self.env.get_db_cnx() showall = req.args.get('show') == 'all' testplan_list = [] for planid, catid, catpath, name, author, ts_str in testmanagersystem.list_all_testplans(): testplan_list.append({'planid': planid, 'catpath': catpath, 'name': name}) data = {} data['testcase_data'] = count data['start_date'] = format_date(from_date) data['end_date'] = format_date(at_date) data['resolution'] = str(graph_res) data['baseurl'] = req.base_url data['testplans'] = testplan_list data['ctestplan'] = testplan return 'testmanagerstats.html', data, None # ITemplateProvider methods def get_templates_dirs(self): """ Return the absolute path of the directory containing the provided Genshi templates. """ from pkg_resources import resource_filename return [resource_filename(__name__, 'templates')] def get_htdocs_dirs(self): """Return the absolute path of a directory containing additional static resources (such as images, style sheets, etc). """ from pkg_resources import resource_filename #return [('testmanager', resource_filename(__name__, 'htdocs'))] return [('testmanager', resource_filename('testmanager', 'htdocs'))] def daterange(begin, end, delta = timedelta(1)): """Stolen from: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/574441 Form a range of dates and iterate over them. Arguments: begin -- a date (or datetime) object; the beginning of the range. end -- a date (or datetime) object; the end of the range. delta -- (optional) a timedelta object; how much to step each iteration. Default step is 1 day. Usage: """ if not isinstance(delta, timedelta): delta = timedelta(delta) ZERO = timedelta(0) if begin < end: if delta <= ZERO: raise StopIteration test = end.__gt__ else: if delta >= ZERO: raise StopIteration test = end.__lt__ while test(begin): yield begin begin += delta testman4trac.1.4.5/testman4trac/trunk/testmanager/util.py0000755000175000017500000000045111565162323023471 0ustar robertoroberto# -*- coding: utf-8 -*- # # Copyright (C) 2010 Roberto Longobardi # import re from trac.core import * from trac.util.text import CRLF def get_page_title(text): return text.split('\n')[0].strip('\r\n').strip('= \'') def get_page_description(text): return text.partition(CRLF)[2] testman4trac.1.4.5/testman4trac/trunk/testmanager/workflow.py0000755000175000017500000000744011565162323024373 0ustar robertoroberto# -*- coding: utf-8 -*- # # Copyright (C) 2010 Roberto Longobardi # from trac.core import * from trac.resource import Resource from trac.util.datefmt import utc from trac.web.api import ITemplateStreamFilter from genshi.builder import tag from genshi.filters.transform import Transformer from genshi import HTML from tracgenericclass.util import * from tracgenericworkflow.model import ResourceWorkflowState from tracgenericworkflow.api import IWorkflowOperationProvider, ResourceWorkflowSystem # Workflow support class TestManagerWorkflowInterface(Component): """Adds workflow capabilities to the TestManager plugin.""" implements(IWorkflowOperationProvider, ITemplateStreamFilter) # IWorkflowOperationProvider methods # Just a sample operation def get_implemented_operations(self): self.log.debug(">>> TestManagerWorkflowInterface - get_implemented_operations") self.log.debug("<<< TestManagerWorkflowInterface - get_implemented_operations") yield 'sample_operation' def get_operation_control(self, req, action, operation, res_wf_state, resource): self.log.debug(">>> TestManagerWorkflowInterface - get_operation_control: %s" % operation) if operation == 'sample_operation': id = 'action_%s_operation_%s' % (action, operation) speech = 'Hello World!' control = tag.input(type='text', id=id, name=id, value=speech) hint = "Will sing %s" % speech self.log.debug("<<< TestManagerWorkflowInterface - get_operation_control") return control, hint return None, '' def perform_operation(self, req, action, operation, old_state, new_state, res_wf_state, resource): self.log.debug("---> Performing operation %s while transitioning from %s to %s." % (operation, old_state, new_state)) speech = req.args.get('action_%s_operation_%s' % (action, operation), 'Not found!') self.log.debug(" The speech is %s" % speech) # ITemplateStreamFilter methods def filter_stream(self, req, method, filename, stream, data): page_name = req.args.get('page', 'WikiStart') planid = req.args.get('planid', '-1') if page_name == 'TC': # The root catalog does not have workflows return stream if page_name.startswith('TC') and filename == 'wiki_view.html': self.log.debug(">>> TestManagerWorkflowInterface - filter_stream") req.perm.require('TEST_VIEW') # Determine which object is being displayed (i.e. realm), # based on Wiki page name and the presence of the planid # request parameter. realm = None if page_name.find('_TC') >= 0: if not planid or planid == '-1': realm = 'testcase' key = {'id': page_name.rpartition('_TC')[2]} else: realm = 'testcaseinplan' key = {'id': page_name.rpartition('_TC')[2], 'planid': planid} else: if not planid or planid == '-1': realm = 'testcatalog' key = {'id': page_name.rpartition('_TT')[2]} else: realm = 'testplan' key = {'id': planid} id = get_string_from_dictionary(key) res = Resource(realm, id) rwsystem = ResourceWorkflowSystem(self.env) workflow_markup = rwsystem.get_workflow_markup(req, '..', realm, res) self.log.debug("<<< TestManagerWorkflowInterface - filter_stream") return stream | Transformer('//div[contains(@class,"wikipage")]').after(workflow_markup) return stream testman4trac.1.4.5/testman4trac/trunk/setup.py0000755000175000017500000000361311565471760021355 0ustar robertorobertofrom setuptools import setup extra = {} try: from trac.util.dist import get_l10n_js_cmdclass cmdclass = get_l10n_js_cmdclass() if cmdclass: # OK, Babel is there extra['cmdclass'] = cmdclass extractors = [ ('**.py', 'python', None), ('**/templates/**.html', 'genshi', None), ('**/templates/**.txt', 'genshi', { 'template_class': 'genshi.template:TextTemplate' }), ] extra['message_extractors'] = { 'testmanager': extractors, } except ImportError: pass setup( name='TestManager', version='1.4.5', packages=['testmanager'], package_data={ 'testmanager' : [ '*.txt', 'templates/*.html', 'htdocs/js/*.js', 'htdocs/css/*.css', 'htdocs/css/blitzer/*.css', 'htdocs/css/blitzer/images/*.*', 'htdocs/css/images/*.*', 'htdocs/images/*.*', 'locale/*.*', 'locale/*/LC_MESSAGES/*.mo', 'htdocs/testmanager/*.js' ] }, author = 'Roberto Longobardi', author_email='seccanj@gmail.com', license='BSD. See the file LICENSE.txt contained in the package.', url='http://trac-hacks.org/wiki/TestManagerForTracPlugin', download_url='https://sourceforge.net/projects/testman4trac/files/', description='Test management plugin for Trac', long_description='A Trac plugin to create Test Cases, organize them in catalogs and track their execution status and outcome.', keywords='trac plugin test case management project quality assurance statistics stats charts charting graph', entry_points = {'trac.plugins': ['testmanager = testmanager']}, dependency_links=['http://svn.edgewall.org/repos/genshi/trunk#egg=Genshi-dev'], install_requires=['Genshi >= 0.5'], **extra ) testman4trac.1.4.5/README.txt0000755000175000017500000002424011566010002015537 0ustar robertorobertoTest Manager plugin for Trac Copyright 2010 Roberto Longobardi Project web page on TracHacks: http://trac-hacks.org/wiki/TestManagerForTracPlugin Project web page on SourceForge.net: http://sourceforge.net/projects/testman4trac/ Project web page on Pypi: http://pypi.python.org/pypi/TestManager A Trac plugin to create Test Cases, organize them in catalogs, generate test plans and track their execution status and outcome. Refer to BUILD.txt for details about how to build. Refer to INSTALL.txt for installation details. ================================================================================================= Change History: (Refer to the tickets on trac-hacks or SourceForge for complete descriptions.) Release 1.4.5 (2011-05-21): o Enhancement #8825 (Track-Hacks): Ability to import test cases from Excel (CSV) file Release 1.4.4 (2011-03-11): o Fixed Ticket #8567 (Track-Hacks): Javascript error when deleting test plans o Enhancement #8596 (Track-Hacks): Remove hard dependency on XML RPC plugin for Trac 0.11 o Enhancement #8761 (Track-Hacks): Copy multiple test cases into another catalog Added wiki documentation for copying multiple test cases into another catalog. Release 1.4.3 (2011-01-20): o Enhancement #8427 (Track-Hacks): Add XML-RPC complete interface for remote management of test objects Release 1.4.2 (2011-01-09): o Fixed Ticket #8378 (Track-Hacks): Set date and time format correctly in Test Stats page Also added support for custom test case outcomes in the Test Stats page Release 1.4.1 (2010-12-27): o Enhancement #7846 (Track-Hacks): Customizable test case outcomes (aka verdicts) o Enhancement #7570 (Track-Hacks): Add a relationship table between tickets and test cases in plan, and corresponding API Release 1.3.12 (2010-12-19): o Enhancement #8321 (Track-Hacks): Add standard internationalization support (i18n) o Enhancement #8322 (Track-Hacks): Show timestamps according to User's locale o Fixed Ticket #8323 (Track-Hacks): Unable to expand Available plans and Test case status change history collapsable sections Release 1.3.11 (2010-12-02): o Added out of the box operation to workflow engine: set_owner and set_owner_to_self o Enhancement #8259 (Track-Hacks): Add navigation from a test case to its related tickets Release 1.3.10 (2010-11-28): o Fixed Ticket #8154 (Track-Hacks): LookupError: unknown encoding: cp0 Release 1.3.9 (2010-11-23): o Fixed Ticket #8144 (Track-Hacks): Test statistical charts don't show successful and failed figures. Release 1.3.8 (2010-11-22): o Fixed Ticket #8121 (Track-Hacks): Catalog Wiki Page not added o Fixed Ticket #8123 (Track-Hacks): Can't move testcase more than one time into different catalog o Fixed Ticket #8124 (Track-Hacks): AttributeError: 'NoneType' object has no attribute 'splitlines' o Fixed Ticket #8125 (Track-Hacks): "duplicate test case" does not work for previously moved test case Release 1.3.7 (2010-11-20): o Enhancement #7704 (Track-Hacks): Add ability to delete a Test Plan o Fixet Ticket #8084 (Track-Hacks): Ordering issue Release 1.3.6 (2010-11-09): o Fixed Ticket #8004 (Track-Hacks): Cannot search if an admin Release 1.3.5 (2010-10-17): o Restored compatibility with Trac 0.11. Now again both 0.11 and 0.12 are supported. Release 1.3.4 (2010-10-15): o Added tabular view to catalogs and plans. Search now works with custom properties in tabular views. Release 1.3.3 (2010-10-05): o Enhanced feature 3076739 (SourceForge): Full Test Plan display / print Release 1.3.2 (2010-10-03): o Added feature 3076739 (SourceForge): Full Test Plan display / print Release 1.3.1 (2010-10-02): o Fixed a base-code bug that prevented test catalog modification with a PostgreSQL binding. (Thanks Rodel for reporting it). Release 1.3.0 (2010-10-01): o The base Test Manager plugin has been separated and other plugins have been created to embed the functionalities of generic class framework and the workflow engine. Now we have four plugins: * Trac Generic Class plugin, providing the persistent class framework * Trac Generic Workflow plugin, providing the generic workflow engine applicable to any Trac Resource. This plugin requires the Trac Generic Class plugin. * SQL Executor plugin, as a debugging tool when dealing with persistent data. Not to be enabled in a production environment. Moreover, the generic class framework has been added a set of new functionalities. Refer to the README file in its package for more details. Release 1.2.0 (2010-09-20): o The data model has been completely rewritten, now using python classes for all the test objects. A generic object supporting programmatic definition of its standard fields, declarative definition of custom fields (in trac.ini) and keeping track of change history has been created, by generalizing the base Ticket code. The specific object "type" is specified during construction as the "realm" parameter. This name must also correspond to the database table storing the corresponding objects, and is used as the base name for the custom fields table and the change tracking table (see below). Features: * Support for custom fields, specified in the trac.ini file with the same syntax as for custom Ticket fields. Custom fields are kept in a "_custom" table * Keeping track of all changes to any field, into a separate "_change" table * A set of callbacks to allow for subclasses to control and perform actions pre and post any operation pertaining the object's lifecycle * Registering listeners, via the ITestObjectChangeListener interface, for object creation, modification and deletion. * Searching objects matching any set of valorized fields, (even non-key fields), applying the "dynamic record" pattern. See the method list_matching_objects. o Enhancement #7704 (Track-Hacks): Add workflow capabilities, with custom states, transitions and operations, and state transition listeners support A generic Trac Resource workflow system has been implemented, allowing to add workflow capabilities to any Trac resource. Test objects have been implemented as Trac resources as well, so they benefit of workflow capabilities. Available objects 'realms' to associate workflows to are: testcatalog, testcase, testcaseinplan, testplan. Note that the object with realm 'resourceworkflowstate', which manages the state of any resource in a workflow, also supports custom properties (see below), so plugins can augment a resource workflow state with additional context information and use it inside listener callbacks, for example. For example, add the following to your trac.ini file to associate a workflow with all Test Case objects. The sample_operation is currently provided by the Test Manager system itself, as an example. It just logs a debug message with the text input by the User in a text field. [testcase-resource_workflow] leave = * -> * leave.operations = sample_operation leave.default = 1 accept = new -> accepted accept.permissions = TEST_MODIFY accept.operations = sample_operation resolve = accepted -> closed resolve.permissions = TEST_MODIFY resolve.operations = sample_operation o Enhancement #7705 (Track-Hacks): Add support for custom properties and change history to all of the test management objects A generic object supporting programmatic definition of its standard fields, declarative definition of custom fields (in trac.ini) and keeping track of change history has been created, by generalizing the base Ticket code. Only text type of properties are currently supported. For example, add the following to your trac.ini file to add custom properties to all of the four test objects. Note that the available realms to augment are, as above, testcatalog, testcase, testcaseinplan and testplan, with the addition of resourceworkflowstate. [testcatalog-tm_custom] prop1 = text prop1.value = Default value [testcaseinplan-tm_custom] prop_strange = text prop_strange.value = windows [testcase-tm_custom] nice_prop = text nice_prop.value = My friend [testplan-tm_custom] good_prop = text good_prop.value = linux o Enhancement #7569 (Track-Hacks): Add listener interface to let other components react to test case status change Added listener interface for all of the test objects lifecycle: * Object created * Object modified (including custom properties) * Object deleted This applies to test catalogs, test cases, test plans and test cases in a plan (i.e. with a status). Release 1.1.2 (2010-08-25): o Enhancement #7552 (Track-Hacks): Export test statistics in CSV and bookmark this chart features in the test stats chart o Fixed Ticket #7551 (Track-Hacks): Test statistics don't work on Trac 0.11 Release 1.1.1 (2010-08-20): o Enhancement #7526 (Track-Hacks): Add ability to duplicate a test case o Enhancement #7536 (Track-Hacks): Add test management statistics o Added "autosave=true" parameter to the RESTful API to create test catalogs and test cases programmatically without need to later submit the wiki editing form. Release 1.1.0 (2010-08-18): o Enhancement #7487 (Track-Hacks): Add multiple test plans capability o Enhancement #7507 (Track-Hacks): Implement security permissions o Enhancement #7484 (Track-Hacks): Reverse the order of changes in the test case status change history Release 1.0.2 (2010-08-17): o Fixed Ticket #7485 (Track-Hacks): "Open ticket on this test case" should work without a patched TracTicketTemplatePlugin Release 1.0.1 (2010-08-12): o First attempt at externalizing strings Release 1.0 (2010-08-10): o First release publicly available testman4trac.1.4.5/INSTALLATION.txt0000755000175000017500000000060311565162323016515 0ustar robertorobertoInstallation: The functionalities are divided in three plugins, which must be installed in this order: 1) Trac Generic Class => TracGenericClass 2) Trac Generic Workflow => TracGenericWorkflow 3) Test Manager => TestManager An additional plugin is only useful for debugging and should not be installed in a production environment. * SQL Executor => SQLExecutor testman4trac.1.4.5/package.sh0000755000175000017500000000073611565162323016011 0ustar robertorobertoVER=$1 zip -r testman4trac.$VER.zip bin mkdir testman4trac.$VER cp *.sh testman4trac.$VER cp *.txt testman4trac.$VER cp *.cmd testman4trac.$VER cp -R sqlexecutor testman4trac.$VER cp -R testman4trac testman4trac.$VER cp -R tracgenericclass testman4trac.$VER cp -R tracgenericworkflow testman4trac.$VER cd testman4trac.$VER . ./clean.sh find . -type d -name .svn -print -exec rm -rf {} \; cd .. zip -r testman4trac.$VER.src.zip testman4trac.$VER rm -rf testman4trac.$VER testman4trac.1.4.5/build.cmd0000755000175000017500000000131711565162323015642 0ustar robertorobertomkdir bin cd tracgenericclass\trunk python setup.py bdist_egg xcopy /y dist\*.egg ..\..\bin cd ..\..\tracgenericworkflow\trunk python setup.py bdist_egg xcopy /y dist\*.egg ..\..\bin cd ..\..\sqlexecutor\trunk python setup.py bdist_egg xcopy /y dist\*.egg ..\..\bin cd ..\..\testman4trac\trunk rem python setup.py extract_messages rem python setup.py extract_messages_js rem python setup.py update_catalog -l it_IT rem python setup.py update_catalog_js -l it_IT rem python ./setup.py compile_catalog -f -l it_IT rem python ./setup.py compile_catalog_js -f -l it_IT python setup.py bdist_egg xcopy /y dist\*.egg ..\..\bin cd ..\.. xcopy /y *.txt bin xcopy /y bin\*.egg %1\plugins testman4trac.1.4.5/tracgenericclass/0000755000175000017500000000000011565162323017365 5ustar robertorobertotestman4trac.1.4.5/tracgenericclass/trunk/0000755000175000017500000000000011566010105020517 5ustar robertorobertotestman4trac.1.4.5/tracgenericclass/trunk/tracgenericclass/0000755000175000017500000000000011565672632024055 5ustar robertorobertotestman4trac.1.4.5/tracgenericclass/trunk/tracgenericclass/__init__.py0000755000175000017500000000015211565162323026156 0ustar robertoroberto# -*- coding: utf-8 -*- # # Copyright (C) 2010 Roberto Longobardi # import api import model import util testman4trac.1.4.5/tracgenericclass/trunk/tracgenericclass/LICENSE.txt0000755000175000017500000000242711565162323025677 0ustar robertorobertoCopyright (c) 2010, Roberto Longobardi All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.testman4trac.1.4.5/tracgenericclass/trunk/tracgenericclass/templates/0000755000175000017500000000000011565162323026042 5ustar robertorobertotestman4trac.1.4.5/tracgenericclass/trunk/tracgenericclass/templates/empty.html0000755000175000017500000000041111565162323030065 0ustar robertoroberto testman4trac.1.4.5/tracgenericclass/trunk/tracgenericclass/htdocs/0000755000175000017500000000000011565162323025330 5ustar robertorobertotestman4trac.1.4.5/tracgenericclass/trunk/tracgenericclass/htdocs/images/0000755000175000017500000000000011565162323026575 5ustar robertorobertotestman4trac.1.4.5/tracgenericclass/trunk/tracgenericclass/htdocs/place.holder0000755000175000017500000000000011565162323027604 0ustar robertorobertotestman4trac.1.4.5/tracgenericclass/trunk/tracgenericclass/htdocs/js/0000755000175000017500000000000011565162323025744 5ustar robertorobertotestman4trac.1.4.5/tracgenericclass/trunk/tracgenericclass/htdocs/css/0000755000175000017500000000000011565162323026120 5ustar robertorobertotestman4trac.1.4.5/tracgenericclass/trunk/tracgenericclass/README.txt0000755000175000017500000000626611565162323025557 0ustar robertorobertoTrac Generic Class - Part of the Test Manager plugin for Trac Copyright 2010 Roberto Longobardi Project web page on TracHacks: http://trac-hacks.org/wiki/TestManagerForTracPlugin Project web page on SourceForge.net: http://sourceforge.net/projects/testman4trac/ Project web page on Pypi: http://pypi.python.org/pypi/TestManager A Trac plugin to create Test Cases, organize them in catalogs, generate test plans and track their execution status and outcome. This module provides a framework to help creating classes on Trac that: * Are persisted on the DB * Support change history * Support extensibility through custom properties that the User can specify declaratively in the trac.ini file * Support custom operations to be performed before and after the standard object lifecycle events. * Listener interface for Components willing to be notified on any object lifecycle event (i.e. creation, modification, deletion). Database tables are also automatically created by the framework as declaratively stated by the client Components. Also provides an intermediate class to build objects that wrap Wiki pages and have additional properties. More details: A generic object framework supporting programmatic and declarative definition of its standard fields, declarative definition of custom fields (in trac.ini) and keeping track of change history has been created, by generalizing the base Ticket code. The specific object "type" is specified during construction as the "realm" parameter. This name must also correspond to the database table storing the corresponding objects, and is used as the base name for the custom fields table and the change tracking table (see below). Features: * Support for custom fields, specified in the trac.ini file with the same syntax as for custom Ticket fields. Custom fields are kept in a "_custom" table * Keeping track of all changes to any field, into a separate "_change" table * A set of callbacks to allow for subclasses to control and perform actions pre and post any operation pertaining the object's lifecycle * Registering listeners, via the ITestObjectChangeListener interface, for object creation, modification and deletion. * Searching objects by similarity, i.e. matching any set of valorized fields (even non-key fields), applying the "dynamic record" pattern. See the method list_matching_objects. * Integration with the Trac Search page. * Integration with the Trac Resource API. * Support for declarative specification of the database backing a particular class. The tables are created automatically. * Fine-grained security access control to any operation on any resource type or instance. ================================================================================================= Change History: (Refer to the tickets on trac-hacks for complete descriptions.) Release 1.0.4 (2011-01-17): o Added support for LIKE match in list_matching_objects. Release 1.0.0 (2010-10-01): o First release publicly available apart from the core Test Manager plugin testman4trac.1.4.5/tracgenericclass/trunk/tracgenericclass/api.py0000755000175000017500000001405311565162323025175 0ustar robertoroberto# -*- coding: utf-8 -*- # # Copyright (C) 2010 Roberto Longobardi # import re import sys import time import traceback from datetime import datetime from trac.core import * from trac.perm import IPermissionRequestor, PermissionError from trac.resource import IResourceManager from trac.search import ISearchSource from trac.util import get_reporter_id from trac.util.datefmt import utc from trac.util.translation import _, N_, gettext from trac.web.api import IRequestHandler from trac.web.chrome import ITemplateProvider from tracgenericclass.model import AbstractVariableFieldsObject, GenericClassModelProvider from tracgenericclass.util import * class IGenericObjectChangeListener(Interface): """ Extension point interface for components that require notification when objects are created, modified, or deleted. """ def object_created(g_object): """Called when an object is created.""" def object_changed(g_object, comment, author, old_values): """Called when an object is modified. `old_values` is a dictionary containing the previous values of the fields that have changed. """ def object_deleted(g_object): """Called when an object is deleted.""" class GenericClassSystem(Component): """ Generic Class system for Trac. """ implements(IRequestHandler, ITemplateProvider, ISearchSource) change_listeners = ExtensionPoint(IGenericObjectChangeListener) # Change listeners management def object_created(self, testobject): for c in self.change_listeners: c.object_created(testobject) def object_changed(self, testobject, comment, author): for c in self.change_listeners: c.object_changed(testobject, comment, author, testobject._old) def object_deleted(self, testobject): for c in self.change_listeners: c.object_deleted(testobject) # IRequestHandler methods def match_request(self, req): return (req.path_info.startswith('/propertyupdate')) def process_request(self, req): """ Handles Ajax requests to change an object's property. """ author = get_reporter_id(req, 'author') if req.path_info.startswith('/propertyupdate'): realm = req.args.get('realm') key_str = req.args.get('key') name = req.args.get('name') value = req.args.get('value') key = get_dictionary_from_string(key_str) try: self.env.log.debug("Setting property %s to %s, in %s with key %s" % (name, value, realm, key)) gclass_modelprovider = GenericClassModelProvider(self.env) gclass_modelprovider.check_permission(req, realm, key_str, name, value) obj = gclass_modelprovider.get_object(realm, key) # Set the required property obj[name] = value obj.author = author obj.remote_addr = req.remote_addr if obj is not None and obj.exists: comment = "Property changed" obj.save_changes(author, comment) # Call listeners self.object_changed(obj, comment, author) else: self.env.log.debug("Object to update not found. Creating it.") props_str = req.args.get('props') if props_str is not None and not props_str == '': # In order to create an object, additional properties may be required props = get_dictionary_from_string(props_str) obj.set_values(props) obj.insert() # Call listeners self.object_created(obj) except: self.env.log.debug(formatExceptionInfo()) return 'empty.html', {}, None # ITemplateProvider methods def get_templates_dirs(self): """ Return the absolute path of the directory containing the provided Genshi templates. """ from pkg_resources import resource_filename return [resource_filename(__name__, 'templates')] def get_htdocs_dirs(self): """ Return the absolute path of a directory containing additional static resources (such as images, style sheets, etc). """ from pkg_resources import resource_filename return [('tracgenericclass', resource_filename(__name__, 'htdocs'))] # ISearchSource methods def get_search_filters(self, req): gclass_modelprovider = GenericClassModelProvider(self.env) for realm in gclass_modelprovider.get_known_realms(): try: gclass_modelprovider.get_class_provider(realm).check_permission(req, realm, key_str=None, operation='search') metadata = gclass_modelprovider.get_metadata(realm) if 'searchable' in metadata and metadata['searchable']: if 'label' in metadata: label = metadata['label'] else: label = realm.capitalize() yield (realm, label) except: self.env.log.debug("No permission to search on realm %s." % realm) def get_search_results(self, req, terms, filters): gclass_modelprovider = GenericClassModelProvider(self.env) known_realms = gclass_modelprovider.get_known_realms() for realm in filters: if realm in known_realms: metadata = gclass_modelprovider.get_metadata(realm) if 'searchable' in metadata and metadata['searchable']: obj = gclass_modelprovider.get_object(realm) if obj is not None: for result in obj.get_search_results(req, terms, filters): yield result testman4trac.1.4.5/tracgenericclass/trunk/tracgenericclass/model.py0000755000175000017500000013606011565672632025540 0ustar robertoroberto# -*- coding: utf-8 -*- # # Copyright (C) 2010 Roberto Longobardi # import copy import re import sys import time import traceback from datetime import date, datetime from trac.attachment import Attachment from trac.core import * from trac.db import Table, Column, Index from trac.resource import Resource, ResourceNotFound from trac.util.datefmt import utc, utcmax from trac.util.text import CRLF from trac.util.translation import _, N_, gettext from trac.wiki.api import WikiSystem from trac.wiki.model import WikiPage from trac.wiki.web_ui import WikiModule from tracgenericclass.util import * class IConcreteClassProvider(Interface): """ Extension point interface for components willing to implement concrete classes based on this generic class framework. """ def get_realms(): """ Return class realms provided by the component. :rtype: `basestring` generator """ def get_data_models(): """ Return database tables metadata to allow the framework to create the db schema for the classes provided by the component. :rtype: a dictionary, which keys are schema names and values are dictionaries with table metadata, as in the following example: return {'sample_realm': {'table': Table('samplerealm', key = ('id', 'otherid'))[ Column('id'), Column('otherid'), Column('prop1'), Column('prop2'), Column('time', type='int64')], 'has_custom': True, 'has_change': True}, } """ def get_fields(): """ Return the standard fields for classes in all the realms provided by the component. :rtype: a dictionary, which keys are realm names and values are arrays of fields metadata, as in the following example: return {'sample_realm': [ {'name': 'id', 'type': 'text', 'label': N_('ID')}, {'name': 'otherid', 'type': 'text', 'label': N_('Other ID')}, {'name': 'prop1', 'type': 'text', 'label': N_('Property 1')}, {'name': 'prop2', 'type': 'text', 'label': N_('Property 2')}, {'name': 'time', 'type': 'time', 'label': N_('Last Change')} } """ def get_metadata(): """ Return a set of metadata about the classes in all the realms provided by the component. :rtype: a dictionary, which keys are realm names and values are dictionaries of properties. Available metadata properties are: label: A User-friendly name for the objects in this class. searchable: If present and equal to True indicates the class partecipates in the Trac search framework, and must implement the get_search_results() method. 'has_custom': If present and equal to True indicates the class supports custom fields. 'has_change': If present and equal to True indicates the class supports property change history. See the following example: return {'sample_realm': { 'label': "Sample Realm", 'searchable': True } } """ def create_instance(realm, props=None): """ Return an instance of the specified realm, with the specified properties, or an empty object if props is None. :rtype: `AbstractVariableFieldsObject` sub-class instance """ def check_permission(req, realm, key_str=None, operation='set', name=None, value=None): """ Checks whether the logged in User has permission to perform the specified operation on a resource of the specified realm and optionally with the specified key. Raise an exception if authorization is denied. Possible operations are: 'set': set a property with a value. 'name' and 'value' parameters are required. 'search': search for objects of this class. :param key_str: optional, the object's key, in the form of a string representing a dictionary. To get a dictionary back from this string, use the get_dictionary_from_string() function in the tracgenericclass.util package. :param operation: optional, the operation to be performed on the object. :param name: optional property name, valid for the 'set' operation type :param value: optional property value, valid for the 'set' operation type """ class AbstractVariableFieldsObject(object): """ An object which fields are declaratively specified. The specific object "type" is specified during construction as the "realm" parameter. This name must also correspond to the database table storing the corresponding objects, and is used as the base name for the custom fields table and the change tracking table (see below). Features: * Support for custom fields, specified in the trac.ini file with the same syntax as for custom Ticket fields. Custom fields are kept in a "_custom" table * Keeping track of all changes to any field, into a separate "_change" table * A set of callbacks to allow for subclasses to control and perform actions pre and post any operation pertaining the object's lifecycle * Registering listeners, via the IGenericObjectChangeListener interface, for object creation, modification and deletion. * Searching objects matching any set of valorized fields, (even non-key fields), applying the "dynamic record" pattern. See the method list_matching_objects. Notes on special fields: self.exists : always tells whether the object currently exists in the database. self.resource: points to a Resource, in the trac environment, corresponding to this object. This is used, for example, in the workflow implementation. self.fields: points to an array of dictionary objects describing name, label, type and other properties of all of this object's fields. self.metadata: points to a dictionary object describing further meta-data about this object. Note: database tables for specific realms are supposed to already exist, this object does not create any tables. See below the GenericClassModelProvider to see how to declaratively create the required tables. """ def __init__(self, env, realm='variable_fields_obj', key=None, db=None): """ Creates an empty object and also tries to fetches it from the database, if an object with a matching key is found. To create an empty, template object, do not specify a key. To create an object to be later stored in the database: 1) specify a key at contruction time 2) set any other property via the obj['fieldname'] = value syntax, including custom fields 3) call the insert() method. To fetch an existing object from the database: 1) specify a key at contruction time: the object will be filled with all of the values form the database 2) modify any other property via the obj['fieldname'] = value syntax, including custom fields. This syntax is the only one to keep track of the changes to any field 3) call the save_changes() method. """ self.env = env self.exists = False self.realm = realm tmmodelprovider = GenericClassModelProvider(self.env) self.fields = tmmodelprovider.get_fields(realm) self.time_fields = [f['name'] for f in self.fields if f['type'] == 'time'] self.metadata = tmmodelprovider.get_metadata(realm) if key is not None and len(key) > 0: self.key = key self.resource = Resource(realm, self.gey_key_string()) else: self.resource = None if not key or not self._fetch_object(key, db): self._init_defaults(db) self.exists = False self.env.log.debug("Exists: %s" % self.exists) self.env.log.debug(self.values) self._old = {} def get_key_prop_names(self): """ Returns an array with the fields representing the identity of this object. The specified fields are assumed being also part of the self.fields array. The specified fields are also assumed to correspond to columns with same name in the database table. """ return ['id'] def get_key_prop_values(self): """ Returns an array of values for the properties returned by get_key_prop_names. """ result = [] for f in self.get_key_prop_names(): result.append(self.values[f]) return result def get_resource_id(self): """ Returns a string representation of the object's identity. Used with the trac Resource API. """ return [str(self.values[f])+'|' for f in self.get_key_prop_names()] def _init_defaults(self, db=None): """ Initializes default values for a new object, based on default values specified in the trac.ini file. """ for field in self.fields: default = None if field['name'] in self.protected_fields: # Ignore for new - only change through workflow pass elif not field.get('custom'): default = self.env.config.get(realm, 'default_' + field['name']) else: default = field.get('value') options = field.get('options') if default and options and default not in options: try: default = options[int(default)] except (ValueError, IndexError): self.env.log.warning('Invalid default value "%s" ' 'for custom field "%s"' % (default, field['name'])) if default: self.values.setdefault(field['name'], default) def _fetch_object(self, key, db=None): self.env.log.debug('>>> _fetch_object') if db is None: db = get_db(self.env, db) if not self.pre_fetch_object(db): return row = None # Fetch the standard fields std_fields = [f['name'] for f in self.fields if not f.get('custom')] cursor = db.cursor() sql_where = "WHERE 1=1" for k in self.get_key_prop_names(): sql_where += " AND " + k + "=%%s" self.env.log.debug("Searching for %s: %s" % (self.realm, sql_where)) for k in self.get_key_prop_names(): self.env.log.debug("%s = %s" % (k, self[k])) cursor.execute(("SELECT %s FROM %s " + sql_where) % (','.join(std_fields), self.realm), self.get_key_prop_values()) row = cursor.fetchone() if not row: #raise ResourceNotFound(_('The specified object of type %(realm)s does not exist.', # realm=self.realm), _('Invalid object key')) self.env.log.debug("Object NOT found.") return False self.env.log.debug("Object found.") self.key = self.build_key_object() for i, field in enumerate(std_fields): value = row[i] if field in self.time_fields: self.values[field] = from_any_timestamp(value) elif value is None: self.values[field] = '0' else: self.values[field] = value # Fetch custom fields if available custom_fields = [f['name'] for f in self.fields if f.get('custom')] cursor.execute(("SELECT name,value FROM %s_custom " + sql_where) % self.realm, self.get_key_prop_values()) for name, value in cursor: if name in custom_fields: if value is None: self.values[name] = '0' else: self.values[name] = value self.post_fetch_object(db) self.exists = True self.env.log.debug('<<< _fetch_object') return True def build_key_object(self): """ Builds and returns a dictionary object with the key properties, as returned by get_key_prop_names. """ key = None for k in self.get_key_prop_names(): if (self.values[k] is not None): if key is None: key = {} key[k] = self.values[k] return key def gey_key_string(self): """ Returns a JSON string with the object key properties """ return get_string_from_dictionary(self.key) def get_values_as_string(self, props): """ Returns a JSON string for the specified object properties. :param props: An array of field names. """ return get_string_from_dictionary(props, self.values) def __getitem__(self, name): """ Allows for using the syntax "obj['fieldname']" to access this object's values. """ return self.values.get(name) def __setitem__(self, name, value): """ Allows for using the syntax "obj['fieldname']" to access this object's values. Also logs object modifications so the table _change can be updated. """ if name in self.values: self.env.log.debug("Value before: %s" % self.values[name]) if name in self.values and self.values[name] == value: return if name not in self._old: # Changed field self.env.log.debug("Changing field value.") self._old[name] = self.values.get(name) elif self._old[name] == value: # Change of field reverted del self._old[name] if value: if isinstance(value, list): raise TracError(_("Multi-values fields not supported yet")) field = [field for field in self.fields if field['name'] == name] if field and field[0].get('type') != 'textarea': value = value.strip() self.values[name] = value self.env.log.debug("Value after: %s" % self.values[name]) def get_value_or_default(self, name): """ Return the value of a field or the default value if it is undefined """ try: value = self.values[name] if value is not '0': return value field = [field for field in self.fields if field['name'] == name] if field: return field[0].get('value', '') except KeyError: pass def populate(self, values): """ Populate the object with 'suitable' values from a dictionary """ field_names = [f['name'] for f in self.fields] for name in [name for name in values.keys() if name in field_names]: self[name] = values.get(name, '') # We have to do an extra trick to catch unchecked checkboxes for name in [name for name in values.keys() if name[9:] in field_names and name.startswith('checkbox_')]: if name[9:] not in values: self[name[9:]] = '0' def insert(self, when=None, db=None): """ Add object to database. Parameters: When: a datetime object to specify a creation date. The `db` argument is deprecated in favor of `with_transaction()`. """ self.env.log.debug('>>> insert') assert not self.exists, 'Cannot insert an existing object' db, handle_ta = get_db_for_write(self.env, db) # Add a timestamp if when is None: when = datetime.now(utc) self.values['time'] = self.values['changetime'] = when # Perform type conversions self.env.log.debug(' Performing type conversions') values = dict(self.values) for field in self.time_fields: if field in values: values[field] = to_any_timestamp(values[field]) # Insert record self.env.log.debug(' Getting fields') std_fields = [] custom_fields = [] for f in self.fields: fname = f['name'] if fname in self.values: if f.get('custom'): custom_fields.append(fname) else: std_fields.append(fname) if not self.pre_insert(db): return self.env.log.debug(' Inserting record') cursor = db.cursor() cursor.execute("INSERT INTO %s (%s) VALUES (%s)" % (self.realm, ','.join(std_fields), ','.join(['%s'] * len(std_fields))), [values[name] for name in std_fields]) # Insert custom fields key_names = self.get_key_prop_names() key_values = self.get_key_prop_values() if custom_fields: self.env.log.debug(' Inserting custom fields') cursor.executemany(""" INSERT INTO %s_custom (%s,name,value) VALUES (%s,%%s,%%s) """ % (self.realm, ','.join(key_names), ','.join(['%s'] * len(key_names))), [to_list((key_values, name, self[name])) for name in custom_fields]) self.post_insert(db) if handle_ta: db.commit() self.env.log.debug(' Setting up internal fields') self.exists = True self.resource = self.resource(id=self.get_resource_id()) self._old = {} self.env.log.debug(' Calling listeners') from tracgenericclass.api import GenericClassSystem for listener in GenericClassSystem(self.env).change_listeners: listener.object_created(self.realm, self) self.env.log.debug('<<< insert') return self.key def save_changes(self, author=None, comment=None, when=None, db=None, cnum=''): """ Store object changes in the database. The object must already exist in the database. Returns False if there were no changes to save, True otherwise. The `db` argument is deprecated in favor of `with_transaction()`. """ self.env.log.debug('>>> save_changes') assert self.exists, 'Cannot update a new object' if not self._old and not comment: return False # Not modified db, handle_ta = get_db_for_write(self.env, db) if when is None: when = datetime.now(utc) when_ts = to_any_timestamp(when) if not self.pre_save_changes(db): return cursor = db.cursor() # store fields custom_fields = [f['name'] for f in self.fields if f.get('custom')] key_names = self.get_key_prop_names() key_values = self.get_key_prop_values() sql_where = '1=1' for k in key_names: sql_where += " AND " + k + "=%%s" for name in self._old.keys(): if name in custom_fields: cursor.execute((""" SELECT * FROM %s_custom WHERE name=%%s AND """ + sql_where) % self.realm, to_list((name, key_values))) if cursor.fetchone(): cursor.execute((""" UPDATE %s_custom SET value=%%s WHERE name=%%s AND """ + sql_where) % self.realm, to_list((self[name], name, key_values))) else: cursor.execute(""" INSERT INTO %s_custom (%s,name,value) VALUES (%s,%%s,%%s) """ % (self.realm, ','.join(key_names), ','.join(['%s'] * len(key_names))), to_list((key_values, name, self[name]))) else: cursor.execute((""" UPDATE %s SET %s=%%s WHERE """ + sql_where) % (self.realm, name), to_list((self[name], key_values))) cursor.execute((""" INSERT INTO %s_change (%s, time,author,field,oldvalue,newvalue) VALUES (%s, %%s, %%s, %%s, %%s, %%s) """ % (self.realm, ','.join(key_names), ','.join(['%s'] * len(key_names)))), to_list((key_values, when_ts, author, name, self._old[name], self[name]))) self.post_save_changes(db) if handle_ta: db.commit() old_values = self._old self._old = {} self.values['changetime'] = when from tracgenericclass.api import GenericClassSystem for listener in GenericClassSystem(self.env).change_listeners: listener.object_changed(self.realm, self, comment, author, old_values) self.env.log.debug('<<< save_changes') return True def delete(self, db=None): """ Delete the object. Also clears the change history and the custom fields. The `db` argument is deprecated in favor of `with_transaction()`. """ self.env.log.debug('>>> delete') db, handle_ta = get_db_for_write(self.env, db) if not self.pre_delete(db): return #Attachment.delete_all(self.env, 'ticket', self.id, db) cursor = db.cursor() key_names = self.get_key_prop_names() key_values = self.get_key_prop_values() sql_where = 'WHERE 1=1' for k in key_names: sql_where += " AND " + k + "=%%s" self.env.log.debug("Deleting %s: %s" % (self.realm, sql_where)) for k in key_names: self.env.log.debug("%s = %s" % (k, self[k])) cursor.execute(("DELETE FROM %s " + sql_where) % self.realm, key_values) cursor.execute(("DELETE FROM %s_change " + sql_where) % self.realm, key_values) cursor.execute(("DELETE FROM %s_custom " + sql_where) % self.realm, key_values) self.post_delete(db) if handle_ta: db.commit() from tracgenericclass.api import GenericClassSystem for listener in GenericClassSystem(self.env).change_listeners: listener.object_deleted(self.realm, self) self.exists = False self.env.log.debug('<<< delete') def save_as(self, new_key, when=None, db=None): """ Saves (a copy of) the object with different key. The previous object is not deleted, so if needed it must be deleted explicitly. """ self.env.log.debug('>>> save_as') db, handle_ta = get_db_for_write(self.env, db) old_key = self.key if self.pre_save_as(old_key, new_key, db): self.key = new_key # Copy values from key into corresponding self.values field for f in self.get_key_prop_names(): self.values[f] = new_key[f] self.exists = False # Create object with new key self.insert(when, db) self.post_save_as(old_key, new_key, db) if handle_ta: db.commit() self.env.log.debug('<<< save_as') def get_non_empty_prop_names(self): """ Returns a list of names of the fields that are not None. """ std_field_names = [] custom_field_names = [] for field in self.fields: n = field.get('name') if n in self.values and self.values[n] is not None: if not field.get('custom'): std_field_names.append(n) else: custom_field_names.append(n) return std_field_names, custom_field_names def get_values(self, prop_names): """ Returns a list of the values for the specified properties, in the same order as the property names. """ result = [] for n in prop_names: result.append(self.values[n]) return result def set_values(self, props): """ Sets multiple properties into this object. Note: this method does not keep history of property changes. """ for n in props: self.values[n] = props[n] def _get_key_from_row(self, row): """ Given a database row with the key properties, builds a dictionary with this object's key. """ key = {} for i, f in enumerate(self.get_key_prop_names()): key[f] = row[i] return key def create_instance(self, key): """ Subclasses should override this method to create an instance of them with the specified key. """ pass def list_matching_objects(self, exact_match=True, db=None): """ List the objects that match the current values of this object's fields. To use this method, first create an instance with no key, then fill some of its fields with the values you want to find a match on, then call this method. A collection of objects found in the database matching the fields you had provided values for will be returned. An exact match, i.e. an SQL '=' operator, will be used, unless you specify exact_match=False, in which case the SQL 'LIKE' operator will be used. The `db` argument is deprecated in favor of `with_transaction()`. """ self.env.log.debug('>>> list_matching_objects') if db is None: db = get_db(self.env, db) self.pre_list_matching_objects(db) cursor = db.cursor() non_empty_std_names, non_empty_custom_names = self.get_non_empty_prop_names() non_empty_std_values = self.get_values(non_empty_std_names) non_empty_custom_values = self.get_values(non_empty_custom_names) operator = '=' if not exact_match: operator = ' LIKE ' sql_where = '1=1' for k in non_empty_std_names: sql_where += " AND " + k + operator + '%%s' cursor.execute(('SELECT %s FROM %s WHERE ' + sql_where) % (','.join(self.get_key_prop_names()), self.realm), non_empty_std_values) for row in cursor: key = self._get_key_from_row(row) self.env.log.debug('<<< list_matching_objects - returning result') yield self.create_instance(key) self.env.log.debug('<<< list_matching_objects') def get_search_results(self, req, terms, filters): """ Called in the context of the trac search API, to return a list of objects of this class matching the specified terms. Concrete classes should override this method to perform class-specific searches. """ if False: yield None # Following is a set of callbacks allowing subclasses to perform # actions around the operations that pertain the lifecycle of # this object. def pre_fetch_object(self, db): """ Use this method to perform initialization before fetching the object from the database. Return False to prevent the object from being fetched from the database. """ return True def post_fetch_object(self, db): """ Use this method to further fulfill your object after being fetched from the database. """ pass def pre_insert(self, db): """ Use this method to perform work before inserting the object into the database. Return False to prevent the object from being inserted into the database. """ return True def post_insert(self, db): """ Use this method to perform further work after your object has been inserted into the database. """ pass def pre_save_changes(self, db): """ Use this method to perform work before saving the object changes into the database. Return False to prevent the object changes from being saved into the database. """ return True def post_save_changes(self, db): """ Use this method to perform further work after your object changes have been saved into the database. """ pass def pre_delete(self, db): """ Use this method to perform work before deleting the object from the database. Return False to prevent the object from being deleted from the database. """ return True def post_delete(self, db): """ Use this method to perform further work after your object has been deleted from the database. """ pass def pre_save_as(self, old_key, new_key, db): """ Use this method to perform work before saving the object with a different identity into the database. Return False to prevent the object from being saved into the database. """ return True def post_save_as(self, old_key, new_key, db): """ Use this method to perform further work after your object has been saved into the database. """ pass def pre_list_matching_objects(self, db): """ Use this method to perform work before finding matches in the database. Return False to prevent the search. """ return True class AbstractWikiPageWrapper(AbstractVariableFieldsObject): """ This subclass is a generic object that is based on a wiki page, identified by the 'page_name' field. The wiki page lifecycle is managed along with the normal object's one. """ def __init__(self, env, realm='wiki_wrapper_obj', key=None, db=None): AbstractVariableFieldsObject.__init__(self, env, realm, key, db) def post_fetch_object(self, db): self.wikipage = WikiPage(self.env, self.values['page_name']) def delete(self, del_wiki_page=True, db=None): """ Delete the object. Also deletes the Wiki page if so specified in the parameters. The `db` argument is deprecated in favor of `with_transaction()`. """ # The actual wiki page deletion is delayed until pre_delete. self.del_wiki_page = del_wiki_page AbstractVariableFieldsObject.delete(self, db) def pre_insert(self, db): """ Assuming the following fields have been given a value before this call: text, author, remote_addr, values['page_name'] """ wikipage = WikiPage(self.env, self.values['page_name']) wikipage.text = self.text wikipage.save(self.author, '', self.remote_addr) self.wikipage = wikipage return True def pre_save_changes(self, db): """ Assuming the following fields have been given a value before this call: text, author, remote_addr, values['page_name'] """ wikipage = WikiPage(self.env, self.values['page_name']) wikipage.text = self.text wikipage.save(self.author, '', self.remote_addr) print("YEEEES!!!") self.wikipage = wikipage return True def pre_delete(self, db): """ Assuming the following fields have been given a value before this call: values['page_name'] """ if self.del_wiki_page: wikipage = WikiPage(self.env, self.values['page_name']) wikipage.delete() self.wikipage = None return True def get_search_results(self, req, terms, filters): """ Currently delegates the search to the Wiki module. """ for result in WikiModule(self.env).get_search_results(req, terms, ('wiki',)): yield result class GenericClassModelProvider(Component): """ This class provides a factory for generic classes and derivatives. The actual data model on the db is created starting from the SCHEMA declaration below. For each table, we specify whether to create also a '_custom' and a '_change' table. This class also provides the specification of the available fields for each class, being them standard fields and the custom fields specified in the trac.ini file. The custom field specification follows the same syntax as for Tickets. Currently, only 'text' type of fields are supported. """ class_providers = ExtensionPoint(IConcreteClassProvider) all_fields = {} all_custom_fields = {} all_metadata = {} _class_providers_map = None # Class providers managament def get_class_provider(self, realm): """ Return the component responsible for providing the specified concrete class implementation. :param realm: the realm which uniquely identifies the class. :return: a `Component` implementing `IConcreteClassProvider` or `None` """ # build a dict of realm keys to IConcreteClassProvider # implementations if not self._class_providers_map: map = {} for provider in self.class_providers: for r in provider.get_realms() or []: self.env.log.debug("Mapping realm %s to provider %s" % (r, provider)) map[r] = provider self._class_providers_map = map if realm in self._class_providers_map: return self._class_providers_map.get(realm) else: return None def get_known_realms(self): """ Return a list of all the realm names of registered class providers. """ realms = [] for provider in self.class_providers: for realm in provider.get_realms() or []: realms.append(realm) return realms # Factory method def get_object(self, realm, key=None): """ Returns an instance of the specified class (by means of its realm name), with the specified key. """ obj = None provider = self.get_class_provider(realm) self.env.log.debug("Provider for realm %s is %s" % (realm, provider)) if provider: self.env.log.debug("Object key is %s" % key) return provider.create_instance(realm, key) else: self.env.log.debug("Provider for realm %s not found" % realm) return None # Permission check def check_permission(self, req, realm, key_str=None, operation='set', name=None, value=None): """ Checks whether the logged in User has permission to perform the specified operation on a resource of the specified realm and optionally with the specified key. Raise an exception if authorization is denied. Actually delegates to the concrete class provider the permission check. See the IConcreteClassProvider method with the same name for more details about the available operations and the function parameters. """ provider = self.get_class_provider(realm) if provider is not None: provider.check_permission(req, realm, key_str, operation, name, value) # Fields management def reset_fields(self): """ Invalidate field cache. """ self.all_fields = {} def get_fields(self, realm): self.env.log.debug(">>> get_fields") if realm not in self.fields(): raise TracError("Requested field information not found for class %s." % realm) fields = copy.deepcopy(self.fields()[realm]) #label = 'label' # workaround gettext extraction bug #for f in fields: # f[label] = gettext(f[label]) self.env.log.debug("<<< get_fields") return fields def get_metadata(self, realm): tmp_metadata = self.metadata() if realm in tmp_metadata: metadata = copy.deepcopy(tmp_metadata[realm]) else: metadata = None return metadata def fields(self, refresh=False): """Return the list of fields available for every realm.""" if refresh or not self.all_fields: fields = {} for provider in self.class_providers: realm_fields = provider.get_fields() for realm in realm_fields: tmp_fields = realm_fields[realm] # Print debug information about all known realms and fields self.env.log.debug(">>> PRIMA") self.env.log.debug("Fields for realm %s:" % realm) for f in tmp_fields: self.env.log.debug(" %s : %s" % (f['name'], f['type'])) if 'custom' in f: self.env.log.debug(" (custom)") self.env.log.debug("<<< PRIMA") self.append_custom_fields(tmp_fields, self.get_custom_fields_for_realm(realm)) # Print debug information about all known realms and fields self.env.log.debug(">>> DOPO") self.env.log.debug("Fields for realm %s:" % realm) for f in tmp_fields: self.env.log.debug(" %s : %s" % (f['name'], f['type'])) if 'custom' in f: self.env.log.debug(" (custom)") self.env.log.debug("<<< DOPO") fields[realm] = tmp_fields self.all_fields = fields # Print debug information about all known realms and fields for r in self.all_fields: self.env.log.debug("Fields for realm %s:" % r) for f in self.all_fields[r]: self.env.log.debug(" %s : %s" % (f['name'], f['type'])) if 'custom' in f: self.env.log.debug(" (custom)") return self.all_fields def metadata(self): """Return metadata information about concrete classes.""" if not self.all_metadata: metadata = {} for provider in self.class_providers: realm_metadata = provider.get_metadata() for realm in realm_metadata: metadata[realm] = realm_metadata[realm] self.all_metadata = metadata return self.all_metadata def append_custom_fields(self, fields, custom_fields): if len(custom_fields) > 0: for f in custom_fields: fields.append(f) def get_custom_fields_for_realm(self, realm): fields = [] for field in self.get_custom_fields(realm): field['custom'] = True fields.append(field) return fields def get_custom_fields(self, realm): return copy.deepcopy(self.custom_fields(realm)) def custom_fields(self, realm, refresh=False): """Return the list of available custom fields.""" if refresh or not realm in self.all_custom_fields: fields = [] config = self.config[realm+'-tm_custom'] self.env.log.debug(config.options()) for name in [option for option, value in config.options() if '.' not in option]: if not re.match('^[a-zA-Z][a-zA-Z0-9_]+$', name): self.log.warning('Invalid name for custom field: "%s" ' '(ignoring)', name) continue self.env.log.debug(" Option: %s" % name) field = { 'name': name, 'type': config.get(name), 'order': config.getint(name + '.order', 0), 'label': config.get(name + '.label') or name.capitalize(), 'value': config.get(name + '.value', '') } if field['type'] == 'select' or field['type'] == 'radio': field['options'] = config.getlist(name + '.options', sep='|') if '' in field['options']: field['optional'] = True field['options'].remove('') elif field['type'] == 'text': field['format'] = config.get(name + '.format', 'plain') elif field['type'] == 'textarea': field['format'] = config.get(name + '.format', 'plain') field['width'] = config.getint(name + '.cols') field['height'] = config.getint(name + '.rows') fields.append(field) fields.sort(lambda x, y: cmp(x['order'], y['order'])) self.all_custom_fields[realm] = fields return self.all_custom_fields[realm] # Methods to help components create their databases def need_db_upgrade(env, schema, db): """ Call this method from inside your Component IEnvironmentSetupParticipant's environment_needs_upgrade() function to check whether your Component using the generic classes needs to create the corresponding database tables. :param schema: The db schema definition, as returned by the get_data_models() function in the IConcreteClassProvider interface. """ cursor = db.cursor() try: for realm in schema: table_metadata = schema[realm] tablem = table_metadata['table'] tname = tablem.name cursor.execute("select count(*) from %s" % tname) cursor.fetchone() return False except: db.rollback() env.log.debug("Need to create db tables for class '%s'." % realm) return True def upgrade_db(env, schema, db): """ Call this method from inside your Component IEnvironmentSetupParticipant's upgrade_environment() function to create the database tables corresponding to your Component's generic classes. :param schema: The db schema definition, as returned by the get_data_models() function in the IConcreteClassProvider interface. """ try: try: from trac.db import DatabaseManager db_backend, _ = DatabaseManager(env)._get_connector() except ImportError: db_backend = env.get_db_cnx() env.log.debug("Upgrading DB...") # Create the required tables cursor = db.cursor() for realm in schema: table_metadata = schema[realm] tablem = table_metadata['table'] tname = tablem.name key_names = [k for k in tablem.key] # Create base table env.log.debug("Creating base table %s..." % tname) for stmt in db_backend.to_sql(tablem): env.log.debug(stmt) cursor.execute(stmt) # Create custom fields table if required if table_metadata['has_custom']: cols = [] for k in key_names: # Determine type of column k type = 'text' for c in tablem.columns: if c.name == k: type = c.type cols.append(Column(k, type=type)) cols.append(Column('name')) cols.append(Column('value')) custom_key = copy.deepcopy(key_names) custom_key.append('name') table_custom = Table(tname+'_custom', key = custom_key)[cols] env.log.debug("Creating custom properties table %s..." % table_custom.name) for stmt in db_backend.to_sql(table_custom): env.log.debug(stmt) cursor.execute(stmt) # Create change history table if required if table_metadata['has_change']: cols = [] for k in key_names: # Determine type of column k type = 'text' for c in tablem.columns: if c.name == k: type = c.type cols.append(Column(k, type=type)) cols.append(Column('time', type='int64')) cols.append(Column('author')) cols.append(Column('field')) cols.append(Column('oldvalue')) cols.append(Column('newvalue')) cols.append(Index(key_names)) change_key = copy.deepcopy(key_names) change_key.append('time') change_key.append('field') table_change = Table(tname+'_change', key = change_key)[cols] env.log.debug("Creating change history table %s..." % table_change.name) for stmt in db_backend.to_sql(table_change): env.log.debug(stmt) cursor.execute(stmt) db.commit() except: env.log.debug(formatExceptionInfo()) env.log.debug("Exception during database creation.") db.rollback() raise testman4trac.1.4.5/tracgenericclass/trunk/tracgenericclass/util.py0000755000175000017500000001047211565433702025404 0ustar robertoroberto# -*- coding: utf-8 -*- # # Copyright (C) 2010 Roberto Longobardi # import os import re import shutil import sys import traceback from datetime import datetime from trac.core import * def formatExceptionInfo(maxTBlevel=5): cla, exc, trbk = sys.exc_info() excName = cla.__name__ try: excArgs = exc.__dict__["args"] except KeyError: excArgs = "" excTb = traceback.format_tb(trbk, maxTBlevel) return (excName, excArgs, excTb) checked_utimestamp = False has_utimestamp = False checked_compatibility = False has_read_db = False def to_any_timestamp(date_obj): global checked_utimestamp global has_utimestamp if not checked_utimestamp: check_utimestamp() if has_utimestamp: from trac.util.datefmt import to_utimestamp return to_utimestamp(date_obj) else: # Trac 0.11 from trac.util.datefmt import to_timestamp return to_timestamp(date_obj) def from_any_timestamp(ts): global checked_utimestamp global has_utimestamp if not checked_utimestamp: check_utimestamp() if has_utimestamp: from trac.util.datefmt import from_utimestamp return from_utimestamp(ts) else: # Trac 0.11 from trac.util.datefmt import utc return datetime.fromtimestamp(ts, utc) def get_db(env, db=None): global checked_compatibility global has_read_db if db: return db if not checked_compatibility: check_compatibility(env) if has_read_db: return env.get_read_db() else: # Trac 0.11 return env.get_db_cnx() def get_db_for_write(env, db=None): global checked_compatibility global has_read_db if db: return (db, True) if not checked_compatibility: check_compatibility(env) if has_read_db: return (env.get_read_db(), True) else: # Trac 0.11 return (env.get_db_cnx(), True) def check_utimestamp(): global checked_utimestamp global has_utimestamp try: from trac.util.datefmt import to_utimestamp, from_utimestamp has_utimestamp = True except: # Trac 0.11 has_utimestamp = False checked_utimestamp = True def check_compatibility(env): global checked_compatibility global has_read_db try: if env.get_read_db(): has_read_db = True except: # Trac 0.11 has_read_db = False checked_compatibility = True def to_list(params=[]): result = [] for i in params: if isinstance(i, list): for v in i: result.append(v) else: result.append(i) return tuple(result) def get_dictionary_from_string(str): result = {} sub = str.partition('{')[2].rpartition('}')[0] tokens = sub.split(",") for tok in tokens: name = remove_quotes(tok.partition(':')[0]) value = remove_quotes(tok.partition(':')[2]) result[name] = value return result def get_string_from_dictionary(dictionary, values=None): if values is None: values = dictionary result = '{' for i, k in enumerate(dictionary): result += "'"+k+"':'"+values[k]+"'" if i < len(dictionary)-1: result += "," result += '}' return result def remove_quotes(str, quote='\''): return str.partition(quote)[2].rpartition(quote)[0] def compatible_domain_functions(domain, function_name_list): return lambda x: x, lambda x: x, lambda x: x, lambda x: x def upload_file_to_subdir(env, req, subdir, param_name, target_filename): upload = param_name if isinstance(upload, unicode) or not upload.filename: raise TracError('You must provide a file.') txt_filename = upload.filename.replace('\\', '/').replace(':', '/') txt_filename = os.path.basename(txt_filename) if not txt_filename: raise TracError('You must provide a file.') target_dir = os.path.join(env.path, 'upload', subdir) if not os.access(target_dir, os.F_OK): os.makedirs(target_dir) target_path = os.path.join(target_dir, target_filename) try: target_file = open(target_path, 'w') shutil.copyfileobj(upload.file, target_file) finally: target_file.close() testman4trac.1.4.5/tracgenericclass/trunk/setup.py0000755000175000017500000000245711565425160022256 0ustar robertorobertofrom setuptools import setup setup( name='TracGenericClass', version='1.0.5', packages=['tracgenericclass'], package_data={'tracgenericclass' : ['*.txt', 'templates/*.html', 'htdocs/*.*', 'htdocs/js/*.js', 'htdocs/css/*.css', 'htdocs/images/*.*']}, author = 'Roberto Longobardi', author_email='seccanj@gmail.com', license='BSD. See the file LICENSE.txt contained in the package.', url='http://trac-hacks.org/wiki/TestManagerForTracPlugin', download_url='https://sourceforge.net/projects/testman4trac/files/', description='Test management plugin for Trac - Trac Generic Class component', long_description='A Trac plugin to create Test Cases, organize them in catalogs and track their execution status and outcome. This module provides a framework to help creating classes on Trac that: are persisted on the DB, support change history, Support extensibility through custom properties that the User can specify declaratively in the trac.ini file. Also provides an intermediate class to build objects that wrap Wiki pages, plus additional properties.', keywords='trac plugin generic class framework persistence test case management project quality assurance statistics stats charts charting graph', entry_points = {'trac.plugins': ['tracgenericclass = tracgenericclass']} ) testman4trac.1.4.5/package.cmd0000755000175000017500000000111511565162323016132 0ustar robertorobertoset VER=%1 zip -r testman4trac.%VER%.zip bin mkdir testman4trac.%VER% xcopy /y *.sh testman4trac.%VER% xcopy /y *.cmd testman4trac.%VER% xcopy /y *.txt testman4trac.%VER% xcopy /y /s /i sqlexecutor testman4trac.%VER%\sqlexecutor xcopy /y /s /i testman4trac testman4trac.%VER%\testman4trac xcopy /y /s /i tracgenericclass testman4trac.%VER%\tracgenericclass xcopy /y /s /i tracgenericworkflow testman4trac.%VER%\tracgenericworkflow cd testman4trac.%VER% call clean.cmd cd .. zip -r testman4trac.%VER%.src.zip testman4trac.%VER% rmdir /s /q testman4trac.%VER%